[Next.js]use cacheについて学んだ

# はじめに
use cacheについて学んだので、学んだことを書いていきます。

# use cacheとは
use cache ディレクティブは、コンポーネント、関数、またはファイルをキャッシュ対象として指定します。
ファイルや関数のトップレベルに宣言することで、ファイル内のすべての関数や、関数の戻り値をキャッシュ可能にしたりすることができます。
ただ、まだ実験的なNext.jsの機能なので、use clientやuse serverのようなネイティブのReact機能ではありません。

# サンプルコード
今回は、以下のようにユーザーの一覧からランダムでユーザーを取得し、画面に表示する簡単なアプリケーションです。
こちらのアプリケーションにuse cacheを組み込んでいきたいと思います。

```/app/(root)/api.ts
export async function getName() {
  const res = await fetch(`http://localhost:3000/api/name`);
  const name = await res.json();
  return name as string;
}
```

``` /app/api/name/route.ts
export async function GET() {
  const names = [
    "山田",
    "佐藤",
    "大塚",
    "斎藤",
    "須藤",
    "島澤",
    "山口",
  ];
  const name = names[Math.floor(Math.random() * names.length)];
  return Response.json(name);
}
```

```/app/(root)/page.tsx
import { getName } from "./api";

export default async function Page() {
  const name = await getName();
  return <div>{name}</div>;
}
```

# インストール、設定
use cacheを使えるようにするための手順です。

## canaryバージョンをインストール
「use cache」は、まだcanaryなので、以下のコマンドで、Next.jsのcanaryバージョンをインストールする必要があります。
```
npm install next@canary
```

```package.json
"dependencies": {
    "next": "^15.1.1-canary.25",
  }
```

# 従来のcache管理
Next.jsには、APIからデータを取得する際には、第二引数にcacheオプションをつけ、no-storeやforce-cacheを指定してcacheするかどうかを指定していた。
詳しくは以下のドキュメントに記載しています。

https://nextjs.org/docs/app/building-your-application/caching

## use cacheを用いたキャッシュ管理

https://nextjsjp.org/docs/app/api-reference/directives/use-cache

キャッシュするコンポーネントや関数を指定することができる機能。
ファイルの先頭で「use cache」を使用して、ファイル内のすべてのエクスポートがキャッシュ可能であることを示すか、関数またはコンポーネントの先頭にインラインで使用して、戻り値をキャッシュして後続のリクエストで再利用する必要があることを Next.jsに通知できます。

### 使い方
3つの使い方が存在する

- コンポーネントレベル
- 関数レベル
- ルート(モジュール)単位

https://nextjsjp.org/docs/app/api-reference/directives/use-cache#use-cache-%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%86%E3%82%A3%E3%83%96

#### コンポーネントレベルでの利用

```tsx
import { getName } from "./api";

export default async function Page() {
  "use cache";
  const name = await getName();
  return <div>{name}</div>;
}
```

#### 関数レベルでの利用

```tsx
export async function getName() {
  "use cache";
  const res = await fetch(`http://localhost:3000/api/name`);
  const name = await res.json();
  return name as string;
}
```

#### モジュール単位での利用
以下のようにすると、getName, getCountのどちらも対象にすることができます。
```tsx
"use cache";

export async function getName() {
  const res = await fetch(`http://localhost:3000/api/name`);
  const name = await res.json();
  return name as string;
}

// 以下の関数はサンプルコードにはありませんが、説明ように追加しました。
export async function getCount() {
  const res = await fetch(`http://localhost:3000/api/count`);
  const count = await res.json();
  return count as number;
}
```

## dynamicIOを有効化
### dynamicIOとは

https://nextjsjp.org/docs/app/api-reference/next-config-js/dynamicIO

dynamicIO フラグは、App Router内のデータフェッチ操作を、明示的にキャッシュされない限りプリレンダリングから除外する、Next.jsの実験的な機能です。これは、サーバーコンポーネントでの動的データフェッチのパフォーマンスを最適化する際に役立ちます。
use cacheを利用する場合、以下のようにdynamicIOを有効にする必要があります。

``` next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  experimental: {
    dynamicIO: true
  },
};

