이미지 로딩 중...
AI Generated
2025. 11. 24. · 4 Views
Vite 환경 변수 및 모드 관리 완벽 가이드
Vite에서 환경 변수를 효과적으로 관리하는 방법을 배웁니다. .env 파일 우선순위부터 커스텀 모드 생성까지, 개발부터 배포까지 필요한 모든 환경 변수 관리 방법을 다룹니다.
목차
- .env 파일 우선순위 이해
- VITE_ prefix 규칙
- development/production 모드 분리
- 커스텀 모드 생성 (staging, preview)
- import.meta.env 활용
- 빌드 타임 vs 런타임 환경 변수
1. .env 파일 우선순위 이해
시작하며
여러분이 프로젝트를 개발할 때 로컬에서는 테스트 DB를 사용하고, 배포할 때는 실제 DB를 사용해야 하는 상황을 겪어본 적 있나요? 또는 개발팀마다 다른 API 서버 주소를 사용해야 하는데, 코드를 매번 수정하기는 번거로운 경우가 있습니다.
이런 문제는 실제 개발 현장에서 매일 발생합니다. 환경 변수를 잘못 관리하면 실수로 개발용 API 키를 배포 버전에 포함시키거나, 팀원들이 각자 다른 설정을 사용해서 일관성이 깨질 수 있습니다.
바로 이럴 때 필요한 것이 .env 파일 우선순위 시스템입니다. Vite는 똑똑하게도 여러 개의 .env 파일을 자동으로 읽어서, 상황에 맞는 설정을 선택해줍니다.
마치 옷장에서 날씨에 맞는 옷을 자동으로 골라주는 것처럼요.
개요
간단히 말해서, .env 파일 우선순위는 Vite가 여러 환경 설정 파일 중에서 어떤 것을 먼저 읽고 적용할지 결정하는 규칙입니다. Vite 프로젝트에서는 여러 개의 .env 파일을 만들 수 있습니다.
예를 들어, .env는 기본 설정, .env.local은 여러분의 개인 설정, .env.production은 배포용 설정을 담습니다. 이렇게 나누면 각 상황에 맞는 설정을 자동으로 적용할 수 있어서 매우 편리합니다.
기존에는 개발자들이 if문으로 환경을 체크하고 수동으로 설정을 바꿔야 했다면, 이제는 파일만 만들어두면 Vite가 알아서 상황에 맞는 설정을 선택합니다. 우선순위 시스템의 핵심 특징은 세 가지입니다.
첫째, 구체적인 파일이 일반적인 파일보다 우선합니다. 둘째, .local이 붙은 파일이 더 높은 우선순위를 가집니다.
셋째, 같은 변수가 여러 파일에 있으면 우선순위가 높은 파일의 값이 적용됩니다. 이러한 특징들이 개발자가 실수 없이 안전하게 환경을 관리할 수 있게 해줍니다.
코드 예제
# .env (모든 환경에서 공통으로 사용)
VITE_APP_TITLE=My Awesome App
VITE_API_VERSION=v1
# .env.local (로컬 개발 전용, Git에 올리지 않음)
VITE_API_URL=http://localhost:3000
VITE_DEBUG_MODE=true
# .env.production (배포 환경 전용)
VITE_API_URL=https://api.production.com
VITE_DEBUG_MODE=false
VITE_ANALYTICS_ID=GA-XXXXXXX
# 우선순위: .env.production.local > .env.production > .env.local > .env
# 위에서 아래로 갈수록 우선순위가 낮아집니다
설명
이것이 하는 일: Vite의 .env 파일 우선순위 시스템은 프로젝트를 실행할 때 자동으로 여러 환경 설정 파일을 순서대로 읽어서, 현재 상황에 가장 적합한 설정값을 선택해줍니다. 첫 번째 단계로, Vite는 프로젝트 루트에서 .env 파일들을 찾습니다.
가장 먼저 .env 파일을 읽어서 기본 설정을 로드합니다. 이 파일은 모든 환경에서 공통으로 사용되는 변수들을 담고 있어야 합니다.
예를 들어 앱 이름이나 API 버전처럼 어디서나 같은 값을 사용하는 것들이죠. 그 다음으로, Vite는 현재 모드를 확인합니다.
만약 개발 모드(npm run dev)라면 .env.development를 읽고, 배포 모드(npm run build)라면 .env.production을 읽습니다. 이 파일들은 각 환경에 특화된 설정을 담고 있습니다.
예를 들어 개발 환경에서는 로컬 API 주소를, 배포 환경에서는 실제 서버 주소를 지정하는 식입니다. 마지막으로, .local이 붙은 파일들을 확인합니다.
.env.local이나 .env.production.local 같은 파일들은 가장 높은 우선순위를 가집니다. 이 파일들에 정의된 변수는 다른 모든 파일의 같은 이름 변수를 덮어씁니다.
최종적으로 모든 변수들이 우선순위에 따라 병합되어 import.meta.env 객체에 담겨서 여러분의 코드에서 사용할 수 있게 됩니다. 여러분이 이 시스템을 사용하면 환경마다 다른 설정을 자동으로 적용할 수 있고, 팀원들의 개인 설정(.local 파일)을 Git에 올리지 않아도 되며, 배포할 때 실수로 개발용 설정이 들어가는 것을 방지할 수 있습니다.
특히 .local 파일을 .gitignore에 추가하면 민감한 API 키나 비밀번호를 안전하게 관리할 수 있습니다.
실전 팁
💡 .env.local 파일은 반드시 .gitignore에 추가하세요. 이 파일에는 개인적인 설정이나 민감한 정보가 들어가므로 Git 저장소에 올리면 안 됩니다.
💡 .env.example 파일을 만들어서 필요한 환경 변수 목록을 공유하세요. 실제 값은 비워두고 변수 이름만 적어두면 새 팀원이 쉽게 설정할 수 있습니다.
💡 같은 변수를 여러 파일에 정의하지 마세요. 공통 변수는 .env에만, 환경별 변수는 해당 환경 파일에만 두면 관리가 쉽습니다.
💡 환경 변수를 변경한 후에는 반드시 개발 서버를 재시작해야 합니다. Vite는 시작할 때만 .env 파일을 읽기 때문에 실행 중에는 변경사항이 반영되지 않습니다.
💡 우선순위가 헷갈리면 console.log(import.meta.env)로 최종 적용된 값을 확인해보세요. 어떤 파일의 값이 적용되었는지 한눈에 알 수 있습니다.
2. VITE_ prefix 규칙
시작하며
여러분이 환경 변수에 API 키를 저장했는데, 브라우저 콘솔에서 확인해보니 undefined가 나와서 당황한 적 있나요? 분명히 .env 파일에 잘 적어뒀는데 코드에서 읽을 수가 없는 상황이 발생합니다.
이런 문제는 Vite의 보안 정책 때문에 발생합니다. Vite는 모든 환경 변수를 브라우저에 노출시키지 않습니다.
왜냐하면 환경 변수에는 데이터베이스 비밀번호나 서버 전용 API 키 같은 민감한 정보가 들어있을 수 있기 때문입니다. 이런 정보가 브라우저 코드에 포함되면 누구나 볼 수 있어서 보안 사고가 발생할 수 있습니다.
바로 이럴 때 필요한 것이 VITE_ prefix 규칙입니다. Vite는 이 접두사가 붙은 변수만 브라우저에 노출시켜서, 여러분이 명시적으로 공개하겠다고 표시한 변수만 안전하게 사용할 수 있게 합니다.
개요
간단히 말해서, VITE_ prefix는 환경 변수 이름 앞에 붙이는 특별한 표시로, 이 변수를 브라우저 코드에서 사용할 수 있게 허용한다는 의미입니다. Vite에서는 보안상의 이유로 모든 환경 변수가 클라이언트 코드에 자동으로 노출되지 않습니다.
예를 들어, DATABASE_PASSWORD 같은 변수는 절대로 브라우저에 노출되면 안 되겠죠. 하지만 VITE_API_URL 같은 변수는 브라우저에서 API를 호출할 때 필요하므로 노출되어도 괜찮습니다.
이런 구분을 위해 VITE_ prefix를 사용합니다. 기존에는 webpack이나 다른 번들러에서 process.env를 통해 모든 환경 변수에 접근할 수 있었다면, Vite는 더 안전한 방식으로 명시적으로 허용한 변수만 import.meta.env를 통해 접근할 수 있게 합니다.
이 규칙의 핵심 특징은 세 가지입니다. 첫째, VITE_로 시작하는 변수만 클라이언트 코드에서 접근 가능합니다.
둘째, 빌드 시점에 변수 값이 코드에 직접 삽입되어서 런타임 오버헤드가 없습니다. 셋째, TypeScript를 사용하면 자동완성과 타입 체크까지 받을 수 있습니다.
이러한 특징들이 보안성과 개발 경험을 동시에 향상시킵니다.
코드 예제
# .env 파일
# ❌ 브라우저에서 접근 불가 (VITE_ 없음)
DATABASE_URL=postgresql://localhost:5432/mydb
API_SECRET_KEY=super-secret-key-123
# ✅ 브라우저에서 접근 가능 (VITE_ prefix 있음)
VITE_API_URL=https://api.example.com
VITE_APP_NAME=My App
VITE_ENABLE_ANALYTICS=true
// 클라이언트 코드에서 사용
console.log(import.meta.env.VITE_API_URL) // ✅ 작동함
console.log(import.meta.env.DATABASE_URL) // ❌ undefined
// API 호출 예시
fetch(`${import.meta.env.VITE_API_URL}/users`)
설명
이것이 하는 일: VITE_ prefix 규칙은 빌드 과정에서 환경 변수를 필터링하여, 안전하게 공개할 수 있는 변수만 선별적으로 클라이언트 번들에 포함시킵니다. 첫 번째 단계로, Vite는 빌드나 개발 서버 시작 시점에 모든 .env 파일들을 읽어들입니다.
이때 수십 개의 환경 변수가 로드될 수 있는데, 이 중에는 데이터베이스 비밀번호, API 시크릿 키, 서버 내부 주소 같은 민감한 정보들이 포함되어 있습니다. 만약 이 모든 변수를 클라이언트 코드에 노출시키면 보안 사고로 이어질 수 있습니다.
그 다음으로, Vite는 변수 이름을 하나씩 검사하면서 VITE_로 시작하는 변수만 골라냅니다. 이 과정은 화이트리스트 방식으로 작동합니다.
즉, 명시적으로 허용된 것만 통과시키고 나머지는 모두 차단하는 것이죠. 예를 들어 VITE_API_URL은 통과하지만 DATABASE_URL은 걸러집니다.
이렇게 필터링된 변수들만 import.meta.env 객체에 담깁니다. 마지막으로, 빌드 시점에 import.meta.env.VITE_API_URL 같은 코드를 찾아서 실제 값으로 치환합니다.
예를 들어 import.meta.env.VITE_API_URL이 "https://api.example.com"이면, 최종 번들 파일에는 이 문자열이 직접 들어갑니다. 최종적으로 브라우저에서 실행되는 코드에는 VITE_ prefix가 붙은 변수의 값만 하드코딩되어 있고, 다른 민감한 변수들은 완전히 제거됩니다.
여러분이 이 규칙을 사용하면 실수로 민감한 정보를 브라우저에 노출시키는 것을 방지할 수 있고, 어떤 변수가 공개되는지 코드만 봐도 명확하게 알 수 있으며, 빌드된 코드가 더 안전하고 예측 가능해집니다. 특히 팀으로 일할 때 누군가 실수로 비밀 키를 클라이언트 코드에서 사용하려고 해도 자동으로 차단되므로 안심할 수 있습니다.
실전 팁
💡 공개해도 되는 변수에만 VITE_를 붙이세요. API 엔드포인트 주소나 기능 플래그는 괜찮지만, 비밀번호나 프라이빗 API 키는 절대 VITE_를 붙이면 안 됩니다.
💡 VITE_로 시작하지 않는 변수는 서버 사이드 코드(vite.config.js 등)에서만 사용하세요. 예를 들어 DATABASE_URL은 서버 설정 파일에서만 접근해야 합니다.
💡 환경 변수가 undefined로 나온다면 VITE_ prefix가 빠졌는지 확인하세요. 이것이 가장 흔한 실수입니다. 그리고 개발 서버를 재시작했는지도 확인하세요.
💡 TypeScript를 사용한다면 vite-env.d.ts 파일에 환경 변수 타입을 정의하세요. interface ImportMetaEnv에 VITE_API_URL: string 같은 타입을 추가하면 자동완성과 타입 체크가 가능합니다.
💡 프로덕션 빌드 후에 번들 파일을 열어서 민감한 정보가 노출되지 않았는지 확인하는 습관을 들이세요. dist 폴더의 JS 파일을 검색해보면 안심할 수 있습니다.
3. development/production 모드 분리
시작하며
여러분이 개발할 때는 상세한 에러 메시지와 디버그 정보를 보고 싶지만, 실제 사용자들에게는 이런 정보를 보여주고 싶지 않은 상황이 있습니다. 또는 개발 중에는 가짜 데이터를 사용하고, 배포할 때는 실제 API를 연결해야 하는 경우도 많습니다.
이런 문제는 소프트웨어 개발의 기본적인 요구사항입니다. 개발 환경과 배포 환경은 목적이 다르기 때문에 설정도 달라야 합니다.
개발 환경에서는 빠른 피드백과 디버깅이 중요하지만, 배포 환경에서는 성능 최적화와 보안이 우선입니다. 이 두 가지를 하나의 설정으로 관리하면 매번 코드를 수정해야 하는 번거로움이 생깁니다.
바로 이럴 때 필요한 것이 development/production 모드 분리입니다. Vite는 자동으로 현재 실행 모드를 감지하고, 그에 맞는 환경 변수와 최적화 전략을 적용해줍니다.
개요
간단히 말해서, 모드 분리는 개발 환경과 배포 환경에서 다른 설정과 동작을 자동으로 적용하는 시스템입니다. Vite에서 npm run dev를 실행하면 development 모드로 동작하고, npm run build를 실행하면 production 모드로 동작합니다.
각 모드는 완전히 다른 .env 파일을 읽고, 다른 최적화를 적용하며, 다른 기능들을 활성화합니다. 예를 들어 개발 모드에서는 Hot Module Replacement가 켜지고 소스맵이 생성되지만, 배포 모드에서는 코드가 압축되고 최적화됩니다.
기존에는 개발자들이 if (process.env.NODE_ENV === 'production') 같은 조건문을 곳곳에 넣어서 환경을 구분해야 했다면, 이제는 .env 파일만 분리해두면 Vite가 자동으로 적절한 설정을 선택합니다. 모드 분리의 핵심 특징은 세 가지입니다.
첫째, 각 모드는 자신만의 .env 파일(.env.development, .env.production)을 가집니다. 둘째, import.meta.env.MODE와 import.meta.env.PROD 같은 내장 변수로 현재 모드를 확인할 수 있습니다.
셋째, 빌드 최적화, 소스맵 생성, 경고 메시지 표시 등이 모드에 따라 자동으로 조정됩니다. 이러한 특징들이 개발과 배포를 깔끔하게 분리하면서도 관리 부담을 줄여줍니다.
코드 예제
# .env.development (개발 환경 설정)
VITE_API_URL=http://localhost:3000/api
VITE_ENABLE_DEBUG=true
VITE_USE_MOCK_DATA=true
VITE_LOG_LEVEL=debug
# .env.production (배포 환경 설정)
VITE_API_URL=https://api.production.com/api
VITE_ENABLE_DEBUG=false
VITE_USE_MOCK_DATA=false
VITE_LOG_LEVEL=error
// 코드에서 모드에 따라 다르게 동작
const apiUrl = import.meta.env.VITE_API_URL
const isDebug = import.meta.env.VITE_ENABLE_DEBUG === 'true'
if (import.meta.env.DEV) {
console.log('개발 모드로 실행 중')
}
if (import.meta.env.PROD) {
// 프로덕션에서만 애널리틱스 초기화
initAnalytics()
}
설명
이것이 하는 일: Vite의 모드 분리 시스템은 npm 스크립트에 따라 자동으로 적절한 환경을 설정하고, 해당 환경에 최적화된 빌드와 설정을 적용합니다. 첫 번째 단계로, Vite는 실행된 명령어를 확인합니다.
npm run dev나 vite 명령으로 개발 서버를 시작하면 자동으로 MODE를 'development'로 설정합니다. 반대로 npm run build나 vite build로 빌드를 실행하면 MODE를 'production'으로 설정합니다.
이 MODE 값에 따라 Vite의 모든 동작이 결정됩니다. 그 다음으로, 설정된 MODE에 맞는 .env 파일을 찾아서 로드합니다.
development 모드라면 .env.development를 읽고, production 모드라면 .env.production을 읽습니다. 이때 .env 파일도 함께 읽어서 기본값을 설정하지만, 모드별 파일의 값이 우선순위를 가집니다.
예를 들어 VITE_API_URL이 .env와 .env.production 둘 다에 있다면 .env.production의 값이 사용됩니다. 마지막으로, 로드된 환경 변수들을 import.meta.env 객체에 담고, 추가로 MODE, DEV, PROD, SSR 같은 유용한 변수들을 자동으로 제공합니다.
최종적으로 코드에서 import.meta.env.DEV로 개발 모드인지 확인하거나, import.meta.env.VITE_API_URL로 현재 모드에 맞는 API 주소를 가져올 수 있습니다. 빌드 시점에 이런 변수들은 실제 값으로 치환되어서 런타임 오버헤드 없이 사용됩니다.
여러분이 이 시스템을 사용하면 개발과 배포 환경을 완벽하게 분리할 수 있고, 배포할 때 설정을 바꾸는 것을 깜빡할 걱정이 없으며, 각 환경에 최적화된 성능을 얻을 수 있습니다. 특히 개발 중에는 상세한 로그와 디버그 도구를 활성화하고, 배포할 때는 자동으로 최적화되고 압축된 코드를 만들어내므로 개발자 경험과 사용자 경험을 모두 향상시킬 수 있습니다.
실전 팁
💡 import.meta.env.DEV와 import.meta.env.PROD를 활용하면 조건문 없이도 모드별 코드를 작성할 수 있습니다. 이 변수들은 빌드 시점에 true/false로 치환되어서 사용하지 않는 코드는 자동으로 제거됩니다(tree-shaking).
💡 개발 환경에서는 VITE_ENABLE_DEBUG=true로 설정해서 console.log를 활성화하고, 배포 환경에서는 false로 설정해서 로그를 제거하세요. 성능과 보안에 도움이 됩니다.
💡 API URL은 반드시 환경 변수로 관리하세요. 코드에 하드코딩하면 환경마다 코드를 수정해야 하지만, .env 파일로 관리하면 한 번만 설정하면 됩니다.
💡 모드 전환 테스트를 자동화하세요. CI/CD 파이프라인에서 npm run build && npm run preview로 배포 버전을 미리 테스트하면 배포 후 문제를 예방할 수 있습니다.
💡 .env.development와 .env.production 파일에 같은 변수들을 정의해두세요. 한쪽에만 있으면 다른 환경에서 undefined 에러가 발생할 수 있습니다.
4. 커스텀 모드 생성 (staging, preview)
시작하며
여러분이 개발과 배포 사이에 중간 단계가 필요한 경우가 있습니다. 예를 들어, 내부 테스트용 서버나 고객 데모용 환경처럼 개발도 배포도 아닌 특별한 환경을 만들고 싶을 때가 있습니다.
또는 A/B 테스트를 위한 별도 환경이나, 특정 고객을 위한 맞춤 환경이 필요할 수도 있습니다. 이런 문제는 프로젝트가 커지고 팀이 성장하면서 자연스럽게 발생합니다.
development와 production만으로는 실제 업무의 복잡한 요구사항을 충족시키기 어렵습니다. 예를 들어 staging 환경에서는 실제 데이터와 비슷한 테스트 데이터를 사용하고, 일부 실험적 기능을 켜두고, 배포 전에 최종 확인을 해야 할 수 있습니다.
바로 이럴 때 필요한 것이 커스텀 모드입니다. Vite는 기본 모드 외에도 여러분이 원하는 만큼 새로운 모드를 만들 수 있게 해줍니다.
staging, preview, testing 같은 이름으로 원하는 모드를 자유롭게 정의할 수 있습니다.
개요
간단히 말해서, 커스텀 모드는 development와 production 외에 여러분이 직접 만드는 추가 환경 설정입니다. Vite에서는 --mode 플래그를 사용해서 원하는 이름의 모드를 실행할 수 있습니다.
예를 들어 vite build --mode staging을 실행하면 Vite는 .env.staging 파일을 찾아서 로드합니다. 이렇게 하면 개발과 배포 사이에 여러 단계의 환경을 만들 수 있어서, 실제 배포 전에 안전하게 테스트하거나 특별한 목적의 빌드를 만들 수 있습니다.
기존에는 복잡한 스크립트나 환경별 설정 파일을 따로 관리해야 했다면, 이제는 .env.staging 파일만 만들고 npm 스크립트에 --mode staging을 추가하면 끝입니다. 커스텀 모드의 핵심 특징은 세 가지입니다.
첫째, 원하는 만큼 많은 모드를 만들 수 있습니다(staging, qa, demo, preview 등). 둘째, 각 모드는 자신만의 .env 파일과 설정을 가집니다.
셋째, package.json에 스크립트를 추가해서 팀원들이 쉽게 사용할 수 있게 만들 수 있습니다. 이러한 특징들이 복잡한 배포 파이프라인을 관리하기 쉽게 만들어줍니다.
코드 예제
# .env.staging (스테이징 환경 설정)
VITE_API_URL=https://staging.example.com/api
VITE_ENABLE_DEBUG=true
VITE_USE_ANALYTICS=false
VITE_FEATURE_FLAG_NEW_UI=true
# .env.preview (프리뷰 환경 설정)
VITE_API_URL=https://preview.example.com/api
VITE_ENABLE_DEBUG=false
VITE_SHOW_DEMO_DATA=true
// package.json 스크립트 설정
{
"scripts": {
"dev": "vite",
"build": "vite build",
"build:staging": "vite build --mode staging",
"build:preview": "vite build --mode preview",
"preview:staging": "vite preview --mode staging"
}
}
// 코드에서 현재 모드 확인
console.log(import.meta.env.MODE) // 'staging' 또는 'preview'
설명
이것이 하는 일: 커스텀 모드 시스템은 --mode 플래그를 통해 원하는 이름의 환경을 동적으로 생성하고, 해당 이름의 .env 파일을 자동으로 로드하여 특별한 목적의 빌드를 만들어냅니다. 첫 번째 단계로, .env.staging이나 .env.preview 같은 새로운 환경 파일을 만듭니다.
파일 이름의 점(.) 뒤에 오는 부분이 모드 이름이 됩니다. 이 파일에는 해당 환경에만 필요한 특별한 설정들을 담습니다.
예를 들어 staging 환경에서는 실제 배포와 거의 같지만 디버그 모드를 켜두고, 분석 도구는 꺼두고, 실험적 기능을 활성화할 수 있습니다. 그 다음으로, package.json의 scripts 섹션에 새로운 명령어를 추가합니다.
"build:staging": "vite build --mode staging" 같은 스크립트를 만들면 npm run build:staging으로 간단하게 실행할 수 있습니다. 이렇게 하면 팀원들이 복잡한 명령어를 외울 필요 없이 npm 스크립트로 쉽게 사용할 수 있습니다.
--mode 플래그는 vite build뿐만 아니라 vite dev나 vite preview에도 사용할 수 있습니다. 마지막으로, Vite는 지정된 모드 이름으로 환경을 초기화합니다.
.env.staging 파일을 읽어서 변수들을 로드하고, import.meta.env.MODE를 'staging'으로 설정합니다. 최종적으로 여러분의 코드에서 import.meta.env.MODE로 현재 어떤 모드인지 확인하거나, 모드별로 다른 동작을 구현할 수 있습니다.
예를 들어 staging 모드에서만 특정 테스트 도구를 표시하거나, preview 모드에서만 데모 데이터를 사용하도록 만들 수 있습니다. 여러분이 커스텀 모드를 사용하면 개발부터 배포까지의 전체 파이프라인을 세밀하게 제어할 수 있고, 각 단계마다 최적의 설정을 적용할 수 있으며, 팀원들과 명확하게 환경을 공유할 수 있습니다.
특히 대형 프로젝트에서는 QA 팀을 위한 qa 모드, 고객 데모를 위한 demo 모드, 내부 테스트를 위한 beta 모드처럼 여러 환경을 만들어서 각각의 목적에 맞게 관리할 수 있습니다.
실전 팁
💡 staging 환경은 production과 최대한 비슷하게 설정하되, 디버그 기능만 추가로 켜두세요. 이렇게 하면 배포 전에 실제와 가까운 조건에서 테스트할 수 있습니다.
💡 모드 이름은 팀 내에서 합의된 명확한 이름을 사용하세요. staging, qa, demo처럼 누구나 이해할 수 있는 이름이 좋습니다. stg나 st 같은 약어는 혼란을 줄 수 있습니다.
💡 각 커스텀 모드에 대한 설명을 README.md에 작성하세요. 언제 어떤 모드를 사용해야 하는지 문서화하면 팀원들이 헷갈리지 않습니다.
💡 CI/CD 파이프라인에서 커스텀 모드를 활용하세요. 예를 들어 PR을 만들면 자동으로 preview 모드로 빌드해서 미리보기 URL을 제공할 수 있습니다.
💡 너무 많은 모드를 만들지 마세요. 3-4개 이상이 되면 관리가 복잡해집니다. 정말 필요한 환경만 만들고, 비슷한 환경은 통합하는 것이 좋습니다.
5. import.meta.env 활용
시작하며
여러분이 환경 변수를 코드에서 사용하려고 할 때, 어떻게 접근해야 하는지 헷갈린 적 있나요? Node.js에서는 process.env를 사용하지만, Vite에서는 다른 방식을 사용합니다.
또한 환경 변수가 어떤 타입인지, 어떤 값들이 있는지 자동완성으로 확인하고 싶을 때도 있습니다. 이런 문제는 Vite가 브라우저 환경에서 실행되는 코드를 다루기 때문에 발생합니다.
Node.js의 process 객체는 브라우저에 존재하지 않으므로, Vite는 ES Module 표준인 import.meta를 사용합니다. 하지만 이 방식에 익숙하지 않으면 환경 변수에 접근하기 어렵고, 오타를 내거나 잘못된 변수를 사용할 수 있습니다.
바로 이럴 때 필요한 것이 import.meta.env입니다. 이 객체는 Vite가 제공하는 특별한 API로, 모든 환경 변수와 유용한 내장 변수들을 담고 있으며, TypeScript와 함께 사용하면 자동완성과 타입 체크까지 받을 수 있습니다.
개요
간단히 말해서, import.meta.env는 Vite에서 환경 변수에 접근하기 위한 전역 객체로, .env 파일의 변수들과 Vite가 자동으로 제공하는 유용한 변수들을 담고 있습니다. Vite 프로젝트에서는 import.meta.env.VITE_API_URL처럼 점 표기법으로 환경 변수에 접근합니다.
이 객체에는 여러분이 정의한 VITE_ prefix 변수들 외에도 MODE, BASE_URL, PROD, DEV, SSR 같은 유용한 내장 변수들이 자동으로 포함됩니다. 이러한 변수들은 빌드 시점에 실제 값으로 치환되므로 런타임 오버헤드가 없습니다.
기존에는 process.env.REACT_APP_API_URL이나 window.ENV처럼 프레임워크마다 다른 방식을 사용했다면, Vite는 표준화된 import.meta.env로 통일된 접근 방식을 제공합니다. import.meta.env의 핵심 특징은 네 가지입니다.
첫째, 모든 VITE_ prefix 변수에 접근할 수 있습니다. 둘째, MODE, DEV, PROD 같은 유용한 내장 변수를 제공합니다.
셋째, TypeScript 인터페이스를 확장해서 자동완성과 타입 체크를 활성화할 수 있습니다. 넷째, 빌드 시점에 정적으로 치환되어서 성능이 뛰어납니다.
이러한 특징들이 개발자 경험을 크게 향상시킵니다.
코드 예제
// 환경 변수 접근
const apiUrl = import.meta.env.VITE_API_URL
const appName = import.meta.env.VITE_APP_NAME
// 내장 변수 활용
if (import.meta.env.DEV) {
// 개발 모드에서만 실행
console.log('개발 중:', apiUrl)
}
if (import.meta.env.PROD) {
// 프로덕션에서만 실행
initAnalytics()
}
const mode = import.meta.env.MODE // 'development' | 'production' | 'staging'
const baseUrl = import.meta.env.BASE_URL // '/my-app/' 같은 base path
// TypeScript 타입 정의 (vite-env.d.ts)
interface ImportMetaEnv {
readonly VITE_API_URL: string
readonly VITE_APP_NAME: string
readonly VITE_ENABLE_DEBUG: string
}
설명
이것이 하는 일: import.meta.env는 빌드 시점에 환경 변수들을 수집하여 하나의 객체로 만들고, 코드에서 이 변수들에 접근할 때 실제 값으로 치환하는 메커니즘을 제공합니다. 첫 번째 단계로, Vite는 시작 시점에 .env 파일들을 읽어서 VITE_ prefix가 붙은 변수들을 필터링합니다.
동시에 MODE, DEV, PROD, SSR, BASE_URL 같은 내장 변수들을 생성합니다. 예를 들어 MODE는 현재 모드 이름이고, DEV는 개발 모드인지를 나타내는 불리언 값입니다.
이 모든 변수들이 import.meta.env 객체에 담깁니다. 그 다음으로, 코드를 파싱하면서 import.meta.env.VITE_API_URL 같은 표현식을 찾습니다.
이런 표현식을 발견하면 실제 값으로 대체합니다. 예를 들어 VITE_API_URL이 "https://api.example.com"이면, 빌드된 코드에는 이 문자열이 직접 들어갑니다.
즉, fetch(import.meta.env.VITE_API_URL)은 fetch("https://api.example.com")으로 변환됩니다. 마지막으로, TypeScript를 사용하는 경우 vite-env.d.ts 파일의 ImportMetaEnv 인터페이스를 읽어서 타입 정보를 제공합니다.
최종적으로 여러분이 import.meta.env.VITE_를 타이핑하면 자동완성이 나타나고, 잘못된 변수 이름을 사용하면 컴파일 에러가 발생합니다. 이렇게 타입 안정성을 확보하면서도 런타임에는 직접 값이 들어있어서 성능 오버헤드가 전혀 없습니다.
여러분이 import.meta.env를 활용하면 환경 변수에 안전하게 접근할 수 있고, 개발과 배포 환경을 쉽게 구분할 수 있으며, TypeScript의 도움을 받아 실수를 줄일 수 있고, 코드가 더 명확하고 유지보수하기 쉬워집니다. 특히 import.meta.env.DEV와 import.meta.env.PROD를 활용하면 조건부 로직을 간단하게 작성할 수 있고, 사용하지 않는 코드는 자동으로 제거되어(tree-shaking) 번들 크기가 줄어듭니다.
실전 팁
💡 환경 변수는 항상 문자열입니다. 불리언이나 숫자를 사용하려면 import.meta.env.VITE_ENABLE === 'true'처럼 비교하거나 Number(import.meta.env.VITE_PORT)로 변환하세요.
💡 import.meta.env.DEV와 import.meta.env.PROD는 불리언 값이므로 조건문에 바로 사용할 수 있습니다. if (import.meta.env.PROD)처럼 간단하게 작성하세요.
💡 TypeScript를 사용한다면 반드시 vite-env.d.ts에 환경 변수 타입을 정의하세요. 자동완성과 타입 체크가 생산성을 크게 높여줍니다.
💡 import.meta.env는 정적으로 치환되므로 동적 키 접근(import.meta.env[key])은 작동하지 않습니다. 변수 이름을 직접 명시해야 합니다.
💡 console.log(import.meta.env)로 전체 환경 변수를 확인하세요. 어떤 변수들이 로드되었는지, 어떤 값을 가지는지 한눈에 볼 수 있습니다. 하지만 프로덕션 빌드에서는 이런 로그를 제거하세요.
6. 빌드 타임 vs 런타임 환경 변수
시작하며
여러분이 환경 변수를 사용하다가 이상한 경험을 한 적 있나요? 예를 들어, 서버를 배포한 후에 환경 변수를 바꿨는데 앱에서는 여전히 옛날 값이 나오는 경우가 있습니다.
또는 Docker 컨테이너에 환경 변수를 주입했는데 앱이 인식하지 못하는 상황도 발생합니다. 이런 문제는 빌드 타임과 런타임의 차이를 이해하지 못해서 발생합니다.
Vite의 환경 변수는 빌드 시점에 코드에 직접 삽입되므로, 빌드 후에는 변경할 수 없습니다. 이것은 성능과 보안 측면에서는 장점이지만, 동적으로 설정을 변경해야 하는 상황에서는 제약이 될 수 있습니다.
특히 Docker나 Kubernetes 같은 컨테이너 환경에서는 이런 제약이 큰 문제가 됩니다. 바로 이럴 때 필요한 것이 빌드 타임과 런타임 환경 변수의 차이를 이해하고, 상황에 맞는 적절한 전략을 선택하는 것입니다.
때로는 빌드 타임 변수로 충분하지만, 때로는 런타임에 동적으로 로드하는 방법이 필요합니다.
개요
간단히 말해서, 빌드 타임 환경 변수는 빌드할 때 코드에 삽입되어 변경할 수 없고, 런타임 환경 변수는 앱이 실행될 때 동적으로 로드되어 변경 가능한 값입니다. Vite의 기본 환경 변수는 모두 빌드 타임에 처리됩니다.
npm run build를 실행하는 순간 import.meta.env.VITE_API_URL 같은 코드가 실제 값으로 치환됩니다. 이것은 매우 빠르고 안전하지만, 한번 빌드하면 다시 빌드하지 않는 한 값을 바꿀 수 없습니다.
반면 런타임 환경 변수는 앱이 시작할 때나 API를 통해 동적으로 가져오므로, 재빌드 없이 설정을 변경할 수 있습니다. 기존에는 모든 환경 변수를 process.env로 런타임에 접근했다면, 프론트엔드에서는 빌드 타임에 최적화하는 것이 더 효율적이지만, 때로는 런타임 접근이 필요한 경우도 있습니다.
이 개념의 핵심 특징은 네 가지입니다. 첫째, 빌드 타임 변수는 성능이 뛰어나고 번들 크기를 줄일 수 있습니다(tree-shaking).
둘째, 런타임 변수는 유연하고 배포 후에도 변경 가능합니다. 셋째, 대부분의 프론트엔드 설정은 빌드 타임으로 충분하지만, 환경별 동적 설정은 런타임이 필요합니다.
넷째, 두 방식을 조합해서 사용할 수 있습니다. 이러한 특징들이 상황에 맞는 최적의 전략을 선택할 수 있게 해줍니다.
코드 예제
// ===== 빌드 타임 환경 변수 (기본 방식) =====
// 빌드 시점에 값이 코드에 직접 삽입됨
const apiUrl = import.meta.env.VITE_API_URL
// 빌드 후: const apiUrl = "https://api.example.com"
// ===== 런타임 환경 변수 (동적 로딩) =====
// 방법 1: public 폴더에 config.js 파일 생성
// public/config.js
window.ENV = {
API_URL: 'https://api.example.com',
FEATURE_FLAG: true
}
// index.html에서 로드
<script src="/config.js"></script>
// 코드에서 사용
const apiUrl = window.ENV.API_URL
// 방법 2: API에서 동적으로 가져오기
const config = await fetch('/api/config').then(r => r.json())
const apiUrl = config.API_URL
// ===== 하이브리드 방식 =====
const apiUrl = window.ENV?.API_URL || import.meta.env.VITE_API_URL
설명
이것이 하는 일: 빌드 타임과 런타임 환경 변수 시스템은 애플리케이션 설정을 언제 어떻게 로드할지 결정하여, 성능과 유연성 사이의 균형을 맞춥니다. 첫 번째 단계로, 빌드 타임 방식에서는 npm run build를 실행할 때 Vite가 코드를 분석합니다.
import.meta.env.VITE_API_URL 같은 표현식을 찾으면 .env 파일에서 읽은 실제 값으로 치환합니다. 예를 들어 "https://api.example.com"으로 바꿉니다.
이 과정은 빌드 시점에 한 번만 일어나고, 결과물인 JavaScript 파일에는 이미 값이 하드코딩되어 있습니다. 따라서 매우 빠르고 효율적이지만, 배포 후에 이 값을 바꾸려면 다시 빌드해야 합니다.
그 다음으로, 런타임 방식에서는 앱이 브라우저에서 실행될 때 설정을 로드합니다. 가장 일반적인 방법은 public 폴더에 config.js 파일을 만들어서 window 객체에 설정을 담는 것입니다.
또는 /api/config 같은 엔드포인트를 만들어서 서버에서 동적으로 설정을 제공할 수도 있습니다. 이렇게 하면 같은 빌드 파일을 여러 환경에 배포하고, 각 환경에서 다른 config.js를 제공하여 다른 설정을 사용할 수 있습니다.
마지막으로, 하이브리드 방식에서는 두 가지를 조합합니다. window.ENV?.API_URL || import.meta.env.VITE_API_URL처럼 런타임 설정이 있으면 그것을 사용하고, 없으면 빌드 타임 기본값을 사용하는 것입니다.
최종적으로 이 방식을 사용하면 개발 중에는 빌드 타임 변수로 빠르게 작업하고, 배포할 때는 런타임 변수로 유연하게 설정을 관리할 수 있습니다. 여러분이 이 차이를 이해하면 언제 빌드 타임을 사용하고 언제 런타임을 사용해야 하는지 판단할 수 있고, Docker나 Kubernetes 같은 환경에서도 유연하게 대응할 수 있으며, 같은 빌드를 여러 환경에 재사용할 수 있어서 CI/CD 파이프라인이 간단해지고, 배포 후에도 긴급하게 설정을 변경할 수 있는 안전장치를 마련할 수 있습니다.
특히 대규모 서비스에서는 런타임 설정이 필수적이므로, 처음부터 하이브리드 방식으로 설계하는 것이 좋습니다.
실전 팁
💡 대부분의 경우 빌드 타임 변수로 충분합니다. API URL, 기능 플래그 같은 것들은 빌드 시점에 결정해도 문제없습니다. 불필요하게 런타임 방식을 사용하면 복잡도만 높아집니다.
💡 런타임 변수가 필요한 경우는 보통: 같은 빌드를 여러 환경(dev/staging/prod)에 배포할 때, Docker 컨테이너로 배포할 때, 클라이언트마다 다른 설정이 필요할 때입니다.
💡 public/config.js 방식을 사용하면 배포 스크립트에서 환경별로 다른 config.js를 복사하면 됩니다. 예: cp config.staging.js public/config.js && npm run build
💡 런타임 설정은 앱이 시작할 때 한 번만 로드하세요. 매번 API를 호출하면 성능이 떨어집니다. 로드한 값을 전역 상태나 context에 저장해서 재사용하세요.
💡 보안에 주의하세요. 런타임으로 로드하는 설정도 브라우저에서 볼 수 있습니다. 민감한 API 키나 비밀번호는 절대 클라이언트에 노출하지 말고, 서버에서만 사용하세요.
댓글 (0)
함께 보면 좋은 카드 뉴스
WebSocket과 Server-Sent Events 실시간 통신 완벽 가이드
웹 애플리케이션에서 실시간 데이터 통신을 구현하는 핵심 기술인 WebSocket과 Server-Sent Events를 다룹니다. 채팅, 알림, 실시간 업데이트 등 현대 웹 서비스의 필수 기능을 구현하는 방법을 배워봅니다.
API 테스트 전략과 자동화 완벽 가이드
API 개발에서 필수적인 테스트 전략을 단계별로 알아봅니다. 단위 테스트부터 부하 테스트까지, 실무에서 바로 적용할 수 있는 자동화 기법을 익혀보세요.
효과적인 API 문서 작성법 완벽 가이드
API 문서는 개발자와 개발자 사이의 가장 중요한 소통 수단입니다. 이 가이드에서는 좋은 API 문서가 갖춰야 할 조건부터 Getting Started, 엔드포인트 설명, 에러 코드 문서화, 인증 가이드, 변경 이력 관리까지 체계적으로 배워봅니다.
API 캐싱과 성능 최적화 완벽 가이드
웹 서비스의 응답 속도를 획기적으로 개선하는 캐싱 전략과 성능 최적화 기법을 다룹니다. HTTP 캐싱부터 Redis, 데이터베이스 최적화, CDN까지 실무에서 바로 적용할 수 있는 핵심 기술을 초급자 눈높이에서 설명합니다.
OAuth 2.0과 소셜 로그인 완벽 가이드
OAuth 2.0의 핵심 개념부터 구글, 카카오 소셜 로그인 구현까지 초급 개발자를 위해 쉽게 설명합니다. 인증과 인가의 차이점, 다양한 Flow의 특징, 그리고 보안 고려사항까지 실무에 바로 적용할 수 있는 내용을 다룹니다.