Next.js publicRuntimeConfig 사용 배경
내가 진행한 프로젝트는 Docker를 사용해 배포하는 Next.js 기반의 어플리케이션이다.
도커 이미지를 빌드하는 시점에 알 수 없는 몇 가지 환경 변수들을 런타임에 주입해야하는 요구사항이 있었다.
온프레미스 환경에 설치되어야하는 프로젝트 특성 상,
개발하는 시점에는 알 수 없고 설치하는 시점에 결정하게되는 서비스의 설정들과 API 엔드포인트에 사용되는 IP 주소와 같은 변수들을 도커 컴포즈의 환경변수로 작성하여 관리되고 있다.
도커 컴포즈의 환경 변수 중 프론트엔드 도커 이미지에 필요한 변수들을 컨테이너 실행 시점에 주입하여 주는 방식으로 서비스를 설계했다.
즉, 한번의 이미지 빌드로 여러 환경에서 실행할 컨테이너를 생성하는 방식이다.
기존에 Next.js 12.0
에서 next.config.js
파일에서 지원하는 publicRuntimeConfig
옵션을 사용하여 런타임 환경 변수를 주입했다.
// https://nextjs.org/docs/pages/api-reference/next-config-js/runtime-configuration
// next.config.js
module.exports = {
serverRuntimeConfig: {
// Will only be available on the server side
mySecret: 'secret',
secondSecret: process.env.SECOND_SECRET, // Pass through env variables
},
publicRuntimeConfig: {
// Will be available on both server and client
staticFolder: '/static',
},
}
그러나 Next.js는 현재 14버전까지 출시되었으며, 13버전부터는 App Router가 도입되는 등 큰 변화가 있었다. 공식 문서에서도 점진적인 마이그레이션을 권장하고 있다.
사내의 다른 최신 프로젝트에서는 최신 버전의 React와 Next.js를 사용하며 문제 해결 방식이 변화하고 있다. 이에 따라 기술 부채가 생기고 있으며, 이를 해소하기 위해서는 버전 업데이트를 따라갈 필요가 있다.
publicRuntimeConfig 기능의 deprecated 상태와 Standalone 모드
현재(2024.08.27) Next.js 공식 문서에서는 publicRuntimeConfig
옵션이 deprecated 되었으며, 대신 환경 변수 사용를 사용할 것을 권장하고 있다.
Warning:
This feature is deprecated. We recommend using environment variables instead, which also can support reading runtime values.
https://nextjs.org/docs/pages/api-reference/next-config-js/runtime-configuration
또한, Next.js 프로젝트를 독립적인 아웃풋으로 만들어주는 standalone
output 모드에서는 runtimeConfig
옵션과의 호환성 문제로 이 옵션의 사용을 권장하지 않는다.
We do not recommend using the runtimeConfig option, as this does not work with the standalone output mode. Instead, we recommend incrementally adopting the App Router.
https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#runtime-environment-variables
Standalone 모드는 Next.js 12.1 이상 버전부터 사용할 수 있는 기능이며, 지금처럼 runtimeConfig
기능에 의존할 경우 프레임워크 버전 및 새로운 기능에 종속성이 생긴다.
따라서, Next.js 버전에 크게 종속되지 않는 방식으로 개선하면 프로젝트가 레거시화되어 아무도 건들고싶어하지 않는(...) 정도의 먼지쌓인 상황까지는 가지 않을 수 있다.
publicRuntimeConfig
를 사용하지 않는 다른 방식으로 개선해보자.
publicRuntimeConfig 제거하기
어플리케이션이 실행되고 브라우저에 접근 가능한 런타임 시점에 사용되어야 할 환경 변수를 window 글로벌 객체의 변수로 등록하면 프레임워크에 종속되지 않는 런타임 환경 변수를 주입할 수 있다.
다음 과정이 필요하다.

