Composition, Caching, and Architecture in modern Next.js
29分 47秒
Next.js App RouterでのスケーラブルなWeb開発:キャッシングとアーキテクチャの最適化
この記事は動画の内容を元にAIが生成したものです。正確な情報は元の動画をご確認ください。
ポイント
- •Next.js App RouterでのWeb開発者が直面する、静的・動的レンダリングの混在によるパフォーマンスと保守性の課題を解説します。
- •本記事は、Next.js 16で利用可能となる`use cache`ディレクティブによる、コンポーザブルなキャッシング手法を紹介します。
- •これにより、データフェッチの最適化やアーキテクチャ改善を通じて、スケーラブルで高性能なアプリケーションを構築する知見が得られます。
こんにちは、皆様。ノルウェーのウェブ開発者であるAuroraです。私はCrane Consultingでコンサルタントとして勤務しており、現在のプロジェクトではNext.js App Routerを活用して積極的に開発を行っています。本日は、Next.jsにおけるコンポジション、キャッシング、およびアーキテクチャに関するパターンをご紹介します。これらのパターンは、スケーラビリティとパフォーマンスを確保するために役立つでしょう。まずは、この講演の最も基本的な概念である静的レンダリングと動的レンダリングについて再確認させてください。私たちはNext.js App Routerにおいて、これら両方のレンダリング方法に遭遇します。## Next.jsのレンダリングの基礎### 静的レンダリング静的レンダリングは、事前レンダリングされたコンテンツをキャッシュしてグローバルに配信できるため、より高速なウェブサイト構築を可能にします。これにより、ユーザーはより迅速にコンテンツにアクセスできます。例えば、next.comのウェブサイトが良い例です。静的レンダリングは、各ユーザーのリクエストごとにコンテンツを生成する必要がないため、サーバーの負荷を軽減します。また、事前レンダリングされたコンテンツは、ページロード時にコンテンツがすでに利用可能であるため、検索エンジンのクローラーにとってもインデックス化が容易です。### 動的レンダリング一方、動的レンダリングは、アプリケーションがリアルタイムまたは頻繁に更新されるデータを表示することを可能にします。これにより、ダッシュボードやユーザープロファイルのようなパーソナライズされたコンテンツを提供することもできます。例えば、Vercelダッシュボードが該当します。動的レンダリングを使用すると、リクエスト時にのみ判明する情報にアクセスできます。この場合、どのユーザーがダッシュボードにアクセスしているか、といった情報です。#### 動的レンダリングを引き起こすAPIページに渡されるparamsやsearch paramsプロップ、またはそれらに相当するフックの使用は、ページを動的にレンダリングさせます。ただし、paramsを使用する場合は、generate static paramsを使用して事前レンダリングされるページセットを事前に定義し、ユーザーによって生成される際にページをキャッシュすることも可能です。さらに、受信リクエストのクッキーやヘッダーを読み取ると、ページは動的レンダリングにオプトインします。ただし、paramsとは異なり、ヘッダーやクッキーを使用して何かをキャッシュまたは事前レンダリングしようとすると、ビルド時にエラーが発生します。これは、それらの情報が事前に判明できないためです。最後に、データキャッシュ構成でno-storeを指定してfetchを使用することも、動的レンダリングを強制します。これら以外にも動的レンダリングを引き起こすAPIはいくつかありますが、これらが最も一般的に遭遇するものです。### 旧Next.jsでの課題以前のNext.jsのバージョンでは、ページは完全に静的または完全に動的にレンダリングされていました。ページ上の1つの動的なAPI(例えば、クッキーの値に対する簡単な認証チェック)を使用するだけで、ページ全体が動的レンダリングにオプトインしていました。React Server ComponentsとSuspenseを利用することで、パーソナライズされたウェルカムバナーやレコメンデーションのような動的コンテンツを準備ができた時点でストリーム配信し、ニュースレターのような静的コンテンツを表示しながら、Suspenseによってフォールバックのみを提供することが可能になりました。しかし、動的なページに複数の非同期コンポーネント(例えば、注目の製品)を追加すると、それらのコンポーネントが動的なAPIに依存していなくても、リクエスト時に実行されてしまいます。そのため、初期ページロードをブロックしないように、これらのコンポーネントも中断してストリーム配信し、スケルトンの作成や累積レイアウトシフトのような懸念事項を伴う余分な作業が発生していました。ページはしばしば静的コンテンツと動的コンテンツの混合物です。例えば、ユーザー情報に依存しつつも、ほとんどが静的データで構成されるEコマースアプリなどが挙げられます。静的または動的のいずれかを選択することを強制されると、全く、あるいはめったに変化しないコンテンツに対して、サーバー上で多くの冗長な処理が発生し、パフォーマンスにとって最適ではありませんでした。## use cache ディレクティブによる解決策この問題を解決するため、昨年のNext.js Confでuse cacheディレクティブが発表され、今年の基調講演で確認されたように、Next.js 16で利用可能になりました。use cacheを使用すると、ページはもはや静的または動的レンダリングのいずれかに強制されることはありません。両方のタイプを組み合わせることができ、Next.jsはparamsのようなものにアクセスするかどうかに基づいてページが何であるかを推測する必要がなくなります。すべてがデフォルトで動的であり、use cacheによって明示的にキャッシングにオプトインできます。use cacheは、コンポーザブルなキャッシングを可能にします。ページ、Reactコンポーネント、または関数をキャッシュ可能としてマークできます。これにより、リクエスト処理を必要とせず、動的なAPIを使用しない「注目の製品」コンポーネントを実際にキャッシュできるようになります。これらのキャッシュされたセグメントは、部分的な事前レンダリングにより、さらに事前レンダリングされ、静的シェルの一部として含めることができます。これは、「注目の製品」がページロード時に利用可能になり、ストリーム配信する必要がなくなることを意味します。## デモによる改善点の紹介この重要な背景知識を踏まえて、Next.jsアプリケーションでよく遭遇する一般的な問題に対するコードベースの改善デモを見てみましょう。これには、ディーププロップドリリング(機能の保守やリファクタリングを困難にする)、冗長なクライアントサイドJavaScript、複数の責任を持つ大規模なコンポーネント、そして静的レンダリングの不足による追加のサーバーコストとパフォーマンスの低下が含まれます。このデモでは、Eコマースプラットフォームにインスパイアされた非常にシンプルなアプリケーションを使用します。このアプリでは、注目の製品、注目のカテゴリ、さまざまな製品データを含むページ、すべての製品を閲覧できるページ、そして静的な「About」ページがあります。ユーザーとしてサインインすると、推奨製品やパーソナライズされた割引などのパーソナライズされたコンテンツがダッシュボードに表示されます。また、最も重要な製品ページもあり、製品情報を確認したり、ユーザーのために保存したりできます。このように、このアプリはユーザー依存の機能が多く、静的コンテンツと動的コンテンツがうまく混在しています。コードベースではApp Routerを使用しており、Next.js 16で「About」ページ、「All」ページ、製品ページなど、すべての異なるページが構成されています。appフォルダをクリーンに保つために、機能スライス(feature slicing)も使用しています。Prismaを使用してデータベースとやり取りするさまざまなコンポーネントやクエリがあります。デモを分かりやすくするために意図的に処理を遅くしているため、非常に長いローディング状態が見られます。このアプリケーションが抱える共通の問題点は、ディーププロップドリリング、過剰なクライアントサイドJavaScript、そして静的レンダリングの不足によるサーバーコストの増加とパフォーマンスの低下でした。このデモの目標は、コンポジション、キャッシング、およびアーキテクチャに関するいくつかのスマートなパターンを用いてこのアプリを改善し、これらの一般的な問題を修正して、より高速でスケーラブルかつ保守しやすいものにすることです。### 具体的な改善策1: プロップドリリングの解消とデータフェッチの最適化最初に取り組む問題は、プロップドリリングに関連しています。現在のコードでは、loggedIn変数がトップで定義され、いくつかのコンポーネントに渡されています。実際には、このloggedInは複数のレベルを経て「Personalized Banner」コンポーネントまで渡されています。この状態では、「Welcome Banner」が常にloggedInに依存するため、コンポーネントの再利用が困難になります。サーバーコンポーネントを使用する場合のベストプラクティスは、データをフェッチする処理を、そのデータを使用するコンポーネントにまで押し下げ、プロミスをツリーのより深い部分で解決することです。getIsAuthenticatedのような関数について言えば、これがfetchまたはreact-cacheのいずれかを使用している限り、複数の呼び出しを重複排除(deduplicate)できるため、コンポーネント内で好きな場所で再利用することが全く問題ありません。したがって、この認証ロジックをパーソナライズされたセクションに移動し、このプロップはもう必要とせず、直接そこに配置することができます。
まとめ
本記事では、Next.js App Routerにおけるスケーラビリティとパフォーマンスの向上に不可欠な、キャッシングとアーキテクチャのパターンについて解説しました。静的レンダリングと動的レンダリングの基本を理解し、旧来のNext.jsが抱えていたレンダリングの制約を把握することで、Next.js 16で導入されたuse cacheディレクティブの真価が明確になります。use cacheは、コンポーザブルなキャッシングを可能にし、ページが静的と動的の両方の性質を持つことを許容することで、不要なサーバー処理を削減し、部分的な事前レンダリングを通じてユーザー体験を向上させます。また、ディーププロップドリリングのような一般的な問題を、データフェッチを適切なコンポーネントに移動させることで解決し、コードの保守性と再利用性を高める方法についても触れました。これらの最適化パターンを適用することで、Next.jsアプリケーションはよりスケーラブルで高性能、そしてメンテナンスしやすいものになるでしょう。
参考動画
https://www.youtube.com/watch?v=iRGc8KQDyQ8