デプロイ

おめでとうございます、本番環境へのデプロイの時が来ました。

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つの異なる方法でセルフホスティングできます:

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静的アセットを提供できる任意のウェブサーバー(AWS S3、Nginx、Apacheなど)にデプロイしてホスティングできます。

静的エクスポートとして実行する場合、サーバーを必要とするNext.js機能はサポートされません。詳細を確認

知っておくと良い:

機能

画像最適化

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

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

知っておくと良い:

  • セルフホスティング時には、本番環境でより高性能な画像最適化のためにsharpをインストールすることを検討してください。プロジェクトディレクトリでnpm install sharpを実行します。Linuxプラットフォームでは、sharp追加の設定を必要とする場合があります。
  • 最適化された画像のキャッシュ動作とTTLの設定方法について詳しく学びます。
  • 好みに応じて、画像最適化を無効にし、next/imageの他の利点を保持することもできます。例えば、画像を自分で別途最適化している場合などです。

ミドルウェア

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

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

すべてのNode.js APIを必要とするロジック(または外部パッケージ)を追加したい場合は、このロジックをレイアウトサーバーコンポーネントとして移動できる可能性があります。例えば、ヘッダーをチェックしたり、リダイレクトしたりします。また、next.config.jsヘッダー、クッキー、またはクエリパラメータを使用してリダイレクトまたはリライトすることもできます。それがうまくいかない場合は、カスタムサーバーを使用することもできます。

環境変数

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

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

実行時環境変数を読み取るには、getServerSidePropsを使用するか、App Routerを段階的に採用することをお勧めします。App Routerを使用すると、動的レンダリング中にサーバー上で安全に環境変数を読み取ることができます。これにより、異なる値を持つ複数の環境を通過できる単一のDockerイメージを使用できます。

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

export default function Component() {
  noStore();
  // cookies(), headers()などの動的関数も
  // 動的レンダリングを選択し、
  // この環境変数は実行時に評価されます
  const value = process.env.MY_VALUE
  ...
}

知っておくと良い:

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

キャッシュとISR

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

ページのキャッシュと再検証(Incremental Static Regeneration (ISR)またはApp Routerの新しい関数を使用)には、同じ共有キャッシュが使用されます。デフォルトでは、このキャッシュは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: <getStaticPropsでの再検証時間>, stale-while-revalidateCache-Controlヘッダーを設定します。この再検証時間はgetStaticProps関数で秒単位で定義されます。revalidate: falseを設定すると、デフォルトで1年間のキャッシュ期間が設定されます。
  • 動的にレンダリングされたページは、ユーザー固有のデータがキャッシュされないようにprivate, no-cache, no-store, max-age=0, must-revalidateCache-Controlヘッダーを設定します。これはApp RouterとPages Routerの両方に適用されます。これにはドラフトモードも含まれます。

静的アセット

静的アセットを別のドメインまたは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(tag) {
    // キャッシュ内のすべてのエントリを反復処理
    for (let [key, value] of cache) {
      // 値のタグに指定されたタグが含まれている場合、このエントリを削除
      if (value.tags.includes(tag)) {
        cache.delete(key)
      }
    }
  }
}

カスタムキャッシュハンドラーを使用すると、Next.jsアプリケーションをホスティングするすべてのポッド間で一貫性を確保できます。例えば、キャッシュされた値をRedisやAWS S3など、どこにでも保存できます。

知っておくと良い:

  • 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',
          },
        ],
      },
    ]
  },
}