- generateEnv.js : window 객체에 등록시킬 환경변수들을 읽어와 객체 형태로 작성해주는 스크립트 파일. nextjs 프로젝트 내의 scripts 폴더에 저장한다.
- 도커 컨테이너 실행: 도커 컴포즈의 환경 변수 파일에서 프론트엔드 도커 컨테이너에서 필요한 변수들을 읽어온다. 컴포즈 설정 파일에서 shell 스크립트를 사용하여 1번에 작성한 스크립트를 실행한다.
- 스크립트 실행: 스크립트가 실행되면서 window.__ENV = { 'key': value } 형식의 __ENV.js 을 생성한다.
- 브라우저 윈도우 객체에 등록: __ENV.js을 nextjs _document.page.tsx 에서 가져와 head 내에 script로 삽입한다.
- 런타임 환경변수 주입 완료: 클라이언트 브라우저의 window.__ENV 객체는 우리가 필요한 클라이언트 환경 변수를 가지게 된다.
(코드 작성 시, 이 아티클을 많이 참고했다. 코드 구현, 폴더 구조, 스크립트 등)
조사하며 혼동되어 찾아본 NEXT_PUBLIC_ 환경 변수
기본적으로 NEXT_PUBLIC
으로 시작하지 않는 환경 변수는 node.js 환경에서만 사용할 수 있다. 즉, 브라우저에서는 접근 가능하지 않다.
Next.js 공식 문서에서는 이렇게 설명한다.
브라우저에서 환경 변수에 접근 가능하게 만들기 위해서, Next.js는 빌드 시 클라이언트에 전달되는 js 번들에 들어갈 값을 "inline"하여, process.env.[variable]
에 대한 모든 참조를 하드 코딩된 값으로 바꿀 수 있다.
inline
한다는 의미는?
해당 값을 해당 위치에 직접 삽입한다는 의미.
즉, Nextjs에서 환경 변수를 인라인한다는 의미는 빌드 시점에, 해당 환경 변수의 값을 소스 코드에 직접 삽입하여 하드코딩한다는 것을 의미한다.다시 말하면,
process.env.NEXT_PUBLIC_*
로 접근하는 모든 부분이 빌드 시점에 실제 값으로 대체되어 코드에 포함되므로 하드 코딩된 값으로 바꾼다고 이야기하고 있는 것이다. 따라서, 당연하게도 이미 소스코드에 하드 코딩되어진 환경 변수들은 빌드 후에는 해당 값들을 변경할 수 없게 된다.
Next.js by Vercel - The React Framework
NEXT_PUBLIC_*
값들이 인라인되어진다는 특성을 고려할 때, NEXT_PUBLIC_로 시작하는 환경 변수는 빌드 시점에 이미 결정된 값이 필요할 때 사용한다.
현재 적용하려는 프로젝트처럼 애플리케이션 배포 후에 환경에 따라 변경되어야 하는 값이 존재한다면 이 값들은 NEXT_PUBLIC_ 방식으로 사용할 수 없다.
환경 변수가 다를 때마다, 매번 빌드하여 재배포한다면 매우 비효율적일 뿐더러 이미 빌드한 도커 이미지를 실행시켜 컨테이너를 생성하는 설계에서 불가능한 방식이다.
Standalone 옵션으로 도커 이미지 95% 경량화
결론적으로 런타임으로 주입하는 방식을 도입하며, 기존의 v12.0 버전에 종속되는 기능 publicRuntimeConfig에서 벗어날 수 있었다.
따라서 v12.1 부터 제공하는 standalone 옵션을 사용할 수 있게 되었다.
환경 변수 주입 방식과 next 버전업을 진행하며 도커 이미지를 빌드하는 방식을 Single stage에서 Multi Stage로 변경하였고, standalone 옵션까지 추가하여 최대한의 도커 이미지 경량을 할 수 있었다.

기존 3.62GB 도커 이미지: 싱글 스테이지에서 모든 의존성 파일과 코드베이스를 카피하면서 standalone을 사용하지 않음.

