第226章:キャッシュと再検証を本番で調整する🧊🔁🚀
本番で「速い✨」と「ちゃんと新しい🆕」を両立させるのが、この章のテーマだよ〜☺️💕
Next.js(App Router)には複数のキャッシュ層があって、どこを・どのくらい・いつ更新するかを決められるんだ🧠✨(Data Cache / Full Route Cache / Router Cache など) (Next.js)
1) まず全体像:どこにキャッシュがあるの?🗺️🧊
ざっくり言うと👇
- ページ全体がキャッシュされる層(Full Route Cache)
- fetchの結果がキャッシュされる層(Data Cache)
- 画面遷移をサクサクにする層(Router Cache) って感じだよ🫶 (Next.js)
2) 「本番で調整」が必要になる理由🥲➡️😌
開発中(dev)って、キャッシュ挙動が本番と違って見えることがあるのね💦 だから 本番っぽく確認したい時は、ローカルでも
next buildnext start
で“本番モード”で動かして確認するのが安心✅ (Next.js)
さらにデバッグ用に、ISRのキャッシュHIT/MISSをログに出す方法もあるよ👇 (Next.js)
3) 調整のつまみ(超重要)🎛️🧊
A) 「常に最新」=キャッシュしない🔥(ダッシュボード向け)
ユーザーごとに変わる画面(例:マイページ、在庫管理)はこれがラク💡
// 例:常に最新がほしい(毎回取りに行く)
const res = await fetch("https://example.com/api/me", { cache: "no-store" })
const me = await res.json()
また、ルート全体を動的にしたいときは Route Segment Config で指定もできるよ(force-dynamic は cache: 'no-store' と同等扱い) (Next.js)
// app/dashboard/page.tsx
export const dynamic = "force-dynamic"
B) 「ほどほどに最新」=◯秒ごとに更新🕒(ニュース/一覧向け)
「1分くらい古くてもOK、でも速くしたい!」みたいなページに最高✨
Next.jsのISRは「期限切れ後、次のアクセスは古いのを返しつつ裏で再生成→次から新しい」って動きになるよ(stale-while-revalidate) (Next.js)
// 例:60秒に1回くらいで更新したい
export const revalidate = 60
fetch単位でも「何秒キャッシュするか」を決められるよ(ページ内でAPIごとに調整できる)🧩 (Next.js)
C) 「更新イベントで反映」=タグ/パスで再検証🔔(CMS/投稿向け)
記事投稿・商品更新みたいに「更新した瞬間に反映したい!」ときの定番✨
✅ タグでまとめて再検証:revalidateTag
revalidateTag は「タグが付いたデータを“古い扱い”にして、次の訪問で裏更新」って感じ(stale-while-revalidate) (Next.js)
※ 2025年時点では **2引数('max' など)**が推奨で、1引数は非推奨だよ (Next.js)
// 取得側(タグを付ける)
const res = await fetch("https://example.com/api/posts", {
next: { tags: ["posts"] },
})
// 更新側(Server Actionなどで呼ぶ)
"use server"
import { revalidateTag } from "next/cache"
export async function publishPost() {
// DB更新など...
revalidateTag("posts", "max")
}
✅ パスでページごと再検証:revalidatePath
「このページ(/blog)だけ作り直してね」って指示できる✨ Server Function(Server Action)と Route Handler で挙動の注意があるので、公式の説明どおり使うのが安全だよ🫶 (Next.js)
"use server"
import { revalidatePath } from "next/cache"
export async function refreshBlog() {
revalidatePath("/blog")
}
4) ミニ実験:本番モードで「10秒キャッシュ」を体験しよ〜🧪🧊✨
ゴール🎯
/cache-demoをリロードしても 10秒間は同じ時刻が出る- 10秒超えたら、次のアクセスで 裏で更新が走る感じを体験する
Step 1:今の時刻を返すAPIを作る⏰
// app/api/now/route.ts
export async function GET() {
return Response.json({ now: new Date().toISOString() })
}
Step 2:10秒キャッシュで表示するページを作る🧊
// app/cache-demo/page.tsx
export const revalidate = 10
export default async function Page() {
const res = await fetch("http://localhost:3000/api/now", {
// revalidate=10 と合わせて、タグも付けておく(後で手動更新できる)
next: { tags: ["now"] },
})
const data: { now: string } = await res.json()
return (
<main style={{ padding: 24 }}>
<h1>Cache Demo 🧊</h1>
<p>APIの時刻:{data.now}</p>
<p>(10秒くらい同じ表示になったら成功〜✨)</p>
</main>
)
}
※
http://localhost:3000/...で見せたけど、環境によっては相対パスで書く流儀もあるよ〜(ここは“動きが分かりやすい”優先でOK☺️)
Step 3:本番モードで確認する(超大事)🚀
PowerShellで👇
npm run build
npm run start
ブラウザで http://localhost:3000/cache-demo を開いて、連打リロードしてみてね🔁✨
- だいたい10秒間は同じ時刻
- 10秒を超えたら、次のアクセスで更新が入る(ISRの動き) (Next.js)
5) 本番(Vercel)での「見える化」👀📮
✅ ヘッダーでCDNキャッシュ状況を見る:x-vercel-cache
Vercelだと x-vercel-cache が HIT / MISS / STALE などになって、CDN側の状況が分かるよ📦 (Vercel)
ただし注意⚠️:Data Cache(fetchのキャッシュ)を使ってても MISS になりがちなので、Data Cacheの命中はログやカスタムヘッダー等で確認してね、って公式にも書いてあるよ〜🫠 (Vercel)
6) 迷った時の決め方(めちゃ実用)🧠✨
- ユーザーごとに違う? →
no-store/force-dynamic🔥 - 更新は少ないけどアクセス多い? →
revalidate: 60〜3600みたいに長め🕒(公式も“短すぎ注意”って言ってる) (Next.js) - 更新イベントがある?(投稿/公開/在庫更新) →
revalidatePath/revalidateTagで“必要な時だけ更新”🔔 (Next.js)
次の章(第227章)では、計測の入口(数字で見る📈)に進めるよ〜☺️✨