shibomb

React Server Components実践入門!2025年のフルスタック開発を体験しよう

はじめに

こんにちは!プログラミングの世界へようこそ。この記事では、これからのWeb開発のスタンダードになると注目されている「React Server Components(RSC)」について、一緒に手を動かしながら学んでいきます。少し難しそうに聞こえるかもしれませんが、心配はいりません。一歩ずつ、丁寧に解説していきますので、初めての方でも安心してついてきてくださいね。

この記事を読み終える頃には、あなたは以下のことができるようになっています:

  • React Server Componentsがなぜ重要なのか、その基本的な考え方を理解できる。
  • 実際にNext.jsを使って、Server ComponentsとClient Componentsを組み合わせたシンプルなアプリケーションを構築できる。
  • これからのフルスタック開発で求められるスキルセットの第一歩を踏み出せる。

技術は目的を達成するための道具です。RSCという新しい道具を使いこなして、より速く、より効率的なWebアプリケーションを作る楽しさを体験しましょう。小さな「できた!」を積み重ねながら、一緒に成長していきましょう!

前提知識の確認

新しい技術を学ぶ前に、まずは現在地を確認しましょう。でも、もし分からないことがあっても大丈夫。プログラミング学習は、分からないことを一つずつクリアしていく冒険のようなものです。

必要な基礎知識

この記事をスムーズに進めるために、以下の知識があると理想的です。

  • HTML/CSS: Webページの骨格と見た目を作る基本的な技術です。
  • JavaScript (ES6+): Webページに動きをつけるプログラミング言語です。特に、const/let、アロー関数、async/awaitなどの基本的な構文に触れたことがあると理解が深まります。
  • Reactの基本: コンポーネント、Props、State、基本的なHooks(useState, useEffect)の考え方を何となくでも知っていると、RSCとの違いが分かりやすくなります。

事前に理解しておきたい概念

完璧に理解している必要はありませんが、以下の言葉を聞いたことがあると、この先の学習がより面白くなります。

  • サーバーサイドレンダリング (SSR): ユーザーがページをリクエストした際に、サーバー側でHTMLを生成してブラウザに送る仕組みです。初期表示が速くなるというメリットがあります。
  • クライアントサイドレンダリング (CSR): 最初に最小限のHTMLとJavaScriptをブラウザに送り、ブラウザ(クライアント)側でJavaScriptを実行してページを組み立てる仕組みです。多くのReactアプリケーションがこの方式でした。

React Server Componentsは、これらの良いところを組み合わせた新しいアプローチだと考えてください。

「分からなくても大丈夫」な部分

  • 複雑なバックエンドの知識: データベースの専門知識や、Express、Ruby on Railsのようなバックエンドフレームワークの深い経験は必要ありません。Next.jsがそのあたりをうまく隠蔽してくれます。
  • サーバー構築の経験: サーバーをゼロから立てたり、ネットワークの設定をしたりする必要はありません。開発環境の準備はコマンド一つで完了します。

焦らず、自分のペースで進んでいきましょう。分からなければ、その都度調べながら進むのが一番の近道です。

サーバーとクライアント間のデータの流れを比喩的に表現したイラスト。サーバーサイドレンダリングではサーバーで処理されたデータがクライアントに送信され、クライアントサイドレンダリングではクライアント側で処理される様子が描かれている。

環境構築:最初の一歩

理論を学ぶのも大切ですが、一番の学習は「手を動かすこと」です。早速、開発環境を準備して、コードを書く準備を始めましょう!

開発環境の準備(初心者向け解説)

プログラミングを始めるには、コンピューターにいくつかのソフトウェアをインストールする必要があります。今回は、JavaScriptを実行するための環境である「Node.js」と、コードを書くためのエディタ「Visual Studio Code(VSCode)」を使います。

  • Node.js: JavaScriptをブラウザの外(サーバーなど)で動かすためのプログラムです。これには、パッケージ管理ツールであるnpmも含まれています。
  • Visual Studio Code: 多くの開発者に愛用されている、高機能で無料のコードエディタです。