export default nextConfig;
```

### ⚠️**注意点**
dynamicIOが有効な場合、Next.jsは、全てのユーザーリクエストでアクセスされるべきデータを待機するコンポーネントの前(親コンポーネント)に、Suspense Boundaryが存在しなければいけません。

# cacheLife

https://nextjsjp.org/docs/app/api-reference/next-config-js/cacheLife

cacheを永続的にではなく、定期的に更新したい場合は、以下のように「unstable_cacheLife」を設定することで、cacheの保持時間を管理できます。
ISRのようなイメージですね。めっちゃ便利だ。

``` api.ts
import { unstable_cacheLife } from "next/cache";

export async function getName() {
  // 3秒おきにcacheを更新
  "use cache";
  unstable_cacheLife({
    revalidate: 3,
  });

  const res = await fetch(`http://localhost:3000/api/name`);
  const name = await res.json();
  return name as string;
}
```
上記のコードでは、3秒おきにcacheを更新するように設定しているため、画面を3秒おきにリロードすると、表示される名前も更新されるはずです。
また、上記のコードでは、3秒としていますが、以下のように、1日おきや毎分、毎秒など様々な時間で設定をすることができます。

![スクリーンショット 2025-01-04 12.00.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2613586/36df9918-c318-3895-161b-ac9fdf3affe0.png)

# cacheTag

https://nextjsjp.org/docs/app/api-reference/functions/cacheTag

cacheTagは、オンデマンド無効化のためにキャッシュされたデータにタグを付けることができます。

``` api.ts
import { unstable_cacheTag } from "next/cache";

export async function getName() {
  "use cache";
  // cacheしたデータに「name」タグを付与
  unstable_cacheTag("name");

  const res = await fetch(`http://localhost:3000/api/name`);
  const name = await res.json();
  return name as string;
}
```
cacheTagを削除するには、「revalidateTag」を使うことで、削除することができます。
利用する場面としては、例えば、ユーザーのアカウント名(キャッシュ済み)を変えた場合に、キャッシュを削除して、ユーザー名の表示を更新するなどが考えられます。
以下のサンプルコードでは、ボタンをクリックすると、cacheをクリアし、新しい名前が画面に表示されます。
``` page.tsx
import { revalidateTag } from "next/cache";
import { getName } from "./api";

export default async function Page() {
  const name = await getName();
  async function revalidate() {
    "use server";
    // nameタグ付きのcacheを削除
    revalidateTag("name");
  };

  return (
    <>
      <div>{name}</div>
      <form action={revalidate}>
        <button type="submit" className="outline">
          revalidate
        </button>
      </form>
    </>
  );
}
```


# まとめ
Next.jsの新しいキャッシュ管理機能について、主要なポイントを整理しました

### 新しいキャッシュ管理の特徴

- use cache ディレクティブによる柔軟なキャッシュ制御
- コンポーネント、関数、モジュールレベルでの適用が可能
- dynamicIOとの連携による最適化


### 主要な機能

use cache: キャッシュ可能な要素の指定
unstable_cacheLife: キャッシュの保持時間の管理
cacheTag: オンデマンドでのキャッシュ無効化
dynamicIO: データフェッチ操作の最適化


### 導入時の注意点

- Next.js canaryバージョンが必要
- dynamicIO有効時はSuspense Boundaryの配置が必要
- キャッシュ戦略の適切な設計が重要

### 利用シーン

- 定期的なデータ更新（ISR的な使用）
- ユーザー操作によるキャッシュ更新
- パフォーマンス最適化
- サーバーコンポーネントでのデータフェッチ

これらの機能を適切に組み合わせることで、より効率的でパフォーマンスの高いアプリケーションを構築できます。
特にキャッシュの粒度やライフサイクルを細かく制御できる点が、従来のNext.jsのキャッシュ管理からの大きな進化といえます。

# 最後に
他にも色々な記事を書いているので、よければ読んでいってください！

https://qiita.com/hukuryo/items/4a02784929b0cf0404a5

https://qiita.com/hukuryo/items/2a649d2ee018308d89f6

https://qiita.com/hukuryo/items/4e957621682a25da5f3b

https://qiita.com/hukuryo/items/e14779aeb0c0cf26c2d2