똑같은 삽질은 2번 하지 말자
React vol.22 (NextJS에서 process.env의 요소에 동적접근하기) 본문
개요
이번건은 React와는 거의 관련이 없고 NextJS에서 env를 다룰때 생긴 문제에 대해 기록하고자 한다.
문제
process.env[변수이름]과 같이 env의 요소에 동적으로 접근하려할 때 서버사이드의 동작에서는 문제가 없지만 클라이언트 사이드에서는 접근이 안되는 현상
예를 들면
const key = 'NEXT_PUBLIC_SAMPLE';
console.log(process.env.NEXT_PUBLIC_SAMPLE);
console.log(process.env['NEXT_PUBLIC_SAMPLE']);
console.log(process.env[key]);
console.log(process.env);
// Server Side Output
somevalue
somevalue
somevalue
{ ... } // object
// Client Side Output
somevalue
somevalue
undefined
{} // empty object
위와 같이 표시되었다.
원인
Next.js에서 환경변수 처리에 관련이 있는데 이는 Webpack의 DefinePlugin의 동작 방식 때문이다.
DefinePlugin은 Webpack에서 컴파일 시간에 환경변수나 다른 값을 글로벌 상수로 삽입할 수 있게 해주는 플러그인 인데 삽입할 때의 처리를 리터럴 값의 치환을 수행하며 주로 환경 설정 값이나 조건부 코드 실행을 위해 사용된다.
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
});
위와 같이 process.env.NODE_ENV로 적힌 코드를 보면 현재 환경의 NODE_ENV값으로 치환한다.
이 때 치환되는 값은 문자 리터럴이며, Webpack 빌드 광정에서 코드내의 process.env.NODE_ENV의 부분을 실제 환경 값으로 대체한다.
위와 같은 방식이기 떄문에 동적 환경변수의 접근을 할때는 process.env.NODE_ENV와 같이 명시적으로 적힌 코드는 빌드 타임에서 치환을 수행하지만 process.env[key]와 같은 코드는 key가 빌드 타임에서 어떤 값인지 해석할 수 없기 때문에 치환을 하지 못한다.
이떄까지의 webpack.DefinePlugin으로 환경변수를 치환하는 작업은 물론 클라이언트 사이드에서 일어나는 것이며 서버 사이드에서는 NodeJS의 process.env 오브젝트에 접근할 수 있기 때문에 이와 같은 치환작업은 필요없으며 process.env에의 동적 접근도 물로 가능하다.
해결
const dynamicEnvs = require(`./dynamicEnvs/${process.env.APP_ENV || 'local'}.json`);
/** @type {import('next').NextConfig} */
const nextConfig = {
// ...생략
webpack: (config, { webpack, isServer }) => {
// For ServerSide
Object.keys(dynamicEnvs).forEach(key => {
process.env[key] = dynamicEnvs[key];
});
// For ClientSide
if (!isServer) {
config.plugins.push(
new webpack.DefinePlugin({
'process.env': JSON.stringify(dynamicEnvs),
}),
);
}
return config;
},
};
module.exports = nextConfig;
- APP_ENV는 각각의 .env, .env.staging, env.production등에 설정된 값으로 환경에 따라 각각 local, stg, prod의 값으로 되어 있다.
- dynamicEnvs의 디렉터리 안에는 동적으로 접근 가능하게 설정하고 싶은 환경변수들이 환경에 따라 환경.json과 같은 파일들이 들어있다.
- nextConfig에서 지원하는 웹팩확장기능을 통해 server, client 양쪽에서 동적환경변수에 접근할 수 있게 한다.
결론
이렇게 하면 이중으로 환경변수를 관리해야 하며 dynamicEnvs에 정의한 민감한 정보들이 클라이언트쪽에서 접근이 가능하게 되므로 보안상으로 위험이 될 수 있다. 어쩔수 없는 상황이 아니라면 동적으로 환경변수에 접근해야 하는 방법 자체를 그만두고 다른 방법을 모색하는게 더 좋은 pratice가 될 수 있을꺼 같다.
밑은 webpack.DefinePlugin의 동작부분