effidevFlutter・Cloudflareエッジ・クラウドコスト最適化
日本語

React Server Components(RSC)ストリーミングSSRとCloudflare Edge SSRアーキテクチャ実践パターン

React Server Components Streaming SSR and Cloudflare Edge Architecture

React Server Components(RSC)ストリーミングSSRとCloudflare Edge SSRアーキテクチャ実践パターン

モダンなウェブフロントエンドアーキテクチャは、クライアントサイドレンダリング(CSR)からサーバーサイドレンダリング(SSR)、そして現在では React Server Components(RSC)ストリーミングSSR(Streaming SSR) の時代へと進化しました。

従来のSSRには、サーバーでページ全体のHTML生成が完了するまでブラウザが何も描写できないというTTFB(Time to First Byte)のボトルネックが存在しました。RSCとストリーミングSSRを活用すると、サーバー側の処理が完了したHTMLチャンクから順次ブラウザへリアルタイムにストリーミング送信できます。

ここに Cloudflare Workers のようなエッジ(Edge)ランタイムを組み合わせることで、ユーザーに最も近いPoP(Point of Presence)からコールドスタートほぼゼロでストリーミングレンダリングを開始できます。本記事では、このアーキテクチャの原理と実装の実践パターンを詳しく解説します。


1. 従来のSSR vs RSCストリーミングSSRの比較

従来のSSRとRSCベースのストリーミングSSRの主な違いは、データ取得およびHTML配信パイプラインにあります。

区分 従来のSSR RSC + ストリーミングSSR
レンダリング単位 ページ全体 (All-or-Nothing) Suspense境界に基づくチャンク単位
TTFB (Time to First Byte) 全てのDBクエリが完了するまで待機(遅い) シェルHTMLを即座に返し、超高速なTTFBを実現
バンドルサイズ サーバー描画コンポーネントのJSライブラリもクライアントへ送信 サーバー専用コンポーネントのコードはクライアントバンドルから完全除外
ハイドレーション DOM全体のハイドレーションが必要 必要なクライアントコンポーネントのみ選択的(Selective)ハイドレーション

2. SuspenseとReact DOM Server Streaming API

React 18/19の renderToReadableStream APIを使用すると、Node.jsのバッファリングなしにWeb Standard StreamベースでHTMLチャンクを順次送信できます。

実践コード:RSCストリーミングパイプライン

以下は、React Server ComponentsとSuspenseを組み合わせてストリーミングSSRを実現する例です。

// Server Component: 非同期データ取得を行う重いコンポーネント
async function SlowRecommendedProducts() {
  // 1.5秒かかるデータ取得
  const products = await fetchProductsFromDB();

  return (
    <div className="recommendations">
      {products.map((p) => (
        <ProductCard key={p.id} product={p} />
      ))}
    </div>
  );
}

// Main Page Component
export default function ProductDetailPage({ params }: { params: { id: string } }) {
  return (
    <main className="product-page">
      {/* シェル領域: 即座にストリーミング送信 */}
      <ProductHeader id={params.id} />

      {/* 重い領域: フォールバックUIを先に送り、完了次第HTMLを置き換えストリーミング */}
      <React.Suspense fallback={<ProductSkeleton />}>
        <SlowRecommendedProducts />
      </React.Suspense>
    </main>
  );
}

3. Cloudflare Workers Edge SSR パイプライン

Cloudflare WorkersはV8 Isolate上で動作し、重いNode.jsのオーバーヘッドなしに世界300拠点以上のエッジデータセンターでSSRを実行します。

[User Browser]
      │  1. HTTP Request

[Cloudflare Edge Worker (V8 Isolate)]
      │  2. Fast Shell HTML Stream (0~10ms)
      ├──────────────────────────────────────────► [Browser Render Shell]
      │  3. Async Fetch DB/Cache (D1 / Hyperdrive)
      │  4. Stream Suspense Chunks via HTTP/2
      └──────────────────────────────────────────► [Browser Replace Skeleton]

Worker Entry Point の実装例

import { renderToReadableStream } from 'react-dom/server';
import App from './App';

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // React 18/19 Web Stream API を利用してエッジでストリーミング描画
    const stream = await renderToReadableStream(<App url={url.pathname} />, {
      onError(error) {
        console.error('Streaming SSR Error:', error);
      },
    });

    // HTTP/2のDATAフレームストリーミングで即座に応答を返却
    return new Response(stream, {
      headers: {
        'content-type': 'text/html; charset=utf-8',
        'cache-control': 'no-transform',
      },
    });
  },
};

4. 実践最適化のコツ:エッジキャッシュと選択的ハイドレーション

  1. Static Shell のエッジキャッシュ: ヘッダーやレイアウトなどの静的シェルはCloudflare Workers KVやCache APIで1次キャッシュし、動的RSCデータのみをストリーミングします。
  2. エッジDBコネクション統合 (Hyperdrive / D1): エッジからメインDB(PostgreSQL等)へのアクセス時のレイテンシを抑えるため、Cloudflare Hyperdriveを活用します。
  3. Client Component の最小化: 'use client' 指示子は、インタラクション(onClickやuseState)が必要な最末端のノードのみに限定して使用します。

まとめ

RSCストリーミングSSRとCloudflare Edgeの組み合わせは、Webパフォーマンスを極限まで高める最新のアーキテクチャです。

[!NOTE]

  1. Suspense境界を適切に設定し、ページシェルが即座に返答するように設計してください。
  2. Node.js固有のAPIではなく renderToReadableStream を活用し、エッジランタイムでの互換性を確保しましょう。
  3. Cloudflare Workersでユーザーに一番近いPoPから描画ストリーミングを行い、体感TTFBを最小化します。

このパターンを導入することで、クライアントJSバンドルサイズを縮小しながら、全世界のユーザーに高速でスムーズな体験を提供できます。