必要なツールとインストール方法

  1. Node.jsのインストール: Node.jsの公式サイトにアクセスし、「LTS(Long Term Support)」版をダウンロードしてインストールしてください。インストールが完了したら、ターミナル(WindowsではコマンドプロンプトやPowerShell)を開き、以下のコマンドを実行してバージョンが表示されれば成功です。

    node -v
    npm -v
  2. Next.jsプロジェクトの作成: 好きな作業ディレクトリに移動し、ターミナルで以下のコマンドを実行します。これが、私たちのプロジェクトの雛形を作成してくれる魔法のコマンドです。

    npx create-next-app@latest react-server-components-app

    実行すると、いくつか質問されます。今回はシンプルに進めるため、以下のように答えてください(Enterキーで選択)。

    • Would you like to use TypeScript? -> Yes
    • Would you like to use ESLint? -> Yes
    • Would you like to use Tailwind CSS? -> No
    • Would you like to use 'src/' directory? -> Yes
    • Would you like to use App Router? -> Yes (これがRSCを使うための重要な設定です!)
    • Would you like to customize the default import alias? -> No
  3. 開発サーバーの起動: プロジェクトのディレクトリに移動し、開発サーバーを起動します。

    cd react-server-components-app
    npm run dev

    ターミナルに表示された http://localhost:3000 をブラウザで開くと、Next.jsのウェルカムページが表示されるはずです。これで準備完了です!

環境構築でつまずきやすいポイント

環境構築は、最初の壁になりがちです。もしうまくいかなくても、それはごく普通のこと。よくあるのは、Node.jsのバージョンが古い、または会社のプロキシ設定が邪魔をしている、といったケースです。エラーメッセージをよく読み、一つずつ解決していきましょう。この試行錯誤も、エンジニアとしての大切な経験です。

基本概念の理解

環境が整ったところで、React Server Componentsの「心臓部」となる考え方を理解していきましょう。ここが分かると、コードを書くのが一気に楽しくなります。

核となる考え方

React Server Componentsの最も重要なアイデアは、「コンポーネントをサーバー側だけで実行し、その結果(HTML)だけをクライアントに送る」 というものです。

これまでのReact(Client Components)では、コンポーネントのコード(JavaScript)がすべてブラウザに送られ、ブラウザがそれを実行して画面を描画していました。しかしRSCでは、

  • Server Components (サーバーコンポーネント): サーバー上でのみ実行される。データベースへのアクセスや、重たい計算処理などを担当する。useStateuseEffectのようなインタラクティブな機能は使えない。
  • Client Components (クライアントコンポーネント): 従来通りの、ブラウザで実行されるコンポーネント。クリックイベントや状態管理など、ユーザーとのインタラクションを担当する。

この2つを適切に使い分けることで、アプリケーション全体のパフォーマンスを劇的に向上させることができるのです。Next.jsのApp Routerでは、デフォルトですべてのコンポーネントがServer Componentとして扱われます。これが大きなパラダイムシフトです。

身近な例での説明

レストランに例えてみましょう。

  • サーバー (厨房): ここでシェフが料理の大部分(HTMLの生成)を完成させます。これが Server Component の仕事です。
  • クライアント (お客様のテーブル): 運ばれてきた料理に、お客様が自分でスパイスをかけたり、ソースを混ぜたりします。この「最後の仕上げ」やインタラクティブな部分が Client Component の役割です。

厨房(サーバー)でできることは済ませておくことで、お客様のテーブル(クライアント)での待ち時間を減らし、素早く料理を提供できます。Web開発でも同じで、サーバー側で処理を済ませることで、ユーザーの体感速度を上げることができるのです。

「なぜそうなるのか」の理解

なぜこのような仕組みが生まれたのでしょうか?主な理由は3つあります。

  1. パフォーマンス向上: サーバーでレンダリングすることで、ブラウザに送るJavaScriptの量を大幅に減らせます。JavaScriptが少ないほど、ページの読み込みや表示は速くなります。
  2. データアクセスの簡素化: Server Componentはサーバー上で動くため、データベースや外部APIに直接、安全にアクセスできます。これまでのように、APIエンドポイントを別途用意する必要がなくなるケースも増えます。
レストランの厨房で料理が作られ、完成した料理が客席に運ばれる様子が描かれている。厨房がServer Component、客席がClient Componentに相当する。
  1. セキュリティ: APIキーやデータベースの接続情報など、機密情報をクライアントに送ることなく、サーバー内に留めておくことができます。

これらのメリットを理解すると、なぜRSCが「次世代の標準」と呼ばれているのかが見えてくるはずです。

実践編:手を動かして学ぶ

