[Next.js] クライアント用環境変数は next build 時のハードコード必須
自分が知らなかったので備忘録として。
認識違いとか公式docに書いているとかあればコメントくださるとすごく幸せです🙇♂️
背景
クライアント(ブラウザ)側で、Firebase SDK + Firebase Authentication による認証をさせようとしたときに発生。
そこから調査した内容で書くのでFIREBASEという単語が出回ります。
分かったこと
タイトルの通り。
Next.js では、 NEXT_PUBLIC_* という環境変数を渡すことにより、クライアント(ブラウザ)で環境変数が使える。
next build
をすると JavaScript がコンパイルされるが、このときクライアント環境変数をハードコードして内包するみたい。
起きうる問題
これはつまり「Node起動時に環境変数を渡して~」が出来ない、ということ。
特にコンテナイメージを作成するときに障壁になりえる。
通常、docker build
のプロセスの中で next build
をすると思うので、このときにクライアント環境変数も何らかの方法で渡しておく必要がある。
コンテナBuild時の対処
Dockerfileによるコンテナビルドを前提とすると、
- .env.production をコンテナイメージに乗せる。(そのあと削除)
- ENV 命令に何らかの形で環境変数を渡す
の2つに選択肢が絞られる。
1のステップが最も楽ではあるが、Github Actions などで CD する場合に使えないビルド構成になる。
わざわざ Github Secrets から .env.production を作るというのも考えられるが、ちょっと美しくない。
では、2を選択するとして、ここからさらに ENV命令への環境変数の渡し方は2通りある。
1. Dockerfileの ENV にハードコードする
下記のイメージ。
FROM node:18-slim AS builder
ENV NEXT_PUBLIC_FIREBASE_WEB_API_KEY=foobar
RUN pnpm i --frozen-lockfile && pnpm build
分かりやすいやり方。だが、
- Dockerfile.development
- Dockerfile.production
みたいにファイルを分ける必要が出てくる。
これは、各環境でビルドプロセスが同じような内容だと、重複ステップが出てきて美しくない。
(マルチステージを使えば何とかできそうだが複雑になる)
また、git管理に環境変数を乗せる必要が出てくる。
もちろんクライアントに渡されるものなのであんまりセキュアな情報ではないはずだが。
2. Dockerfileの ARG を経由して ENV に渡す
基本はこのやり方が理想に近いと思っている。
下記のイメージ。
FROM node:18-slim AS builder
ARG NEXT_PUBLIC_FIREBASE_WEB_API_KEY
ENV NEXT_PUBLIC_FIREBASE_WEB_API_KEY=${NEXT_PUBLIC_FIREBASE_WEB_API_KEY}
RUN pnpm i --frozen-lockfile && pnpm build
この場合、Dockerfileを環境ごとに用意する必要がなくなる。
環境変数のハードコードもしなくてよくなる。
ARG/ENV のあたりが冗長で汚くみえてしまうのが難点。
ここで、さらにさらに ARG の渡し方は 2通りある。
ARG の渡し方
1. docker build の build-args オプション
下記のイメージ。
docker build --build-arg FIREBASE_WEB_API_KEY=*** --build-arg FIREBASE_AUTH_DOMAIN=*** .
直感的。
が、ARGが増えれば増えるほどコマンドが長く可読性が下がる。
また、例えばGoogle Artifact RegistryにPushしたい場合、イメージタグが相当長くなるので可読性はさらに下がる。
2. docker-compose.yaml で定義
基本はこちらが理想に近い。
version: '3'
services:
web:
build:
context: .
dockerfile: Dockerfile
args:
- NEXT_PUBLIC_FIREBASE_WEB_API_KEY=${NEXT_PUBLIC_FIREBASE_WEB_API_KEY}
- NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=${NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN}
image: asia-northeast1-docker.pkg.dev/${GCP_PROJECT}/${GAR_REPOSITORY}/project
argsが増えれば、Dockerfile/docker-compose.yaml の両方で増やせばいいだけ。
imageの部分は色々やり方がありそう。
このように定義しておけば最も短くしたもので
docker compose --env-file .env.production build
これだけのコマンドでビルドを通せる。
docker compose --env-file .env.production up --build
こうすれば、そのまま動作確認まで出来る。
Discussion