Reactでちょっとだけ環境変数を使いたい
開発環境と本番環境で使い分けるなら NODE_ENV
だけでいいが、ローカルで開発、検証サーバ、本番環境と環境が増えていくと足りないので自前で環境変数を設定したいことがある。というか個人的にデバッグやログレベルの切り替えなどしようとすると少なくとも開発or本番というだけではどう考えても足りないと思う。
環境変数をたくさん(個人的な直感では3つ以上)使うなら素直にdotenvを使った方が良さそうという感想だが、例えば MODE と DEBUG の2つだけなら(それこそコマンドで打ち込むぐらいで済む範囲なら)逆に重いよな……という感想を持った次第、じゃあどうするか考えてみた。
React上でコマンドから環境変数を渡す
まず大前提だがReactでは環境変数は REACT_APP_
のprefixが必要である。
使うときも必要なのでこんな感じになる
$ HOGE=piyo REACT_APP_HOGE=poyo yarn start
このコマンドで以下のコードを読ませても
const reacthoge = process.env.REACT_APP_HOGE // poyo
const hoge = process.env.HOGE // undefined
となる。
いちいち REACT_APP_
のprefixがめんどい場合は
"scripts":{
"start": "REACT_APP_HOGE=$HOGE react-scripts start",
...
}
こうしてから
$ HOGE=piyo yarn start
こうすると
const reacthoge = process.env.REACT_APP_HOGE // piyo
const hoge = process.env.HOGE // undefined
こうなる。
この時点でだいたい言いたいことは終わったのであとは実際にどう使ったかという話になる。
開発での使用
とりあえず最初の通り
- dev, staging, product の三つの状態が出せる環境変数をおきたい
- debugフラグを環境変数で持ちたい
という感じでいく。
先に結論を書くが、package.jsonを変えたあとそれをビルドするコンテナのDockerfile、そのコンテナビルドを呼び出すCIのコマンドを書き換える形になった。今回は開発運用途中のプロジェクトで変更すると言う形なためだが、最初から設計するならもうちょいマシな方法が考えられたのではないかとも思う。
package.jsonの変更
上述の通りいちいちREACT_APP_
とかつけてられないので
"scripts": {
"start": "REACT_APP_MODE=dev REACT_APP_DEBUG=$DEBUG react-scripts start",
"build": "REACT_APP_MODE=$MODE REACT_APP_DEBUG=$DEBUG react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
...
},
こうする。ちなみに(本業がバックエンド側でJavaScriptをはじめフロント側をほとんど触らないという言い訳の上で言うが)ビルド後に環境変数を読ませるのは無理っぽいのでビルド時に渡すように build
に環境変数を渡している。
さらに、 start
は開発時のローカルでしか使わないのでdevは決め打ちで入れている。
あとはビルド環境で $MODE
を設定してあげればいい。今回はGithub Actionsを使用しておりブランチ情報が取れるためそこからCIのコマンドの中で設定する方法を取った。
呼び出し元の変更
ビルドはDockerfileでやっているのでdocker buildに--build-arg
オプションで渡す。
...
# CIが実行するのでブランチなど適当な条件で分けられるようにする
MODE='PRODUCTION'
DEBUG='false'
docker build -t project:${TAG} --build-arg mode=${MODE} --build-arg debug=${DEBUG} .
...
Dockerfileの変更
受け取る側のDockerfileも用意をする必要がある。
FROM node as build
ARG mode
ARG debug
...
ENV MODE=${mode}
ENV DEBUG=${debug}
RUN MODE=$MODE DEBUG=$DEBUG yarn build
...
プロジェクトコードの変更
もちろんこっちも変更する
const mode = process.env.REACT_APP_MODE
const debug = process.env.REACT_APP_DEBUG
// あとは mode や debug を使ってあれこれ
まとめ
どっちかと言うとキモはコンテナやCIによる仮想化・コード化で、上の方で設定した変数をいかに下の方まで引き回すかと言う点かもしれない。実際そこが面倒ではあったが、対応する分にはコストに見合った結果が得られると思う。
Discussion