お好きな金額でサポートしていただけると嬉しいです!
(50円〜10,000円[税込]まで)
作成日時:2025年12月18日 00:58
更新日時:2025年12月19日 00:09
Next.js
この記事は、React Tokyo アドベントカレンダーの20日目の記事です。
Data Cache、Full Route Cache、Router Cacheがそれぞれどのように連動して今作っているものが動いているのか?明確に意識してNext.jsを触れていますか? この場合どうなるの・・・?これは・・・?と?が飛びまくっている日々をしばし過ごしていたので、今回はNext.jsのキャッシングがどのように構成されているのか、について、日本昔ばなしの桃太郎に例えながら、楽しくまとめてみます。
公式ドキュメントを見たい方はこちら(桃太郎はでてきません)
Next.jsのキャッシングを理解するうえで、まずは登場人物から紹介します。
この3人があなたのオトモです。こちらに加えて、
というおじいさんおばあさんがいるイメージです。
そして、
という鬼が出てきます。
Data Cache、Full Route Cache、Router Cacheはそれぞれ出身やスキルが異なります。 そして、あなたが出会った時、こんな持ち物を持っていたそうです。
Data Cache | Full Route Cache | Router Cache | |
|---|---|---|---|
出身 | サーバー | サーバー | クライアント |
スキル | fetch単位のCache管理 | サーバー側のルート単位のCache管理 | クライアント側のルート単位のCache管理 |
持ち物 | サーバーからのレスポンスデータ | RSC Payload・HTML | RSC Payload |
そして、これを適材適所であなたは配置を指示しました。するとこうなったそうです。

どうですか?お三方を好きになれそうですか? でも、鬼退治にいくなら、おじいさんとおばあさんのサポート、欲しいですよね。
そこで準備のために、おじいさんはサーバーに、おばあさんはおじいさんが持ってきたデータを関数というカゴベースで管理をしてあげることにしました。 なんでおじいさんはサーバーに行ったのかって?桃はサーバーから流れてくるからです。

