🚀

【8章】Next.jsのチュートリアルをやってみた

2025/01/12に公開

前章のメモ

https://zenn.dev/kuuki/articles/nextjs-tutorial-07/

Next.js の公式チュートリアルの該当ページ

https://nextjs.org/learn/dashboard-app/static-and-dynamic-rendering

学ぶこと

  • 静的レンダリングとは何か?、どのようにパフォーマンスを向上させるか?
  • 動的レンダリングとは何か?、いつ使うのか?
  • ダッシュボードを動的にするためのアプローチ
  • 遅いデータフェッチが何を起こすのか?

静的レンダリングとは何か?

サーバーコンポーネントにおけるデフォルトのレンダリング方法

アプリのビルドやrevalidateの際にデータを取得&レンダリングすること。

revalidateとは、 一定時間の経過や特定のイベントをトリガーとしてキャッシュを消去すること。 これにより、定期的に最新のデータを取得できる

ビルド時にレンダリングしたものはCDN にキャッシュされます。

ユーザに対してはキャッシュされた結果が返されるので下記のメリットがあります。

  1. レスポンスが速い
  2. サーバへの負荷が低い
  3. ページ読み込み時にすでにレンダリングされているので、SEO に強い

常に最新のデータを表示できるわけではないので、ブログ記事や商品ページといった

比較的更新頻度が低く、どのユーザにも同じように表示されるページに適しています。

動的レンダリングとは何か?

ユーザのリクエストごとにレンダリングすること。

  1. 最新のデータが反映される
  2. 各ユーザにあったデータを表示できる
  3. Cookie やクエリパラメータなどリクエスト時のパラメータを受け取れる

ダッシュボードや SNS のタイムラインなど

更新頻度が高いページやユーザごとに異なる表示となるページに適しています

ダッシュボードを動的にする

サーバーコンポーネントはデフォルトで静的レンダリングを使用します。

では、動的レンダリングを使用するにはどうすればよいでしょうか?

unstable_noStoreという Next.js の API を呼び出すことで動的レンダリングにします

早速、前章で使用した /app/lib/data.tsで使ってみましょう!

とはいえ、next/cache から unstable_noStore をインポートして実行するのみ。

/app/lib/data.ts
// 一部抜粋

import { unstable_noStore as noStore } from 'next/cache';

export async function fetchRevenue() {
  // Add noStore() here to prevent the response from being cached.
  // This is equivalent to in fetch(..., {cache: 'no-store'}).
  noStore();

  //...
}

みたいな感じで

  • fetchLatestInvoices
  • fetchCardData
  • fetchFilteredInvoices
  • fetchInvoicesPages
  • fetchFilteredCustomers
  • fetchInvoiceById

にもnoStore() を記述します

unstable_noStore は実験的な API なので仕様が変更される可能性があるみたいです 安定的な API を使いたい場合は、

  1. export const dynamic = "force-dynamic"
  2. fetch('https://...', { cache: 'no-store' })

を使ってみてください

遅いデータフェッチを再現する

ダッシュボードを動的レンダリングにできました!

ただ、前章でも触れた1つでもデータ取得に時間がかかるものがあった場合どうなるでしょうか?

実際にアプリケーションを使用して試してみます。

/app/lib/data.ts の fetchRevenue 関数内のコメントを ↓ のように外していきます。

/app/lib/data.ts
// 一部抜粋

export async function fetchRevenue() {
  // Add noStore() here to prevent the response from being cached.
  // This is equivalent to in fetch(..., {cache: 'no-store'}).
  noStore();

  try {
    // Artificially delay a response for demo purposes.
    // Don't do this in production :)

    console.log('Fetching revenue data...');
    await new Promise((resolve) => setTimeout(resolve, 3000));

    const data = await sql<Revenue>`SELECT * FROM revenue`;

    console.log('Data fetch completed after 3 seconds.');

    return data.rows;
  } catch (error) {
    console.error('Database Error:', error);
    throw new Error('Failed to fetch revenue data.');
  }
}

Promise インスタンス+ setTimeout を使用することで指定した時間だけ待つことができます

引数に3000msと指定しているので3 秒待つことになります。

つまり、この関数の処理に 3 秒+ α かかります。

ローカルサーバを起動して、http://localhost:3000/dashboard  にアクセスすると

表示に時間がかかることがわかると思います

curl コマンドで時間を計測してみると。。。

// 3秒待つ処理を入れた前
$ curl -w"time_total: %{time_total}\n" -o /dev/null http://localhost:3000/dashboard
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 40461    0 40461    0     0  64364      0 --:--:-- --:--:-- --:--:-- 64325
time_total: 0.628620

// 3秒待つ処理を入れた後
$ curl -w"time_total: %{time_total}\n" -o /dev/null http://localhost:3000/dashboard
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 40461    0 40461    0     0   8638      0 --:--:-- 0:00:04 --:--:-- 11620
time_total: 4.683759

約 0.6 秒と約 4.7 秒なのでパフォーマンスが大幅に異なりますね

なので、

Q. 1つでもデータ取得に時間がかかるものがあった場合どうなるでしょうか?

A. 動的レンダリングでは、アプリケーションの速度は最も遅いデータ取得速度と同じになる。

次章でこの課題を解決する方法を見ていきます

次章のメモ

https://zenn.dev/kuuki/articles/nextjs-tutorial-09/

Discussion