Next.jsにおけるSSG/SSR/ISR/CSRの違いと使い分け

3 min read読了の目安(約3300字

はじめに

Next.jsを使っていると、SSGだのSSRだの横文字かつ略語の概念がたくさん出てきて混乱しませんか?

この記事では、自分の中でようやくこの辺の概念の理解が進んできたような気がしているのでまとめていこうと思います。

SSG/SSR/ISR/CSRの違い

SSGとは

SSGはStatic Site Generationの略で、日本語に訳すと静的サイト生成です。

これはビルド時に事前にHTMLを生成しておいて、リクエストがあったら単純にそれを返す、といった仕組みです。静的ページなのでCDNにキャッシュさせることができ、パフォーマンスに優れます。

またSSG自体も2つのケースに分けられると思っていて、1つが外部からフェッチするデータがないケース、もう1つが外部からフェッチするデータがあるケースです。

Next.jsではSSG用にgetStaticPropsという関数が用意されているのですが、前者のケースではこの関数を使わず、後者のケースで使用します。
getStaticProps内に外部データをフェッチする処理を書くと、ビルド時にサーバー側で外部データをフェッチしてからそのデータを使用してHTMLが生成されます。

SSRとは

SSRはServer Side Renderingの略で、日本語に訳すとサーバー側でのレンダリング(HTML生成)です。

これはリクエストごとにその都度サーバー側でHTMLを生成し、それを返すといった仕組みです。リクエストごとにその都度といった仕組み上、頻繁に変更され得る動的コンテンツでも対応できます。

Next.jsではSSR用にgetServerSidePropsという関数が用意されており、getServerSideProps内に外部データをフェッチする処理を書くと、リクエスト時にサーバー側で外部データをフェッチしてからそのデータを使用してHTMLが生成されます。

ISRとは

ISRはIncremental Static Regenerationの略で、日本語に訳すと段階的な静的サイト生成です。

これはビルド時に事前にHTMLを生成しておいた上で、stale-while-revalidateというキャッシュの仕組み[1]を使って、指定時間経過後にリクエストがあった場合は裏側でHTMLを再生成し次のリクエスト時にはそれを返すといった仕組みです。

このようにすることで動的コンテンツを含むページでもSSGで対応可能になります。個人的にはSSGとSSRのいいとこ取りのようなイメージを持っています。ただし指定時間の間のリクエストや指定時間経過後の最初のリクエストではキャッシュされたページを返すので、頻繁に変更され得る動的コンテンツの場合に問題がないかの検討が必要です。

Next.jsではgetStaticProps内でrevalidate: <秒数>のオプションを指定することで使用することができます。

CSRとは

CSRはClient Side Renderingの略で、日本語に訳すとクライアント側でのレンダリングです(ほぼ訳せてない)。

これはクライアント側でDOMをレンダリングする仕組みで、Reactのみを使ってSPAを作る場合にuseEffect内で外部データをフェッチしてDOMに流し込むお馴染みのやつです。SSRではないため、SEO対策やOGP対応が必要な場合には不向きです。

Next.js(というかVercel)ではuseSWRというライブラリを使用することが推奨[2]されています。
useSWRは外部データのフェッチを宣言的に書けるようにしてくれて、さらにクライアントキャッシュをよしなに管理してくれる優れものです。

SSG/SSR/ISR/CSRの使い分け

以下の順番で検討していけば基本問題ないと思います。

  1. SSG
  2. ISR
  3. SSR
  4. CSR

SSGを使うケース

ページが静的コンテンツのみの場合はSSGが良いでしょう。この場合はgetStaticPropsgetServerPropsも使わないことになります。

後述するISRが登場する前はgetStaticPropsgetStaticPathsを用いて、動的コンテンツが含まれる場合でもSSGを使っていたと思いますが、ISRが登場したことでそういった場合はSSGではなくISRを使うようになっていっているのではないかと思います。

この場合の典型的なケースはHeadless CMSを用いたブログで、CMSでブログ記事を作成する度にWebhookでビルドするように設定するのが定石だったと思います。現在はISRを使えばブログ記事を作成する度にビルドし直す必要はなく、ビルド後に作成されたブログ記事へのリクエストはSSRにフォールバックされ、その後はstale-while-revalidateの仕組みを使ってキャッシュされます。

ISRを使うケース

以下の条件を全て満たす場合はISRが良いでしょう。

  • ページに動的コンテンツが含まれる
  • 強整合性が求められない

getStaticProps内でrevalidate: <秒数>のオプションを指定することで使用することができます。

実は以下の記事で言及されているように、stale-while-revalidate対応のCDNであればSSRでもISRと同じような挙動を実現することは可能なのですが、ISRであればpre-fetchが行われることから、基本的にはISRを使用できる時はISRを使用するのが良いのではないかと思います。

https://zenn.dev/catnose99/articles/0b601c1f62019b

SSRを使うケース

以下の条件を全て満たす場合はSSRが良いでしょう。

  • ページに動的コンテンツが含まれる
  • 強整合性が求められる
  • SEO対策やOGP対応のためにpre-renderが必要

SSRを使用するケースはISRのpre-fetchで大量のHTMLが再生成されてしまい、サーバーへの負荷がかかることが懸念される場合でしょうか(あまり思いつかない)。

CSRを使うケース

以下の条件を全て満たす場合はCSRが良いでしょう。

  • ページに動的コンテンツが含まれる
  • 強整合性が求められる
  • pre-renderが不要

CSRを使用する典型的なケースはログインが必要なページです。ユーザーごとにコンテンツが異なるが、SEO対策やOGP対応は不要なため、SSRする必要はないといったケースですね。

番外編: ISR+CSR

ISRで生成されたHTMLをCSRで最新の状態に更新する、といったやり方も存在するようです。

以下の記事で言及されているように、Zennではこのやり方を使用してISRの記事ページでも、記事作成者の場合はCSRで最新の状態に更新することで、記事を編集したのに反映されていないように見えるのを防いでいるそうです。こんな使い方もあるんですね。

https://zenn.dev/catnose99/articles/8bed46fb271e44

おわりに

以上のようにNext.jsにおけるSSG/SSR/ISR/CSRの違いと使い分けについてまとめてみました。
ある程度は整理できたかなと思いますが、もし間違っている箇所があればご指摘お願いします。
また、以下のNext.jsのドキュメントでも言及がありますのでぜひそちらも確認してみてください。

https://nextjs.org/docs/basic-features/data-fetching
脚注
  1. https://web.dev/stale-while-revalidate/ ↩︎

  2. https://nextjs.org/docs/basic-features/data-fetching#swr ↩︎