Next.jsの環境変数はレンダリング手法によるよというお話
はじめに
Next.js で環境変数を設定したにも関わらず反映されないという問題は、よく起きがちな問題だと思います。
自分も例にもれずだいぶハマってしまいました。
今回は、Next.js の環境変数がどのようにアプリケーションに反映されるのかがレンダリング手法によって異なる、ということがわかったので共有したいと思います。
Next.js のレンダリング手法
Next.js では、SSG(静的サイト生成)、SSR(サーバーサイドレンダリング)、CSR(クライアントサイドレンダリング)の 3 つのレンダリング手法を使い分けることができます。
厳密には ISR(Incremental Static Regeneration)もありますが、今回は本筋ではないため省略します。
それぞれのレンダリング手法について簡単にまとめると、以下の表のようになります。
レンダリング場所 | レンダリングタイミング | |
---|---|---|
SSG | サーバー | ビルド時 |
SSR | サーバー | サーバーへのリクエスト時 |
CSR | クライアント (ブラウザなど) |
ハイドレーション時 |
レンダリングの場所とタイミングが異なることがわかります。
このため、環境変数についてもレンダリング手法によって取得タイミングが異なってきます。
レンダリング手法と環境変数
SSG(静的サイト生成)
SSG は、ユーザーからのリクエスト時にあらかじめビルドしておいた資源を返すレンダリング手法です。
この場合、環境変数はビルド時に含める必要があるため、Next.js では next build
を実行する際に環境変数を設定しておく必要があります。
また、Next.js ではデフォルトで SSG が使用されるため、サーバーの環境変数を動的に変更させたい場合は、Page Router では getServerSideProps
を、App Router では export const dynamic = "force-dynamic"
をページコンポーネント内に書くなど、明示的に SSR に切り替える必要があります。
SSR(サーバーサイドレンダリング)
SSR は、ユーザーからのリクエスト時に毎回レンダリングを行う手法です。
SSG とは異なり、毎回環境変数を取得するため、サーバーの環境変数によって制御できます。
Next.js 13 以降の App Router では、RSC(React Server Components)を使用できるため、サーバーコンポーネント内で直接 process.env
を使用して環境変数にアクセスすることができます。
この場合、前述の通り Next.js では明示的に SSR を指定しないと、サーバーマシンに設定した環境変数を取得できないので注意が必要です。
ちなみに、Route Handlers(API Routes)では、リクエストごとに実行されるため、サーバーマシンから動的に環境変数を取得できます。
CSR(クライアントサイドレンダリング)
CSR は、サーバーから送られてきた JavaScript を使用して、ブラウザなどのクライアントサイドで動的に HTML を組み立てるハイドレーションによってレンダリングが行われる手法です。
App Router では、クライアントコンポーネントがこれに該当します。
環境変数はサーバーから送られる JavaScript に含める必要があるため、SSG と同様に next build
時に設定しておく必要があります。
また、単に設定するだけでは JavaScript に含まれないため、各環境変数には NEXT_PUBLIC_
を付与することで、Next.js がビルド時に JavaScript に環境変数をバンドルしてくれます。
まとめ
上記の内容を表にまとめると、以下のようになります。
設定場所 | 設定タイミング | Next.js の概念 | |
---|---|---|---|
SSG | ビルドするマシン (GitHub Actions など) |
ビルド時 (ビルド後は不変) |
・getStaticProps ・force-static |
SSR | サーバーとしてホストするマシン (Cloud Run、AppService など) |
実行時 (書き換えによって可変) |
・Route Handlers ・force-dynamic ・getServerSideProps |
CSR | ビルドするマシン (GitHub Actions など) |
ビルド時 (ビルド後は不変) |
・NEXT_PUBLIC |
Next.js はフレームワークが自動的にビルドしてくれるため、ローカルでは動作するけれど本番環境にデプロイしたら動かない、というような環境差分が顕著に現れることがあるかと思います。
そんなときにフレームワークがどんな動きをしているか、今回で言うと実装しているコンポーネントがどこでレンダリングされているかを常に意識しておかないと細かい部分でハマってしまうことがあるので、注意が必要だなと感じました。
Discussion