Slide 1

Slide 1 text

ReactベースのSPA開発で 開発者が気をつけたいSEOのお話 株式会社クラウドポート IT・業務管理部⻑/エンジニア 若松 慶信

Slide 2

Slide 2 text

Introduction 2019/8/21 2

Slide 3

Slide 3 text

⾃⼰紹介 株式会社クラウドポート IT・業務管理部⻑/エンジニア 若松 慶信 2019/8/21 3 • クラウドポートのFundsではフロントエンド含む開発・運⽤全般を担当 • SEOについては前職で経験

Slide 4

Slide 4 text

資産形成をしたい個⼈とお⾦を借りたい企業を結ぶ、 「貸付ファンド」のオンラインマーケット 第⼆種⾦融商品取引業者のクラウドポートが運営

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

2019/8/21 6 今回のテーマについて

Slide 7

Slide 7 text

2019/8/21 7 Reactなどを使うモダンなフロントエンド開発での SEOに関する情報は少ない・・・

Slide 8

Slide 8 text

FundsでのReactの使い⽅を SEOという視点からお話します 2019/8/21 8 ※SEOの話多めです

Slide 9

Slide 9 text

SEOとフロントエンドエンジニアの役割 2019/8/21 9

Slide 10

Slide 10 text

SEO(検索エンジン最適化)とは Webサイトが検索エンジンで⾒つかりやすくすること 2019/8/21 10

Slide 11

Slide 11 text

SEOに取り組む上での基本的な考え⽅ • ベストエフォートで取り組み、結果が出るのを待つ • 銀の弾丸はない 2019/8/21 11

Slide 12

Slide 12 text

今回はフロントエンドエンジニアの役割にフォーカスした話をします 2019/8/21 12

Slide 13

Slide 13 text

SEOにおけるフロントエンドエンジニアの役割 クローラーが正しくWebサイトを解釈できるようにし サイトが正当な評価を受けられるようにする 2019/8/21 13

Slide 14

Slide 14 text

「正当な評価を受けられる」とは︖ 2019/8/21 14

Slide 15

Slide 15 text

正当な評価を受けられないケースの例 • クローラーが正しくレンダリングできない • ページ間の関係性をリンクで伝えられていない • 複数のURLで同じコンテンツを表⽰できてしまう • 存在しないページなのに、200でコンテンツが表⽰できてしまう 2019/8/21 15

Slide 16

Slide 16 text

SEOにおけるフロントエンドエンジニアの役割 クローラーが正しくWebサイトを解釈できるようにし サイトが正当な評価を受けられるようにする 2019/8/21 16

Slide 17

Slide 17 text

SEOにおけるフロントエンドエンジニアの役割 クローラーが正しくWebサイトを解釈できるようにし サイトが正当な評価を受けられるようにする 2019/8/21 17 エンジニアの考慮不⾜で機会損失を⽣まないことが重要

Slide 18

Slide 18 text

SEOを考慮したレンダリング⼿法の選択 2019/8/21 18

Slide 19

Slide 19 text

レンダリング (今回の⽂脈では) HTMLとJavaScriptからブラウザ上へ描画するための DOMツリーを構築すること 2019/8/21 19

Slide 20

Slide 20 text

レンダリング 正しくレンダリングさせることは、正当な評価を受ける必要条件の1つ 2019/8/21 20

Slide 21

Slide 21 text

Googlebotのレンダリング能⼒ • (〜2019/5)Chrome 41 程度のレンダリング能⼒ • ES6などのシンタックスは未サポートでpolyfillが必要 • (2019/5〜現在)最新のChromium エンジンを適⽤可能に • ES6などにも対応 • クローラー向けのpolyfillは不要になっている 2019/8/21 21

Slide 22

Slide 22 text

2019/8/21 22

Slide 23

Slide 23 text

