【Next.js 15】cache完全ガイド

# はじめに
Next.jsには現在4つのキャッシュがあります。

1. Request Memoization
2. Data Chache
3. Full Route Cache
4. Router Cache


そして、現在のv15でのカナリー版では`use cache`が追加され、宣言的なcachingが可能となりました。
これはData CacheやFull Route Cacheの機能等のcachingを宣言的に行うことが可能な新しいdirectiveです。

今回は、それぞれに対して、仕組みや注意点などの部分まで解説を行いたいと思います。

# 4つのcache概要
概要ですが、公式に概要を示す表があったので、それをわかりやすく訳したものを記載します。
すると、以下のようになります。

| 機能            | cacheされるもの                        | 保存場所  | 目的                                      | cache期間                           |
|----------------------|---------------------------|--------|----------------------------------------------|------------------------------------|
| Request Memoization | Return values of functions | Server | 	React コンポーネントツリーでデータを再利用する       | リクエスト毎             |
| Data Cache          | Data                        | Server | ユーザーのリクエストとデプロイメント全体にわたってデータを保存する | 永続的（再検証可能） |
| Full Route Cache    | HTML and RSC payload       | Server | レンダリングコストを削減し、パフォーマンスを向上 | 永続的（再検証可能） |
| Router Cache        | RSC Payload                | Client | ナビゲーション時のサーバーリクエストを削減        |　ユーザーセッション または time-based（時間ベース）        |


次に、各cacheについて、より詳細に見ていきましょう。

# Request Memoization
これは**同じリクエストを重複排除する**というcachingです。
以下の画像のように、Next.jsでは同一リクエストを自動でメモ化します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/879778/bc877481-7384-8f92-cfbb-fe27ee929784.png)

## 仕組み
仕組みとしては、初回リクエストは後述する`Data Cache`もしくは、DBやServerにリクエストをし、通常通りfetchします。そして、受け取ったレスポンスを`in-memory`に保存します。

2回目以降のリクエストは、同じレンダリングパスの中で同じリクエストを呼び出した場合に、`in-memory`を参照し、メモ化したものを使用するといったことをします。

ルート（`/about`などの各segment（ページ）のこと）がレンダリングされ、レンダリングパスが完了すると、`in-memory`がリセットされ、すべてのリクエストのメモ化エントリがクリアされます。

つまり、Request Memoizationの目的は**重複リクエストの排除によるリクエスト数の削減**と言えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/879778/aff49e4c-bf28-fea0-aeed-ed7191852da6.png)

少し耳慣れない用語もでてきたので、以下にまとめます
- レンダリングパス：ルートのレンダリングが開始されてから完了するまでの一連のプロセス
- メモ化エントリ：リクエストの結果をcachingしたデータの記録のこと


つまり、特定のパスのリクエストにおいて、レンダリング中はRequest Memoizationが発火するが、レンダリングが完了するとcache purgeされるため、リクエスト毎に1回は外部ソースにfetchリクエストが走るという仕組みです。

上記の仕組みがあるからこそ、Next.jsでは積極的にコロケーションを行い、リーフコンポーネントにfetch処理をカプセル化することができます。
（動的メタデータの設定の際には、`generateMetadata()`を使用しますが、その関数内での`fetch()`に対しも有効です）

## 注意点
- Server Componentでは`fetch()`を使用する場合に適用
　- `fetch()`のURLやオプションが少しでも違うとメモ化されない
- ORMなど`fetch()`以外を使用する場合は、React.cacheを使用する
- RouteHandlers（API）での`fetch()`利用では適用されない
- `server-only`パッケージーを積極利用する
　- ServerComponentでないとRequestMemoizationは効力を持たない

## キャッシュ期間
リクエスト毎（各fetch毎）に行われます。

# Data Cache
概要は以下の記事がかなり正確なので、引用しようと思います。

https://zenn.dev/frontendflat/articles/nextjs-cache#data-cache


> Next.jsでは、fetchした結果をData Cacheとして保存します。fetchによりデータソースにリクエストした結果はData Cacheに保存され、以降同一リクエストはData Cacheから返されます。つまりData Cacheの役割は、オリジンデータソースへのリクエスト数を減らすことです。
>
>また、Data Cacheは永続的で複数デプロイ間にわたって有効なので、再デプロイしてもキャッシュは破棄されません。なので一定期間や特定のタイミングでキャッシュを破棄するためにfetchオプションなどによる再検証が用意されています。先ほどのReact Cacheはレンダリングごとに自動的にキャッシュが破棄されましたが、キャッシュされる期間が大きく異なるので混同しないように注意しましょう。