いよいよコーディングの時間です!簡単なブログ記事一覧ページを作りながら、Server ComponentsとClient Componentsの連携を体験していきましょう。

ステップ1: 基本的な実装

まずは、サーバーサイドでデータを取得して表示する、基本的なServer Componentを作ります。Next.jsプロジェクトの src/app/page.tsx ファイルを以下のように書き換えてください。

// src/app/page.tsx

// 記事データの型を定義します
interface Post {
  id: number;
  title: string;
  body: string;
}

// サーバーサイドでデータをフェッチする非同期関数
async function getPosts(): Promise<Post[]> {
  // JSONPlaceholderという公開APIからデータを取得します
  const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5', {
    // Next.jsのキャッシュ機能を無効にして、常に最新のデータを取得します
    cache: 'no-store',
  });

  if (!res.ok) {
    throw new Error('Failed to fetch data');
  }

  return res.json();
}

// このHomePageコンポーネントはServer Componentです
export default async function HomePage() {
  const posts = await getPosts();

  return (
    <main style={{ padding: '2rem' }}>
      <h1 style={{ marginBottom: '2rem' }}>最新記事一覧</h1>
      <ul style={{ listStyle: 'none', padding: 0 }}>
        {posts.map((post) => (
          <li key={post.id} style={{ marginBottom: '1.5rem', border: '1px solid #ccc', padding: '1rem' }}>
            <h2 style={{ marginTop: 0, marginBottom: '0.5rem' }}>{post.title}</h2>
            <p style={{ margin: 0 }}>{post.body}</p>
          </li>
        ))}
      </ul>
    </main>
  );
}

解説:

  • async function HomePage() のように、コンポーネントをasyncにできるのがServer Componentの大きな特徴です。これにより、コンポーネント内で直接awaitを使って非同期処理(データ取得など)を待つことができます。
  • getPosts関数内でfetchを使い、外部APIからデータを取得しています。この処理はすべてサーバーサイドで完結し、クライアントにはレンダリングされた後のHTMLだけが送られます。

ブラウザで http://localhost:3000 を確認すると、5つの記事タイトルと本文が表示されているはずです。これがServer Componentの基本です。

ステップ2: 機能の拡張

次に、ユーザーが操作できるインタラクティブな要素、つまりClient Componentを追加しましょう。「いいね!」ボタンを作成します。

まず、srcフォルダ内にcomponentsという新しいフォルダを作成し、その中にLikeButton.tsxというファイルを作成します。

// src/components/LikeButton.tsx

// ファイルの先頭に 'use client' を記述します
'use client';

import { useState } from 'react';

export default function LikeButton() {
  const [likes, setLikes] = useState(0);

  const handleClick = () => {
    setLikes(likes + 1);
  };

  return (
    <button onClick={handleClick} style={{ marginTop: '1rem' }}>
      👍 いいね! ({likes})
    </button>
  );
}

解説:

  • 'use client';: この一行が魔法の呪文です。これをファイルの先頭に書くことで、「このファイルとその中でインポートされるコンポーネントはClient Componentである」とNext.jsに伝えます。
  • useStateonClick: これらはユーザーの操作に応じて状態が変化し、再レンダリングを引き起こすための機能です。このようなインタラクティブな処理はClient Componentでしか使えません。

ステップ3: 実用的な応用

Server ComponentとClient Componentを組み合わせてみましょう。先ほど作成した src/app/page.tsx を修正して、各記事に LikeButton を表示させます。

// src/app/page.tsx

// LikeButtonをインポートします
import LikeButton from '@/components/LikeButton';

interface Post {
  id: number;
  title: string;
  body: string;
}

async function getPosts(): Promise<Post[]> {
  const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5', {
    cache: 'no-store',
  });

  if (!res.ok) {
    throw new Error('Failed to fetch data');
  }

  return res.json();
}

export default async function HomePage() {
  const posts = await getPosts();

  return (
    <main style={{ padding: '2rem' }}>
      <h1 style={{ marginBottom: '2rem' }}>最新記事一覧</h1>
      <ul style={{ listStyle: 'none', padding: 0 }}>
        {posts.map((post) => (
          <li key={post.id} style={{ marginBottom: '1.5rem', border: '1px solid #ccc', padding: '1rem' }}>
            <h2 style={{ marginTop: 0, marginBottom: '0.5rem' }}>{post.title}</h2>
            <p style={{ margin: 0 }}>{post.body}</p>
            {/* ここでClient Componentを呼び出します */}
            <LikeButton />
          </li>
        ))}
      </ul>
    </main>
  );
}

