-
[webpack] 환경변수 직접 선언 : dotenv, dotenv-webpackEnvironment(개발환경)/Configs 2022. 10. 11. 07:54반응형
🧐 서론
회사에서 KTLO(Keeping the lights on) 건으로 미뤄지던 환경변수 선언방식을 개선하는 작업을 할당받아 진행하게 되었다.
React나 Next를 쓰게 되면 보편적으로 Prefix (React_APP_...) 를 붙여 사용하게 되지만,
우리 프로젝트에서는 Webpack의 플러그인을 통해 직접 주입하는 방식을 사용하고 있었다.
물론 문제가 큰 방법은 아니지만, 환경변수를 JS모듈로 관리하는만큼 모든 환경의 파일이 번들에 포함되거나 토큰같은 인증정보가 배포버전에 포함될 수 있다는 등의 문제점은 있다.
그렇기에, .env(환경변수 파일) 기반의 관리방법에 대해 찾아보았고 dotenv를 비롯한 다양한 방법들을 알 수 있었다.
이번 포스팅은 이러한 방법들을 정리하고, 각각의 특징과 장점 등에 대해 적어보도록 하겠다!
📓 ENV(환경변수) 를 사용하는 방법
- 환경변수(Environment Variables) 란?
환경변수는 프로세스가 컴퓨터에서 동작하는 방식에 영향을 미치는 동적인 값들의 모임이라고 정의한다.
즉, OS 입장에서 해당 프로세스를 실행시키기 위하여 참조하는 변수들인 것이다.
각종 개발 프로젝트를 진행하다보면, 서비스나 API의 URL처럼 개발 / 테스트 / 배포 각 환경별로 다른 값을 적용해야 하는 경우가 있다.
또한, API를 사용하기 위해 발급받은 Key값들이 존재하고, 이러한 정보는 외부(Github 혹은 빌드물 등)에 올라가지 않는 것이 좋다.
위와 같은 상황에서, 특정 변수값을 개발환경 별로 세팅하거나 이를 빌드 단계가 끝나면 제외되도록 하기 위해 환경변수를 활용하는 것이다.
React를 비롯한 FE 프레임워크는 기본적으로 Node 환경에서 개발하기 때문에,
NODE_ENV처럼 Node의 환경변수 세팅을 활용할 수 있으며 필요한 변수들을 등록해서 사용할 수 있다.
- React의 환경변수 사용법
# .env REACT_APP_ENV=development REACT_APP_API_URL=https://api.random-api.com REACT_APP_API_KEY=1234asdf
- root에 .env 파일을 만든다.
- 파일에 변수를 추가하는데, 이 때 REACT_APP_[변수명] Prefix를 붙여줘야한다. 이는 시스템 환경변수와 차이를 두기 위함이다.
(확인해보니, React는 Node에서 fs(filesystem)으로 .env 파일을 읽은 뒤, 여기서 Prefix가 붙은 변수만 추가시킨다.) - 변수에 접근할때는, process.env.REACT_APP_[변수명] 객체로 접근하면 된다.
- .env 파일의 변수를 추가 혹은 수정한 경우, 프로젝트를 재실행해야 설정이 다시 반영된다.
Next나 Vue도 비슷하게 환경변수를 적용하며, Next의 경우엔 환경별 .env 파일분기와 빌드별로 그 우선순위를 제공하고있다.
* https://nextjs.org/docs/basic-features/environment-variables -> Load Order 참고
보시다시피, 프레임워크 레벨에서 이미 충분한 환경변수 솔루션들을 제공하고 있다.
다만, Prefix를 붙여야한다는 제약이라던지, 파일명들도 Order 조건에 부합하게 작성해야한다는 Rule들이 존재한다.
특히, 우리 서비스는 2-track 환경을 제공하는만큼 수많은 환경(8개)이 존재했고, 이에 맞는 커스텀된 환경변수 관리 시스템을 구축할 필요가 있었다.
📓 dotenv : Webpack 환경변수 직접 선언
1. dotenv
dotenv는 Webpack 라이브러리의 일종으로, .env 파일을 통해 process.env 환경변수를 로드하는 zero-dependency 모듈이다.
개발 시 로컬서버를 돌리거나 빌드를 시작할 때, 환경변수 파일을 외부에서 설정하여 해당값들을 접근 및 활용한 뒤 빌드 결과물에서 제외시키기 위해 이 dotenv가 사용되는 것이다.
1) 설치
npm i dotenv yarn add dotenv // --save-dev 옵션 권고
2) 적용(Webpack)
webpack.config.js 파일에서 dotenv 모듈을 불러와서 설정해주면 된다.
// webpack.config.js const dotenv = require("dotenv") dotenv.config() module.exports = { // ... console.log(process.env.REACT_APP_ENV) // development }
이제, .env 파일에서 불러온 변수들이 process.env에 등록되어 Node 환경에서 사용이 가능하다.
* 아직 React에선 사용이 불가하다. 빌드물 안에서 환경변수 값에 접근하기 위해선 추가적인 플러그인 설정이 필요하다.
dotenv는 config(환경변수 파일 로드 설정), parse(환경변수 설정 객체파일로 파싱) 2가지 메서드를 제공한다.
주로 사용할 config() 메서드는 Options 객체를 인자로 받으며, 그 안에는 아래와 같은 설정이 가능하다.
dotenv.config({ path: './src/env/.env.local', encoding: 'latin1', debug: process.env.DEBUG, override: true, })
- path : 로드할 환경변수 파일의 경로를 직접 설정할 수 있다. 기본값은 path.resolve(process.cwd(), '.env')
- encoding : 환경변수 파일의 인코딩을 설정한다. 기본값은 utf8
- debug : 특정 변수에 대한 디버깅 여부를 설정. 기본값은 false
- override : 기본적으로 세팅된 환경변수 값을 오버라이딩 할 지 여부이다. 기본값은 false
3) 환경별 분기 방법
webpack-dev-server 혹은 webpack-cli로 빌드를 실행하면서, 커멘드별 실행환경 변수값을 env 객체에 전달해준다.
// package.json // ... "scripts": { "dev": "webpack serve --env TARGET_ENV=development", "build": "webpack serve --env TARGET_ENV=production" },
env에 설정한 TARGET_ENV 변수값이 포함되어 전달되며, 이를 webpack.config.js에서 받아 환경변수 파일링크를 동적으로 설정한다.
// webpack.config.js const dotenv = require("dotenv") module.exports = (env) => { const { TARGET_ENV } = env dotenv.config({ path: `./.env.${TARGET_ENV}` }) }
2. DefinePlugin
dotenv 적용에서 언급했듯, .env 파일의 변수들이 추가됬지만 이를 React 환경에서 사용할 수 없다.
Node runtime에만 사용가능한 이 변수들을, JS context에서 사용하기 위해 빌드타임에 선언을 해주는 단계가 필요하다.
이를 위해 DefinePlugin이 필요한 것이다.
Webpack이 빌드될 때 entry 파일부터 컴파일을 실행하는데, 이 빌드타임에 DefinePlugin에서 설정한 문자열 값들을 전역변수 형태로 선언해주는 것이다.
(이는, Webpack의 빌드타임에서만 활용되며, 브라우저 런타임에서는 사용할 수 없다!)
* 적용
// .env CUSTOM_VAR=custom_variable CUSTOM_VAR2=coustom_variable2
위와 같은 환경변수 파일을 Webpack 빌드타임에 추가한다고 가정하자!
// webpack.config.js const webpack = require("webpack") const dotenv = require("dotenv") dotenv.config() module.exports { // ... plugins: [ new webpack.DefinePlugin({ CUSTOM_VAR: JSON.stringify(process.env.CUSTOM_VAR), CUSTOM_VAR2: JSON.stringify(process.env.CUSTOM_VAR2), }), ], }
plugins 설정에 webpack에서 제공하는 DefinePlugin을 불러오고, 전역변수 설정을 위해 객체의 key-value 형태로 전달한다.
여기서, value를 JSON.stringify() 설정을 해줘야하며 이를 누락하면 Syntex Error가 발생한다.
3. EnvironmentPlugin
EnvironmentPlugin은 DefinePlugin과 같이 Webpack 빌드타임에 전역변수를 설정하는 플러그인이며,
DefinePlugin에 비해 환경변수 값을 추가하는 문법이 더욱 간단하다는 장점이 있다.
* 적용
// webpack.config.js const webpack = require("webpack") const dotenv = require("dotenv") dotenv.config() module.exports { // ... plugins: [ new webpack.EnvironmentPlugin(["CUSTOM_VAR", "CUSTOM_VAR2"]), ], }
이처럼, 환경변수 파일에서 추가할 key값들만 배열로 전달하면 알아서 설정이 된다.
* DefinePlugin, EnvironmentPlugin의 추가적인 장점 : JS Module
이 플러그인들은 .env 파일의 등록 외에도, JS객체 모듈을 전역변수로 등록할 수 있다. 아래와 같이 적용될 수 있을 것이다.
new webpack.DefinePlugin({ "process.env.MODULE_ENV": JSON.stringify("module_variable"), "process.env.MODULE_ENV2": JSON.stringify("module_variable2"), })
new webpack.EnvironmentPlugin({ MODULE_ENV: "module_variable", MODULE_ENV2: "module_variable2", })
위와 같이, 객체를 직접 주입해서 적용할 수 있으므로 JS파일로 환경변수 값들을 작성할 수 있다.
4. dotenv-webpack
dotenv-webpack은 dotenv와 DefinePlugin을 병합한 외부 라이브러리이다.
나 역시 이 플러그인을 사용했는데, 다른 플러그인으로 변수를 설정할 필요없이 .env 파일의 변수들을 자동으로 적용시켜준다.
1) 설치
npm i dotenv-webpack --save-dev
2) 적용(Webpack)
webpack.config.js 설정파일에서 모듈을 불러온 뒤, plugins 설정에 추가해주면 된다. 별도의 설정이 없으면 root의 .env를 불러온다.
// webpack.config.js const DotenvWebpack = require("dotenv-webpack") module.exports = { plugins: [ new DotenvWebpack(), ], }
dotenv.config() 처럼 Options 객체를 인자로 받으며, 아래와 같은 값들을 설정할 수 있다.
- path : 환경변수 파일 경로. 기본값은 ./.env이며, 아래 설정들에 따라 동일경로 상의 .env.example, .env.defaults를 오버라이딩.
- safe : .env.example을 먼저 적용할지 여부. 기본값은 false
- allowEmptyValues : 빈 값을 허용할지 여부. 기본값은 false며, safe 모드에서만 허용된다.
- systemvars : Node 환경변수를 같이 불러올지 여부. 기본값은 false
- silent : 에러 경고를 노출할지 여부. 기본값은 false
- expand : .env에서 환경변수의 확장여부. 기본값은 false
- defaults : dotenv-defaults 플러그인 설정을 통해 .env.defaults를 기본값으로 백업할지 여부. 기본값은 false
- ignoreSub : process.env의 기존 환경변수들을 모두 오버라이딩할지 여부. 기본값은 false
- prefix : 환경변수를 사용할 때 앞에 붙일 구문. 기본값은 'process.env.'
- 환경변수 Typescript 설정
선언파일(d.ts)에서, NodeJS 네임스페이스의 ProcessEnv 인터페이스에 추가하면 process.env에서 해당 변수명들이 추론된다.
declare namespace NodeJS { interface ProcessEnv { TARGET_ENV: 'development' | 'production'; CUSTOM_ENV: string; CUSTOM_ENV2: string; } }
React와 Webpack의 환경변수 선언에서 반드시 위 방법이 맞다는 것은 아니다.
우리 프로젝트도 기존에는 DefinePlugin과 JS Module 형태로 선언되었었고,
많은 환경의 변수값들을 스위칭하여 선언하는만큼 하나의 파일로 관리된다는 장점이 있었다. (문법도 JS 친화적)
물론, 이 파일이 빌드물에 포함되어 개발자 도구에서 확인이 가능하고, 또한 API key같은 정보들도 제외될 수 없다는 한계점은 분명 있다.
React 등 FE 프레임워크들이 사용하는 정론에 가까운 방법으로 커스텀 할 수 있는 방법을 소개했다고 이해해주면 되고,
비슷한 컨셉의 환경변수를 선언하고자 한다면 심플하게 구현할 수 있는 dotenv-webpack을 사용할 것을 권장한다.
📎 출처
- [dotenv] NPM docs : https://www.npmjs.com/package/dotenv
- [DefinePlugin] webpack docs : https://webpack.kr/plugins/define-plugin/
- [EnvironmentPlugin] webpack docs : https://webpack.kr/plugins/environment-plugin/
- [dotenv-webpack] NPM docs : https://www.npmjs.com/package/dotenv-webpack
- [React 환경변수 사용] carmack-kim 님의 블로그 : https://carmack-kim.tistory.com/110
- [환경변수와 dotenv 개념] KIMRY 님의 블로그 : https://velog.io/@reveloper-1311/DB-Node.js%EC%97%90%EC%84%9C-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0dotenv
- [Dotenv와 Plugins] bhautikbharadava's blog : https://medium.com/@bhautikbharadava/environment-variables-webpack-config-using-defineplugin-1a7f38e2236e
- [dotenv, DefinePlugin] dev-son 님의 블로그 : https://dev-son.tistory.com/10
- [DefinePlugin, EnvironmentPlugin] daleseo 님의 블로그 : https://www.daleseo.com/webpack-plugins-define-environment/
반응형