SPAの主なレンダリング⽅式 • CSR (Client Side Rendering) • ブラウザ上でJavaScriptによりレンダリングする⽅式 • SSG (Static Site Generator) • ビルド時に静的なHTMLを出⼒する⽅式 • Dynamic Rendering • ⼀般のユーザー向けにはCSRを適⽤する • クローラー向けの配信に限りレンダリング済みの結果を返す • SSR (Server Side Rendering) • コンテンツ配信時にサーバサイドでレンダリングする⽅式 • Rehydration により継続して CSR でレンダリングできるようにすることも多い • ⾯倒だが確実性はある 2019/8/21 23

Slide 24

Slide 24 text

SSRは必要か︖ • レンダリングの問題に限ればSSRは必須ではない • SSRでなければ対応できない問題もある 2019/8/21 24

Slide 25

Slide 25 text

SSRでなければできないこと • ステータスコードを適切に返す • ページ単位のURL正規化/ソフト404エラーの防⽌に必要 • メタ要素 (title, meta, link タグなど)を確実に認識させる • URL正規化で使⽤する rel=canonical はSSR以外では解釈されない • その他のメタ要素もサーバサイドでレンダリングして返すことが望ましい • 頻繁に更新されるコンテンツ • JavaScriptを使⽤するレンダリングは遅い(次に説明) • これはDynamic Renderingでも対応できる 2019/8/21 25

Slide 26

Slide 26 text

引⽤元: https://developers.google.com/search/docs/guides/javascript-seo-basics GoogleがJavaScriptをレンダリングするプロセス JavaScriptのレンダリングには このプロセスが追加で必要 レンダリングされるまで時間を要する 2019/8/21 26

Slide 27

Slide 27 text

SEOを考慮したレンダリング⼿法の選択 ⾃然検索流⼊が重要な集客チャネルかどうか︖に注⽬する 2019/8/21 27

Slide 28

Slide 28 text

SEOを考慮したレンダリング⼿法の選択 ⾃然検索流⼊が重要 ⾃然検索流⼊はあまり重要ではない サイトの例 • Webメディア • ECサイト/マーケットプレイス • ポータルサイト • コーポレートサイト • 会員登録が必須のサイト • 他の集客チャネルがメインのサイト 採⽤する アプローチ SSRを採⽤することが望ましい SSRを採⽤するかはUX次第 2019/8/21 28

Slide 29

Slide 29 text

Fundsの集客チャネルと要件 ⾃然検索流⼊が重要 ⾃然検索流⼊はあまり重要ではない サイトの例 • Webメディア • ECサイト/マーケットプレイス • ポータルサイト • コーポレートサイト • 会員登録が必須のサイト • 他の集客チャネルがメインのサイト 採⽤する アプローチ SSRを採⽤することが望ましい SSRを採⽤するかはUX次第 2019/8/21 29 ⾃然検索流⼊は主要な集客チャネルではないが、機会損失がないようにしたい + UXも考慮してSSRを採⽤

Slide 30

Slide 30 text

Fundsのフロントエンド概要 2019/8/21 30

Slide 31

Slide 31 text

Fundsのフロントエンド概要 Service app (JavaScript on Browser) Service API (Scala) Service app (Node.js) on Server on Browser ClientとServerの双⽅で動作するIsomorphicなアプリケーション 2019/8/21 31

Slide 32

Slide 32 text

Fundsのフロントエンド概要 • ES6 + Flow(TypeScript移⾏したい) • React + Redux • WebpackでPC⽤, SP⽤, SSR⽤に3種類のビルドを⽣成 • Node.js + Express での SSR(Next.js など使⽤せず) 2019/8/21 32

Slide 33

Slide 33 text

3種類のビルド⽣成 • 単⼀のコードベースからPC⽤とSP⽤のビルドを⽣成 • またNode.jsでの実⾏⽤にSSR⽤のビルドを別に⽣成 source code build (PC) build (SP) build (server) webpack 2019/8/21 33

Slide 34

Slide 34 text

clientではPC/SPでentrypointを分離 (webpack.client.config.js) client/server⽤の設定 (webpack.config.js) 3種類のビルド⽣成: Webpackの設定(抜粋) 2019/8/21 34 const client = require('./webpack.client.config’); const server = require('./webpack.server.config’); module.exports = [client, server]; entry: { appPc: [ './src/funds_pc.jsx’, ], appSp: [ './src/funds_sp.jsx’, ], }, …