解説: HomePage (Server Component) の中で、LikeButton (Client Component) を何の問題もなく呼び出せています。これがRSCの強力な点です。サーバーでレンダリングされた静的なコンテンツの中に、クライアントで動作するインタラクティブな部品を「埋め込む」ことができます。

ブラウザを更新して、各記事の下に「いいね!」ボタンが表示され、クリックするとカウントが増えることを確認してください。素晴らしい!あなたはServer ComponentとClient Componentの連携をマスターしました。

ステップ4: チーム開発を意識した改善

実際の開発では、コンポーネントを適切に分割し、見通しを良くすることが重要です。記事一つ分を表示するコンポーネントを切り出してみましょう。

src/componentsフォルダにPostCard.tsxを作成します。

// src/components/PostCard.tsx

import LikeButton from './LikeButton';

interface PostCardProps {
  title: string;
  body: string;
}

// このコンポーネントは 'use client' がないのでServer Componentです
export default function PostCard({ title, body }: PostCardProps) {
  return (
    <li style={{ marginBottom: '1.5rem', border: '1px solid #ccc', padding: '1rem' }}>
      <h2 style={{ marginTop: 0, marginBottom: '0.5rem' }}>{title}</h2>
      <p style={{ margin: 0 }}>{body}</p>
      <LikeButton />
    </li>
  );
}

そして、src/app/page.tsxを修正してPostCardを使います。

// src/app/page.tsx

import PostCard from '@/components/PostCard'; // PostCardをインポート

// LikeButtonのインポートは不要になります

interface Post {
  id: number;
  title: string;
  body: string;
}

async function getPosts(): Promise<Post[]> {
  // ... (getPosts関数は変更なし)
  const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5', {
    cache: 'no-store',
  });

  if (!res.ok) {
    throw new Error('Failed to fetch data');
  }

  return res.json();
}

export default async function HomePage() {
  const posts = await getPosts();

  return (
    <main style={{ padding: '2rem' }}>
      <h1 style={{ marginBottom: '2rem' }}>最新記事一覧</h1>
      <ul style={{ listStyle: 'none', padding: 0 }}>
        {posts.map((post) => (
          // PostCardコンポーネントを使用
          <PostCard key={post.id} title={post.title} body={post.body} />
        ))}
      </ul>
    </main>
  );
}

このようにコンポーネントを分割することで、page.tsxは全体のレイアウトに集中し、個々の記事の表示はPostCardが担当するという役割分担が明確になりました。コードが読みやすくなり、チームでの開発や将来のメンテナンスが格段にしやすくなります。

実際の開発現場での活用

学んだ技術を実際の仕事でどう活かすか、イメージを膨らませてみましょう。

業務での使用例

  • ECサイトの商品一覧ページ: 商品データはServer Componentで高速に表示し、「お気に入り」ボタンやカート追加ボタンはClient Componentで実装します。
  • ブログプラットフォーム: 記事の本文やコメント一覧はServer Componentで静的に生成し、コメント投稿フォームやリアクションボタンはClient Componentにします。
  • 企業のダッシュボード: 最初に表示されるグラフや数値データはサーバーで取得・レンダリングし、ユーザーが操作する日付フィルターなどはClient Componentで動的に制御します。

チーム開発でのベストプラクティス

  • 'use client'の境界を意識する: なるべくコンポーネントツリーの末端(葉)に近いコンポーネントをClient Componentにしましょう。これにより、サーバーでレンダリングできる範囲が最大化され、パフォーマンスが向上します。
  • 責務の分離: データ取得やビジネスロジックはServer Componentに寄せ、UIの状態管理やイベントハンドリングはClient Componentに集中させるように設計すると、見通しが良くなります。
  • コンポーネントの命名規則: チーム内で「このコンポーネントはサーバーとクライアントのどちらで動くか」が分かるような命名規則(例: LikeButton.client.tsx)を検討するのも良いでしょう。(ただし、これは規約であり、Next.jsの機能ではありません)

保守性を意識した書き方

