본문으로 건너뛰기

SPA를 한 번 빌드해 여러 환경에 띄우기 (런타임 설정)

· 약 5분

API 주소처럼 환경마다 다른 값을, 빌드에 박지 않고 앱이 켜질 때 읽게 만들어 한 번 빌드로 여러 환경에 배포하는 방법을 정리합니다.

결론부터 말하면, 환경별 값은 빌드에 박지 말고, 앱이 켜질 때 읽는 런타임 설정 파일로 분리합니다. 그러면 앱은 한 번만 빌드하고, 설정 파일만 환경에 맞게 갈아 끼우면 됩니다.

빌드타임 주입과 런타임 주입, 무엇이 다른가

차이는 환경별 값을 언제 결정하느냐입니다. 빌드타임 주입은 빌드할 때 값을 코드에 박아 넣고, 런타임 주입은 앱이 브라우저에서 켜질 때 값을 읽습니다.

구분빌드타임 주입런타임 주입
값이 정해지는 시점빌드할 때앱이 켜질 때
환경이 N개면N번 빌드한 번 빌드 + 설정 N개
대표 예import.meta.env.VITE_*window.__ENV__

빌드타임 주입(VITE_*, REACT_APP_* 등)은 편하지만, 값이 결과물에 박혀 버립니다. 환경이 늘면 그만큼 다시 빌드해야 합니다.

왜 런타임 설정인가

런타임 설정이 필요한 이유는, 같은 앱을 환경마다 다른 값으로 띄워야 할 때 매번 다시 빌드할 수 없기 때문입니다. 특히 고객사마다 API 주소가 다른 온프레미스 배포에서는 빌드타임 주입이 곧 무너집니다.

한 번 인쇄한 안내 책자에 비유할 수 있습니다. 본문(앱)은 한 번만 찍고, 마지막 주소 페이지(설정)만 환경마다 끼워 넣는 것입니다. 책자를 통째로 다시 찍지 않아도 됩니다.

구현 방법

핵심은 빌드 결과물 바깥에 설정 파일을 따로 두고, 앱이 시작할 때 그 파일을 읽게 하는 것입니다.

빌드타임 주입은 환경마다 다시 빌드하고, 런타임 주입은 한 번 빌드한 뒤 환경별 설정 파일만 갈아 끼우는 비교

1. 설정 파일을 따로 둔다

빌드에 포함하지 않는 env-config.js를 만들어 전역 객체에 값을 싣습니다.

// env-config.js  (빌드 산출물이 아니라 배포 시 갈아 끼우는 파일)
window.__ENV__ = {
API_BASE_URL: "https://api.example.com",
};

2. index.html에서 먼저 읽는다

앱 번들보다 먼저 설정 파일을 로드합니다.

<script src="/env-config.js"></script>
<script type="module" src="/assets/index.js"></script>

3. 코드에서 사용한다

const API_BASE_URL =
window.__ENV__?.API_BASE_URL ?? "http://localhost:8080"; // 로컬 기본값

4. 환경마다 설정 파일만 교체

배포 환경에서 env-config.js만 그 환경 값으로 바꿉니다. 쿠버네티스라면 ConfigMap을 볼륨으로 마운트해 이 파일을 덮어쓰는 식입니다. 앱 번들은 그대로입니다.

흔한 함정 (CDN 캐시)

가장 자주 데이는 함정은 CDN 캐시입니다. 화면 파일은 빠른 응답을 위해 CDN에 캐시되는데, 설정 파일까지 캐시되면 값을 바꿔도 옛 설정이 계속 내려갑니다.

해결은 설정 파일만 캐시를 끄는 것입니다.

Cache-Control: no-store      # env-config.js 에만 적용
CDN이 설정 파일까지 캐시해 옛 값이 내려가는 문제와, 설정 파일에만 no-store를 적용한 해결

그 외 주의점:

  • 빌드타임과 런타임을 섞지 않기: import.meta.envwindow.__ENV__를 한 값에 섞으면 어느 쪽이 적용됐는지 헷갈립니다. 환경별 값은 런타임으로 일원화합니다.
  • 로컬 기본값 두기: 설정 파일이 없을 때를 대비해 코드에 안전한 기본값(?? localhost)을 둡니다.
  • 민감 정보 금지: env-config.js는 브라우저가 그대로 읽습니다. 비밀 키 같은 민감 값은 절대 넣지 않습니다.

Q&A

  • Next.js 같은 SSR도 같은 방법인가요?
    • SSR은 서버에서 환경 변수를 직접 읽을 수 있어 사정이 다릅니다. 이 방법은 정적으로 빌드되는 SPA(CSR)에서 특히 유용합니다.
  • .env 파일을 여러 개 두면 안 되나요?
    • .env.production 등은 결국 빌드타임에 박힙니다. 환경이 빌드 후에 정해지는 경우(온프레미스 등)에는 런타임 설정이 필요합니다.
  • 설정 값이 많아지면요?
    • window.__ENV__ 객체에 모읍니다. 타입 안전을 위해 한 곳에서 읽어 검증한 뒤 앱 전역에 제공하는 래퍼를 두면 좋습니다.

참고자료

  • MDN: Cache-Control
  • 12-Factor App: Config