Slide 35

Slide 35 text

Dynamic Serving(動的な配信) PC・SPで異なるコンテンツを表⽰する⽅式 Server Browser (PC) Browser (SP) HTML (for SP) request request HTML (for PC) 2019/8/21 35

Slide 36

Slide 36 text

Dynamic Serving(動的な配信) UserAgent⽂字列を識別して、PC/SPのレンダリング内容を分離 // モバイル判定 const isMobile = (req) => { const md = new MobileDetect(req.headers['user-agent']); return !!md.mobile() && !md.tablet(); }; // モバイル判定結果に応じてレンダリングするコンポーネントを変更 {isMobile(req) ? : } 2019/8/21 36

Slide 37

Slide 37 text

ReactでのSEOを加味したアプローチ例 2019/8/21 37

Slide 38

Slide 38 text

エンジニアが注意すべきSEO上のポイントの⼀例 • レンダリング • 正しく有効なマークアップ • ステータスコードの制御 • URL正規化 • ページの表⽰速度 • モバイルフレンドリー化 • 構造化データ (ほかにもあるよ) 2019/8/21 38

Slide 39

Slide 39 text

今回お話するトピック • レンダリング • 正しく有効なマークアップ • ステータスコードの制御 • URL正規化 • ページの表⽰速度 • モバイルフレンドリー化 • 構造化データ 2019/8/21 39

Slide 40

Slide 40 text

今回お話するトピック • レンダリング • 正しく有効なマークアップ • ステータスコードの制御 • URL正規化 • ページの表⽰速度 • モバイルフレンドリー化 • 構造化データ 2019/8/21 40

Slide 41

Slide 41 text

レンダリング • FundsではReactでSSR • SSR時のstateをクライアント側でRehydrationし CSRできるようにしている 2019/8/21 41

Slide 42

Slide 42 text

SSR with Rehydration FundsではReactのSSRにRehydrationでCSRを組み合わせています Browser Server state state API ②更新されたredux stateで renderToString ①レンダリングに 必要な情報取得 ③クライアントサイドに stateを引き継ぎ ④ブラウザ上で差分更新 2019/8/21 42

Slide 43

Slide 43 text

SSR with Rehydration // react-redux の Providerでreactとredux stateを結合 const provider = ( ... ); // サーバサイドでレンダリング const renderedContent = renderToString(provider); ... // レンダリングしたコンテンツを埋め込み
${renderedContent}
②更新されたredux stateを注⼊しrenderToString on Server 2019/8/21 43

Slide 44

Slide 44 text

SSR with Rehydration // state はrenderToStringで使⽤したRedux State const state = store.getState(); const serializedState = JSON.stringify(state); // SSRでレンダリングしておきクライアントサイドで利⽤ var __CWP_INITIAL_STORE=${serializedState}; // クライアントサイドでは以下のようにロード const store = configureStore(window.__CWP_INITIAL_STORE, history); ③クライアントサイドにstateを引き継ぎ on Server on Browser 2019/8/21 44

Slide 45

Slide 45 text

SSR with Rehydration FundsではReactのSSRにRehydrationでCSRを組み合わせています Browser Server state state API ②更新されたredux stateで renderToString ①レンダリングに 必要な情報取得 ③クライアントサイドに stateを引き継ぎ ④ブラウザ上で差分更新 2019/8/21 45

Slide 46

Slide 46 text

今回お話するトピック • レンダリング • 正しく有効なマークアップ • ステータスコードの制御 • URL正規化 • ページの表⽰速度 • モバイルフレンドリー化 • 構造化データ 2019/8/21 46

Slide 47

Slide 47 text

ソフト404エラー • ステータスコード200で、エラー⾵のページを表⽰している状態 • クローラーは404ページであることを認識できない 404 HTTP上では・・・ HTTP/2 200 Content-Encoding: gzip Accept-Ranges: bytes Cache-Control: max-age=604800 Content-Type: text/html; charset=UTF-8 … 2019/8/21 47

