デプロイ
おめでとうございます。本番環境へのデプロイの時が来ました。
VercelでのマネージドNext.jsとしてデプロイするか、Node.jsサーバー、Dockerイメージ、あるいは静的HTMLファイルとしてセルフホスティングすることができます。next start
を使用してデプロイする場合、すべてのNext.js機能がサポートされます。
本番ビルド
next build
を実行すると、本番用に最適化されたアプリケーションのバージョンが生成されます。ページに基づいてHTML、CSS、JavaScriptファイルが作成されます。JavaScriptはNext.jsコンパイラを使用してコンパイルされ、ブラウザバンドルは縮小化されて、最高のパフォーマンスを実現し、すべてのモダンブラウザをサポートします。
Next.jsはマネージドおよびセルフホスティングのNext.jsで使用される標準的なデプロイ出力を生成します。これにより、両方のデプロイ方法ですべての機能がサポートされます。次のメジャーバージョンでは、この出力をBuild Output API仕様に変換する予定です。
VercelでのマネージドNext.js
Next.jsの作成者および保守者であるVercelは、Next.jsアプリケーション向けのマネージドインフラストラクチャと開発者エクスペリエンスプラットフォームを提供しています。
Vercelへのデプロイは設定不要で、グローバルなスケーラビリティ、可用性、パフォーマンスを向上させる追加機能を提供します。ただし、セルフホスティングの場合でもすべてのNext.js機能はサポートされています。
VercelのNext.jsについて詳しく学ぶか、テンプレートを無料でデプロイして試してみてください。
セルフホスティング
Next.jsは3つの異なる方法でセルフホスティングできます:
🎥 視聴: Next.jsのセルフホスティングについて詳しく学ぶ → YouTube(45分)
以下のプロバイダーについては、コミュニティがメンテナンスしているデプロイ例があります:
Node.jsサーバー
Next.jsはNode.jsをサポートする任意のホスティングプロバイダーにデプロイできます。package.json
に"build"
と"start"
スクリプトがあることを確認してください:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}
次に、npm run build
を実行してアプリケーションをビルドします。最後に、npm run start
を実行してNode.jsサーバーを起動します。このサーバーはすべてのNext.js機能をサポートしています。
Dockerイメージ
Next.jsはDockerコンテナをサポートする任意のホスティングプロバイダーにデプロイできます。このアプローチは、Kubernetesなどのコンテナオーケストレーターにデプロイする場合や、クラウドプロバイダーでコンテナ内で実行する場合に使用できます。
- お使いのマシンにDockerをインストール
- サンプルをクローン(またはマルチ環境サンプル)
- コンテナをビルド:
docker build -t nextjs-docker .
- コンテナを実行:
docker run -p 3000:3000 nextjs-docker
Docker経由のNext.jsはすべてのNext.js機能をサポートしています。
静的HTMLエクスポート
Next.jsでは、静的サイトまたはシングルページアプリケーション(SPA)として始め、後でオプションでサーバーが必要な機能を使用するアップグレードが可能です。
Next.jsはこの静的エクスポートをサポートしているため、HTML/CSS/JS静的アセットを提供できるWebサーバーにデプロイしてホスティングできます。これにはAWS S3、Nginx、Apacheなどのツールが含まれます。
静的エクスポートとして実行する場合、サーバーが必要なNext.js機能はサポートされません。詳細はこちら。
補足:
- サーバーコンポーネントは静的エクスポートでサポートされています。
機能
画像最適化
next/image
を通じての画像最適化は、next start
を使用してデプロイする場合、設定なしでセルフホスティングで動作します。画像を最適化するための別のサービスを使用したい場合は、画像ローダーを設定できます。
画像最適化はnext.config.js
でカスタム画像ローダーを定義することで、静的エクスポートで使用できます。画像はビルド時ではなく実行時に最適化されることに注意してください。
補足:
- glibcベースのLinuxシステムでは、過剰なメモリ使用を防ぐために画像最適化に追加の設定が必要な場合があります。
- 最適化された画像のキャッシュ動作とTTLの設定方法について詳しく学ぶことができます。
- 希望する場合は、画像最適化を無効化しても、
next/image
の使用による他の利点は維持できます。例えば、画像を別途最適化している場合などです。
ミドルウェア
ミドルウェアは、next start
を使用してデプロイする場合、設定なしでセルフホスティングで動作します。着信リクエストへのアクセスが必要なため、静的エクスポートを使用する場合はサポートされていません。
ミドルウェアは、アプリケーションのすべてのルートやアセットの前で実行される可能性があるため、低レイテンシーを確保するために使用可能なNode.js APIのサブセットであるランタイムを使用します。このランタイムは「エッジ」で実行する必要はなく、単一リージョンのサーバーでも動作します。ミドルウェアを複数のリージョンで実行するには、追加の設定とインフラストラクチャが必要です。
すべてのNode.js APIを必要とするロジックを追加したり(または外部パッケージを使用したり)する場合は、このロジックをサーバーコンポーネントとしてレイアウトに移動できるかもしれません。例えば、ヘッダーのチェックやリダイレクトなどです。また、ヘッダー、クッキー、クエリパラメータを使用して、next.config.js
を通じてリダイレクトやリライトを行うこともできます。それが機能しない場合は、カスタムサーバーを使用することもできます。
環境変数
Next.jsはビルド時とランタイムの両方の環境変数をサポートできます。
デフォルトでは、環境変数はサーバーでのみ利用可能です。環境変数をブラウザに公開するには、NEXT_PUBLIC_
をプレフィックスとして付ける必要があります。ただし、これらの公開環境変数はnext build
中にJavaScriptバンドルにインライン化されます。
動的レンダリング中にサーバー上で安全に環境変数を読み取ることができます。
import { connection } from 'next/server'
export default async function Component() {
await connection()
// cookies、headers、その他の動的APIも
// 動的レンダリングを選択することになり、
// この環境変数はランタイムで評価されます
const value = process.env.MY_VALUE
// ...
}
これにより、複数の環境で異なる値を持つ単一のDockerイメージを使用できます。
補足:
register
関数を使用してサーバーの起動時にコードを実行できます。- runtimeConfigオプションの使用はお勧めしません。これはスタンドアロン出力モードでは動作しません。代わりに、段階的に採用するAppルーターをお勧めします。
キャッシュとISR
Next.jsはレスポンス、生成された静的ページ、ビルド出力、および画像、フォント、スクリプトなどの静的アセットをキャッシュできます。
ページのキャッシュと再検証(Incremental Static Regenerationを使用)は同じ共有キャッシュを使用します。デフォルトでは、このキャッシュはNext.jsサーバーのファイルシステム(ディスク)に保存されます。これはPagesルーターとAppルーターの両方でセルフホスティングする場合に自動的に機能します。
キャッシュされたページとデータを永続的なストレージに保存したり、Next.jsアプリケーションの複数のコンテナやインスタンス間でキャッシュを共有したりする場合は、Next.jsキャッシュの場所を設定できます。
自動キャッシュ
- Next.jsは本当に不変のアセットに
Cache-Control
ヘッダーをpublic, max-age=31536000, immutable
に設定します。これはオーバーライドできません。これらの不変ファイルには、ファイル名にSHAハッシュが含まれているため、無期限にキャッシュしても安全です。例えば、静的画像インポートなどです。画像のTTLを設定できます。 - Incremental Static Regeneration(ISR)は
Cache-Control
ヘッダーをs-maxage: <revalidate in getStaticProps>, stale-while-revalidate
に設定します。この再検証時間はgetStaticProps
関数で秒単位で定義されています。revalidate: false
を設定すると、デフォルトで1年間のキャッシュ期間になります。 - 動的にレンダリングされたページは、ユーザー固有のデータがキャッシュされないように、
Cache-Control
ヘッダーをprivate, no-cache, no-store, max-age=0, must-revalidate
に設定します。これはAppルーターとPagesルーターの両方に適用されます。これにはドラフトモードも含まれます。
静的アセット
静的アセットを別のドメインやCDNでホストしたい場合は、next.config.js
のassetPrefix
設定を使用できます。Next.jsはJavaScriptやCSSファイルを取得するときにこのアセットプレフィックスを使用します。アセットを別のドメインに分離すると、DNSとTLS解決に余分な時間がかかるというデメリットがあります。
キャッシュの設定
デフォルトでは、生成されたキャッシュアセットはメモリ(デフォルトで50MB)とディスクに保存されます。KubernetesなどのコンテナオーケストレーションプラットフォームでNext.jsをホスティングしている場合、各ポッドにキャッシュのコピーが作成されます。デフォルトではポッド間でキャッシュが共有されないため、古いデータが表示されるのを防ぐために、キャッシュハンドラーを提供しメモリ内キャッシュを無効にするようにNext.jsキャッシュを設定できます。
セルフホスティング時にISR/データキャッシュの場所を設定するには、next.config.js
ファイルでカスタムハンドラーを設定できます:
module.exports = {
cacheHandler: require.resolve('./cache-handler.js'),
cacheMaxMemorySize: 0, // デフォルトのメモリ内キャッシュを無効化
}
次に、プロジェクトのルートにcache-handler.js
を作成します。例えば:
const cache = new Map()
module.exports = class CacheHandler {
constructor(options) {
this.options = options
}
async get(key) {
// これは永続的なストレージなど、どこにでも保存できます
return cache.get(key)
}
async set(key, data, ctx) {
// これは永続的なストレージなど、どこにでも保存できます
cache.set(key, {
value: data,
lastModified: Date.now(),
tags: ctx.tags,
})
}
async revalidateTag(tags) {
// tagsは文字列または文字列の配列です
tags = [tags].flat()
// キャッシュ内のすべてのエントリを反復処理
for (let [key, value] of cache) {
// 値のタグに指定されたタグが含まれている場合、このエントリを削除
if (value.tags.some((tag) => tags.includes(tag))) {
cache.delete(key)
}
}
}
// 次のリクエスト前にリセットされる単一のリクエスト用の一時的なメモリ内キャッシュが
// 必要な場合は、このメソッドを活用できます
resetRequestCache() {}
}
カスタムキャッシュハンドラーを使用することで、Next.jsアプリケーションをホストするすべてのポッド間での一貫性を確保できます。例えば、キャッシュされた値をRedisやAWS S3などどこにでも保存できます。
補足:
revalidatePath
はキャッシュタグの上に構築された便利なレイヤーです。revalidatePath
を呼び出すと、指定されたページの特別なデフォルトタグでrevalidateTag
関数が呼び出されます。
ビルドキャッシュ
Next.jsはnext build
中にIDを生成して、提供されているアプリケーションのバージョンを識別します。同じビルドを使用して複数のコンテナを起動する必要があります。
環境の各ステージでリビルドしている場合は、コンテナ間で使用する一貫したビルドIDを生成する必要があります。next.config.js
でgenerateBuildId
コマンドを使用してください:
module.exports = {
generateBuildId: async () => {
// これは何でもよく、最新のgitハッシュを使用するなど
return process.env.GIT_HASH
},
}
バージョンスキュー
Next.jsはバージョンスキューのほとんどのインスタンスを自動的に軽減し、検出されたときに新しいアセットを取得するためにアプリケーションを自動的に再読み込みします。例えば、deploymentId
に不一致がある場合、ページ間の遷移はプリフェッチされた値を使用するのではなく、ハードナビゲーションを実行します。
アプリケーションが再読み込みされると、ページナビゲーション間で持続するように設計されていない場合、アプリケーションの状態が失われる可能性があります。例えば、URLの状態やローカルストレージを使用すると、ページの更新後も状態は保持されます。ただし、useState
などのコンポーネント状態は、そのようなナビゲーションで失われます。
Vercelは、新しいバージョンがデプロイされた後でも、以前のバージョンのアセットと関数が古いクライアントでも利用可能であることを確認するために、Next.jsアプリケーション用の追加のスキュー保護を提供しています。
各リクエストが?dpl
クエリ文字列またはx-deployment-id
ヘッダーを使用するようにするために、next.config.js
ファイルでdeploymentId
プロパティを手動で設定できます。
ストリーミングとSuspense
Next.js Appルーターはセルフホスティング時にストリーミングレスポンスをサポートしています。Nginxまたはそれに類似したプロキシを使用している場合は、ストリーミングを有効にするためにバッファリングを無効にするように設定する必要があります。
例えば、NginxではX-Accel-Buffering
をno
に設定することでバッファリングを無効にできます:
module.exports = {
async headers() {
return [
{
source: '/:path*{/}?',
headers: [
{
key: 'X-Accel-Buffering',
value: 'no',
},
],
},
]
},
}
部分的プリレンダリング
部分的プリレンダリング(実験的)はNext.jsではデフォルトで動作し、CDN機能ではありません。これには(next start
を通じての)Node.jsサーバーとしてのデプロイとDockerコンテナで使用する場合が含まれます。
CDNとの使用
Next.jsアプリケーションの前にCDNを使用する場合、動的APIにアクセスするとページにはCache-Control: private
レスポンスヘッダーが含まれます。これにより、結果のHTMLページがキャッシュ不可としてマークされます。ページが完全に静的にプリレンダリングされている場合は、Cache-Control: public
が含まれ、ページがCDNにキャッシュされることが許可されます。
静的コンポーネントと動的コンポーネントの両方を混合する必要がない場合は、ルート全体を静的にしてCDNに出力HTMLをキャッシュできます。この自動静的最適化は、動的APIが使用されていない場合にnext build
を実行するときのデフォルトの動作です。
after
after
はnext start
でセルフホスティングする場合、完全にサポートされています。
サーバーを停止する場合は、SIGINT
またはSIGTERM
シグナルを送信して待機することで、グレースフルシャットダウンを確保してください。これにより、Next.jsサーバーはafter
の中で使用されている保留中のコールバック関数やプロミスが完了するまで待機できます。
カスタムインフラストラクチャでafter
を使用したい場合は、プロバイダーのドキュメントを確認してafter
のサポートを確認してください。
参考:サーバーレスプラットフォームで
サーバーレスコンテキストでafter
をサポートするafter
を使用するには、レスポンスが送信された後に非同期タスクが完了するのを待つ必要があります。Next.jsとVercelでは、これはwaitUntil(promise)
と呼ばれるプリミティブを使用して実現されており、waitUntil
に渡されたすべてのプロミスが解決されるまでサーバーレス呼び出しの寿命を延長します。
ユーザーがafter
を実行できるようにするには、同様の方法で動作するwaitUntil
の独自の実装を提供する必要があります。
after
が呼び出されると、Next.jsは次のようにwaitUntil
にアクセスします:
const RequestContext = globalThis[Symbol.for('@next/request-context')]
const contextValue = RequestContext?.get()
const waitUntil = contextValue?.waitUntil
つまり、globalThis[Symbol.for('@next/request-context')]
には次のようなオブジェクトが含まれていることが期待されます:
type NextRequestContext = {
get(): NextRequestContextValue | undefined
}
type NextRequestContextValue = {
waitUntil?: (promise: Promise<any>) => void
}
実装例を以下に示します。
import { AsyncLocalStorage } from 'node:async_hooks'
const RequestContextStorage = new AsyncLocalStorage<NextRequestContextValue>()
// next.jsが使用するアクセサーを定義して注入
const RequestContext: NextRequestContext = {
get() {
return RequestContextStorage.getStore()
},
}
globalThis[Symbol.for('@next/request-context')] = RequestContext
const handler = (req, res) => {
const contextValue = { waitUntil: YOUR_WAITUNTIL }
// 値を提供
return RequestContextStorage.run(contextValue, () => nextJsHandler(req, res))
}