중간 경량 1.97GB: 멀티 스테이지만 도입함.
최종 179.37MB 🚀: 멀티 스테이지와 standalone 도입으로 최대 경량. Standalone 옵션으로 약 2GB에서 179MB로 경량이 가능했다!
한번에 두 가지 변화를 주었기 때문에 standalone 옵션의 위력을 알아보기위해 한번 이 옵션을 끄고 빌드해보았다.
빌드 방식의 변화(싱글 -> 멀티 스테이지)를 이 글에서 다루기에는 조금 범위가 벗어나는 것같아 다른 글에서 정리해보려고 한다.
회사에서 오래된 프로젝트를 맡아 개발했다보니, 오래된 기능이나 레거시를 담당하는게 주 업무(?)가 되었다. 최신기술을 자유롭게 붙였다 뗐다 할수있는 프로젝트들이 얼마나 있을까 싶어, 레거시를 다루는 것도 꽤나 재미있다고 생각한다.
아무튼 이렇게 기존 v12.0이었던 Next.js 프로젝트에서 슬며시 12.1로 올리고 앞으로도 점진적 업데이트를 진행할 수 있는 기반을 만들었다.
standalone 을 도입했으며, standalone을 실험적 기능으로 제공하는 v12.1에서 standalone을 정식 기능으로 제공하는 v12.3까지 바로 업데이트가 가능할 것이다.
Ref
- https://techblog.uplus.co.kr/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%97%90%EC%84%9C-%EB%9F%B0%ED%83%80%EC%9E%84-%ED%99%98%EA%B2%BD-%EB%B3%80%EC%88%98-%EC%A3%BC%EC%9E%85%ED%95%98%EA%B8%B0-d8848e6756de
- https://fe-developers.kakaoent.com/2022/220505-runtime-environment/
- https://nextjs.org/docs/pages/api-reference/next-config-js/runtime-configuration
'Development' 카테고리의 다른 글
어? 이거 설치한적없는데? 👻 패키지 매니저 알아보기 - npm, yarn, yarn berry, pnpm (2) | 2024.11.24 |
---|---|
Vanilla-extract 라이브러리 번들사이즈 60% 최적화하기(feat. 내가 Anti-pattern이라니..) (3) | 2024.11.10 |
Issue 템플릿 작성하기(라벨, 할당자, 타이틀 자동 설정, 오픈소스 참고) (0) | 2024.08.29 |
Github PR, Issue 템플릿에서 주석달기 (개발자에게만 보이고 실제 pr, issue에는 안보이도록 만들기) (0) | 2024.08.20 |
[AWS elastic beanstalk] postgresql, redis 설치하기(feat. GPT 드리븐 디벨롭먼트) (0) | 2024.08.05 |
Next.js publicRuntimeConfig 사용 배경
내가 진행한 프로젝트는 Docker를 사용해 배포하는 Next.js 기반의 어플리케이션이다.
도커 이미지를 빌드하는 시점에 알 수 없는 몇 가지 환경 변수들을 런타임에 주입해야하는 요구사항이 있었다.
온프레미스 환경에 설치되어야하는 프로젝트 특성 상,
개발하는 시점에는 알 수 없고 설치하는 시점에 결정하게되는 서비스의 설정들과 API 엔드포인트에 사용되는 IP 주소와 같은 변수들을 도커 컴포즈의 환경변수로 작성하여 관리되고 있다.
도커 컴포즈의 환경 변수 중 프론트엔드 도커 이미지에 필요한 변수들을 컨테이너 실행 시점에 주입하여 주는 방식으로 서비스를 설계했다.
즉, 한번의 이미지 빌드로 여러 환경에서 실행할 컨테이너를 생성하는 방식이다.
기존에 Next.js 12.0
에서 next.config.js
파일에서 지원하는 publicRuntimeConfig
옵션을 사용하여 런타임 환경 변수를 주입했다.
// https://nextjs.org/docs/pages/api-reference/next-config-js/runtime-configuration
// next.config.js
module.exports = {
serverRuntimeConfig: {
// Will only be available on the server side
mySecret: 'secret',
secondSecret: process.env.SECOND_SECRET, // Pass through env variables
},
publicRuntimeConfig: {
// Will be available on both server and client
staticFolder: '/static',
},
}
그러나 Next.js는 현재 14버전까지 출시되었으며, 13버전부터는 App Router가 도입되는 등 큰 변화가 있었다. 공식 문서에서도 점진적인 마이그레이션을 권장하고 있다.
사내의 다른 최신 프로젝트에서는 최신 버전의 React와 Next.js를 사용하며 문제 해결 방식이 변화하고 있다. 이에 따라 기술 부채가 생기고 있으며, 이를 해소하기 위해서는 버전 업데이트를 따라갈 필요가 있다.
publicRuntimeConfig 기능의 deprecated 상태와 Standalone 모드
현재(2024.08.27) Next.js 공식 문서에서는 publicRuntimeConfig
옵션이 deprecated 되었으며, 대신 환경 변수 사용를 사용할 것을 권장하고 있다.
Warning:
This feature is deprecated. We recommend using environment variables instead, which also can support reading runtime values.
https://nextjs.org/docs/pages/api-reference/next-config-js/runtime-configuration
또한, Next.js 프로젝트를 독립적인 아웃풋으로 만들어주는 standalone
output 모드에서는 runtimeConfig
옵션과의 호환성 문제로 이 옵션의 사용을 권장하지 않는다.
We do not recommend using the runtimeConfig option, as this does not work with the standalone output mode. Instead, we recommend incrementally adopting the App Router.
https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#runtime-environment-variables
Standalone 모드는 Next.js 12.1 이상 버전부터 사용할 수 있는 기능이며, 지금처럼 runtimeConfig
기능에 의존할 경우 프레임워크 버전 및 새로운 기능에 종속성이 생긴다.
따라서, Next.js 버전에 크게 종속되지 않는 방식으로 개선하면 프로젝트가 레거시화되어 아무도 건들고싶어하지 않는(...) 정도의 먼지쌓인 상황까지는 가지 않을 수 있다.
publicRuntimeConfig
를 사용하지 않는 다른 방식으로 개선해보자.
publicRuntimeConfig 제거하기
어플리케이션이 실행되고 브라우저에 접근 가능한 런타임 시점에 사용되어야 할 환경 변수를 window 글로벌 객체의 변수로 등록하면 프레임워크에 종속되지 않는 런타임 환경 변수를 주입할 수 있다.
다음 과정이 필요하다.

