⚛️

ISR の紹介と実装【Next.js】

に公開

はじめに

先日、Next.js の勉強会で、ISR(Incremental Static Regeneration)について取り上げました 🫐

静的なサイトの高速性を保ちながら、動的なコンテンツ更新を可能にする技術は、
モダンな Web アプリケーションにおいて非常に重要な要素です。

今回は、Next.js の ISR について調査したので、基礎的な内容をまとめました!
時間の節約になれば、嬉しいです 🙌

ISR とは?

https://nextjs.org/docs/app/guides/incremental-static-regeneration

ISR(Incremental Static Regeneration)とは、静的コンテンツをサイト全体をリビルドすることなく更新できる機能です

通常の Static Site Generation(SSG)では、コンテンツを更新するたびに全体をビルドし直す必要がありましたが、
ISR では、指定した間隔で個別のページを再生成できます!

ISR の主な利点として、以下が挙げられます:

  • パフォーマンス向上: 静的ページは一貫して高速
  • サーバー負荷軽減: キャッシュされたコンテンツを使用することで、データソースへのリクエストを削減
  • ビルド時間短縮: 必要なページのみを再生成するため、大規模なサイトでも効率的

SSG の高速性と SSR の最新性を、組み合わせることができるので、便利です 👍

ISR の使い方【App Router】

Next.js の公式ドキュメントのでは、ISR の実装方法については、主に2通りあります。

  • fetch 関数を使用する方法
  • revalidate オプションを使用する方法

それぞれの方法について、詳しく見ていきましょう!

1: fetch 関数を使用する方法

https://nextjs.org/docs/app/api-reference/functions/fetch

上記の公式掲載の通りですが、fetch 関数のオプションとして、revalidate を指定することで、ISR を実装できます。

例:

// app/blog/[id]/page.tsx
interface Post {
  id: string;
  title: string;
  content: string;
}

const getPost = async (id: string) => {
  const post: Post = await fetch(`https://api.vercel.app/blog/${id}`, {
    next: {
      revalidate: 60, // 60秒ごとに再検証
    },
  }).then((res) => res.json());
  return post;
};

export default async function Page({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const post = await getPost(id);
  return (
    <main>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </main>
  );
}

意外と簡単ですね!

2: revalidate オプションを使用する方法

https://nextjs.org/docs/app/guides/incremental-static-regeneration

さて、先ほどの fetch 関数を使用する方法では、
fetchを使用しない、データ操作の関数などでは、ISR を使用できません。

例えば、

  • supabaseや、microCMSなど、外部サービスを使用しているケース
  • データベースのデータを取得するケース

などでは、直接fetch 関数を使用しません。

そのような場合は、revalidate オプションを使用して ISR を実装できます。

// app/blog/[id]/page.tsx
interface Post {
  id: string;
  title: string;
  content: string;
}

// 60秒ごとに再検証
export const revalidate = 60;

export default async function Page({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const post = await getPost(id);

  return (
    <main>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </main>
  );
}

fetch 関数のオプションではなく、
revalidateを定義してエクスポートをすることでも、ISR を実装できます!

ISR の活用とユースケース

1: オンデマンド再検証

https://nextjs.org/docs/app/guides/incremental-static-regeneration#on-demand-revalidation-with-revalidatepath

より精密な制御が必要な場合は、revalidatePathrevalidateTag を使用できます。

これにより、特定のタイミングで、再検証を行うことができます:

// app/actions.ts
"use server";

import { revalidatePath } from "next/cache";

export async function createPost() {
  // 投稿作成処理...

  // /blog パスのキャッシュを無効化
  revalidatePath("/blog");
}

この方法により、コンテンツが変更されたタイミングで、柔軟にキャッシュを更新できますね!

2: ビルド時間の短縮が可能

ISR の利点の一つは、ビルド時間の短縮です。

従来の SSG では、サイト全体をリビルドする必要がありましたが、
ISR では必要なページのみを再生成するため、特に大規模なサイトにおいて効果的です。

例えば、1000 ページあるブログサイトでも、
更新されたページのみを再生成することで、ビルド時間を大幅に短縮できます!

プロジェクトの規模が大きくなると、SSG から ISR への移行を検討してもいいかも知れませんね 😎

3: Cloudflare Pages では、一部非対応

以前から、この ISR という機能が、痒い所に手が届く感じで便利だなーと思っていたのですが、
Next.js 特有の機能ということもあり、1部のホスティングサービスでは、サポートされていないケースがあります。

使用の際は確認が必要です!

https://developers.cloudflare.com/pages/framework-guides/nextjs/ssr/supported-features/#incremental-static-regeneration

例えば、上記の通り、
Cloudflare Pages では、ISR の完全な機能は現在サポートされていません

Cloudflare Pages の @cloudflare/next-on-pages を使用する場合、
ISR ページは静的フォールバックファイルが使用されます。

つまり、静的ページとして配信されますが、
再生成機能は動作しません

この制限により、ISR のメリットを活かしたい場合は、
サーバーサイドレンダリング(SSR)への変更を検討する必要があります。

ちなみに:OpenNext では対応済み!

https://opennext.js.org/cloudflare

OpenNext の Cloudflare アダプターを使用すると、ISR の完全サポートが可能です!

OpenNext は、Next.js アプリケーションをさまざまなプラットフォームで動作させるためのプロジェクトで、
Cloudflare Workers 上で、ISR を含む多くの Next.js 機能をサポートしています。

OpenNext を使用することで、以下が実現できます:

  • 完全な ISR サポート
  • Next.js の最新機能への対応
  • Node.js ランタイムサポート
  • 高度なキャッシュ管理

この方法により、Cloudflare 環境でも ISR の恩恵を受けることができる用になりました!🎉

おわりに

最後まで読んでいただき、ありがとうございます 🥳

下記の、Next.js ハンズオン勉強会での、振り返りのような記事ですが、
少しでも参考になれば、嬉しいです!

https://b13o.com/services/handson-workshop

そして、もし、間違いや補足情報などがありましたら、
ぜひコメントを追加してください!

Happy Hacking :)

参考

https://nextjs.org/docs/app/guides/incremental-static-regeneration
https://developers.cloudflare.com/pages/framework-guides/nextjs/ssr/supported-features/
https://opennext.js.org/cloudflare/get-started
https://blog.cloudflare.com/deploying-nextjs-apps-to-cloudflare-workers-with-the-opennext-adapter/
https://www.zenryoku-kun.com/new-post/nextjs-isr

b13o Tech Blog

Discussion