React Server ComponentsとApp Routerをそろそろちゃんと理解したい

<strong>「仕事で React や Next.js を使っているのに、RSC とか App Router とか Suspense とか何にも分からない。。やばい。。。」</strong>と焦りを覚えたので、勉強しつつ、Qiita にまとめてみました。

この記事を読めば、以下の項目が理解できるようになるでしょう。

- React や Next.js の基礎知識
- React Server Components とは何か？
- React Server Components のレンダリングの流れ
- Suspense とは何か？
- App Router とは何か？

少しでも私と同じような悩みを抱えている方の助けになれば幸いです。

## TL;DR

- React は UI を簡単に構築するための JavaScript ライブラリ
- Next.js は React のフレームワーク
- React は以下の流れでレンダリング（CSR）を行う
  1. レンダリングのトリガーを検知
  2. ブラウザレンダリングする内容の決定
  3. 変更を DOM に適用
- Next.js には以下の二つのモード（ルーティング方式）がある
  - Pages Router
  - App Router
- Next.js の Pages Router では以下の 4 つのレンダリング方式を選択できる
  - SSR
  - SSG
  - ISR
  - CSR
- React Server Components(RSC) とは、コンポーネントを「サーバー側でレンダリングされるコンポーネント」と「クライアント側でレンダリングされるコンポーネント」に分ける技術
- Next.js の App Router では、デフォルトで作成したコンポーネントがサーバーコンポーネントになる
  - クライアントコンポーネントにするには`use client`を記述する必要がある
- RSC と SSR を組み合わせることで、初期表示を早めつつ、クライアント側に送信する JavaScript の量を抑えることができる
- Suspense は、useState に頼らずに「ローディング中」を表現できる機能
- Suspense と React Server Components を使用することで、サーバー側でもコンポーネント単位の非同期的なデータ取得が可能となる

## React と Next.js について

まずは、React と Next.js について簡単に解説します。

