Uenishi.Web

大阪に生息しているプログラマーのブログ

CSR, SSR, SSG, ISRやらのあれこれを完全理解

いろいろなレンダリング方法について

普段、Next.jsを用いて開発を行っています。 Next.jsでは、さまざまなレンダリング方法の仕組みが提供されています。

しかしながら、なぜNext.jsがそんなにいろいろなレンダリング方法が存在しているのか、なんでそんなややこしいことになっているのか全然理解できていませんでした。

そのため、ひとつひとつのメリット・デメリットについて挙げながら、それぞれの関係性や用語について歴史的経緯を調べつつ、自分自身の理解を深めていくための記事です。

また、今回はコードで検証というよりは知識理解を前提としているため、基本的に文章や図オンリーです。ご了承ください。

間違い等ありましたらTwitter等で指摘いただけるとありがたいです。

SPA(Single Page Application)とは

まず初めにSPAから。

Single Page(1枚のページ)で構成されるアプリケーションのことを指します。

対義語とされているものに、MPA(Multi Page Application)があります。

もともと、SPAの登場以前はそれぞれのURLごとにサーバーサイドにアクセスし、返却されたHTMLを表示する、MVCフレームワーク(SpringやASP.NET MVC, Ruby on Railsなど)での開発が主流でした。

これらのフレームワークで開発されたアプリケーションは、ページ遷移時にサーバーに問い合わせて新たなHTMLを読み込むため、たびたびロード時間が発生してしまいます。

(厳密にはMVCフレームワークを用いてもSPAを開発することは可能です)

画像が読み込まれない場合はページを更新してみてください。

この「ページ遷移のたびに発生するロード時間」を短縮し、ユーザー体験を向上する目的でSPAでのアプリケーション開発が広まっていきました。

SPAはその名の通り、「Single PageのApplication」です。ユーザーがアプリケーションに初めてリクエストした際、ページレンダリングに必要な情報を全てレスポンスで返します。その後はページ遷移のためにサーバーサイドへアクセスすることなく、JavaScriptを用いて動的にコンテンツを更新していきます。そのため、SPAのアプリケーションはロード時間が非常に少なくなり、ユーザー体験が非常に向上します。

後述するCSRとSPAはしばし同義に扱われることもありますが、SPAはアプリケーションの種別であり、CSRはレンダリングの技術のため厳密には別物であると僕は考えています。

※ SPA=CSRとする記事もちらほらあるため、自分の認識はそうであるという主張に留めておきます。 主張の参考にさせていただいた記事たち

CSR(Client Side Rendering)

SPAを実現するためのレンダリング技術で、クライアント(ブラウザ)側でレンダリングを行う仕組みです。

ブラウザからリクエストが送られると、Webサーバはbody部が空のHTMLとCSS, JavaScript, その他諸々… を返します。

画像が読み込まれない場合はページを更新してみてください。

その後、クライアント側でJavaScriptを実行することで、HTML/CSSを生成します。

初回にページ全体に必要な要素を返却するため、初めの表示は重くなってしまいます。しかし、その後のページ遷移に関してはサーバー側に問い合わせてHTMLを作成するのではなく、APIから取得したデータをもとに差分のみをJavaScriptでレンダリングします。

これにより、ページ間遷移のロード時間が大幅に短縮されることでUXが向上し、よりインタラクティブ(対話的な)操作が可能になります。

メリット
  • ページ遷移のたびにWebサーバーにHTMLを返却してもらう必要がない(通信が発生しないため)、高速に動作するようになる。
  • クライアント側でレンダリングが行われるため、サーバーは主にAPIからのデータ取得に徹することができる、そのためサーバーの負荷が軽減される。

デメリット
  • SEOに弱い(現代はクローラーの性能向上により、大きな差は無くなっているらしい。この辺りは正確な出典が見当たらなかったので、情報募集しています)
  • ページごとのOGP設定ができない(あくまでJavaScriptで生成した仮想のページ遷移のため、実態は1ページしかない)
  • 初回ロード時にリソースを全て渡しJavaScriptで処理するため、規模が大きくなるとクライアント側の処理負荷が増大する。

