
TL;DR: Next.jsのApp Routerは、デフォルトでデータを積極的にキャッシュします。これはパフォーマンスには優れていますが、データが更新されない場合にはフラストレーションがたまります。Request Memoization、Data Cache、Full Route Cache、Router Cacheという4つのキャッシュレイヤーを理解することで、アプリケーションのデータフローを予測可能に制御できるようになります。

データベースを更新したのにNext.jsアプリが古いデータを表示し続けるという経験があるなら、あなただけではありません。ほとんどの開発者は、キャッシュに受動的にアプローチします。つまり、「何か遅いからキャッシュを追加する」「何か古いからクリアする」といった具合です。しかし、Next.jsでは複数のキャッシュが同時に動作しており、互いに認識していないことが多いため、このアプローチはうまくいきません。
それでは、各レイヤー、それらを制御する方法、そしてフレームワークとの戦いをやめる方法について詳しく解説します。
リクエストメモ化(Request Memoization)は、Next.jsで最も誤解されているキャッシュです。なぜなら、永続的なデータキャッシュのように見えますが、そうではないからです。単一のサーバーレンダリング中に、異なるコンポーネントから同じURLとオプションでfetch()を複数回呼び出すと、Next.jsはそれらを単一のHTTPリクエストに重複排除します。

これは単一のリクエストの期間中のみ存在します。ユーザー間やページのリロードをまたいで永続化することはありません。これにより、N+1クエリを心配することなく、レイアウト(Layouts)とページ(Pages)で独立してデータをフェッチできます。
// Both components call the same URL: only ONE HTTP request is made
async function Header() {
const user = await fetch('https://api.example.com/me').then(r => r.json());
return <header>Welcome, {user.name}</header>;
}
async function Sidebar() {
const user = await fetch('https://api.example.com/me').then(r => r.json());
return <aside>Profile: {user.avatar}</aside>;
}
fetch()を使用していない場合(例: PrismaやDrizzleでデータベースを直接呼び出す場合)、同じ重複排除を実現するには、Reactのcache()関数を使用してクエリを手動でメモ化する必要があります。
データキャッシュ(Data Cache)は、Pagesルーターから移行する開発者を困惑させるものです。デフォルトでは、Next.js App Routerはすべてのfetch()呼び出しをサーバー上で無期限にキャッシュします。これは、明示的に無効化されない限り、リクエストやデプロイをまたいで永続化します。

リアルタイムデータが必要なダッシュボードを構築している場合、この動作をオプトアウトする必要があります。これは、no-storeを使用してフェッチごとに細かく設定できます。
// Opt out of caching entirely for real-time data
const liveData = await fetch('https://api.example.com/metrics', {
cache: 'no-store'
});
定期的に更新されるデータには、再検証間隔を定義することで増分静的再生成(ISR: Incremental Static Regeneration)を使用します。
// Cache data but refresh it in the background every 60 seconds
const cachedData = await fetch('https://api.example.com/stats', {
next: { revalidate: 60 }
});
エンタープライズアプリケーションは通常、**オンデマンド再検証(On-Demand Revalidation)**に依存しています。データを無期限にキャッシュし(force-cache)、CMSまたはデータベースが更新されたときにWebhookを介して再検証をトリガーします。これにより、バックグラウンドリソースを無駄にすることなく、即座の更新が保証されます。特定のキャッシュエントリをパージするには、サーバーアクション(Server Actions)またはルートハンドラー(Route Handlers)でrevalidatePath('/route')またはrevalidateTag('my-tag')を使用します。
**フルルートキャッシュ(Full Route Cache)**は、ビルド時に静的ルートのHTMLとReact Server Component(RSC)ペイロードを保存します。ユーザーがルートにアクセスすると、サーバーは再レンダリングする代わりにキャッシュされたペイロードを即座に返します。このキャッシュは、revalidatePathまたはrevalidateTagを使用してデータを再検証すると自動的に無効化されます。

**ルーターキャッシュ(Router Cache)**は、クライアント側のインメモリキャッシュです。ユーザーがアプリケーションをナビゲートすると、Next.jsは訪問したルートセグメントをキャッシュします。これは、以前に訪問したページに戻るのが瞬時に行われることを意味します。ただし、クライアント側のミューテーションがすぐに反映されない可能性もあります。その場合は、ブラウザにサーバーから最新のRSCペイロードを強制的にフェッチさせるために、明示的にrouter.refresh()を呼び出す必要があります。
これら4つのレイヤーがどのように相互作用するかを理解することで、パフォーマンスとデータの鮮度を完全に制御できます。どのキャッシュが古いのかを推測するのをやめ、データ戦略を明示的に定義し、Next.jsに重い処理を任せましょう。
関連資料: SSRで苦労している場合は、Next.jsのハイドレーションエラーの修正に関するこのガイドをチェックしてください。
AI CLIログの整形、Cron式の作成、JWTの解析、chmod権限の計算をオフラインで実行。

本番環境で検証済みのNext.js App Routerフォルダ構造ガイドで、route groups、private folders、colocationとFSDを学び、スケーラブルなエンタープライズテンプレートをダウンロードしましょう。
続きを読む
Next.js App Routerのキャッシュアーキテクチャ、fetchリクエストのメモ化、revalidateTagによるデータキャッシュ無効化、オンデマンドISR再検証を習得しましょう。
続きを読む
Next.js App Routerの包括的なプロダクションガイド:サーバーコンポーネントの境界、高度なキャッシング、Suspenseによるストリーミング、サーバーアクション、コネクションプーリング、およびエンタープライズ向けルートアーキテクチャ。
続きを読む