[React](https://react.dev/)とは、<strong>UI を簡単に構築するための JavaScript ライブラリ</strong>です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/9c6f61f5-2f7b-0ec7-32c0-b41ef114cc24.png)

「コンポーネント」という概念を使って宣言的に UI を定義することで、簡単に画面の構築を行うことができます。

そして、[Next.js](https://nextjs.org/) とは <strong>React のフレームワーク</strong>です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/f43ba74c-4a25-a771-4418-89cc1780e54f.png)

「Reactの機能を拡張して、より使いやすくしたもの」と捉えて良いでしょう。

## React のレンダリングについて

React は、create-react-app で作成された初期状態では、CSR（クライアントサイドレンダリング）でレンダリングを行います。

CSR とは、<strong>ブラウザ上で JavaScript を実行して DOM を生成しコンテンツを表示させる方法</strong>です。

![1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/46eaea37-152d-16f4-dac1-e9a1e2a9ec1f.jpeg)

ページの初期ロード時にはコンテンツは何も表示されず、<strong>ブラウザでの JavaScript の実行後に初めて画面が表示されます。</strong>

より詳細に説明すると、React のレンダリングは、以下のステップで行われます。

1. レンダリングのトリガーを検知
2. ブラウザレンダリングする内容の決定
3. 変更を DOM に適用

### 1. レンダリングのトリガーを検知

まずは、レンダリングのきっかけとなるトリガーを検知します。

ここでのトリガーとは、以下の二つです。

- コンポーネントの初期レンダリング（画面の初期ロード）
- コンポーネントの状態（state）の更新

このいずれかのトリガーを検知したとき、React はレンダリングを開始します。

### 2. ブラウザレンダリングする内容の決定

次に、ブラウザレンダリングする内容を決定します。

これは以下の流れで行われます。

1. 対象のコンポーネントの呼び出し
2. 以前のコンポーネントの状態との比較
3. コミット（ブラウザレンダリング）する内容の決定

1 の「対象のコンポーネントの呼び出し」について、最初のレンダリングではルートコンポーネントを呼び出し、それ以降のレンダリングでは、状態の更新がレンダリングのトリガーとなったコンポーネントを呼び出します。

そして、2 で以前のコンポーネントの状態との差分を計算し、（画面を更新する必要があるかどうかを含めて）画面更新の内容を決定します。

最後に、差分が検知されて画面を更新する必要がある場合、3 を実行します。

つまり、<strong>React におけるレンダリング</strong>とは、<strong>「レンダリング対象のコンポーネントを呼び出して前回の内容との差分を比較し、何をブラウザレンダリング（commit）するかを決定すること」</strong>と言えるでしょう。

同じ「レンダリング」という名前が付いているので混同しがちですが、<strong>「ブラウザ・レンダリング（画面への描画）」とは異なる概念</strong>なので、しっかりと区別するようにしましょう。

順番としては、「React のレンダリング」を行った後に「ブラウザのレンダリング」を行うイメージです。

※ Reactの公式では「Reactのレンダリング」と「ブラウザのレンダリング」を区別するために、「ブラウザのレンダリング」を <strong>painting(ペインティング)</strong> と表現しています

### 3. 変更を DOM に適用

2 の「ブラウザレンダリングする内容の決定」で以前の状態との差分があった場合、その差分をコミット（変更を DOM に適用）します。

つまり、<strong>DOM ツリーの構造の修正</strong>を行います。

そして、<strong>React によるこれらの 3 つの処理が行われた後、ブラウザはその変更を画面に適用します</strong>（ブラウザ・レンダリング/ペインティング）。

以上の流れにより、React で画面の表示や更新が行われます。

※ これらの 3 つの流れについて、[Reactの公式](https://react.dev/learn/render-and-commit)ではレストランを例にして解説されています。とても分かりやすいのでぜひ読んでみてください。

## Next.js のレンダリングについて

次に、Next.js のレンダリング方式についてまとめていきます。

2023 年 9 月現在、<strong>Next.js には以下の二つのモード（ルーティング方式）があります。</strong>

もともとは Pages Router しかありませんでしたが、最近 App Router が追加されました。

- Pages Router
- App Router

最初に Pages Router でのレンダリングについてまとめていきます。

## Next.js のレンダリングについて(Pages Router)

デフォルトでは、Next.js はすべてのページを<strong>「プリレンダリング」</strong>します。

プリレンダリングとは、<strong>Next.js が各ページの HTML をクライアントサイド JavaScript で生成するのではなく、あらかじめ生成しておくこと</strong>です。

このプリレンダリングにより、パフォーマンスと SEO が向上すると言われています。

生成された HTML は必要な JavaScript と関連づけられており、ページがブラウザによってロードされると、その JavaScript が実行され、ページが完全にインタラクティブになります。（これをハイドレーションと言います）

Next.js（Pages Router）には以下の 2 種類のプリレンダリング方式があり、状況に応じて使い分けることができます。

- 静的生成(Static Generation)
  - HTML はビルド時に生成され、リクエストごとに再利用される
- サーバサイドレンダリング(Server-side Rendering)
  - HTML はリクエストごとに生成される

## ハイドレーションとは何か？

ハイドレーションとは、<strong>サーバー側からレンダリングされた HTML に紐付けられた JavaScript を実行し、対象のページを完成された状態にすること</strong>です。

要はこういう流れです。

1. (SSR などで)サーバー側から HTML が返る
2. クライアントに送信された JavaScript を実行する(イベントリスナの登録やインタラクティブな動作の追加)

サーバから受け取った初期 HTML は、インタラクティブな機能を持たない乾いた HTML で、そこにクライアント側で水分（必要な設定や機能）を加えてやるイメージです。

主に SSR のように、サーバー側で HTML を生成して返す場合に使用されます。

## Next.js のレンダリング種別について(Pages Router)

React の場合は基本的には CSR での描画を行なっていましたが、Next.js では以下のように様々なレンダリング方式を選択できます。

- SSR
- SSG
- ISR
- CSR

それぞれを簡単に解説していきます。

### SSR(Server-side Rendering)

SSR は Dynamic Rendering とも言われています。

<strong>ページの HTML がリクエストごとに生成される方式</strong>です。

![2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/123e786d-26b5-cae9-9c7d-3b99f0afd024.jpeg)

<strong>サーバー側で生成された生の HTML(DOM) が JavaScript の実行前にブラウザで表示されるため、画面の初期表示速度を速めることができます。</strong>

これはユーザー体験の向上はもちろんのこと、SEO にも効果があると言われています。

Pages Router でサーバサイド・レンダリングを使用するには、getServerSideProps という非同期関数をエクスポートします。

### SSG(Static Site Generation)

SSG を使用する場合、<strong>ページの HTML はビルド時に生成されます。</strong>

この HTML はリクエストごとに再利用され、CDN によってキャッシュすることも可能です。

Next.js では、データが存在しないページ（静的 HTML）だけでなく、getStaticProps や getStaticPaths を使用して build 時にデータを取得・登録して HTML を生成することもできます。

SSG ではリクエストのたびにサーバーがページをレンダリングする必要がないため、<strong>レンダリングが非常に高速</strong>なのが特徴です。

そのため、基本的には<strong>レンダリング方式として SSG の使用が推奨されています。</strong>

### ISR(Incremental Static Regeneration)

SSG は、事前にページを生成しておき、リクエストごとにその静的なコピーを提供する手法ですが、ISR はこれを更に発展させたものです。

ISR を使うことで、静的ページがあらかじめ生成された後も、一定の時間間隔でそのページを再生成することができます。

これにより、<strong>SSG による高速レスポンスを実現しつつも、ある程度のリアルタイム性も提供することができます。</strong>

ISR の実装は、Next.js のページごとに revalidate というパラメータを設定することで行います。

このパラメータには、再生成の間隔を指定します。例えば、revalidate: 60 と設定すると、60 秒ごとにページが再生成されます。

つまり、ISR は、<strong>静的なコンテンツを効果的に提供しつつ、定期的に最新の情報を反映させる仕組みを提供する機能</strong>です。

### CSR(Client-side rendering)

CSR は React のデフォルトのレンダリング方式です。

![1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/998d2db2-6cfc-d48d-7275-15988976e051.jpeg)

ブラウザ上で JavaScript を実行して DOM を生成しコンテンツを表示させます。

Next.js では useEffect フックを使用するなどして CSR を実装することができます。

## RSC（React Server Components）とは何か？

次に、いよいよ Pages Router に続く Next.js 二つ目のモードである、App Router について解説していきます。

ただ、App Router を理解するためには、そのベースの技術である RSC（React Server Components）を理解する必要があります。

そのため、まずは<strong>「RSC とは何か」</strong>について説明していきます。

\---

もともと React には、先ほど説明した CSR しかありませんでした。

しかし、<strong>CSR の場合、クライアントに全てのコンポーネントのリソース（JavaScript）を送信する必要があるため、クライアント側のパフォーマンス悪化が懸念されていました。</strong>

![3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/f891d878-aa7b-480b-b967-4a47c7eb4ce4.jpeg)

そこで誕生したのが、RSC（React Server Components）です。

RSC とは、一言で言うと、<strong>コンポーネントを「サーバー側でレンダリングされるコンポーネント」と「クライアント側でレンダリングされるコンポーネント」に分ける技術</strong>です。

これまで React には「クライアントコンポーネント」しかありませんでした。しかし <strong>RSC では、どのコンポーネントをサーバー専用にし、どのコンポーネントをクライアント専用にするかを選択できます。</strong>

![4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/167a674a-a45d-c5cb-1ec9-fb3155e0e6e1.jpeg)

RSC により、データ取得をより DB に近いサーバー側で実行でき、サーバーコンポーネントや依存パッケージの分クライアント側に送信する JavaScript のサイズ（bundle サイズ）を減らせるため、パフォーマンスが向上すると言われています。

さらに、RSC には以下のような特徴があります。

- サーバー側からより高速にデータ取得が可能になる（クライアントからのリクエスト量も減る）
- console.log はブラウザのコンソールではなく、サーバーのコンソールに情報を出力する
- onClick や onChange などのイベントリスナーは使用できない
- 状態管理（useState）と効果管理（useEffect）は使用できない
- サーバーコンポーネントはクライアントコンポーネントをインポートしてレンダリングできるが、クライアントコンポーネントはその中のサーバーコンポーネントをレンダリングできない

※ ただし、RSC を使えば無条件にbundleサイズが減少するというわけではないようです。詳しくは以下を参考にしてください

https://qiita.com/uhyo/items/06b0cd7292256f66d7b7

## App Router とは何か？

App Router とは Next.js13 で追加された、新しいルーターの実装です。

<strong>App Router では、デフォルトで RSC（React Server Components）が適用されます。</strong>つまり、作成したコンポーネントがデフォルトでサーバー側で実行されるということです。

<strong>クライアント側で実行させるには、コンポーネントのトップに`use client` を定義する必要があります。</strong>

できるだけサーバー側に処理を寄せることで、パフォーマンスの改善を図ろうとする意図が読み取れます。

基本的にはサーバーコンポーネントとして実装して、必要な箇所だけクライアントで実行させるのが基本的な考え方と言えるでしょう。

## SSR と App Router(RSC)の違いについて

次に、多くの初学者が混乱しがちな、「SSR と RSC の違い」について、図を交えつつ解説します。

SSR は、以下のようにレンダリングされます。

![2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/bb81110d-0221-9af6-d9c0-2544a9752849.jpeg)


1. サーバー側で全体をレンダリングし、生の HTML にする
2. 生成した HTML を DOM に反映させてクライアント側で表示する（初期表示を早める）
3. bundle した JavaScript（コンポーネント）をクライアントに送信しハイドレーションを行う

<strong>最初にサーバー側で HTML を生成してクライアント側に反映させることで初期表示を早めるのが最大の特徴</strong>です。

一方、RSC では、以下のようにレンダリングされます。

![5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/25c951fe-0196-3b95-5b9d-05b2a92b6cb1.jpeg)

1. サーバー側でサーバーコンポーネントをレンダリングする
2. サーバーコンポーネントの HTML とクライアントコンポーネントの JavaScript をクライアントに送信する
3. クライアントコンポーネントをレンダリングする
4. 生成した HTML を DOM に反映させてクライアント側で表示する

大きな違いは以下の三つです。

- SSR の場合は初期表示が速い
- RSC の場合はサーバーとクライアントでそれぞれのコンポーネントがレンダリングされる
- SSR の方がクライアントに送信される JavaScript の量が多い

この <strong>SSR と RSC は交わらない技術ではなく、組み合わせて使用することもできます。</strong>（というか、組み合わせた方が有効な場合が多いです）

SSR と RSC を組み合わせた場合、処理の流れは以下のようになります。

![6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/c40ac409-8465-fbe5-51c5-bcfa3697bdf4.jpeg)

1. サーバー側でサーバーコンポーネントをレンダリングする
2. サーバー側でクライアントコンポーネントもレンダリングする（SSR特有の挙動）
3. 生成したサーバーコンポーネントとクライアントコンポーネントの HTML をクライアント側に送信して DOM に反映させてクライアント側で表示させる（初期表示を早める）
4. クライアント側に JavaScript（クライアントコンポーネント）を送信し、レンダリングとハイドレーションを実行する

このように、<strong>SSR と RSC を組み合わせることで、初期表示を早めつつ、クライアント側に送信する JavaScript の量を抑えることができます。</strong>

そのため、基本的には RSC 単体で使うよりも、RSC と SSR を組み合わせて使うことの方が多いでしょう。

## React Server Components とデータの取得について

ここまでは、特に「外部からのデータの取得」は意識せずに解説してきました。

しかし、実際のアプリケーションだと外部（DB や API）からデータを取得して使用することが多いでしょう。

次に、その場合にどういった流れになるのかについて解説します。

\---

もともと、クライアント側では useState 等を使ってローディング中の状態を表現することで、コンポーネント単位での非同期的なデータ取得が可能でした。

しかし、<strong>SSR をする場合、useState 等を使ってサーバー側でコンポーネント単位で非同期的なデータ取得ができない問題がありました。</strong>

Next.js の getServerSideProps を使ってデータの取得自体はできていましたが、データ取得が「同期的」という課題がありました。

これだと、ページをユーザーに表示する前に、サーバーでのデータ取得をすべて完了させる必要があるため、ユーザーに対する画面の初期表示が遅れてしまうことがあります。

![7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/d4fdcdbe-d232-0b91-cd52-7967102d86d9.jpeg)


しかし、この状況が Suspense と React Server Components の登場で変わります。

<strong>Suspense とは、useState 等に頼らずに「ローディング中」を宣言的に表現できる機能</strong>です。

例えば、以下のようなコンポーネントを用意します。

```tsx
import { Suspense } from "react";
import Loading from "./components/loading";
import { ServerComponent } from "./components/ServerComponent";

export default async function Home() {
  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        marginTop: "200px",
      }}
    >
      <Suspense fallback={<Loading />}>
        <ServerComponent />
      </Suspense>
    </div>
  );
}
```

これを画面に表示すると、以下のようになります。

![suspense.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/638fe544-f3d2-5d37-ea87-eaeace28d556.gif)

useState を使わずに「ローディング中」を表現できていることがわかるでしょう。

また、<strong>React Server Components では、Async/await（非同期関数）を使用することができます。</strong>

このような特性を持った Suspense と React Server Components を使うことで、<strong>SSR を使用するサーバー側でも（getSSP を使わない）コンポーネント単位の非同期的なデータ取得ができるようになりました。</strong>

![8.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/1b7b2c9a-9d38-6fe6-a0df-0230d209e6fd.jpeg)

これにより、完全にデータ取得をする前にユーザーは画面を見ることができます。従来の SSR よりも表示速度を早めることができるでしょう。

<strong>SSR でデータ取得をする場合、RSC を使用してサーバーコンポーネント内で Suspense を使って loading 中を表現しつつデータ取得をするのが現状の定石</strong>と言えるでしょう。

## 実際に RSCとApp Router を触ってみる

次に、実際にアプリを動かしつつ、挙動を確かめていこうと思います。

簡単な操作なので、ぜひ実際に手を動かして試してみてください。

前提として、Node.js,npm,TypeScript は使える状態になっているとします。

https://www.engilaboo.com/build-environment-of-typescript/

まずは、任意のディレクトリ配下で以下のコマンドを実行して Next.js をインストールします。

```
npx create-next-app@latest .
```

※聞かれた質問は全部 Enter でOKです。Pages Routerではなく、App Routerを選択してください

最初に、シンプルなサーバーコンポーネントとクライアントコンポーネントを作っていきます。

以下のファイルをそれぞれ作成してください。

```tsx:app/page.tsx
import { ClientComponent } from "./components/ClientComponent";
import { ServerComponent } from "./components/ServerComponent";

export default async function Home() {
  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        marginTop: "200px",
      }}
    >
      <ClientComponent />
      <ServerComponent />
    </div>
  );
}
```

```tsx:app/components/ServerComponent.tsx
export async function ServerComponent() {
  const boxStyle = {
    width: "400px",
    height: "300px",
    backgroundColor: "#006400",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
  };

  const textStyle = { color: "white", footSize: "larger", fontWeight: "bold" };

  console.log("Server Componentを実行しています");

  return (
    <div style={boxStyle}>
      <p style={textStyle}>Server Component</p>
    </div>
  );
}
```

```tsx:app/components/ClientComponent.tsx
"use client";

export function ClientComponent() {
  const boxStyle = {
    width: "400px",
    height: "300px",
    backgroundColor: "#ffff00",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
  };

  const textStyle = { footSize: "larger", fontWeight: "bold", color: "black" };

  console.log("Client Componentを実行しています");

  return (
    <div style={boxStyle}>
      <p style={textStyle}>Client Component</p>
    </div>
  );
}
```

App Router では、コンポーネントはデフォルトでサーバーコンポーネントになります。

クライアントコンポーネントにするには、ページの最初に`use client;`を記述する必要があります。

ここでは、<strong>サーバーコンポーネントとクライアントコンポーネントをそれぞれ作成し、root コンポーネント（page.tsx）で両方を読み込みました。</strong>

ここまで出来たら、`npm run dev`でアプリケーションを実行します。

`localhost:3000` にアクセスすると、以下の画面が表示されるでしょう。

![Screen Shot 0005-09-17 at 13.40.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/ed9bd3bd-e2a6-7d72-534a-e32208927c4a.png)

まずは開発者ツールのコンソールを開きます。
すると、クライアント側でクライアントコンポーネントだけが実行（レンダリング）されていることが分かります。

![Screen Shot 0005-09-17 at 13.42.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/c28f8c56-ccdd-27fc-7dc7-f49892cc8812.png)

さらに、Network タブを見てみると、page.js は 39.2kB になっています。

![Screen Shot 0005-09-17 at 13.47.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/99b552f6-8102-779a-8add-8d2d8128074b.png)


次に、以下のように全てのコンポーネントに`use client;`を付けます。

```tsx:app/page.tsx
"use client";

import { ClientComponent } from "./components/ClientComponent";
import { ServerComponent } from "./components/ServerComponent";

export default async function Home() {
  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        marginTop: "200px",
      }}
    >
      <ClientComponent />
      <ServerComponent />
    </div>
  );
}
```

```tsx:app/components/ServerComponent.tsx
"use client";

export async function ServerComponent() {
  const boxStyle = {
    width: "400px",
    height: "300px",
    backgroundColor: "#006400",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
  };

  const textStyle = { color: "white", footSize: "larger", fontWeight: "bold" };

  console.log("Server Componentを実行しています");

  return (
    <div style={boxStyle}>
      <p style={textStyle}>Server Component</p>
    </div>
  );
}
```

```tsx:app/components/ClientComponent.tsx
"use client";

export function ClientComponent() {
  const boxStyle = {
    width: "400px",
    height: "300px",
    backgroundColor: "#ffff00",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
  };

  const textStyle = { footSize: "larger", fontWeight: "bold", color: "black" };

  console.log("Client Componentを実行しています");

  return (
    <div style={boxStyle}>
      <p style={textStyle}>Client Component</p>
    </div>
  );
}
```

この状態で再度画面を表示します。

すると、ServerComponentもクライアント側で表示されていることが分かります。

![Screen Shot 0005-09-17 at 13.50.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/293098c4-ff77-51d9-716b-0d2d22bb5e1e.png)

そして、ネットワークタブを見ると、page.js のサイズが 40.6kB になっています。

![Screen Shot 0005-09-17 at 13.51.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/f10f2543-0cfb-3413-1559-d3d3a2e7bcec.png)

<strong>ServerComponent をクライアントコンポーネントにした分、サイズが増加している</strong>ことが分かると思います。

今度は逆に、以下のように全てのコンポーネントから`use client`を消します。

```tsx:app/page.tsx
import { ClientComponent } from "./components/ClientComponent";
import { ServerComponent } from "./components/ServerComponent";

export default async function Home() {
  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        marginTop: "200px",
      }}
    >
      <ClientComponent />
      <ServerComponent />
    </div>
  );
}
```

```tsx:app/components/ServerComponent.tsx
export async function ServerComponent() {
  const boxStyle = {
    width: "400px",
    height: "300px",
    backgroundColor: "#006400",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
  };

  const textStyle = { color: "white", footSize: "larger", fontWeight: "bold" };

  console.log("Server Componentを実行しています");

  return (
    <div style={boxStyle}>
      <p style={textStyle}>Server Component</p>
    </div>
  );
}
```

```tsx:app/components/ClientComponent.tsx
export function ClientComponent() {
  const boxStyle = {
    width: "400px",
    height: "300px",
    backgroundColor: "#ffff00",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
  };

  const textStyle = { footSize: "larger", fontWeight: "bold", color: "black" };

  console.log("Client Componentを実行しています");

  return (
    <div style={boxStyle}>
      <p style={textStyle}>Client Component</p>
    </div>
  );
}
```

`use client`を消して、サーバーコンポーネントに変更しました。

この状態で再度画面を表示します。

コンソールには、何も表示されません。

![Screen Shot 0005-09-17 at 13.53.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/68a81c89-364a-e224-1353-d1c89d19fac1.png)

そして、ネットワークタブを見ると、page.js が消えていることが分かると思います。

![Screen Shot 0005-09-17 at 13.53.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/df289437-c77e-0031-86c2-588c76a4ea57.png)

これは、<strong>コンポーネントの全てのレンダリングがサーバー側で行われた</strong>ことを示しています。

次に、<strong>Suspense を使ってサーバーコンポーネントでの非同期的なデータ取得をシミュレーション</strong>してみます。

それぞれ以下のように変更してください。

```tsx:app/page.tsx
import { Suspense } from "react";
import { ClientComponent } from "./components/ClientComponent";
import Loading from "./components/loading";
import { ServerComponent } from "./components/ServerComponent";

export default async function Home() {
  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        marginTop: "200px",
      }}
    >
      <ClientComponent />
      <Suspense fallback={<Loading />}>
        <ServerComponent />
      </Suspense>
    </div>
  );
}
```

```tsx:app/components/ServerComponent.tsx
const sleep = async (ms: number) => {
  return new Promise((res) => setTimeout(res, ms));
};

export async function ServerComponent() {
  console.log("ServerComponentを実行しています(sleepの前)");

  // データの取得をイメージ
  await sleep(3000);

  const boxStyle = {
    width: "400px",
    height: "300px",
    backgroundColor: "#006400",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
  };

  const textStyle = { color: "white", footSize: "larger", fontWeight: "bold" };

  console.log("Server Componentを実行しています(sleepの後)");

  return (
    <div style={boxStyle}>
      <p style={textStyle}>Server Component</p>
    </div>
  );
}
```

```tsx:app/components/ClientComponent.tsx
"use client";

export function ClientComponent() {
  const boxStyle = {
    width: "400px",
    height: "300px",
    backgroundColor: "#ffff00",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
  };

  const textStyle = { footSize: "larger", fontWeight: "bold", color: "black" };

  console.log("Client Componentを実行しています");

  return (
    <div style={boxStyle}>
      <p style={textStyle}>Client Component</p>
    </div>
  );
}
```

さらに、以下の loading.tsx も追加します。

```tsx:app/components/loading.tsx
export default function Loading() {
  const boxStyle = {
    width: "400px",
    height: "300px",
    backgroundColor: "#CACACA",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
  };

  const textStyle = { color: "black", fontSize: "larger", fontWeight: "bold" };

  return (
    <div style={boxStyle}>
      <p style={textStyle}>...Loading</p>
    </div>
  );
}
```

この状態で画面を表示すると、以下のようになります。

![loading.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351205/904190af-b790-6e87-f8ae-f283a058cd63.gif)

<strong>サーバー側で非同期的にデータを取得した上で画面の表示を行っている</strong>ことが分かると思います。

## まとめ

最後にもう一度内容をまとめておきます。

- React は UI を簡単に構築するための JavaScript ライブラリ
- Next.js は React のフレームワーク
- React は以下の流れでレンダリング（CSR）を行う
  1. レンダリングのトリガーを検知
  2. ブラウザレンダリングする内容の決定
  3. 変更を DOM に適用
- Next.js には以下の二つのモード（ルーティング方式）がある
  - Pages Router
  - App Router
- Next.js の Pages Router では以下の 4 つのレンダリング方式を選択できる
  - SSR
  - SSG
  - ISR
  - CSR
- React Server Components とは、コンポーネントを「サーバー側でレンダリングされるコンポーネント」と「クライアント側でレンダリングされるコンポーネント」に分ける技術
- Next.js の App Router では、デフォルトで作成したコンポーネントがサーバーコンポーネントになる
  - クライアントコンポーネントにするには`use client`を記述する必要がある
- RSC と SSR を組み合わせることで、初期表示を早めつつ、クライアント側に送信する JavaScript の量を抑えることができる
- Suspense は、useState に頼らずに「ローディング中」を表現できる機能
- Suspense と React Server Components を使うことで、SSR を使用するサーバー側でもコンポーネント単位の非同期的なデータ取得が可能となる

## おわりに

今回は、React Server Components や App Router などのフロントエンドのトレンド技術にキャッチアップすべく、勉強しつつQiitaにまとめてみました。

サンプルコードを書いたりする中でイメージを深めることはできましたが、まだまだ深く理解できているとは言い難い状況です。

自分で試すのはもちろんのこと、実務でもチャンスがあれば積極的に React Server Components や App Router を試していきたいと考えています。

この記事が React Server Components や App Router の勉強をする上で、少しでも参考になっていれば幸いです。

最後まで読んでいただきありがとうございました。

## 主な参考資料
- [一言で理解するReact Server Components](https://zenn.dev/uhyo/articles/react-server-components-multi-stage)
- [Next.js 公式ドキュメント](https://nextjs.org/docs)
- [What's "Next" JS Meetup](https://www.youtube.com/watch?v=WHMm6w41_WI&ab_channel=TimeeEngineering)
- [React Server Components の仕組み：詳細ガイド](https://postd.cc/how-react-server-components-work/)
- [Nextjs で理解する React Server Components 徹底解説【React18】](https://youtu.be/A78v05JSyqg?si=EJiKhE35K11TbcGe)
- [Understanding React Server Components](https://vercel.com/blog/understanding-react-server-components)
- [Next.js から学ぶ Web レンダリング ~React 誕生以前から App Router with RSC までの流れ~](https://zenn.dev/suzu_4/articles/2e6dbb25c12ee5)
- [Render and Commit](https://react.dev/learn/render-and-commit)