重要なのは以下の部分です。

- fetchした結果をData Cacheとして保存すること
- Data Cacheの役割は、オリジンデータソースへのリクエスト数を減らすこと
- Data Cacheは永続的で複数デプロイ間にわたって有効なので、再デプロイしてもキャッシュは破棄されないこと
- 上記のため一定期間や特定のタイミングでキャッシュを破棄するためにfetchオプションなどによる再検証が用意されていること

より詳しく見ていきましょう。

## 仕組み
Data Cacheの仕組みですが、`force-cache`によるfetchにて、リクエストされると、まずはData Cacheを見に行きます。
そこで、cacheがない場合は、オリジンデータソースにリクエストが行われ、結果がData Cacheに保存され、メモ化されます。

cacheが見つかった場合は、cacheをすぐに返すというような仕組みです。

つまり、Data Cacheの目的は**オリジンデータソースへのリクエスト数の削減**と言えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/879778/2d73af54-0b1a-e3dc-e05e-4e03341025b6.png)

上述の通り、これは`fetch()`のオプションを利用して、cacheを制御するというようなメカニズムになっております。

`no-store`を指定すれば、Data Chaceの参照はスキップされ、毎回オリジンデータソースにリクエストされることになります。

そのため、revalidate（再検証）の指定も可能です。
再検証については後述します。

## キャッシュ期間
永続的です。ただし再検証の指定が可能です。

## 再検証
再検証には2種類あります。

1. Time-based Revalidation：一定の時間が経過後に、新規リクエストが行われた後に、データを再検証する
2. On-demand Revalidation：フォーム送信などのイベントに基づきデータを再検証する

### Time-based Revalidation
1.はいわゆるISRのことです。
これはSWRの挙動を取るため、再検証後の初回リクエストのみ古いデータを返却します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/879778/bad3d15b-7eae-1c2f-121a-4e264ffa770d.png)


つまり、新しいリクエストの後、古いデータを返しつつ、その裏で最新のデータを取得し、Data Cacheにセットするというような挙動をしています。


### On-demand Revaldation
2.は`revalidatePath()`や`revalidateTag()`による再検証を指します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/879778/05c9cac2-cadf-f9ef-f4bf-f95c15014c28.png)


これは、指定したパスもしくはタグに関連するキャッシュエントリをすべて削除します。
そして、ISRと異なり、次のリクエスト時には新しいデータをオリジンデータソースから取得し、Data Cacheにセットするという動きを取ります。

## 注意点
> また、Data Cacheは永続的で複数デプロイ間にわたって有効なので、再デプロイしてもキャッシュは破棄されません。なので一定期間や特定のタイミングでキャッシュを破棄するためにfetchオプションなどによる再検証が用意されています。

Data Cacheは永続的なので、サーバーを再起動したり、再デプロイしたりしても残ります。
（もちろんタブを閉じるなどの行為をしても残ります）

なので、以下の記事にある通り、build時に`.next/cache/fetch-cache`を削除し、再デプロイをすることでキャッシュを破棄したうえで、デプロイすることが可能です。
個人的には`"build:clean": "rm -rf .next && next build"`というコマンドをプロジェクト立ち上げ時に追加しておくことをおすすめします。

https://help.microcms.io/ja/knowledge/app-router-cache

# Full Route Cache
Next.jsではbuild時または再検証時に静的ページ（静的にレンダリングされたルート）に対して、サーバー側でcachingを行います。
つまり、SSG・ISRで生成されたページをサーバー側でcachingするということです。

具体的には、HTMLとRSC Payloadをサーバー側でcachingします。
RSC Payloadとそのcacheについての概要は以下となります。
- RSC Payload：レンダリングされたReact Server Components Treeのバイナリ
　- コンポーネントのTree構造や必要なpropsなどの情報をバイナリー化しReactがcachingする

以上が概要ですが、少し曖昧なので、仕組みの方でより詳しく解説します。

## 仕組み
Full Route Cacheでは、HTMLとRSC Payloadをbuild時または再検証時にcachingします。
そして、Next.jsではサーバー側でRSC PayloadとHTMLをレンダリングします。

つまり、静的なルート（ページ）にクライアントサイドでアクセスした場合、**Data Cache や Request Memoization などのサーバーで行うリクエストや処理はすでに完了している** 状態になります。  
また、Full Route Cache によって **RSC Payload と HTML はすでにレンダリング済み** です。

そのため、クライアント側での作業は **JS の Hydration（React によるインタラクティブ化）** のみとなります。  

