Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ReactベースのSPA開発で開発者が気をつけたいSEOのお話

 ReactベースのSPA開発で開発者が気をつけたいSEOのお話

【シューマイ】Tech Lead Engineerから最新技術を学べ!React編
https://shuuu-mai.connpass.com/event/140002/

Yoshinobu Wakamatsu

August 21, 2019
Tweet

More Decks by Yoshinobu Wakamatsu

Other Decks in Technology

Transcript

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

    View full-size slide

  2. Introduction
    2019/8/21 2

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide


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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  21. 2019/8/21 22

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  33. 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’,
    ],
    },

    View full-size slide

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

    View full-size slide

  35. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  42. 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

    View full-size slide

  43. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  46. ソフト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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    exact // exact を指定することで /cat/hoge などはマッチしない
    path="/cat”
    component={CatPage}
    />
    ...
    2019/8/21 54

    View full-size slide

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

    View full-size slide

  55. 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

    View full-size slide

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

    2019/8/21 57

    View full-size slide

  57. react-helmetでのメタ情報設定
    // Helmet (react-helmet)
    import Helmet from 'react-helmet’;
    ...
    return (
    title=“ファンドを探す”
    meta={[
    {
    name: 'description’,
    content: 'Funds(ファンズ)のファンド⼀覧ページです。',
    },
    react-helmet はメタ要素を挿⼊するためのライブラリ
    で定義すると
    ⾃動的に 内へ含めてくれる
    2019/8/21 58

    View full-size slide

  58. rel=canonical によるURL正規化
    rel=canonicalを指定する場合も react-helmet を使⽤
    // Helmet (react-helmet)
    import Helmet from 'react-helmet’;
    ...
    return (
    link={[
    {
    ref: ‘canonical’,
    href: ‘https://funds.jp/fund/list',
    },
    2019/8/21 59

    View full-size slide

  59. 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

    View full-size slide

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

    View full-size slide