개발자
류준열
Next.js에서 런타임에 환경변수를 설정 할 수 있게 하는 법
런타임 환경변수가 필요했던 이유
Next와 React에서는 환경변수가 빌드타임에 주입된다. Node.js가 없는 고객사 PC에서 환경변수를 수정하려면 본사인원에게 빌드를 요청하고 파일을 메일로 주고받는 번거로움이 있었다. 하지만 런타임에서 환경변수를 설정 할 수 있게 되면, docker-compose만 수정하면 되므로 빌드 없이 어떤 환경에서든 즉시 배포가 가능해진다.
next-runtime-env 라이브러리
런타임 환경변수를 도입하기 위해 next-runtime-env 라이브러리를 사용했다.
원리
- process.env는 Node.js 환경에서는 런타임에 접근 가능하다.
- Next.js의 서버는 Node.js이다.
- 서버컴포넌트에서는 런타임에서도 process.env에 접근이 가능하다.
위 성질을 이용하여 아래와 같이 window 객체 내부에 환경변수를 등록하는 것이다.
const innerHTML = { __html: `window['__ENV'] = ${JSON.stringify(env)}`, }; return ( <Script strategy="beforeInteractive" // 페이지 로드전에 실행 nonce={nonceString} // 콘텐츠 보안을 위해 외부 스크립트를 막는 웹사이트를 개발할때 필요 dangerouslySetInnerHTML={innerHTML} /> );
특정 페이지에서 새로고침시 환경변수 못불러오는 문제
하지만, next-runtime-env를 사용했을때 특정 페이지에서는 환경변수가 undefined로 뜨는 문제가 있었다.
라우팅을 통해 이동할때는 정상작동 하는데, 새로고침 할때만 발생하는 문제인걸로 봐서 해당 페이지에서 브라우저를 렌더링 할 때 문제가 있는 것으로 보였다.
useEffect, window 객체 등등을 살폈지만 문제가 해결되지 않았고 next-runtime-env 를 까보았지만 라이브러리 코드에서도 문제점을 찾을 수 없었다.
심지어 정적렌더링을 차단하기 위해 noStore를 이미 사용하고 있었다.
import { unstable_noStore as noStore } from 'next/cache';
import { isBrowser } from '../helpers/is-browser';
import { PUBLIC_ENV_KEY } from './constants';
/**
* Reads a safe environment variable from the browser or any environment
* variable from the server (process.env).
*
* Usage:
* ```ts
* const API_URL = env('NEXT_PUBLIC_API_URL');
* ```
*/
export function env(key: string): string | undefined {
if (isBrowser()) {
if (!key.startsWith('NEXT_PUBLIC_')) {
throw new Error(
`Environment variable '${key}' is not public and cannot be accessed in the browser.`,
);
}
return window[PUBLIC_ENV_KEY][key];
}
noStore();
return process.env[key];
}
가설 및 해결
- 정적렌더링 하는 페이지에서는 docker compose에서 주입되는 런타임 환경변수를 확인하지 못한다.
- 페이지를 동적렌더링시키면 런타임 환경변수를 확인 할 수 있다.
unstable_noStore는 unstable 하고, 제 역할을 못한다.
위의 가설을 토대로 아래와 같이 강제 동적렌더링 시키니 환경변수가 잘 반영되었다.
export const dynamic = 'force-dynamic';
런타임 환경변수를 사용하려면 Next.js의 캐싱을 포기해야 한다.
느낀점
아래와 같은 것들은 이전에는 생각하지 못했던 것들이다.
- Node.js에서는 process.env에 접근 가능 하기 때문에 서버컴포넌트에서 런타임 환경변수에 접근 할 수 있다는 것은 생각하지 못했다.
- Next.js가 스스로 최적화를 하는 과정이 버그로 나타날 수 있다.
그리고 Next.js의 환경변수 사용 방식에 대해 개발자들의 불만이 많은 모양이다. (discussion)
discussion을 쭉 읽으면서 알게된건 next는 빌드시점에 최대한 많은걸 결정하고 이를 캐싱가능한 상태로 두려 하는 반면, 쿠버네티스는 빌드와 설정을 분리하여 런타임에 외부에서 환경변수를 주입하려 한다는 것이다.
Next.js의 입장도 이해가 간다. 페이지를 빨리 렌더링시키려면 캐싱시키는게 좋을테니..
결국 불편함을 감수하고 성능을 살릴지, 성능희생을 하고 편안함을 선택할지 트레이드오프이다.