つまり、Full Route Cache の目的は **静的ルート（ページ）自体をキャッシュし、不要なレンダリングやリクエストを削減する** ことを目的としています。  
簡単に言えば、**レンダリングコストを削減し、静的なページを即座に表示させる** ためのキャッシュです。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/879778/7eeb7f79-ca6f-d3ce-b758-7b098f423323.png)

さらに、クライアント側には **Router Cache** が存在し、**初回以降のページ遷移時にサーバーリクエストを減らすことで、さらなる高速化** が可能になります。  
これにより、特に `next/link` などを使ったクライアントサイドのページ遷移では、不要なサーバー通信を行わずにスムーズなナビゲーションが実現されます。

## 静的ルートと動的ルートでの挙動の違い
前述した通り、クライアント側ではRouter Cahceが存在しています。
そのため、静的ルートと動的ルートで挙動に違いが生まれます。

### 静的ルートの場合
初回アクセス時、Full Route Cache（サーバー側）のCacheを取得し、このときに、クライアントの`in-memory`に**RSC Payloadのみcaching**します。

そして、ページ遷移時には、クライアントのin-memory cacheを参照することで非常に高速な遷移が可能となっています。（詳しくは後述しますが、処理がサーバー側までいかないので高速化が実現できます）


### 動的ルートの場合
Full Route Cacheの参照はスキップされ、Data Cacheを参照するなどのfetch等の処理に入ります。
そして、fetch等の処理後、クライアントの`in-memory`にRSC Payloadをcachingします。

つまり、SSRやDynamic Routeなどの動的ルート（ページ）ではサーバー側にcachingはされないが、クライントにはcachingされるということです。

![Cursor_と_アプリケーションの構築__キャッシュ___Next_js.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/879778/4047131e-417b-db03-e223-4eaf242cefb9.png)

そして、ページ遷移については静的ルートと同様にクライアントの`in-memory`を参照します。
こと、ページ遷移については静的か動的かは関係ないということです。

ただし、あくまでcachingされるのはRSC Payloadなので、動的ルートでData Cacheなどが存在するのであれば、サーバー側にまでリクエストが行われるということは覚えておいてください。

## キャッシュ期間
永続的です。
build時および再検証時にcacheがセットされるので、どのユーザーに対してもcacheを返却します。

# Router Cache
クライアントでRSC Payloadをcachingする機能です。
これは唯一クライアント側でのcacheとなるため、ページアクセス時にcachingが行われます。

これにより、フォワード / バックナビゲーション（戻る / 進むボタン）による高速な画面遷移を実現できるため、UXが大幅に向上します。

## 仕組み
アクセスしたページをクライアントの`in-memory`にcachingします。
これは静的/動的問わず、cachingを行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/879778/ef4f10a2-a49d-e0de-cf1d-f5eda54aa094.png)

また、next/linkの`<Link />`コンポーネントにより指定したルートを`prefetch`することができます。
これにより、ユーザーがルートにアクセスする前にバックグラウンドでルートをプリロードすることが可能となります。


つまり、ナビゲーション間でページ全体が再読込されることがなくなり、Reactおよびブラウザの状態を保持できるため、高速なページ遷移が可能となります。
※ prefetchはbuild時の環境のみに適用されるので、`next dev`では確認できないということに注意が必要です

上記のため、Router Cacheの目的は**ナビゲーションのUX向上**と言えます。

ちなみに、`<Link />`のナビゲーションはソフトナビゲーションなので、差分があるルートセグメントのみレンダリングする部分レンダリングの方式となっています。
（従来のページ遷移は基本的にハードナビゲーションでページ遷移する度にクライアント全体がレンダリングされてしまいます）

## 静的ルートと動的ルートによるprefetchの挙動の差異
prefetchのデフォルトの挙動は静的か動的かで異なります。

### 静的ルートの場合
デフォルトでprefecthが`true`となっており、30秒間 Router Cacheにcachingされます。
また、preloadのタイミングについては以下のタイミングで、preloadが行われます。

- ユーザーのViewPortに`<Link />`が入った時（スクロールして見えた時）

これが静的ルートの場合の挙動です。

### 動的ルートの場合
動的ルートの場合は、デフォルトで共通レイアウト（静的な部分）と`loading.js`がprefetchされます。
ただし、`<Suspense />`の`fallback`はprefetchされないので、混同に注意が必要です。

つまり、動的ページは`layout.js`などの共通レイアウトと、`loading.js`のフォールバックUIをRouter Cacheに保存することで、動的ルート全体のfetchコスト（レンダリングコスト）を削減し、UXを向上しているわけです。