最強の布陣ができあがりました。これでようやく鬼退治に向かうことができそうです。
サーバーサイドからクライアントサイドに向かって、あなたは今一度、フォーメーションを確認します。
Data SourceはNext.jsやReactの機能とかではなく、fetch先のデータソースそのものです。
おじいさんは、あなたがおばあさんにお願いしたリクエストをおばあさんから受け取って、データを取得して返すんだと思ってください。
Data CacheはData Sourceが返した生のデータを保管します。 おじいさんの一番そばでおじいさんが渡してきたデータを持ち帰れるようにきれいにカゴにいれるイメージです。
Request Memoizationは同じくData Sourceから受け取った生のデータをインメモリベース・fetch単位で保管します。その場限りなので、セッションが変わればリセットされます。 おばあさんはおじいさんから受け取ったデータを管理するんでしたよね。 そう、おばあさんはfetch単位でメモ化を担当しているのです。
(もしくは、あなた:「おばあちゃん、きびだんご、もうなくなったから、もう一回ちょうだい。」→ おばあさん:「ええ!?さっきあげたでしょ!?もう作り直さないわよ!」という動きをイメージした方が正確かもしれない)
※このメモ化はNext.jsの機能ではなく、Reactの機能です。なのでお三方ではなくおばあさんにお願いしました。
また、この時にレンダリングの動作として、動的レンダリングの場合はRSC payloadとHTMLを、静的レンダリングの場合はRSC payloadのみを生成します。
Full Route Cacheは生成されたRSC payloadやHTMLを保管します。
同じルートのリクエストが来た際に、保管してあるRSC payloadやHTMLがあれば、それ以上サーバー側にリクエストを渡さずにクライアントサイドへ返します。ルート単位での管理ですね。
静的ルートの場合は、ビルド時に生成されたRSC payloadがここに保管されます。
Router Cacheは唯一クライアントサイドを守る存在で、インメモリベースRSC Payloadを保管します。 インメモリベースなので、Request Memoizationと同じく、セッションが変わればリセットされます。
これもルート単位での管理です。
Next.jsでは、これらのキャッシュが連動して、可能な限り再利用することでパフォーマンスを最大化してくれます。
例えば、静的ルートで最もこれらのキャッシュが活用されるケースを考えてみましょう。
クライアントサイドでナビゲーションが発生し、静的レンダリングとなる /exampleのパスにアクセスしようとしたときは以下のような動きになります。
なお、Request Memoizationは?おばあさんどこいったの!?と思うかもしれません。
思い出してください。おじいさんやおばあさんは、あくまでサポート役なのです(おじいさん役のData Sourceもあくまで外部のものであるという見方)。
Rquest Memoizationは同じfetchリクエストが複数あった場合に自動で重複を防ぐ(メモ化する)Reactの仕組みでした。
なので、このフローには直接含まれない、あくまでサポートするだけ、と整理するとよいと思います。
ただし、ここまでの内容は静的レンダリングなのか、動的レンダリングなのか、また最後に出てくるcacheオプションの設定状況で挙動が細かく異なります!
Full Route Cacheは動的レンダリングの場合、デフォルトではSkipされてData CacheやData Sourceを見に行く挙動になりますし、dynamic = 'force-dynamic'またはrevalidate = 0オプションが使用されている場合はFull Route CacheとData Cacheがスキップされたりします。
ここまでで、なんとなくイメージができてきたら、公式ドキュメントをもう一度見てみることをお勧めします。ちょっとだけでも理解の助けになっていれば嬉しいです。
さて、最後に鬼退治に行きましょう。冒頭で、下記の2つがあるという話をしました。
まずはPrefetchとの連動について。ここではRoute Cache&静的ルートの組み合わせが関係してきます。
静的ルートでは全ルートがPrefetchの動作対象となりますが、この時、Route CacheにRSC Payloadがあるのかを確認しています。
RSC Payloadがあれば、リクエストが実際に発生した際に即座にそのデータを返すことで素早くページを表示します。
cacheオプションの設定内容によって、レンダリングの扱いが変わります。つまりキャッシュの挙動がここで変わってくるということです。
具体的にはData Cache、Full Route Cacheに関係してきます。
オプション指定 | レンダリング | Data Cache | Full Route Cache(HTML / RSC) | 補足 |
|---|---|---|---|---|
fetch(url, { cache: 'force-cache' }) | 静的レンダリング寄り(SSG相当) | 使う(永続・再利用) | 有効になりやすい(※) | デフォルトでキャッシュされるfetchの代表例。ルート側で dynamic = 'force-dynamic' 等を指定すると、ルート自体は動的にもできる。 |
fetch(url, { next: { revalidate: N } })(N > 0) | ISR(静的 + 再検証) | 使う(期限付きで再検証) | 有効(再検証単位で更新) | Data Cacheが再検証されると、結果的にルートのRSC/HTML(Full Route Cache)も再生成される。 |
fetch(url, { cache: 'no-store' }) | 動的レンダリング(SSR相当) | 使わない | 基本スキップ(※) | 毎リクエストでデータ取得 → RSC/HTMLを再生成する方向になる。 |
fetch(url, { next: { revalidate: 0 } }) | 動的レンダリング(SSR相当) | 使わない | 基本スキップ(※) | 「常に最新」指定。cache: 'no-store' と同じイメージでOK。 |
※ここで、「有効になりやすい」とか「基本スキップ」と書いているのは、Full Route Cache は 「fetch の cache オプション単体で決まる」というより、最終的にそのルートが「静的にできるか/できないか」で決まるという意図です。
例えば、fetch(..., \\{ cache: 'force-cache' \\}) を書いていても、ルート側で dynamic = 'force-dynamic' を指定したり、cookies() や headers() を使ったりしていると、そのルートは動的レンダリング扱いになり、結果として Full Route Cache は使われなくなるという状態になります。
なので、まずはそのルートが静的なのか動的なのかを切り分けたのち、chacheオプションを使った場合にキャッシングがどうなるのか、という順序で考えるとスムーズかもしれません。
鬼退治、成功しましたか?(逆にややこしくなってたらすみません・・・)
自身まだまだですが、調べながらまとめたこの記事を自分でも見返しながら使っていこうと思います!
あと、ドキュメント見ながらまとめてますが、もし間違ってるところあればぜひご指摘いただきたいです><
それでは、また!
HTNCode