SSR(Server Side Rendering)

その名の通り、サーバーサイドでレンダリングする技術。

ここで「サーバーサイドでレンダリング」とだけ聞くと、SPA以前のレンダリング方法と同じと思うところですが、SSRと呼ばれる場合ではCSRの弱点を補う形で用いられることが多いです。

画像が読み込まれない場合はページを更新してみてください。

初めに記載したMVCフレームワークでのレンダリング手法も、同じようにサーバーサイドに問い合わせ → HTMLを生成 → クライアント側にレスポンスとして返す。

という流れでした。方法としては同じことをしているようにも見えます。

この違いを理解するために調べた結果、以下のような記述を見つけたため引用します。

サーバーサイドレンダリング(SSR)とは、その名の通りサーバー側でアプリケーションの HTML を生成し、レスポンスとして返すことを言います。 一般的に利用されている MPA(Multiple Page Application)では言うまでもなく行われていることなので、SSR というワードは自ずと SPA(Single Page Application)を構築する際のオプション機能を指します。

違いとしては、SSRはSPAでのCSRの弱点を補う形で行われることが挙げられます。CSRを行った際の「初期表示が遅い」や「SEOに弱い」といった点、またはクライアント側に処理の負荷が偏ってしまう点を、事前にサーバーサイドでレンダリングしたHTMLを渡すことで解決しようというアプローチです。

今回記事を書くきっかけとなったNext.jsを用いた場合であれば、不足するデータがあったり、ユーザーの操作によってインタラクティブに変化するUIなどは引き続きCSRで行うこともできるため、SPA開発においては「CSR + SSR」というような選択肢が生まれ、より自由度が高まったのではないかと思います。

※ MPAのサーバーでのレンダリングはSR(Server Rendering)と区別する場合もあるようです。

※ SSRなどで生成したHTML(静的なページ)に対して、JavaScriptを実行することで動的なWebページにすることを「Hydration(ハイドレーション)」と呼びます。ハイドレーションとは水和や水分補給というような意味を持つ言葉で、静的になっているものを動的に戻す = 水を得る という連想でこのワードが使われているようです。

参考:TypeScriptとReact/Next.jsでつくる実践Webアプリケーション開発

メリット
  • 初めのリクエストで全てのリソースを読み込む必要がなくなり、初回表示が(CSRのみより)速くなる
  • 複数ページのアプリケーションを作成可能のため、ページ単位のOGPなども設定可能
  • (CSRに比べて)サーバーサイドでHTMLを生成するため、SEOに強くなる
  • レンダリングがクライアント側の処理に依存しなくてよくなり、重たい処理などはサーバー側で対処可能となる

デメリット
  • SSR用のサーバーが必要となり、実装コストも増加する
  • サーバーサイドでHTMLを生成するため、サーバー側の負荷が増加する
  • HTMLを生成するためにAPIからデータを取得したり、データベースと通信をしたりする時間が必要となる。結果、TTFB(Time to First Byte)が長くなり、表示速度に影響を与えてしまうことがある

SSG(Static Site Generator)

日本語では「静的(Static)サイトジェネレーター」と呼びます。

前述のCSRやSSRと違って、こちらはビルド時にデータの取得などを全て行い、最終的に静的なHTMLファイルとして生成してしまう方法のことです。

ユーザーから実際にリクエストが来た場合、SSRであればリクエストに応じてHTMLファイルを生成してからレスポンスを返していました。しかし、SSGであればリクエストの前から事前にHTMLを生成しているため、ページアクセス時にはそれを返却するだけでよく、非常に高速なレスポンスが可能になります。

画像が読み込まれない場合はページを更新してみてください。

また、CSRのように動的にHTMLをクライアントで作成している訳ではなく、静的なHTMLのためSEOに対しても強くなります。高速かつ、SEOにも強い。一見最強に見えますがもちろん欠点もあります。