- generateEnv.js : window 객체에 등록시킬 환경변수들을 읽어와 객체 형태로 작성해주는 스크립트 파일. nextjs 프로젝트 내의 scripts 폴더에 저장한다.
- 도커 컨테이너 실행: 도커 컴포즈의 환경 변수 파일에서 프론트엔드 도커 컨테이너에서 필요한 변수들을 읽어온다. 컴포즈 설정 파일에서 shell 스크립트를 사용하여 1번에 작성한 스크립트를 실행한다.
- 스크립트 실행: 스크립트가 실행되면서 window.__ENV = { 'key': value } 형식의 __ENV.js 을 생성한다.
- 브라우저 윈도우 객체에 등록: __ENV.js을 nextjs _document.page.tsx 에서 가져와 head 내에 script로 삽입한다.
- 런타임 환경변수 주입 완료: 클라이언트 브라우저의 window.__ENV 객체는 우리가 필요한 클라이언트 환경 변수를 가지게 된다.
(코드 작성 시, 이 아티클을 많이 참고했다. 코드 구현, 폴더 구조, 스크립트 등)
조사하며 혼동되어 찾아본 NEXT_PUBLIC_ 환경 변수
기본적으로 NEXT_PUBLIC
으로 시작하지 않는 환경 변수는 node.js 환경에서만 사용할 수 있다. 즉, 브라우저에서는 접근 가능하지 않다.
Next.js 공식 문서에서는 이렇게 설명한다.
브라우저에서 환경 변수에 접근 가능하게 만들기 위해서, Next.js는 빌드 시 클라이언트에 전달되는 js 번들에 들어갈 값을 "inline"하여, process.env.[variable]
에 대한 모든 참조를 하드 코딩된 값으로 바꿀 수 있다.
inline
한다는 의미는?
해당 값을 해당 위치에 직접 삽입한다는 의미.
즉, Nextjs에서 환경 변수를 인라인한다는 의미는 빌드 시점에, 해당 환경 변수의 값을 소스 코드에 직접 삽입하여 하드코딩한다는 것을 의미한다.다시 말하면,
process.env.NEXT_PUBLIC_*
로 접근하는 모든 부분이 빌드 시점에 실제 값으로 대체되어 코드에 포함되므로 하드 코딩된 값으로 바꾼다고 이야기하고 있는 것이다. 따라서, 당연하게도 이미 소스코드에 하드 코딩되어진 환경 변수들은 빌드 후에는 해당 값들을 변경할 수 없게 된다.
Next.js by Vercel - The React Framework
NEXT_PUBLIC_*
값들이 인라인되어진다는 특성을 고려할 때, NEXT_PUBLIC_로 시작하는 환경 변수는 빌드 시점에 이미 결정된 값이 필요할 때 사용한다.
현재 적용하려는 프로젝트처럼 애플리케이션 배포 후에 환경에 따라 변경되어야 하는 값이 존재한다면 이 값들은 NEXT_PUBLIC_ 방식으로 사용할 수 없다.
환경 변수가 다를 때마다, 매번 빌드하여 재배포한다면 매우 비효율적일 뿐더러 이미 빌드한 도커 이미지를 실행시켜 컨테이너를 생성하는 설계에서 불가능한 방식이다.
Standalone 옵션으로 도커 이미지 95% 경량화
결론적으로 런타임으로 주입하는 방식을 도입하며, 기존의 v12.0 버전에 종속되는 기능 publicRuntimeConfig에서 벗어날 수 있었다.
따라서 v12.1 부터 제공하는 standalone 옵션을 사용할 수 있게 되었다.
환경 변수 주입 방식과 next 버전업을 진행하며 도커 이미지를 빌드하는 방식을 Single stage에서 Multi Stage로 변경하였고, standalone 옵션까지 추가하여 최대한의 도커 이미지 경량을 할 수 있었다.

