【Next.js/App Router】レンダリングについて

## はじめに
　エンジニアを目指す26卒大学院生です。個人開発では主にNext.jsを使用することが多いです。しかし、ファイルベースルーティングなどの開発の容易さから選んでおり、Next.jsの一番の強みと言われるサーバーサイドレンダリングを十分に活用できていないと感じています。そこで、今回はそのレンダリング方法についてまとめたいと思います。なるべくシンプルにわかりやすく書いたつもりなのでぜひ一読お願いいたします！

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/bdaa8964-a23d-2452-bd47-852774ac1748.jpeg">

:::note warn
技術系の記事についてはしっかり調べた上で掲載しておりますが、情報に間違いがあれば、気軽にコメントしていただけると幸いです！
:::

:::note warn
Next.jsのバージョンは14.2.5で**App Router**を使用しています。
:::

## レンダリング種類
　レンダリングとは一言で説明すると、「HTML/CSS/JavaScriptを組み合わせて、普段見ているページの状態にすること」である。Next.jsは4種類のレンダリングをサポートしている。
 
### 1. <font color="Red">CSR</font>(Client Side Rendering)
　サーバから最小限のデータを受け取り、**クライアント側**でレンダリングする方式。フレームワークなしの**React単体はCSR**である。

#### 【メリット】
+ 一度ページを読み込んでしまえば、ページ遷移のたびにサーバへのリクエストが発生することがないため、画面遷移が高速
+ サーバの負荷が少ない → サーバのスペックに依存しないため対応サーバが多い

#### 【デメリット】
+ 初回ロード時に全データをレンダリングするため初期表示が遅い
+ パフォーマンスがクライアント側のスペックに依存する
+ リクエストに対して空のHTMLとJSを返すため、SEOに対応できない

#### 【用途】
+ SEOが必要ないページ
+ 動的にデータを更新したいページ

### 2. <font color="Red">SSR</font>(Server Side Rendering)
　**サーバ側**でレンダリングを行ったものをクライアントへ返す方式。

#### 【メリット】
+ 初回ロードがCSRよりも速い
+ 常に最新のデータを提供できる
+ 初回ロードが早い＋サーバから完全なHTMLが返ってくるためSEOに強い
+ クライアント側での情報漏洩の心配がなくなる

#### 【デメリット】
+ サーバにNode.js環境が必要 → 対応サーバが限られる
+ リクエスト毎にレンダリングするため、ユーザ操作によって発生する動的な画面遷移に弱い(WebSocket等)


#### 【用途】
+ データのリアルタイム性が求められる大規模なアプリケーション

### 3. <font color="Red">SSG</font>(Static Site Generation)
　**ビルド時にページを事前に生成**しておく方式。クライアント側からのリクエストに対して生成したページを返す。

#### 【メリット】
+ すでにページを生成しているため表示速度がかなり速い
+ サーバから完全なHTMLが返ってくるためSEOに強い
+ サーバの負荷が少ない → サーバのスペックに依存しないため対応サーバが多い

#### 【デメリット】
+ ビルド後に動的にページの内容を変更することができないため、頻繁な更新に不向き
+ リアルタイム性が低い

#### 【用途】
+ ブログやホームページなどの更新頻度が低いサービス


### 4. <font color="Red">ISR</font>(Incremental Static Regeneration)
　SSGにSSRを統合したようなイメージで、指定した**時間間隔で再ビルドする**方式。SSGのデメリットを打ち消すことができる。

#### 【メリット】
+ SSGの高速性を維持しつつ、指定した時間間隔でページを再生成できる
+ 必要なページだけを更新するため大規模サイトでも効率的に最新コンテンツを提供できる
+ サーバから完全なHTMLが返ってくるためSEOに強い

#### 【デメリット】
+ 更新のタイミングによっては古い情報が表示される

#### 【用途】
+ 頻繁に更新はされるがリアルタイム性があまり重視されないサービス

<br>

簡単にまとめると以下のようになる。

| 種類 | どこでレンダリングするか | いつレンダリングするか |
|:-:|:-:|:-:|
| CSR  | クライアント  |  リクエスト時 |
| SSR  |  サーバ | リクエスト時  |
| SSG  |  サーバ | ビルド時  |
|  ISR |  サーバ |ビルド時 + 指定した時間ごと  |

## 実装
　App Routerでは全てのコンポーネントがデフォルトでServer Component(↔Client Component)となっている。データ取得(fetch)がないコンポーネントはデフォルトでSSGになる。データ取得を行うページではSSR、SSG、ISRかオプションで選択できる。また、複数のデータをフェッチするときはコンポーネントに分割すると自動的に非同期データフェッチになる。