事前にサイトの中身を準備しているため、ビルドを実行しなければデータが更新されません。そのため、もし頻繁にデータの更新があるようなWebアプリケーションなどの場合、使い物にはなりません。

また、ページ数が非常に多くなった場合、「事前に全て生成する」という性質上非常にビルド時間が長くなってしまう、といった問題点もあります。そのため、ブログなどのような、ユーザーがデータの更新削除をする訳ではなく、管理者側でコンテンツの増減を行うサイトなどによく用いられます。

メリット
  • リクエストに応じてビルド時に生成した静的ファイルを返却するだけなので、高速な表示が可能
  • SSRと同様に、SEOに強い
  • ページ単位のOGPも設定可能
デメリット
  • ビルド時に事前にレンダリングしているので、情報が更新されてもまたビルドしないと反映できない
  • ページ数が増大すると、その分だけビルド時間も増大していく

ISR(Incremental Static Generator)

最後に、Next.jsを触り始めてからずっと謎の存在として認識していた、ISRについて記述していこうと思います。

日本語では「インクリメンタル静的再生成」と呼ばれます。言葉だけ聞いても意味があんまりわからないですね。

「インクリメンタル」は「段階的」のように訳されます。

ISRは前述したSSGの応用というべきレンダリング方法です。

具体的には、指定した一定の間隔でバックグラウンドでデータの再取得、再レンダリングを行います。

ISRは「Stale While Revalidation」というコンセプトがベースになっており、Next.js 9.5から導入されました。

Stale While Revalidationは「SWR」というReactライブラリでもお馴染みです。こちらもNext.jsと同じくVercelが中心となって開発しているライブラリです

実際にはどんな動きをするのか、図で見ていきます。

Next.jsでは、getStaticProps関数の中のrevalidateというキーに対して秒数を指定します。

以下の図では10分(600秒)を指定しました。

画像が読み込まれない場合はページを更新してみてください。

この時返されるのは、SSGの時と同じく「事前に生成されたHTMLファイル」です。

次の例では、10分後にまたクライアントからリクエストを送信した場合です。

画像が読み込まれない場合はページを更新してみてください。

この場合、リクエストに対するレスポンスは、ビルド時に生成された(キャッシュに保存された)HTMLファイルです。そして、バックグラウンドではサーバーが新たにデータのリクエストを行い、HTMLを再生成します。

そして、次回以降リクエストが行われた場合には、先ほど再生成したHTMLファイルがレスポンスで返却されます。SSGの「高速にレスポンスを返すことができる」という利点を生かしたまま、任意の時間で「段階的に」最新データの取得とHTMLの再生成を行うことで、ビルドしなければ最新データが反映されないという欠点を補っています。

画像が読み込まれない場合はページを更新してみてください。

ISRは静的生成のデメリットである情報の最新性を保ってくれますが、それでもリアルタイムなデータのやり取りを行わなければいけない場合などは問題点もあります。

こちら参考になりました。2020年時点のツイートのため、現在はもっと状況は変わっているかと思いますが、概要理解までに…。

※ 現在(Next.js 12.1以降)では、秒数指定ではなく任意のタイミングで再生成を行うようにできる 「On-Demand ISR」も提供されてます。こちらはまた別の記事で…。

まとめ

CSR, SSR, SSG, ISRと3文字の略語がずらっと並ぶことで、初見ではウッとなってしまうレンダリングの方法ですが、それぞれの意義やメリット・デメリットを理解することで「何のためにこのレンダリング方法なのか」が明快になりました。

歴史的な背景とあわせて俯瞰してみると、常に何らかの問題があり、それを解消するために新しいアプローチが生まれ、改善を繰り返している。ということがレンダリングの方法ひとつにフォーカスを当てても感じ取れます。

このブログもNext.jsで作成しているため、どのようにすれば爆速になるのかをいろいろ試行錯誤していこうと思います。

今回はこんなところで!

参考

めっちゃいろいろ見比べてたら多くなりました。

もっと公式Docから深く読み取れるように精進…!

ChatGPTくんにもいろいろ聞きました。Thanks!