みかづきブログ・カスタム

基本的にはちょちょいのほいです。

App Router(Next.js 13.4.4)のSSGとISRを切り替える 💻

App Router(Next.js 13.4.4)のSSGで作成したサイト を、 ISR にしたらどれぐらいパフォーマンスが変わるのかを比べてみようと思い、キャッシュ を調べながら、SSGからISRに切り替えてみました。

blog.kimizuka.org
nextjs.org
nextjs.org

SSGからISRへの変更手順

❶ next.config.js のoutputを削除

/**
 * @type {import('next').NextConfig}
 */
const nextConfig = {
  // output: 'export' // 削除
};
 
module.exports = nextConfig;

❷ APIディレクトリを作成

nextjs.org

Pages Routerの頃とAPIの作成方法に変更があり、 pages/api/works/index.tsx ではなく app/api/works/route.ts という感じで route.ts を作成する形となっております。

app/api/works/route.ts
import { NextResponse } from 'next/server';
import { getWorks } from '@/scripts/getWorks'; // SSGの際に使っていた記事取得のスクリプト

export async function GET(request: Request) {
  const works = await getWorks();

  return NextResponse.json(works);
}

❸ 記事の取得部分をfetchに変更

nextjs.org
nextjs.org

記事一覧の取得をAPI経由に変更します。

app/works/page.tsx
import { notFound } from 'next/navigation';
// import { getWorks } from '@/scripts/getWorks'; // 削除
import { nextRevalidate } from '@/scripts/variable';
import { WorksPageTemplate } from '@/components/templates/WorksPageTemplate';

export default async function WorksPage({
    id: string;
  };
}) {
  // const contents = await getWorks(); // 削除
  const response = await fetch(               // 追加
    `${ process.env.BASE_URL }/api/v1/works`, // 追加
    {                                         // 追加
      next: {                                 // 追加
        revalidate: nextRevalidate            // 追加
      }                                       // 追加
    }                                         // 追加
  );                                          // 追加

  if (!response.ok) {                         // 追加
    return notFound();                        // 追加
  }                                           // 追加

  const { contents } = await response.json(); // 追加

  return (
    <WorksPageTemplate contents={ contents } />
  );
}

ローカルでは、localhost:3000/api/v1/works から、Vercelにデプロイした後はプレビューURLからデータを取得したいので、URLは.envに記載します。また、キャッシュの有効期限も管理しやすいように別ファイル(scripts/variable.ts)にしておきます。

❸ buildの確認

ローカルでは、localhost:3000/api/v1/works からデータを取得するため、

  • yarn dev でローカルサーバをたてる
  • yarn build でビルドを実行

でビルドを実行します。
localhost:3000/api/v1/works からデータを取得できないとビルドがコケるからです。

❹ Vercelにデプロイ

  • environment-variablesからPreviewのBASE_URLを設定する
  • next.config.js と app/api 以下を先にデプロイする
  • すべてをデプロイする

という順に実行します。
/api/v1/works からデータを取得できないとビルドがコケるからです。

ISRからSSGへの変更手順

ISR化の逆の手順を踏んでいきます。

❶ next.config.js のoutputを追加

/**
 * @type {import('next').NextConfig}
 */
const nextConfig = {
  output: 'export' // 追加
};
 
module.exports = nextConfig;

❷ APIディレクトリを削除

app/api か残っているとビルドがコケるので削除します。

❸ 記事の取得部分のfetchを削除

app/works/page.tsx
import { notFound } from 'next/navigation';
import { getWorks } from '@/scripts/getWorks'; // 追加
import { nextRevalidate } from '@/scripts/variable';
import { WorksPageTemplate } from '@/components/templates/WorksPageTemplate';

export default async function WorksPage({
    id: string;
  };
}) {
  const contents = await getWorks(); // 追加
  // const response = await fetch(               // 削除
  //   `${ process.env.BASE_URL }/api/v1/works`, // 削除
  //   {                                         // 削除
  //     next: {                                 // 削除
  //       revalidate: nextRevalidate            // 削除
  //     }                                       // 削除
  //   }                                         // 削除
  // );                                          // 削除

  // if (!response.ok) {                         // 削除
  //   return notFound();                        // 削除
  // }                                           // 削除

  // const { contents } = await response.json(); // 削除

  return (
    <WorksPageTemplate contents={ contents } />
  );
}

❹ Vercelにデプロイ

  • environment-variablesからPreviewのBASE_URLを削除する
  • Vercelにデプロイする




という感じで、ISRとSSGを切り替えながら検証したのですが、てみました。結果として、 今回のサイト ではSSGを採用しました。
ISRは記事を投稿した瞬間にページにアクセスできるようになる魅力があるものの、記事の更新頻度が高くないので、記事を書いたタイミングでページを作成した方が効率が良いと考えたからです。記事を誰でも投稿できるサイト(WikiやBBSなど)で、API取得ではなくサーバサイドでレンダリングをかけたい場合はISRがよさそうです。