개발자
류준열

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)

그런데 Next.js의 입장도 어느정도는 이해가 간다. 페이지를 빨리 렌더링시키려면 캐싱시키는게 좋을테니..