환경 변수는 시크릿을 소스 코드에서 분리하고 재배포 없이 설정을 변경할 수 있게 합니다. .env 파일은 로컬에서 환경 변수를 관리하는 표준 방법입니다. 이 가이드에서는 구문 규칙, 프레임워크 설정, 보안, 환경별 파일, Docker 통합, 일반적인 오류 및 프로덕션 대안을 다룹니다.
.env 구문 규칙
.env 파일은 한 줄에 하나의 변수가 있는 일반 텍스트 파일입니다. 모든 개발자가 알아야 할 규칙:
기본 구문
각 줄은 KEY=VALUE 패턴을 따릅니다. 등호 주위에 공백을 넣지 마세요.
# .env
DATABASE_URL=postgres://localhost:5432/mydb
API_KEY=sk-1234567890abcdef
PORT=3000
DEBUG=true따옴표 규칙
값은 따옴표 없이, 작은따옴표, 큰따옴표로 사용할 수 있습니다. 동작이 다릅니다:
# Unquoted — trailing whitespace trimmed
APP_NAME=My Application
# Single quotes — literal, no interpolation
PASSWORD='p@$$w0rd#123'
# Double quotes — escape sequences + interpolation
GREETING="Hello\nWorld"
FULL_URL="${BASE_URL}/api/v1"- 따옴표 없음: 후행 공백이 제거되고, 이스케이프 시퀀스 없음.
- 작은따옴표: 값이 리터럴로 처리되며, 보간이나 이스케이프 없음.
- 큰따옴표: 이스케이프 시퀀스(\n, \t)와 변수 보간 지원.
여러 줄 값
큰따옴표와 \n으로 줄 바꿈을 표현하거나, 큰따옴표 안에서 실제 줄 바꿈 사용:
# Method 1: \n in double quotes
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIB...\n-----END RSA PRIVATE KEY-----"
# Method 2: actual newlines in double quotes
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
-----END RSA PRIVATE KEY-----"주석
#으로 시작하는 줄은 주석입니다. 일부 파서에서는 따옴표 없는 값에 인라인 주석을 사용할 수 있습니다:
# This is a full-line comment
DATABASE_URL=postgres://localhost/mydb # inline comment (some parsers)
# Empty lines are ignored
API_KEY=abc123 # This may or may not work depending on the parser변수 보간
${VAR} 구문으로 다른 변수를 참조 (대부분의 파서가 큰따옴표 값에서 지원):
BASE_URL=https://api.example.com
API_V1=${BASE_URL}/v1
API_V2=${BASE_URL}/v2
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DATABASE_URL=postgres://${DB_HOST}:${DB_PORT}/${DB_NAME}프레임워크 설정
Node.js (dotenv)
Node.js에서 가장 인기 있는 .env 로더. 설치 및 설정:
# Install
npm install dotenv
# --- app.js (CommonJS) ---
require('dotenv').config();
console.log(process.env.DATABASE_URL);
# --- app.ts (ES Modules) ---
import 'dotenv/config';
console.log(process.env.DATABASE_URL);
# --- Or load from CLI ---
node -r dotenv/config app.js
node -r dotenv/config app.js dotenv_config_path=.env.localNext.js, Vite, Create React App은 .env 파일을 자동으로 로드합니다. 패키지 설치가 필요 없습니다.
Python (python-dotenv)
Python 프로젝트에서 .env 로드:
# Install
pip install python-dotenv
# --- app.py ---
from dotenv import load_dotenv
import os
load_dotenv() # loads .env from current directory
# load_dotenv('.env.local') # or specify a path
database_url = os.getenv('DATABASE_URL')
print(database_url)
# --- Django: manage.py or settings.py ---
from dotenv import load_dotenv
from pathlib import Path
env_path = Path('.') / '.env'
load_dotenv(dotenv_path=env_path)Go (godotenv)
Go 프로젝트에서 .env 로드:
// Install
// go get github.com/joho/godotenv
package main
import (
"fmt"
"log"
"os"
"github.com/joho/godotenv"
)
func main() {
err := godotenv.Load() // loads .env
if err != nil {
log.Fatal("Error loading .env file")
}
dbURL := os.Getenv("DATABASE_URL")
fmt.Println(dbURL)
}PHP (vlucas/phpdotenv)
Laravel과 대부분의 PHP 프레임워크에서 사용:
// Install
// composer require vlucas/phpdotenv
<?php
require 'vendor/autoload.php';
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
// Access variables
$dbUrl = $_ENV['DATABASE_URL'];
// or
$dbUrl = getenv('DATABASE_URL');
// Required variables (throws exception if missing)
$dotenv->required(['DATABASE_URL', 'API_KEY']);Ruby (dotenv gem)
Rails와 기타 Ruby 프로젝트에서 사용:
# Gemfile
gem 'dotenv-rails', groups: [:development, :test]
# Or for non-Rails Ruby:
gem 'dotenv'
# --- app.rb ---
require 'dotenv/load'
puts ENV['DATABASE_URL']
# --- Rails: .env is loaded automatically ---
# Access via ENV['DATABASE_URL'] anywhere in your app보안: .env 파일을 절대 커밋하지 마세요
.env 파일에는 시크릿(API 키, 데이터베이스 비밀번호, 토큰)이 포함되어 있습니다. 이를 버전 관리에 커밋하는 것은 가장 큰 보안 실수입니다.
.gitignore 패턴
다음 패턴을 즉시 .gitignore 파일에 추가하세요:
# .gitignore
# Ignore all .env files
.env
.env.local
.env.*.local
.env.development
.env.production
# More aggressive pattern — ignore all .env variants
.env*
# But DO commit the example file
!.env.example.env.example 사용
빈 값으로 .env.example 파일을 커밋하여 팀원에게 필요한 변수를 알려주세요:
# .env.example — commit this file
# Copy to .env and fill in your values
DATABASE_URL=
API_KEY=
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASS=
REDIS_URL=
JWT_SECRET=
# Optional
DEBUG=false
LOG_LEVEL=info이미 .env를 커밋한 경우
실수로 .env 파일을 커밋한 경우, 추적에서 제거하고 모든 시크릿을 교체하세요:
# Step 1: Remove .env from Git tracking (keeps local file)
git rm --cached .env
# Step 2: Add to .gitignore
echo ".env" >> .gitignore
# Step 3: Commit the removal
git add .gitignore
git commit -m "Remove .env from tracking, add to .gitignore"
# Step 4: CRITICAL — Rotate ALL secrets in the .env file
# Every API key, password, and token that was exposed
# must be regenerated immediately환경별 파일
대부분의 프레임워크는 다양한 환경을 위한 여러 .env 파일을 지원합니다. 로딩 순서를 이해하는 것이 중요합니다.
로딩 순서 (Next.js / Vite / CRA)
파일은 다음 우선순위로 로드됩니다 (나중 파일이 이전 파일을 덮어씀):
# Loading priority (highest to lowest):
# 1. Shell environment variables (always win)
# 2. .env.{NODE_ENV}.local (e.g. .env.production.local)
# 3. .env.local (NOT loaded in test)
# 4. .env.{NODE_ENV} (e.g. .env.production)
# 5. .env (default fallback)
# Example for NODE_ENV=production:
# .env → loaded first (base defaults)
# .env.production → overrides .env
# .env.local → overrides .env.production
# .env.production.local → overrides everything above.env.local은 항상 git에서 무시됩니다 (.gitignore에 추가). 테스트의 결정성을 유지하기 위해 테스트 중에는 이 파일이 로드되지 않습니다.
어떤 파일을 어디에 사용?
| File | Purpose | Git |
|---|---|---|
| .env | Default values, non-secret config | Commit |
| .env.example | Template with empty values | Commit |
| .env.local | Local secrets & overrides | Ignore |
| .env.development | Dev-specific (shared) | Commit |
| .env.production | Production-specific (non-secret) | Commit |
| .env.test | Test environment config | Commit |
| .env.production.local | Production secrets (local only) | Ignore |
Docker 및 Docker Compose 통합
env_file 지시자
Docker Compose는 .env 파일을 컨테이너에 직접 로드할 수 있습니다:
# docker-compose.yml (or compose.yml)
services:
web:
image: node:20-alpine
env_file:
- .env # base defaults
- .env.production # production overrides
ports:
- "3000:3000"
db:
image: postgres:16
env_file:
- .env.db # separate file for DB secrets
volumes:
- pgdata:/var/lib/postgresql/dataenvironment vs env_file
환경 변수를 인라인으로 설정할 수도 있습니다. 차이점:
# Using env_file (loads from file)
services:
web:
env_file:
- .env
# Using environment (inline)
services:
web:
environment:
- NODE_ENV=production
- PORT=3000
- DATABASE_URL=${DATABASE_URL} # from shell or .env
# Using environment (mapping syntax)
services:
web:
environment:
NODE_ENV: production
PORT: 3000- env_file: 파일에서 로드, compose.yml을 깔끔하게 유지, 환경별로 쉽게 교체.
- environment: compose.yml에서 확인 가능, 비밀이 아닌 값에 적합, 변수 치환 지원.
Docker Compose 변수용 .env
Docker Compose는 프로젝트 루트의 .env 파일을 자동으로 읽어 compose.yml의 변수 치환에 사용:
# .env (in same directory as compose.yml)
POSTGRES_VERSION=16
NODE_VERSION=20
APP_PORT=3000
# compose.yml — uses .env for variable substitution
services:
web:
image: node:${NODE_VERSION}-alpine
ports:
- "${APP_PORT}:3000"
db:
image: postgres:${POSTGRES_VERSION}
environment:
POSTGRES_DB: myapp일반적인 .env 오류 10가지와 수정 방법
개발자가 가장 자주 겪는 .env 문제:
| # | 오류 | 원인 | 수정 |
|---|---|---|---|
| 1 | process.env.VAR가 undefined | dotenv가 로드되지 않았거나 사용 후 로드됨 | 엔트리 파일 최상단에서 dotenv.config() 호출 |
| 2 | 프로덕션에서 변수가 비어 있음 | .env 파일이 배포되지 않음; 플랫폼 환경 변수 대신 파일에 의존 | 호스팅 플랫폼(Vercel, AWS 등)에서 환경 변수 설정 |
| 3 | .env 값에 따옴표가 포함됨 | 파서가 따옴표를 리터럴 문자로 처리 | 파서 문서 확인; 대부분은 큰따옴표를 제거하지만 전부는 아님 |
| 4 | 잘못된 .env 파일이 로드됨 | 작업 디렉터리가 예상 경로와 다름 | path 옵션 사용: dotenv.config({ path: ".env.local" }) |
| 5 | 여러 줄 값이 잘림 | 값이 올바르게 큰따옴표로 감싸지지 않음 | 여러 줄 값을 큰따옴표로 감싸기 |
| 6 | 특수 문자가 값을 손상 | $ 또는 #이 보간/주석으로 해석됨 | 작은따옴표로 보간 방지, 또는 \로 이스케이프 |
| 7 | BOM 인코딩 오류 | Windows 편집기가 UTF-8 BOM으로 .env를 저장 | BOM 없는 UTF-8로 저장; 그렇지 않으면 첫 번째 변수를 읽을 수 없을 수 있음 |
| 8 | Docker 컨테이너 환경 변수가 비어 있음 | env_file 경로가 잘못되었거나 파일이 빌드 컨텍스트 밖에 있음 | 경로가 compose.yml 위치 기준 상대 경로인지 확인 |
| 9 | = 주위의 공백으로 파싱 실패 | KEY=VALUE 대신 KEY = VALUE | 등호 주위의 공백 제거 |
| 10 | 브라우저에서 변수를 사용할 수 없음 (React/Next) | 필수 접두사(NEXT_PUBLIC_ 또는 REACT_APP_)가 없음 | 프레임워크가 요구하는 접두사를 추가하여 클라이언트 측 코드에 노출 |
프로덕션 대안
프로덕션에서는 .env 파일이 권장되지 않습니다. 다음 대안을 사용하세요:
플랫폼 환경 변수
모든 주요 호스팅 플랫폼이 환경 변수 설정을 위한 UI 또는 CLI를 제공:
# Vercel
vercel env add DATABASE_URL production
vercel env ls
# AWS (Parameter Store)
aws ssm put-parameter \
--name "/myapp/prod/DATABASE_URL" \
--value "postgres://..." \
--type SecureString
# Heroku
heroku config:set DATABASE_URL=postgres://...
heroku config
# Railway
railway variables set DATABASE_URL=postgres://...
# Fly.io
fly secrets set DATABASE_URL=postgres://...Docker Secrets
Docker Swarm 또는 Compose에서 민감한 데이터에 secrets 사용:
# compose.yml with Docker secrets
services:
web:
image: myapp:latest
secrets:
- db_password
- api_key
environment:
DB_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txt # for Compose
# external: true # for Swarm
api_key:
file: ./secrets/api_key.txt
# In your app, read from file:
# const secret = fs.readFileSync('/run/secrets/db_password', 'utf8').trim();시크릿 매니저 (Vault, AWS Secrets Manager)
엔터프라이즈 애플리케이션에는 전용 시크릿 매니저 사용:
# HashiCorp Vault
vault kv put secret/myapp/production \
DATABASE_URL="postgres://..." \
API_KEY="sk-..."
# Read in application
vault kv get -field=DATABASE_URL secret/myapp/production
# AWS Secrets Manager
aws secretsmanager create-secret \
--name "myapp/production/db" \
--secret-string '{"url":"postgres://...","password":"..."}'
# Read in Node.js with AWS SDK
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
const client = new SecretsManager({ region: 'us-east-1' });
const { SecretString } = await client.getSecretValue({
SecretId: 'myapp/production/db'
});
const secrets = JSON.parse(SecretString);시크릿 매니저는 교체, 감사 로그, 접근 제어, 저장 시 암호화를 제공합니다. 이는 .env 파일로는 불가능한 기능입니다.
자주 묻는 질문
.env 파일을 Git에 커밋해야 하나요?
시크릿(API 키, 비밀번호, 토큰)이 포함된 .env 파일은 절대 버전 관리에 커밋하지 마세요. 대신 빈 플레이스홀더 값이 있는 .env.example 파일을 커밋하세요. .gitignore에 .env* 패턴을 추가하세요.
.env, .env.local, .env.production의 차이점은?
.env는 모든 환경에서 로드되는 기본값을 포함합니다. .env.local은 로컬 오버라이드를 포함하며 git에 커밋하지 않습니다. .env.production은 NODE_ENV=production일 때만 로드되는 프로덕션 전용 값을 포함합니다.
Node.js에서 process.env.MY_VAR가 undefined인 이유는?
가장 일반적인 원인: 1) 엔트리 파일 상단에서 require("dotenv").config() 호출을 잊음, 2) .env 파일이 Node가 실행되는 루트 디렉터리에 없음, 3) 변수 이름 오타, 4) ES 모듈을 사용하여 "import dotenv/config"가 필요.
Docker Compose에서 .env 변수를 사용하는 방법은?
Docker Compose는 compose.yml과 같은 디렉터리의 .env 파일을 자동으로 읽어 ${VAR} 구문으로 변수 치환을 수행합니다. 컨테이너에 변수를 전달하려면 env_file 지시자로 파일에서 로드하거나 environment 키로 인라인 설정합니다.
프로덕션에서 .env 파일을 사용해도 되나요?
권장되지 않습니다. 프로덕션에서는 플랫폼 제공 환경 변수, Docker secrets, 또는 HashiCorp Vault나 AWS Secrets Manager 같은 시크릿 매니저를 사용하세요.
React나 Next.js에서 .env 변수를 브라우저에 노출하는 방법은?
Next.js에서는 변수 이름에 NEXT_PUBLIC_ 접두사를 붙입니다 (예: NEXT_PUBLIC_API_URL). Create React App에서는 REACT_APP_, Vite에서는 VITE_ 접두사를 사용합니다. 접두사가 있는 변수만 클라이언트 번들에 포함됩니다.