ビルド時間増加の問題
このブログサイトの記事数が増えてきた頃、ビルド時間が気になるようになりました。記事が10件から20件、30件と増えるにつれて、明らかにビルドが遅くなっていく感覚がありました。
原因を調べてみると、記事一覧やタグページ、カテゴリページの生成で、同じ記事ファイルを何度も読み込んでいることが分かりました。Next.jsのgenerateStaticParamsやgenerateMetadataが、それぞれで独立してファイルシステムにアクセスしているためです。
unstable_cacheの概要と特徴
調べていくうちに、Next.js 14から提供されているunstable_cacheという機能を知りました。名前に「unstable」と付いているのでちょっと不安でしたが、実際に使ってみると非常に安定して動作することが分かりました。
この機能は、サーバーサイドの関数実行結果をキャッシュして、同じ処理を重複実行しないようにしてくれます。
import { unstable_cache } from "next/cache";
export const getPostsSummary = unstable_cache(
getPostsSummaryInternal,
["blog-posts-summary"],
{
revalidate: 3600, // 1時間キャッシュ
tags: ["blog-posts"],
},
);
この書き方で、getPostsSummaryInternalの実行結果が1時間キャッシュされます。同じキーで何度呼び出されても、実際の処理は最初の1回だけです。
実装方法と効果
記事データの取得を最適化
まず、記事の基本情報を取得する関数にキャッシュを適用しました。この関数は記事一覧、RSS生成、検索インデックス生成で使われているため、効果が期待できそうでした。
export const getPostsSummary = unstable_cache(
getPostsSummaryInternal,
["blog-posts-summary"],
{
revalidate: 3600,
tags: ["blog-posts"],
},
);
タグ情報の集計も連携
タグ情報の生成処理でも、既にキャッシュされた記事データを活用できるようにしました。
export const getAllTags = unstable_cache(
async (): Promise<Tag[]> => {
const posts = await getPostsSummary(); // ここでキャッシュが効く
return extractTags(posts);
},
["all-tags"],
{ revalidate: 1800, tags: ["blog-posts"] }
);
この結果、ファイルシステムへのアクセスは初回の1回だけになり、その後の処理はメモリから高速で取得できるようになりました。ビルドログを見ていても、各ページの生成速度が明らかに向上しているのが分かりました。
実装時の注意点とトラブルシューティング
最初、パラメータを持つ関数でキャッシュキーの設計を間違えてしまいました。
// ❌ これだと異なる引数でも同じ結果が返される
export const getBlogPostsByTag = unstable_cache(
async (tagSlug: string) => { /* ... */ },
["posts-by-tag"], // 引数を無視している
{ revalidate: 1800 }
);
異なるタグでも同じ記事が表示されてしまい、しばらく原因が分からず困りました。解決策は、キーに引数を含めることでした。
// ✅ 引数をキーに含める
[`posts-by-tag-${tagSlug}`]
まとめ
実装後、ビルド時間の改善を体感できました。数値的な測定はしていませんが、ビルドプロセス全体がスムーズに進むようになりました。開発サーバーでの画面更新も速くなり、開発体験が向上しています。
unstable_cacheという名前からは安定性に不安を感じるかもしれませんが、実際には信頼して使える機能でした。特にブログのような静的サイトでは、同じデータを複数箇所で使用することが多いため、導入効果は高いと思います。
