React Server Components 実践:ToolsKu サイトの RSC アーキテクチャ

技术架构(更新: 2026年5月21日)

Server Components は何を解決するか?

従来の React アプリの課題:

ブラウザが JS bundle を取得 → React 実行 → API リクエスト → UI 描画
         ↑ 遅い           ↑ ブロック        ↑ ウォーターフォール

各ページで React ランタイム、コンポーネント、データ取得ロジックをすべて配信していました。コンテンツ中心のツールサイトでは、多くの JS は不要でした。

React Server Components (RSC) はサーバー(またはビルド時)でレンダリングし、ブラウザには HTML と最小限の Client Component JS だけを送ります。


レンダリングモデルの比較

モデル レンダリング場所 ブラウザへの JS データ取得
CSR ブラウザ 全コンポーネント JS useEffect の連鎖
SSR サーバー → HTML 全コンポーネント JS(hydration) getServerSideProps
RSC サーバー/ビルド Client Component のみ コンポーネント内 async/await
SSG ビルド → HTML Client Component のみ ビルド時取得

ToolsKu は SSG + RSC:Server Component をビルド時に描画し、Client Component は必要時に読み込みます。


Server と Client の境界

デフォルトはすべて Server Component

// ✅ Server Component(デフォルト)— 'use client' なし
export default async function BlogPage({ params }) {
  const posts = getAllPosts(params.locale, 'blog'); // ファイルシステムを直接読む
  return (
    <div>
      <h1>{posts.length} 件の記事</h1>
      {posts.map(p => <PostCard key={p.slug} post={p} />)}
    </div>
  );
}

Server Component でできること:

  • ファイルシステム・DB への直接アクセス
  • async/await によるデータ取得
  • サーバー専用モジュール(fsgray-matter など)の import

Server Component でできないこと:

  • useState、useEffect などの Hooks
  • イベントハンドラ(onClick など)のバインド
  • ブラウザ API(window、localStorage)

Client Component が必要なとき

'use client'; // ← Client Component

import { useState } from 'react';

export function PdfMergeClient() {
  const [files, setFiles] = useState<File[]>([]);
  // 操作・状態・ブラウザ API → Client Component
}

ToolsKu の分担:

種別 理由
Server ブログ一覧、チュートリアル、ナビ 静的コンテンツ、JS ゼロ
Client PDF 結合、JSON 整形、動画変換 File API と操作
Server + Client ツールページ layout (Server) + UI (Client) JS 最小化

ToolsKu の RSC アーキテクチャ

ページ構造

src/app/[locale]/pdf/merge/page.tsx          ← Server Component
  ├── generateMetadata()                      ← SEO(ビルド時)
  ├── ToolPageLayout                          ← Server(パンくず、タイトル)
  └── PdfMergeClient.tsx                      ← Client('use client')
        ├── ファイルアップロード UI
        ├── pdf-lib 処理
        └── ダウンロードボタン

コード分割の効果

従来 CSR ページ JS: ~180KB(React + ツール + UI)
RSC ページ JS:      ~45KB(Client + ツールのみ)
削減:               約 75%

Server Component が出力する HTML(タイトル、説明、パンくず、FAQ)は JS コストゼロです。

データ取得:ビルド時 vs 実行時

// ブログ:ビルド時に MDX を読む
export default async function BlogPostPage({ params }) {
  const { slug, locale } = await params;
  const result = getRenderedPost(slug, locale, 'blog');
  if (!result) notFound();
  return <ContentPostArticle post={result.post} rendered={result.rendered} />;
}
// ツールページ:ビルド時に i18n を読み込む
export default async function PdfMergePage({ params }) {
  const { locale } = await params;
  const t = await getTranslations({ locale, namespace: 'tools.pdf_merge' });
  return (
    <ToolPageLayout title={t('title')} description={t('description')}>
      <PdfMergeClient />
    </ToolPageLayout>
  );
}

すべてのデータは next build で取得・静的 HTML にレンダリング—実行時のデータリクエストはゼロです。


性能測定

ToolsKu トップページ(zh-CN)のリソース読み込み:

指標 純 CSR(推定) RSC + SSG(実測)
初回 JS ~180KB ~12KB
FCP ~1.2s ~0.4s
LCP ~2.1s ~0.8s
TTI ~2.8s ~1.2s
HTML サイズ ~5KB(空殻) ~45KB(全文)

HTML は大きくなるが JS は大幅削減—コンテンツ中心のツールサイトでは正しいトレードオフです。ユーザーもクローラも即座に全文を見られます。


よくある落とし穴

1. 意図せず Client 境界を引き込む

// ❌ Server が Client 専用依存を import
import { someUtil } from './client-utils'; // client-utils に 'use client' がある場合

// ✅ 共有ロジックを server-safe モジュールへ
import { someUtil } from './shared-utils';

'use client'モジュール単位—Client モジュールを import するとチェーン全体が client になります。

2. Server Component で Hooks

// ❌ コンパイルエラー
export default function Page() {
  const [state, setState] = useState(0);
}

// ✅ Server + Client に分割
export default function Page() {
  return <InteractivePart />; // 'use client'
}

3. 過剰な Client 化

// ❌ ページ全体が Client
'use client';
export default function ToolPage() {
  return (
    <div>
      <h1>PDF 結合</h1>        {/* 静的—client 不要 */}
      <p>複数 PDF を結合</p>
      <PdfMerger />              {/* これだけ client */}
    </div>
  );
}

// ✅ Client 境界を最小化
export default function ToolPage() {
  return (
    <div>
      <h1>PDF 結合</h1>
      <p>複数 PDF を結合</p>
      <PdfMerger />
    </div>
  );
}

RSC と静的エクスポート

ToolsKu は output: "export" を使用。RSC はビルド時にレンダリングされます:

next build
  → generateStaticParams() を走査
  → 各 (locale, path) で Server Component 実行
  → 静的 HTML + Client Component JS chunks
  → CDN へデプロイ

静的エクスポートでは、RSC のサーバー負荷軽減は より小さい JS と高速な初回表示 に変換されます。


まとめ

React Server Components は Client Components の代替ではなく、JS が必要な境界の再定義です。ToolsKu のようにコンテンツ主・操作従のサイトでは、約 90% のページを JS ゼロで描画し、ツール操作部分だけ Client を読み込む—極限の初回表示性能のための重要な設計判断です。

ブラウザローカルツールを無料で試す →

#React#Server Components#Next.js#SSR#性能