包括的なキャッシング戦略
包括的なキャッシング戦略
Section titled “包括的なキャッシング戦略”Next.jsのキャッシング戦略を包括的に理解し、実践的に活用する方法を詳しく解説します。
なぜキャッシング戦略が重要なのか
Section titled “なぜキャッシング戦略が重要なのか”キャッシングなしの問題
Section titled “キャッシングなしの問題”問題のある実装:
// キャッシングなし: 毎回APIリクエストが発生export default async function ProductList() { const products = await fetch('https://api.example.com/products'); const data = await products.json();
return ( <div> {data.map(product => ( <ProductItem key={product.id} product={product} /> ))} </div> );}
// 問題点:// - 毎回APIリクエストが発生// - サーバーの負荷が高い// - レスポンス時間が遅い// - ユーザー体験が悪い影響:
- サーバーの負荷増加
- レスポンス時間の増加
- ユーザー体験の低下
- コストの増加
キャッシングによる解決
Section titled “キャッシングによる解決”改善された実装:
// キャッシングあり: 効率的なデータ取得export default async function ProductList() { const products = await fetch('https://api.example.com/products', { next: { revalidate: 3600 } // 1時間キャッシュ }); const data = await products.json();
return ( <div> {data.map(product => ( <ProductItem key={product.id} product={product} /> ))} </div> );}
// メリット:// - APIリクエストが削減される// - サーバーの負荷が軽減される// - レスポンス時間が短縮される// - ユーザー体験が向上するNext.jsのキャッシングレイヤー
Section titled “Next.jsのキャッシングレイヤー”1. Request Memoization(リクエストメモ化)
Section titled “1. Request Memoization(リクエストメモ化)”定義: 同一リクエスト内での重複したfetchリクエストを自動的にメモ化します。
実装例:
// 同一リクエスト内で複数回呼び出されても、1回だけ実行されるasync function getUser(id: string) { const res = await fetch(`https://api.example.com/users/${id}`); return res.json();}
export default async function UserProfile({ params }: { params: { id: string } }) { // これらの呼び出しは自動的にメモ化される const user = await getUser(params.id); const userPosts = await getUser(params.id); // 同じリクエストが再利用される
return ( <div> <h1>{user.name}</h1> <p>{user.email}</p> </div> );}メリット:
- 自動的に重複リクエストを防ぐ
- パフォーマンスの向上
- コードの変更が不要
2. Data Cache(データキャッシュ)
Section titled “2. Data Cache(データキャッシュ)”定義:
fetchの結果を自動的にキャッシュし、同じリクエストに対してキャッシュから返します。
実装例:
// デフォルトでキャッシュされるconst res = await fetch('https://api.example.com/products');const data = await res.json();
// キャッシュの制御// 1. 時間ベースの再検証(ISR)const res1 = await fetch('https://api.example.com/products', { next: { revalidate: 3600 } // 1時間ごとに再検証});
// 2. キャッシュを無効化const res2 = await fetch('https://api.example.com/products', { cache: 'no-store' // キャッシュしない});
// 3. 強制キャッシュconst res3 = await fetch('https://api.example.com/products', { cache: 'force-cache' // 強制的にキャッシュから取得});タグベースの再検証:
// タグを指定してキャッシュconst res = await fetch('https://api.example.com/products', { next: { revalidate: 3600, tags: ['products'] // タグでキャッシュを管理 }});
// タグベースの再検証(API Route)// app/api/revalidate/route.tsimport { revalidateTag } from 'next/cache';
export async function POST(request: Request) { const { tag } = await request.json(); revalidateTag(tag); // 特定のタグのキャッシュを無効化 return Response.json({ revalidated: true });}3. Full Route Cache(完全ルートキャッシュ)
Section titled “3. Full Route Cache(完全ルートキャッシュ)”定義: 静的レンダリングされたページのレンダリング結果をキャッシュします。
実装例:
// 静的ページ: ビルド時にレンダリングされ、キャッシュされるexport default async function BlogPost({ params }: { params: { slug: string } }) { const post = await fetch(`https://api.example.com/posts/${params.slug}`, { next: { revalidate: false } // キャッシュを無効化しない }); const data = await post.json();
return ( <article> <h1>{data.title}</h1> <div>{data.content}</div> </article> );}動的ルートのキャッシュ:
// 動的ルート: キャッシュを無効化export const dynamic = 'force-dynamic'; // 動的レンダリングを強制
export default async function Dashboard() { const data = await fetch('https://api.example.com/dashboard', { cache: 'no-store' // キャッシュしない }); const dashboard = await data.json();
return <Dashboard data={dashboard} />;}4. Router Cache(ルーターキャッシュ)
Section titled “4. Router Cache(ルーターキャッシュ)”定義: クライアント側でナビゲーション時にページをキャッシュします。
実装例:
// クライアント側のキャッシュ設定module.exports = { experimental: { staleTimes: { dynamic: 30, // 動的ページ: 30秒 static: 180, // 静的ページ: 180秒 }, },};実践的なキャッシング戦略
Section titled “実践的なキャッシング戦略”1. 常に最新のデータが必要な場合
Section titled “1. 常に最新のデータが必要な場合”// ダッシュボード: 常に最新のデータが必要export const dynamic = 'force-dynamic';export const revalidate = 0;
export default async function Dashboard() { const data = await fetch('https://api.example.com/dashboard', { cache: 'no-store' // キャッシュしない }); const dashboard = await data.json();
return <Dashboard data={dashboard} />;}2. 定期的に更新されるデータ
Section titled “2. 定期的に更新されるデータ”// 商品一覧: 1時間ごとに更新export default async function Products() { const res = await fetch('https://api.example.com/products', { next: { revalidate: 3600 } // 1時間ごとに再検証 }); const products = await res.json();
return <ProductList products={products} />;}3. ビルド時に生成されるデータ
Section titled “3. ビルド時に生成されるデータ”// ブログ記事: ビルド時に生成export default async function BlogPost({ params }: { params: { slug: string } }) { const res = await fetch(`https://api.example.com/posts/${params.slug}`, { next: { revalidate: false } // キャッシュを無効化しない }); const post = await res.json();
return ( <article> <h1>{post.title}</h1> <div>{post.content}</div> </article> );}4. オンデマンド再検証
Section titled “4. オンデマンド再検証”// 商品詳細: 更新時に再検証export default async function Product({ params }: { params: { id: string } }) { const res = await fetch(`https://api.example.com/products/${params.id}`, { next: { revalidate: 3600, tags: [`product-${params.id}`] // 商品ごとにタグを設定 } }); const product = await res.json();
return <ProductDetails product={product} />;}
// API Route: 商品更新時に再検証// app/api/products/[id]/route.tsimport { revalidateTag } from 'next/cache';
export async function PUT( request: Request, { params }: { params: { id: string } }) { // 商品を更新 await updateProduct(params.id, await request.json());
// キャッシュを再検証 revalidateTag(`product-${params.id}`);
return Response.json({ success: true });}キャッシング戦略の選択指針
Section titled “キャッシング戦略の選択指針”データの特性に応じた選択
Section titled “データの特性に応じた選択”| データの特性 | 推奨戦略 | 実装例 |
|---|---|---|
| 常に最新が必要 | cache: 'no-store' | ダッシュボード、リアルタイムデータ |
| 定期的に更新 | revalidate: 3600 | 商品一覧、ニュース記事 |
| ビルド時に生成 | revalidate: false | ブログ記事、ドキュメント |
| 更新時に再検証 | タグベース再検証 | 商品詳細、ユーザープロフィール |
パフォーマンスとデータの鮮度のバランス
Section titled “パフォーマンスとデータの鮮度のバランス”// パフォーマンス重視: 長いキャッシュ時間const res = await fetch('https://api.example.com/products', { next: { revalidate: 86400 } // 24時間});
// データの鮮度重視: 短いキャッシュ時間const res = await fetch('https://api.example.com/dashboard', { next: { revalidate: 60 } // 1分});
// バランス: 中程度のキャッシュ時間const res = await fetch('https://api.example.com/news', { next: { revalidate: 3600 } // 1時間});包括的なキャッシング戦略のポイント:
- Request Memoization: 同一リクエスト内での重複リクエストを防ぐ
- Data Cache: fetchの結果をキャッシュ、時間ベース・タグベースの再検証
- Full Route Cache: 静的レンダリング結果のキャッシュ
- Router Cache: クライアント側のルーターキャッシュ
- 実践的な戦略: データの特性に応じた選択、パフォーマンスとデータの鮮度のバランス
適切にキャッシング戦略を使用することで、パフォーマンスとデータの鮮度のバランスを最適化できます。