📝

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

2025/01/04に公開

はじめに

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

use cacheとは

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

サンプルコード

今回は、以下のようにユーザーの一覧からランダムでユーザーを取得し、画面に表示する簡単なアプリケーションです。
こちらのアプリケーションに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 GET() {
  const names = [
    "山田",
    "佐藤",
    "大塚",
    "斎藤",
    "須藤",
    "島澤",
    "山口",
  ];
  const name = names[Math.floor(Math.random() * names.length)];
  return Response.json(name);
}
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
"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-ディレクティブ

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

import { getName } from "./api";

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

関数レベルでの利用

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のどちらも対象にすることができます。

"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を有効にする必要があります。

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のようなイメージですね。めっちゃ便利だ。

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

cacheTag

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

cacheTagは、オンデマンド無効化のためにキャッシュされたデータにタグを付けることができます。
結構使われそうな機能で、例えば、ユーザーの自分のアカウント名を変える時などに、キャッシュしているユーザー名を削除して、キャッシュを更新した場合などに利用するなどがあります。

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をクリアし、新しい名前が画面に表示されます。

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のキャッシュ管理からの大きな進化といえます。

Discussion