Slide 48

Slide 48 text

SPA固有の404ページに関する問題 SPAでは⼀度200で返したHTML・JavaScriptから CSRでレンダリングするため、ステータスコードを404に設定できない 404 CSR前のページを表⽰ この時点で200 ⾒た⽬が404⾵のページをレンダリング 実際はステータスコード200 2019/8/21 48

Slide 49

Slide 49 text

Fundsの404ページ ステータスコード404で404ページを表⽰(あたりまえですが・・・) 2019/8/21 49

Slide 50

Slide 50 text

Fundsの404ページ ステータスコードに対応するフィールドをredux state上に定義 SSR時にredux stateを参照しステータスコードを変更する ステータス変更⽤のredux action⽣成 ステータスコードに対応するフィールドを更新 SSR時にstateからステータスコードを変更 Fundsで404ページを表⽰する場合の流れ 2019/8/21 50

Slide 51

Slide 51 text

今回お話するトピック • レンダリング • 正しく有効なマークアップ • ステータスコードの制御 • URL正規化 • ページの表⽰速度 • モバイルフレンドリー化 • 構造化データ 2019/8/21 51

Slide 52

Slide 52 text

重複コンテンツとURL制御 複数のURLで同じコンテンツにアクセス可能だと、 本来得られるべき適切な評価が得られない ページ ページ /cat /cat/hoge ページ /cat/fuga ミスなのか、本当に表⽰したいページが複数あるのかわからない 2019/8/21 52

Slide 53

Slide 53 text

重複コンテンツとURL制御 まずは正しいURL以外でアクセスできないようにするのが原則 ページ ページ /cat /cat/hoge ページ /cat/fuga 2019/8/21 53

Slide 54

Slide 54 text

重複コンテンツとURL制御 • react-router でURLに対応するコンポーネントを選択 • できる限りexactを指定 import { Route, Switch } from 'react-router’; return (

Slide 55

Slide 55 text

URL正規化 クエリパラメータの有無など、複数のURLでアクセス可能になる場合もある ページ /cat/1 ページ /cat/1?foo=1 2019/8/21 55

Slide 56

Slide 56 text

URL正規化 ステータスコード 301でリダイレクト ページ /cat/1 ページ /cat/1?foo=1 正しいURLの場所をHTTP Responseで明⽰ リソースが直接参照できるURLを1つに限定する(URL正規化) HTTP/1.1 301 Location: /cat/1 2019/8/21 56

Slide 57

Slide 57 text

rel=canonical によるURL正規化 301リダイレクトの代わりに rel=canonical で正しいURLを指定することで評価の分散を防⽌ ページ /cat/1 ページ /cat/1?foo=1 クローラーは /cat/1 の⽅をインデックスすべきURLと解釈し /cat/1?foo=1 はインデックスしない 2019/8/21 57

Slide 58

Slide 58 text

react-helmetでのメタ情報設定 // Helmet (react-helmet) import Helmet from 'react-helmet’; ... return ( で定義すると ⾃動的に 内へ含めてくれる 2019/8/21 58

Slide 59

Slide 59 text

rel=canonical によるURL正規化 rel=canonicalを指定する場合も react-helmet を使⽤ // Helmet (react-helmet) import Helmet from 'react-helmet’; ... return (

Slide 60

Slide 60 text

react-helmet で設定可能なメタ要素の例 • title • meta description • SERPs(検索結果のページ)に表⽰される内容には影響する • meta robots • OGP • og:image など。Facebookなどでのシェアの際に有効 • Twitter Cards • Twitterでのシェアの際の表⽰ • rel=canonical • URL正規化で使⽤ このあたりの指定は SSR or Dynamic Renderingが必要 2019/8/21 60

Slide 61

Slide 61 text

まとめ 公開サイトを開発するフロントエンドエンジニアなら SEOについても知っておこう 2019/8/21 61