「動けば良い」ではなく、「半年後の自分やチームメイトが読んでも理解できる」コードを目指すことがプロの仕事です。

  • 状態の置き場所を考える: 本当にクライアント側で状態を持つ必要があるのかを常に問いかけましょう。URLのクエリパラメータで状態を管理できる場合もあります。
  • Server ComponentからClient Componentへのデータ渡し: Server ComponentからClient ComponentへはProps経由でデータを渡せます。渡すデータはシリアライズ可能(JSONに変換可能)である必要があります。関数などを直接渡すことはできないので注意しましょう。

よくあるつまずきポイントと解決策

新しいことを学べば、必ず壁にぶつかります。それは成長の証です。よくある失敗例とその対処法を知っておきましょう。

初心者が陥りやすい問題

  • Server Componentで useStateuseEffect を使おうとする: これは最もよくあるエラーです。これらのフックはブラウザ上で動作する必要があるため、Client Componentでしか使えません。エラーメッセージが「useState is not defined」などと表示されたら、'use client'; をファイルの先頭に追加し忘れていないか確認しましょう。
  • Client Componentに async をつけてしまう: async/awaitをコンポーネントで直接使えるのはServer Componentの特権です。Client Componentでデータを取得したい場合は、従来通りuseEffectの中で非同期関数を呼び出す必要があります。

エラーメッセージの読み方

Next.jsのエラーメッセージは非常に親切です。例えば、Server ComponentでuseStateを使おうとすると、「You’re importing a component that needs useState. It only works in a Client Component.」といったメッセージが表示されます。慌てずにメッセージを読み、「Client Componentでしか動かないのか。じゃあ'use client'が必要だな」と原因を推測する訓練をしましょう。

デバッグの基本的な考え方

  • Server Componentのデバッグ: console.log()を仕込んだ場合、その出力はブラウザの開発者コンソールではなく、開発サーバーを動かしているターミナルに表示されます。
  • Client Componentのデバッグ: こちらは従来通り、ブラウザの開発者コンソールconsole.log()の出力を確認できます。

この「どこに出力されるか」の違いを理解することが、RSC時代のデバッグの第一歩です。

継続的な学習のために

今回のチュートリアルは、広大なRSCの世界への入り口に過ぎません。ここからさらに学びを深めていくための道筋を示します。

次に学ぶべきこと

  • Server Actions: フォームの送信などを、クライアントサイドのJavaScriptなしでサーバー側の関数を直接呼び出して処理する仕組みです。よりシームレスなフルスタック開発を実現します。
  • Streaming (ストリーミング): ページの重たい部分の準備ができていなくても、先に準備ができた部分から順次表示していく技術です。ユーザーの体感速度をさらに向上させます。
  • Next.jsのキャッシュ戦略: revalidateオプションなどを使い、データの鮮度とパフォーマンスをどう両立させるかを学ぶことは、実運用において非常に重要です。

おすすめの学習リソース

最高の情報源は、いつの時代も公式ドキュメントです。Next.jsやReactの公式ドキュメントは非常に丁寧に書かれており、最新かつ正確な情報が手に入ります。技術ブログや動画も素晴らしいですが、まずは公式ドキュメントに目を通す習慣をつけましょう。

コミュニティとの関わり方

一人で学習していると、モチベーションの維持が難しいこともあります。X(旧Twitter)で同じ技術を学ぶ仲間を探したり、勉強会に参加したりすることで、新しい情報に触れたり、疑問を解決するきっかけが得られます。アウトプットすることも重要です。学んだことをブログにまとめたり、小さな作品を作って公開したりすることで、知識はより深く定着します。

まとめ:成長のための次のステップ

ここまで本当にお疲れ様でした!あなたはReact Server Componentsの基本を理解し、実際に動くアプリケーションを作成することができました。これは非常に大きな一歩です。

RSCは、Web開発の常識を大きく変える可能性を秘めた技術です。サーバーとクライアントの境界線をより柔軟にし、開発者が「何を作りたいか」に集中できる環境を提供してくれます。

今日学んだことを土台として、ぜひ自分だけのオリジナルアプリケーションを作ってみてください。失敗を恐れず、たくさんのエラーを経験してください。その一つ一つが、あなたをより優れたエンジニアへと成長させてくれるはずです。

プログラミング学習の旅は長く、時に困難な道ですが、新しいことを学び、自分の手で何かを創り出す喜びは、何物にも代えがたいものです。これからも一緒に、楽しみながら学び続けていきましょう!

関連記事