そして、最終的には動的ページはRouter Cacheに保存されるので、以降のアクセス時にはRouter Cacheを参照し、動的ページのRSC Payloadを即返却するため、同じページであれば、即画面表示できるということになります。

## キャッシュ期間
セッション中（ブラウザのタブを閉じるまで）のみ有効です。

また、静的ルートの場合は、デフォルトで5分間Cacheを保持しますが、動的ルートの場合は、0秒です。
ただし、Router Cacheの場合、next.configの`staleTImes`により詳細に時間を決められます。

なので、基本デフォルトを使用し、必要があれば、設定を編集することになると思います。

https://nextjs.org/docs/app/api-reference/config/next-config-js/staleTimes

また、オプトアウトについては、以下の方法があります。

1. Server Actionsで以下を行う
    a. revalidatePath() / revalidateTag()
    b. cookies.set() / cookies.delete()
2. router.refresh()

以上の方法でcacheをオプトアウトし、ルートの最新化が可能となるので、これも覚えておきましょう。

# use cache
これはNext.js v15から使用可能となった`use cache`ディレクティブという方法です。
（現在はカナリー版でのみ使用可能です）

これは以下のような経緯から作成された機能です。
- Next.jsではWeb標準のfetchを拡張しており、デフォルトのfetchとの違いがある
- Data Cacheの最適化が難しい
　- ここに[Route Segment Config](https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config)も絡んでくるため、より難しい


このような経緯があり、それなら、今までのCache指定方法はすべて忘れて、`use cache`ディレクティブ1つにしてしまおうというようなものが`use cache`です。
（ただし、Data Cacheとの関連性が強いので、その他のcacheについては覚えておく必要があります）

要するに、よりCache最適化しやすいように作られた機能と言えます。

より具体的に知りたいという方は以下の記事をご参照ください。

https://zenn.dev/sc30gsw/articles/22fa89a432de90

## 使用方法
以下のように各Levelで宣言・cachingが可能です。

```tsx
// File level
'use cache'
 
export default async function Page() {
  // ...
}
 
// Component level
export async function MyComponent() {
  'use cache'
  return <></>
}
 
// Function level
export async function getData() {
  'use cache'
  const data = await fetch('/api/data')
  return data
}
```

もちろん、revalidateTag()やISRのようなOn-demandなCacheとすることも可能です。
これには`cacheTag()`と`cacheLife()`を使用します。

```tsx
import { cacheTag } from 'next/cache';
 
async function getNotice() {
  'use cache';
  cacheTag('my-tag');
}
```

```tsx
"use cache"
import { unstable_cacheLife as cacheLife } from 'next/cache'
 
export default async function Page() {
  cacheLife("minutes")
  return
}
```

`cacheLife()`の設定値は以下を指定できます。

- "seconds"
- "minutes"
- "hours"
- "days"
- "weeks"
- "max"

## 注意点
`use cahce`の使用ではいくつか注意点があるので、そちらについても記載しておきます。

### cache kyeの自動生成
`use cache`ではcache-keyが自動生成されます。

関数などに渡した引数を自動で検出して、生成されるようなのですが、この値はシリアライズ可能な値でないといけないです。（シリアライズ不可な値やクローズドオーバーされた値（クロージャで定義された変数）を引数等で使用してもcache-kyeの生成に使用されないだけなのでエラーとはなりません）

https://react.dev/reference/rsc/use-server#serializable-parameters-and-return-values

### cache可能な値について
`use cache`使用時には、**cacheされる値（関数の戻り値など）はシリアライズ可能である**必要があります。
シリアライズ可能な値でないとエラーとなります。

逆に言えば、Reactがレンダリングできるシリアライズ可能なあらゆるデータをcaching可能ということです。

ここが`unsatable_cache`と異なる点で、コンポーネントのレンダリング出力を含めて、Reactがシリアライズ可能な値であれば、すべてcachingできます。


# おわりに
いかがだったでしょうか。
現状のNext.jsのすべてのcache機能について網羅的に解説できているかと思います。

不明点や誤り等あれば、コメントいただけると幸いです。
また、本記事がNext.jsの理解に少しでも寄与する部分があればと願っております。

# 参考文献
https://nextjs.org/docs/app/building-your-application/caching

https://zenn.dev/frontendflat/articles/nextjs-cache

https://help.microcms.io/ja/knowledge/app-router-cache

https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating

https://nextjs.org/docs/app/api-reference/config/next-config-js/staleTimes

https://nextjs.org/blog/our-journey-with-caching

https://zenn.dev/sc30gsw/articles/22fa89a432de90

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

https://react.dev/reference/rsc/use-server#serializable-parameters-and-return-values

https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config