Magicode logo
Magicode
5 min read

【設計例付き】Next.jsのISRとfallback

https://cdn.apollon.ai/media/notebox/54103c95-98f6-43eb-b9be-ed5d8452a188.jpeg
Next.jsのレンダリングについては、「どう設定すべきかは業務要件やアーキテクチャ特性による」みたいな解説が多く見受けられる気がしました。
もちろんそうだと思うのですが、設計例があるだけでずっととっつきやすくなると思いますので、この記事では設計例も交えながら、ISRとfallbackについて解説したいと思います。
前半でISRとfallbackについて解説し、後半で設計例について解説します。

想定読者

SSGのレンダリング種別について理解していることが前提です。 「next build時にビルドサーバー上でgetStaticPropsに記載されたロジックを実行してデータを取得する。ビルドの成果物としてデータを含めることで、実行時にはデータソースへのアクセスなしで画面を表示できる」くらいの理解度で問題ありません。
また、Next.jsでの静的生成の正式名称はStatic Geneartionとなりますが、本記事内では、一般的な用語に合わせてSSGで統一します。

ISRとfallback

1. 全体感

ざっくりISRとfallbackの全体感を掴むための図解は以下。
  • どちらもSSGに関する概念
  • fallbackは動的ルーティングするページにのみ関係する。実際に、getStaticPaths内で指定する設定値である。
  • ISRはデータが古くなっていないかの確認のため、静的ルーティング/動的ルーティング両方のページに指定する。実際に、getStaticProps内で指定する設定値である。
  • ISRもfallbackも、ページの追加や更新の処理はgetStaticPropsの処理を流用している。
    • ので、ISRやfallbackを指定する場合、例えばgetStaticPropsで環境変数を使っていれば、ビルドサーバーだけではなく実際のNext.jsサーバーにも環境変数が必要となる。他にも、データソース側にIP制限をかけている場合なども同様である。
ISRとfallbackはこのような棲み分けとなっており、それぞれ独立して設定可能である。

2. ISRについて

  • 概要
    • ISRは、データが古くなっていないかの確認をする。
    • SSGを行う全てのページに設定でき、getStaticProps内でrevalidateプロパティで間隔を設定する。
    • つまり、revalidateプロパティに値が指定されているのであれば、そのページはISRされるということになる。
  • データ取得の最小間隔
    • そのページが生成されてから、revalidateに指定した秒数経つまで
  • データ取得の発生タイミング
    • データアクセスを最小限にするため、revalidateに指定した秒数が経ってから、当該ページが取得されたタイミングで行われる。
      • 例:間隔が30秒なら、そのページが生成されてから30秒経ってから最初に受けたリクエスト時にデータ更新を行う
  • 返されるページとの関係性
    • 遅延を起こさないため、ページの返却とは無関係に行われる。
      • 例:1つ上の例では、30秒経ってから最初に受けたリクエストに対しては、その時点のページが返される

3. fallback

  • 概要
    • fallbackは、動的ルーティングを行うページについて、生成していないパスに対するリクエストが来たときにページを生成するかエラー扱いにするかを設定する。
    • SSGを行うページのうち、動的ルーティングを行うページに設定でき、getStaticPaths内のfallbackプロパティに有効化有無を設定する。
    • 有効化する場合、fallback:truefallback:blockingの設定が存在する。これは、loadingのような画面描画を挟むか、同期的にページを生成するかといった違いになる。
  • データ取得の最小間隔
    • 既に存在するページに対しては発生しないため、間隔といった概念は存在しない。
  • データ取得の発生タイミング
    • 当該ページが取得されたタイミング
  • 返されるページとの関係性
    • fallback:truefallback:blockingの設定がある。
      • fallback:trueとした場合は、一度「loading...」のような描画を挟んで、非同期で遷移が可能。
        • 仕組みとしては、router.isFallbackを判定してページ生成中のエレメントを記述しておく。fallbackが発動したときは、ページを即座に返却し、フロント側でisFallback:trueのエレメントが表示しつつ、画面遷移時同様の(ページ名).jsonリクエストが即座に行われる。(ページ名).jsonが返ってきたタイミングでisFallback:trueになったり、Propsがundefinedでなくなったりすることで、ユーザー目線の初期表示が完了する。
        • この動作はページ再読み込みのときだけである。next/linkなどでのクライアントサイド画面遷移については、挙動はfallback: blockingと同等となる。
      • fallback:blockingとした場合は、SSRと同様に、同期的にページを生成して返却する。
  • fallback: falseとした場合
    • 生成していないパスにリクエストが来た場合、データ取得や生成は行われず、404エラーとなる。

設計例

1. 題材

ブログ一覧とブログ詳細ページを想定する。
  • ブログ詳細ページは、記事のIDがURLのパスに含まれているものとする。
  • ブログ投稿は追加や変更がありえるものの、頻繁ではないものとする。

2. 結論

今回は以下のように設計した。
  • ブログ一覧ページ:SSG、ISRあり、fallbackなし
  • ブログ詳細ページ:SSG、ISRあり、fallbackあり

3. 解説

  • ブログ一覧ページ
    • ISR:データソースに対するブログ投稿全件のクエリの結果が変わる可能性がある(ブログ投稿の追加・変更)。ISRなしとすると、再度デプロイを行うまで一覧上にブログ投稿の追加・変更が反映されない。そこで、ISRはありとする。
    • fallback:動的ルーティングが不要なページとなるので、getStaticPathsを定義せず、fallbackは指定しない
  • ブログ詳細ページ
    • ISR:データソースに対するブログ投稿1件のクエリ結果が変わる可能性がある(ブログ投稿の変更)。ISRなしとすると、再度デプロイを行うまで詳細ページにブログ投稿の変更が反映されない。そこで、ISRはありとする。
    • fallback:ブログ記事ごとに異なるパスとなるため、getStaticPathsが定義される。一覧ページにISRを指定したため、fallbackは有効化されるべきである。そうしなければ、新規投稿が一覧ページに表示されても、ブログ詳細ページに遷移したときにエラーとなってしまうためである。
      • fallbackの指定はtrueでも"blocking"でも機能上は問題ない。実現したいユーザー体験に応じて選んでよい。

Discussion

コメントにはログインが必要です。