🙆‍♀️

[Next.js] クライアント用環境変数は next build 時のハードコード必須

2023/09/26に公開

自分が知らなかったので備忘録として。
認識違いとか公式docに書いているとかあればコメントくださるとすごく幸せです🙇‍♂️

背景

クライアント(ブラウザ)側で、Firebase SDK + Firebase Authentication による認証をさせようとしたときに発生。
そこから調査した内容で書くのでFIREBASEという単語が出回ります。

分かったこと

タイトルの通り。

Next.js では、 NEXT_PUBLIC_* という環境変数を渡すことにより、クライアント(ブラウザ)で環境変数が使える。
next build をすると JavaScript がコンパイルされるが、このときクライアント環境変数をハードコードして内包するみたい。

起きうる問題

これはつまり「Node起動時に環境変数を渡して~」が出来ない、ということ。
特にコンテナイメージを作成するときに障壁になりえる。

通常、docker build のプロセスの中で next build をすると思うので、このときにクライアント環境変数も何らかの方法で渡しておく必要がある。

コンテナBuild時の対処

Dockerfileによるコンテナビルドを前提とすると、

  1. .env.production をコンテナイメージに乗せる。(そのあと削除)
  2. 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