📈

Streaming with Suspense(SSRの限界)

2025/02/26に公開

https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming
Next.jsのSSRは、クライアントがリクエストするたびにサーバーでHTMLを作って返すやり方。
でも、SSRは順番に処理するから、パフォーマンス的にちょっとキツい部分がある。
その問題を解決するために Streaming が導入されて、さらに Suspense を使うともっと効率よくなる。
SSRの限界と、それを Streaming でどうやって解決できるのかを解説しようと思う。

SSRのプロセスと限界


SSRは「Dynamic Rendering」とも呼ばれ、クライアントがリクエストするたびに新しいHTMLを生成する方式である。

基本的なSSRのプロセスは以下の通り。

  1. データフェッチ(Fetching): サーバーで必要なデータを取得する。
  2. HTMLレンダリング: データを含んだHTMLをサーバーで生成する。
  3. クライアントローディング: ブラウザがHTML、CSS、JavaScriptをダウンロードする。
  4. Hydration: Reactがアクティブになり、インタラクションが可能になる。

しかし、SSRは順次(Sequential)で実行されるため、いくつかの制約がある。
例えば、ページに「おすすめ商品セクション」と「フッター」があるとしよう。おすすめ商品はサーバーからデータを取得する必要があるが、フッターは静的なコンテンツなので即座にレンダリングできる。しかし、SSRでは処理が順番に進行するため、おすすめ商品のデータをすべて取得するまでフッターがレンダリングされないという問題が発生する。

Streamingを利用した解決策

Streamingを使用すると、ページ内の特定のコンポーネント(例:静的なレイアウトや即座にレンダリング可能なUI)と、データを取得する必要があるコンポーネント(例:おすすめ商品)を分離し、並列で処理することができる。

Streaming方式の特徴

  1. データを必要としない静的なコンポーネントは先にレンダリングされる。
  2. データを必要とするコンポーネントは別途ロードされ、完了次第画面に反映される。
  3. これを実現するために、ReactのSuspenseを活用する。
import { Suspense } from 'react';
import FechingDataComponent from './FechingDataComponent';
import StaticComponent from './StaticComponent';

export default function Page() {
  return (
    <div>
      <StaticComponent /> {/* 静的なUIは即座にレンダリング */}
      <Suspense fallback={<div>Loading...</div>}>
        <FetchingDataComponent /> {/* データが準備でき次第レンダリング */}
      </Suspense>
    </div>
  );
}

上記のようにSuspenseを使用すると、StaticComponent は即座に画面に表示され、FetchingDataComponent はデータが準備できた後にレンダリングされる。

Streaming vs. PPR (Partial Prerendering)

https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering
Next.jsでは Partial Prerendering(PPR) という概念も登場した。PPRはStreamingと類似しているが、主な違いは以下の通りである。

  • Streaming: ページをサーバーで順次レンダリングするのではなく、特定のコンポーネント単位でデータを非同期ロードする。
  • PPR: 特定の動的コンポーネントを事前に静的レンダリング(SSG)した後、クライアント側で動的にデータを結合する。

つまり、Streaming はサーバーでデータを取得しながら並行してレンダリングを進めるのに対し、PPR は事前に準備されたHTMLを活用して、高速な初期ロードを提供する方式である。

※Next.js15のcanaryバージョンでも実験的機能として提供されているので、まだ安定的ではない

Streaming PPR (Partial Prerendering)
概念 サーバーからデータを取得しながら順次ページをレンダリングし、ストリーミングする あらかじめ静的レンダリング(SSG)されたHTMLを活用し、一部のコンポーネントはクライアントで動的に結合
初期ローディング速度 初期HTMLをすぐに送るが、一部のデータはロードされながら段階的に表示される 初期HTMLが事前に準備されているため、はるかに速い初回ローディングを提供
サーバーの役割 リクエストを受けるたびにサーバーがデータを取得し、必要な部分をストリーミング 一部の静的部分をあらかじめ生成し、残りはクライアントで動的に処理
クライアントの役割 ブラウザがHTMLを受け取り、サーバーから追加データを取得しながら画面を段階的に完成 静的なHTMLを先に受け取って素早く表示し、残りのデータはクライアントで結合
SEO SEO最適化は可能だが、すべてのデータが一度に来ないため、クローラーが完全に解析するのに時間がかかる可能性あり 事前に生成されたHTMLがあるため、SEOに有利
使用例 大規模ページでリアルタイムにデータを取得する場合(例:ニュースフィード、無限スクロール) 静的ページだが、一部のデータをユーザーごとにカスタマイズする必要がある場合(例:パーソナライズされたダッシュボード)
Next.js対応バージョン Next.js 13+ Next.js 14+(試験的機能)

結論

Next.jsで Streaming with Suspense を活用すると、SSRの順次処理による問題を解決できる。静的なUIを先にレンダリングし、データが必要なコンポーネントは Suspense を利用して非同期に読み込むことで、ユーザーエクスペリエンス(UX)とパフォーマンスを向上 させることが可能である。

また、PPRとの比較では、それぞれに長所と短所があるため、プロジェクトの要件に応じて適切な方法を選択することが重要 である。

Bizlink Developers Blog

Discussion