기존 3.62GB 도커 이미지: 싱글 스테이지에서 모든 의존성 파일과 코드베이스를 카피하면서 standalone을 사용하지 않음.

중간 경량 1.97GB: 멀티 스테이지만 도입함.
최종 179.37MB 🚀: 멀티 스테이지와 standalone 도입으로 최대 경량. Standalone 옵션으로 약 2GB에서 179MB로 경량이 가능했다!
한번에 두 가지 변화를 주었기 때문에 standalone 옵션의 위력을 알아보기위해 한번 이 옵션을 끄고 빌드해보았다.
빌드 방식의 변화(싱글 -> 멀티 스테이지)를 이 글에서 다루기에는 조금 범위가 벗어나는 것같아 다른 글에서 정리해보려고 한다.
회사에서 오래된 프로젝트를 맡아 개발했다보니, 오래된 기능이나 레거시를 담당하는게 주 업무(?)가 되었다. 최신기술을 자유롭게 붙였다 뗐다 할수있는 프로젝트들이 얼마나 있을까 싶어, 레거시를 다루는 것도 꽤나 재미있다고 생각한다.
아무튼 이렇게 기존 v12.0이었던 Next.js 프로젝트에서 슬며시 12.1로 올리고 앞으로도 점진적 업데이트를 진행할 수 있는 기반을 만들었다.
standalone 을 도입했으며, standalone을 실험적 기능으로 제공하는 v12.1에서 standalone을 정식 기능으로 제공하는 v12.3까지 바로 업데이트가 가능할 것이다.
Ref
- https://techblog.uplus.co.kr/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%97%90%EC%84%9C-%EB%9F%B0%ED%83%80%EC%9E%84-%ED%99%98%EA%B2%BD-%EB%B3%80%EC%88%98-%EC%A3%BC%EC%9E%85%ED%95%98%EA%B8%B0-d8848e6756de
- https://fe-developers.kakaoent.com/2022/220505-runtime-environment/
- https://nextjs.org/docs/pages/api-reference/next-config-js/runtime-configuration
'Development' 카테고리의 다른 글
어? 이거 설치한적없는데? 👻 패키지 매니저 알아보기 - npm, yarn, yarn berry, pnpm (2) | 2024.11.24 |
---|---|
Vanilla-extract 라이브러리 번들사이즈 60% 최적화하기(feat. 내가 Anti-pattern이라니..) (3) | 2024.11.10 |
Issue 템플릿 작성하기(라벨, 할당자, 타이틀 자동 설정, 오픈소스 참고) (0) | 2024.08.29 |
Github PR, Issue 템플릿에서 주석달기 (개발자에게만 보이고 실제 pr, issue에는 안보이도록 만들기) (0) | 2024.08.20 |
[AWS elastic beanstalk] postgresql, redis 설치하기(feat. GPT 드리븐 디벨롭먼트) (0) | 2024.08.05 |