純SPAでNext.jsに対抗する 〜ページによってmetaタグを切り替える編〜
by
kiyoshiro
×
Copy
Open
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Slide 1
Slide 1 text
純SPAでNext.jsに対抗する 〜ページによってmetaタグを切り替える編〜 清川 航⼀
Slide 2
Slide 2 text
© DMM.com 自己紹介 2 名前 清川航一 (𝕏 @kiyoshiro944) おしごと おもにフロントエンド(React/TypeScript) バックエンドも書く(Golang) 所属 ポイントクラブチーム 新卒2年目
Slide 3
Slide 3 text
© DMM.com 前提・背景 3
Slide 4
Slide 4 text
© DMM.com 前提:用語の定義 • SPA • ※人によって定義が違う😇 • CSRしかしていないアプリ • CSR / SSR / SGを1つ以上組み合わせたアプリ • 純SPA • CSRしかしていないアプリ • 例:Create React AppやViteで作ったアプリ
Slide 5
Slide 5 text
© DMM.com • 2年くらい純SPAでフロントをつくっていた • パフォーマンス・SEOが必要になったため、今年の7月に2人で1ヶ月 かけ てNext.jsへ移行した • 「純SPAのままNext.jsに対抗する」方法も考えていた • そのアイデアを少し一般化してこの発表で供養 背景:ポイントクラブでは
Slide 6
Slide 6 text
© DMM.com 純SPAの課題 6
Slide 7
Slide 7 text
© DMM.com (色々あるけど、今回取り上げるのは) 「ページによってmetaタグを切り替える」 metaタグの例: • Next.jsではページごとにHTMLを生成するのが簡単 /users → users.html /posts → posts.html • 純SPAだと、基本的にページによらずひとつのindex.htmlを使う /users → index.html /posts → index.html 純SPAだとムズいこと
Slide 8
Slide 8 text
© DMM.com 純SPAの課題に直面する例 Webアプリケーションのフロントエンドを開発することになりました とくにSEOや初回表示速度が重要なわけでもないので、純SPAでつくることに ✅開発も終盤にさしかかってそろそろリリースできるかと思った矢先、 「SNSで共有されたときのために、 ページによってmetaタグ変えたい」 という要件がでてきました。。。 ※最初からNext.jsを採用しておけというツッコミはNG
Slide 9
Slide 9 text
© DMM.com 純SPAはブラウザでしか実行されないが、Next.jsだと、 Node.jsとブラウザの両方で実行される。 例えば、useEffectの外でWeb APIにアクセスしている場合エラーがでる (ポイントクラブで実際にNext.jsへの移行に 1ヶ月くらいかかった原因のひとつ😇) 純SPAからNext.jsへの移行は案外大変
Slide 10
Slide 10 text
© DMM.com 純SPAのまま この課題を解決する方法を 見ていきましょう 10
Slide 11
Slide 11 text
© DMM.com ⚠注意点 • 時間が足りなそうなので、細かい実装の話はしません • 気になる人はhttps://github.com/KoichiKiyokawa/spa-meta • AWSを使っていますがCloudflareとかのほうが簡単にできるかも • この発表は純SPAの使用を推奨するものではありません • あくまで回避策のお話
Slide 12
Slide 12 text
© DMM.com 「ページによってmetaタグを変えたい」の解像度を上げる • 動的か静的かの2種類に分類できる • 静的のほうがインフラ構成などが楽 動的 静的 パターン数が多すぎて事前生成ができないのでリ クエスト時に実行する 例:/posts/1, /posts/2, …/posts/9999 でmetaタグを変える パターン数が少なく事前生成できる 例:/aboutだけmetaタグを変える
Slide 13
Slide 13 text
© DMM.com 4種類の方法一覧 動的(リクエスト時に実行) 静的(ビルド時に実行) 方法 dynamic rendering head-only SSR head-only SG static prerendering 概要 ? ? ? ? インフラ 構築難度 ? ? ? ? 保守難度 ? ? ? ? その他 ※dynamic-rendering以外の名称は自分が適当に考えました
Slide 14
Slide 14 text
© DMM.com • デモ:https://dynamic-rendering.kiyoshiro.me/posts/123 方法1:dynamic rendering(概要)
Slide 15
Slide 15 text
© DMM.com Lambda@Edge + CloudFront + S3の場合 方法1:dynamic rendering(インフラ例)
Slide 16
Slide 16 text
© DMM.com Lambda@Edge + CloudFront + S3の場合 方法1:dynamic rendering(インフラ例) キャッシュの有無に関わらず、 リクエストのたびに必ず実行される • キャッシュヒットしなかったときだ け実行される • ここで返したレスポンスはキャッ シュされる
Slide 17
Slide 17 text
© DMM.com 方法1:dynamic rendering(アプリコード)
Slide 18
Slide 18 text
© DMM.com 😄GOOD • SEO:ページコンテンツも描画できる • 保守性:一度インフラを整えたら手を加える必要なし • アプリコードだけに集中できる 方法1:dynamic rendering
Slide 19
Slide 19 text
© DMM.com 方法1:dynamic rendering 😩BAD • 最初にインフラ構築するのが大変 bot判定・ユーザーとbotでキャッシュの分離・ヘッドレスブラウザの実行etc… • Google曰く、インフラが複雑になるので「回避策」 • 回避策としてのダイナミック レンダリング | Google 検索セントラル • Next.jsとかのSSRと比べてレスポンスが遅くなりがち • Google Botへのレスポンスが遅くなると SEOに悪影響が(CoreWebVitals) • CloudFrontなどCDNのstale-while-revalidate機能 を使うと軽減できそう ※キャッシュヒットしなかったとき
Slide 20
Slide 20 text
© DMM.com 補足:CDNのstale-while-revalidate機能 キャッシュヒットしなかったときの比較(矢印の長さに注目)
Slide 21
Slide 21 text
© DMM.com 4種類の方法一覧 動的(リクエスト時に実行) 静的(ビルド時に実行) 方法 dynamic rendering head-only SSR head-only SG static prerendering 概要 botからアクセスされたとき だけヘッドレスブラウザでレ ンダリングしたHTMLを返却 ? ? ? インフラ 構築難度 ✕難 ? ? ? 保守難度 ◎必要なし ? ? ? その他 😄ページコンテンツも レンダリング 😩不安定で遅い
Slide 22
Slide 22 text
© DMM.com 4種類の方法一覧 動的(リクエスト時に実行) 静的(ビルド時に実行) 方法 dynamic rendering head-only SSR head-only SG static prerendering 概要 botからアクセスされたとき だけヘッドレスブラウザでレ ンダリングしたHTMLを返却 ? ? ? インフラ 構築難度 ✕難 ? ? ? 保守難度 ◎必要なし ? ? ? その他 😄ページコンテンツも レンダリング 😩不安定で遅い
Slide 23
Slide 23 text
© DMM.com 方法2:head-only SSR(概要) デモ:https://head-only-ssr.kiyoshiro.me/posts/123 dist/index.html
Slide 24
Slide 24 text
© DMM.com 方法2:head-only SSR(コード) esbuildなどを使ってビルド時に埋め込むと S3 へのアクセスが省略できて良い
Slide 25
Slide 25 text
© DMM.com 方法2:head-only SSR(インフラ) • キャッシュヒットしなかったときだ け実行。レスポンスはキャッシュさ れる
Slide 26
Slide 26 text
© DMM.com 方法2:head-only SSR 😄GOOD • dynamic renderingよりもインフラ構成が楽&レスポンスも早い 😩BAD • 保守性:ページが増えるたびにエッジ関数に追記してデプロイする必要あり 例:/posts/:idに加えて、/users/:idパスが増えたらエッジ関数に追記 • SEO:ページのコンテンツはレンダリングしない
Slide 27
Slide 27 text
© DMM.com 4種類の方法一覧 動的(リクエスト時に実行) 静的(ビルド時に実行) 方法 dynamic rendering head-only SSR head-only SG static prerendering 概要 botからアクセスされたとき だけヘッドレスブラウザでレ ンダリングしたHTMLを返却 エッジ関数でmeta タグを注入した HTMLを返却 ? ? インフラ 構築難度 ✕難 △微難 ? ? 保守難度 ◎必要なし ✕大変 ? ? その他 😄ページコンテンツも レンダリング 😩不安定で遅い
Slide 28
Slide 28 text
© DMM.com 4種類の方法一覧 動的(リクエスト時に実行) 静的(ビルド時に実行) 方法 dynamic rendering head-only SSR head-only SG static prerendering 概要 botからアクセスされたとき だけヘッドレスブラウザでレ ンダリングしたHTMLを返却 エッジ関数でmeta タグを注入した HTMLを返却 ? ? インフラ 構築難度 ✕難 △微難 ? ? 保守難度 ◎必要なし ✕大変 ? ? その他 😄ページコンテンツも レンダリング 😩不安定で遅い
Slide 29
Slide 29 text
© DMM.com 方法3:head-only SG ↑index.html ↓そこから生成したabout.html 例:/aboutページだけmetaタグを変えたい場合 インフラ /aboutでアクセスされたらabout.htmlを参照するよう に設定 ビルド時 /aboutページ用のabout.htmlを作る (headタグだけ注入)
Slide 30
Slide 30 text
© DMM.com 方法3:head-only SG dist ├ index.html ├ … dist ├ index.html ├ about.html ├ … ビルド スクリプト実 行 metaタグ注入
Slide 31
Slide 31 text
© DMM.com 方法3:head-only SG
Slide 32
Slide 32 text
© DMM.com 方法3:head-only SG 😄GOOD • head-only SSRよりもインフラ構成が楽 • /aboutを/about.htmlにルーティングするだけ 😩BAD • 保守性:アプリコードとは別に、head注入するスクリプトを保持する必要あり • SEO:ページのコンテンツはレンダリングしない
Slide 33
Slide 33 text
© DMM.com 4種類の方法一覧 動的(リクエスト時に実行) 静的(ビルド時に実行) 方法 dynamic rendering head-only SSR head-only SG static prerendering 概要 botからアクセスされたとき だけヘッドレスブラウザでレ ンダリングしたHTMLを返却 エッジ関数でmeta タグを注入した HTMLを返却 ビルド時にmetaタグを 注入したHTMLをつくる ? インフラ 構築難度 ✕難 △微難 ◯簡単 ? 保守難度 ◎必要なし ✕大変 △普通 ? その他 😄ページコンテンツも レンダリング 😩不安定で遅い
Slide 34
Slide 34 text
© DMM.com 4種類の方法一覧 動的(リクエスト時に実行) 静的(ビルド時に実行) 方法 dynamic rendering head-only SSR head-only SG static prerendering 概要 botからアクセスされたとき だけヘッドレスブラウザでレ ンダリングしたHTMLを返却 エッジ関数でmeta タグを注入した HTMLを返却 ビルド時にmetaタグを 注入したHTMLをつくる ? インフラ 構築難度 ✕難 △微難 ◯簡単 ? 保守難度 ◎必要なし ✕大変 △普通 ? その他 😄ページコンテンツも レンダリング 😩不安定で遅い
Slide 35
Slide 35 text
© DMM.com ビルド時に、Bot用のレンダリングされたHTMLをヘッドレスブラウザで作成 方法4:static prerendering リクエスト時ではなく ビルド時に行う 👆(再掲)dynamic renderingの図
Slide 36
Slide 36 text
© DMM.com 方法4:static prerendering ※細かい処理は省略
Slide 37
Slide 37 text
© DMM.com 方法4:static prerendering
Slide 38
Slide 38 text
© DMM.com 方法4:static prerendering 😄GOOD • head-only SGと同様、インフラ構成は楽 • Botからのアクセス時に/aboutへのリクエストを/about.htmlにルーティングするだ け • SEO:ページコンテンツもレンダリングできる 😩BAD • 保守性:アプリコードとは別で、ヘッドレスブラウザを実行するスクリプトを保守する必 要あり • ビルド時間:ヘッドレスブラウザを動かす分、時間がかかる
Slide 39
Slide 39 text
© DMM.com 4種類の方法一覧 動的(リクエスト時に実行) 静的(ビルド時に実行) 方法 dynamic rendering head-only SSR head-only SG static prerendering 概要 botからアクセスされたとき だけヘッドレスブラウザでレ ンダリングしたHTMLを返却 エッジ関数でmeta タグを注入した HTMLを返却 ビルド時にmetaタグを 注入したHTMLをつくる ビルド時にヘッドレスブ ラウザでHTMLを生成す る インフラ 構築難度 ✕難 △微難 ◯簡単 ◯簡単 保守難度 ◎必要なし ✕大変 △普通 △普通 その他 😄ページコンテンツも レンダリング 😩不安定で遅い 😄ページコンテンツもレ ンダリング
Slide 40
Slide 40 text
© DMM.com まとめ 40
Slide 41
Slide 41 text
© DMM.com • 純SPAでもLambda@Edgeなどを使えばページごとにmetaタグを切り替える ことができる まとめ
Slide 42
Slide 42 text
© DMM.com • 面倒なので、最初からNext.jsを採用しよう • ページによってmetaタグを切り替えられる以外にもメリット多い • zero config • viewportに入ったリンクのprefetch • 画像/フォント/外部scriptの読み込み最適化 • etc… とはいえ・・・
Slide 43
Slide 43 text
© DMM.com ご清聴ありがとうございました 43