メインコンテンツまでスキップ

デプロイ

おめでとうございます。いよいよ本番環境に出荷する時が来ました。

管理されたNext.jsをVercelでデプロイするか、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機能がサポートされています。

Next.js on Vercelについて詳しく学ぶか、無料でテンプレートをデプロイして試してみてください。

セルフホスティング

Next.jsをセルフホスティングする方法は3つあります:

🎥 視聴: Next.jsのセルフホスティングについて詳しく学ぶ → YouTube (45分)

以下のプロバイダーでコミュニティが維持するデプロイ例があります:

Node.jsサーバー

Next.jsは、Node.jsをサポートする任意のホスティングプロバイダーにデプロイできます。package.json"build""start"スクリプトがあることを確認してください:

package.json
{
"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などのコンテナオーケストレーターにデプロイする場合や、任意のクラウドプロバイダー内でコンテナ内で実行する場合に使用できます。

  1. マシンにDockerをインストールします
  2. 例をクローン(またはマルチ環境の例)します
  3. コンテナをビルドします:docker build -t nextjs-docker .
  4. コンテナを実行します: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機能はサポートされません。詳細はこちら

Good to know:

機能

画像最適化

next/imageを通じた画像最適化は、next startを使用してデプロイする際にゼロコンフィグレーションでセルフホスティングで動作します。画像を最適化するための別のサービスを使用したい場合は、画像ローダーを設定できます。

画像最適化は、next.config.jsでカスタム画像ローダーを定義することで静的エクスポートと一緒に使用できます。画像はビルド時ではなく、実行時に最適化されることに注意してください。

Good to know:

  • glibcベースのLinuxシステムでは、画像最適化に過剰なメモリ使用を防ぐために追加の設定が必要な場合があります。
  • 最適化された画像のキャッシュ動作とTTLの設定方法について詳しく学びましょう。
  • 画像を別途最適化している場合でも、next/imageを使用する他の利点を保持しながら画像最適化を無効化することもできます。

ミドルウェア

ミドルウェアは、next startを使用してデプロイする際にゼロコンフィグレーションでセルフホスティングで動作します。受信リクエストへのアクセスが必要なため、静的エクスポートを使用する場合はサポートされていません。

ミドルウェアは、アプリケーション内のすべてのルートまたはアセットの前で実行される可能性があるため、低遅延を確保するために利用可能なすべてのNode.js APIのサブセットであるruntimeを使用します。このランタイムは「エッジで」実行する必要はなく、単一のリージョンサーバーで動作します。ミドルウェアを複数のリージョンで実行するには、追加の設定とインフラストラクチャが必要です。

すべてのNode.js APIを必要とするロジックを追加したい場合(または外部パッケージを使用したい場合)、このロジックをServer Componentとしてlayoutに移動することができます。たとえば、headersをチェックしてリダイレクトする場合です。また、next.config.jsを通じて、headers、cookies、またはクエリパラメータを使用してリダイレクトまたは書き換えすることもできます。それがうまくいかない場合は、カスタムサーバーを使用することもできます。

環境変数

Next.jsは、ビルド時と実行時の両方の環境変数をサポートできます。

デフォルトでは、環境変数はサーバーでのみ利用可能です。環境変数をブラウザに公開するには、NEXT_PUBLIC_でプレフィックスを付ける必要があります。ただし、これらの公開された環境変数は、next build中にJavaScriptバンドルにインライン化されます。

動的レンダリング中にサーバーで環境変数を安全に読み取ることができます。

app/page.ts
import { connection } from 'next/server'

export default async function Component() {
await connection()
// cookies, headers, and other Dynamic APIs
// will also opt into dynamic rendering, meaning
// this env variable is evaluated at runtime
const value = process.env.MY_VALUE
// ...
}

これにより、異なる値を持つ複数の環境を通じて昇格できる単一のDockerイメージを使用することができます。

Good to know:

  • register関数を使用してサーバーの起動時にコードを実行できます。
  • runtimeConfigオプションの使用は推奨されません。これはスタンドアロン出力モードでは機能しないためです。代わりに、App Routerを段階的に採用することをお勧めします。

キャッシングとISR

Next.jsは、レスポンス、生成された静的ページ、ビルド出力、画像、フォント、スクリプトなどの他の静的アセットをキャッシュできます。

ページのキャッシングと再検証(Incremental Static Regenerationを使用)は、同じ共有キャッシュを使用します。デフォルトでは、このキャッシュはNext.jsサーバー上のファイルシステム(ディスク上)に保存されます。セルフホスティング時には自動的に動作します。Pages RouterとApp Routerの両方で使用されます。

キャッシュされたページとデータを永続的なストレージに保存したり、Next.jsアプリケーションの複数のコンテナやインスタンス間でキャッシュを共有したい場合は、Next.jsキャッシュの場所を設定できます。

自動キャッシング

  • Next.jsは、public, max-age=31536000, immutableCache-Controlヘッダーを真に不変のアセットに設定します。これは上書きできません。これらの不変ファイルにはファイル名にSHAハッシュが含まれているため、安全に無期限にキャッシュできます。たとえば、静的画像インポートです。画像のTTLを設定できます。
  • Incremental Static Regeneration(ISR)は、s-maxage: <revalidate in getStaticProps>, stale-while-revalidateCache-Controlヘッダーを設定します。この再検証時間は、getStaticProps関数で秒単位で定義されます。revalidate: falseを設定すると、デフォルトで1年間のキャッシュ期間になります。
  • 動的にレンダリングされたページは、ユーザー固有のデータがキャッシュされないようにするために、private, no-cache, no-store, max-age=0, must-revalidateCache-Controlヘッダーを設定します。これは、App RouterとPages Routerの両方に適用されます。これにはDraft Modeも含まれます。

静的アセット

静的アセットを別のドメインまたはCDNにホストしたい場合は、next.config.jsassetPrefix設定を使用できます。Next.jsは、JavaScriptまたはCSSファイルを取得する際にこのアセットプレフィックスを使用します。アセットを別のドメインに分離することには、DNSおよびTLS解決に余分な時間がかかるというデメリットがあります。

assetPrefixについて詳しく学ぶ

キャッシングの設定

デフォルトでは、生成されたキャッシュアセットはメモリ(デフォルトで50mb)とディスクに保存されます。Kubernetesのようなコンテナオーケストレーションプラットフォームを使用してNext.jsをホスティングしている場合、各ポッドにはキャッシュのコピーがあります。デフォルトでポッド間でキャッシュが共有されないため、古いデータが表示されるのを防ぐために、Next.jsキャッシュを設定してキャッシュハンドラーを提供し、メモリ内キャッシュを無効にすることができます。

セルフホスティング時にISR/データキャッシュの場所を設定するには、next.config.jsファイルでカスタムハンドラーを設定できます:

next.config.js
module.exports = {
cacheHandler: require.resolve('./cache-handler.js'),
cacheMaxMemorySize: 0, // デフォルトのメモリ内キャッシュを無効にする
}

次に、プロジェクトのルートにcache-handler.jsを作成します。たとえば:

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など、どこにでも保存できます。

Good to know:

  • revalidatePathはキャッシュタグの上にある便利なレイヤーです。revalidatePathを呼び出すと、指定されたページの特別なデフォルトタグでrevalidateTag関数が呼び出されます。

ビルドキャッシュ

Next.jsは、next build中にアプリケーションのどのバージョンが提供されているかを識別するためのIDを生成します。同じビルドを使用して複数のコンテナを起動する必要があります。

環境の各ステージごとに再ビルドしている場合は、コンテナ間で使用する一貫したビルドIDを生成する必要があります。next.config.jsgenerateBuildIdコマンドを使用します:

next.config.js
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プロパティを手動で設定できます。

ストリーミングとサスペンス

Next.js App Routerは、セルフホスティング時にストリーミングレスポンスをサポートします。Nginxや同様のプロキシを使用している場合は、ストリーミングを有効にするためにバッファリングを無効にするように設定する必要があります。

たとえば、NginxでX-Accel-Bufferingnoに設定してバッファリングを無効にできます:

next.config.js
module.exports = {
async headers() {
return [
{
source: '/:path*{/}?',
headers: [
{
key: 'X-Accel-Buffering',
value: 'no',
},
],
},
]
},
}

部分的なプリレンダリング

部分的なプリレンダリング(実験的)は、Next.jsでデフォルトで動作し、CDN機能ではありません。これには、Node.jsサーバー(next startを通じて)としてのデプロイや、Dockerコンテナと一緒に使用する場合が含まれます。

CDNとの使用

Next.jsアプリケーションの前にCDNを使用する場合、動的APIがアクセスされると、ページにはCache-Control: privateレスポンスヘッダーが含まれます。これにより、生成されたHTMLページがキャッシュ不可としてマークされます。ページが完全に静的にプリレンダリングされると、Cache-Control: publicが含まれ、ページがCDNでキャッシュされることが許可されます。

静的および動的コンポーネントの両方を必要としない場合は、ルート全体を静的にして、出力HTMLをCDNでキャッシュできます。この自動静的最適化は、動的APIが使用されていない場合にnext buildを実行するときのデフォルトの動作です。

after

afterは、next startでセルフホスティングする際に完全にサポートされています。

サーバーを停止する際には、SIGINTまたはSIGTERMシグナルを送信して待機することで、優雅なシャットダウンを確保してください。これにより、Next.jsサーバーはafter内で使用される保留中のコールバック関数またはプロミスが終了するまで待機できます。

カスタムインフラストラクチャでafterを使用したい場合は、プロバイダのドキュメントを確認してafterのサポートを確認してください。

Details

サーバーレスプラットフォームでのafterのサポートに関するリファレンス サーバーレスコンテキストでafterを使用するには、レスポンスが送信された後に非同期タスクが終了するのを待つ必要があります。Next.jsとVercelでは、waitUntil(promise)というプリミティブを使用して、すべてのプロミスが解決されるまでサーバーレス呼び出しの寿命を延ばします。

ユーザーが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))
}