　以下に示すサンプルコードは、データのfetchを各レンダリング手法で行うコードである。各手法を確認するために`json-server`でモックAPIを作成し、検証した。詳しい実装内容は[こちら](https://github.com/vivy-phtela/nextjs-rendering-test)のGitHubリポジトリにアップロードしています。ぜひ試してみてください。
 
### 1. <font color="Red">CSR</font>(Client Side Rendering)
　ファイルの先頭に`use client`を付けるとそのコンポーネントはClient Componentになる。しかし、Server ComponentのほうがNext.jsの恩恵を多く受けるため、**CSRの使用は最小限に抑えるほうが良い**。また、親コンポーネントがCSRの場合、子コンポーネントは自動的にCSRになるので注意が必要(回避方法はある)。

<details open><summary>サンプルコード</summary>

```JavaScript
"use client";

import React, { useEffect, useState } from "react";

const CsrPage = () => {
  const [products, setProducts] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchData = async () => {
      const res = await fetch("http://localhost:3001/products");
      const data = await res.json();

      setProducts(data);
      setLoading(false);
    };

    fetchData();
  }, []);

  if (loading) return <p style={{ color: "red" }}>Loading...</p>;

  return (
    <div>
      <h1>CSR</h1>
      <ul>
        {products.map((product) => (
          <li key={product.id}>
            {product.id}: {product.title}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default CsrPage;
```
</details>

**【CSRでしかできないこと】**
+ **Hooks**(use state, use effect等)使用時
+ **イベントハンドラ**(on click, on change等)使用時
+ クライアント操作があるコンポーネント(フォーム、ハンバーガーメニュー等)→Server Actionsを使用するとServer Componentでも使用可能
+ ブラウザAPI(window, localStorage等)

### 2. <font color="Red">SSR</font>(Server Side Rendering)
　fetch関数の第二引数に`{ cache: "no-store" }`を記述するとSSRとなる。

<details open><summary>サンプルコード</summary>

```JavaScript
const SsrPage = async () => {
  const res = await fetch("http://localhost:3001/products", {
    cache: "no-store",
  });
  const data = await res.json();

  return (
    <div>
      <h1>SSR</h1>
      <ul>
        {data.map((product) => (
          <li key={product.id}>
            {product.id}: {product.title}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default SsrPage;
```

</details>

### 3. <font color="Red">SSG</font>(Static Site Generation)
　fetch関数の第二引数に`{ cache: "force-cache" }`を記述するとSSRとなる。バージョン14系ではデフォルトがSSGとなっているため、引数に何も指定しない場合はSSGとなる。

<details open><summary>サンプルコード</summary>

```JavaScript
const SsgPage = async () => {
  const res = await fetch("http://localhost:3001/products", {
    cache: "force-cache",
  });
  const data = await res.json();

  return (
    <div>
      <h1>SSG</h1>
      <ul>
        {data.map((product) => (
          <li key={product.id}>
            {product.id}: {product.title}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default SsgPage;
```

</details>

### 4. <font color="Red">ISR</font>(Incremental Static Regeneration)
　fetch関数の第二引数に`{ next: { revalidate: 30 } }`を記述するとSSRとなる。数値の部分はデータの再検証を行う間隔(秒)を指定する。

<details open><summary>サンプルコード</summary>

```JavaScript
const IsrPage = async () => {
  const res = await fetch("http://localhost:3001/products", {
    next: { revalidate: 30 }, // 30秒ごとに再生成
  });
  const data = await res.json();

  return (
    <div>
      <h1>ISR</h1>
      <ul>
        {data.map((product) => (
          <li key={product.id}>
            {product.id}: {product.title}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default IsrPage;
```

</details>
<br>

　どのレンダリングにも長所・短所があるため、開発するサービスの特性に合わせて選定する必要がある。**基本的にはStatic Rendering(SSG/ISR)を使用、必要に応じてDynamic Rendering(SSR)を使用する。そして、クライアントサイドの機能が必要なときのみCSRを使う**。これがベストプラクティスのようだ。

## おわりに
　私はNext.jsの良さをまだまだ活かしきれていないなと感じました！これまでは開発の容易さと流行っているということからNext.jsを選択していましたが、今後はもっとその強みを引き出せるように実装に取り組みたいと思いました。また、Next.jsについて調べれば調べるほど、Next.jsには多くの機能があることがわかり、その奥深さに驚きました。しかし、現段階の私のエンジニア脳では、理解できなかったため、詳細については以下の参考文献を見ていただければと思います。以下の参考文献は非常にわかりやすいです！

## 参考文献(2024/10/20参照)
https://nextjs.org

https://zenn.dev/akfm/books/nextjs-basic-principle

https://zenn.dev/oreo2990/articles/280d39a45c203e

https://youtu.be/Ca1h3KUfQ5k?si=g2MDf9G-M06k7uy2

https://tech-blog.rakus.co.jp/entry/20240927/rendering

https://zenn.dev/yamadadayo123/articles/6cb4f586de0183

https://qiita.com/whopper1962/items/1d1a7179845b3e1d3084