환경별 설정 관리
같은 코드가 개발 노트북, 테스트 서버, 프로덕션 서버에서 실행됩니다. 세 환경의 데이터베이스 주소, API 키, 디버그 모드가 모두 다릅니다. 이 설정들을 코드에 하드코딩하면 실수로 프로덕션 DB를 날려먹는 사고가 일어납니다.
환경별 설정을 코드와 분리해서 관리하는 것이 핵심입니다.
환경 분리 전략
세 가지 환경을 분리해서 관리합니다.
| 환경 | 용도 | 특징 |
|---|---|---|
dev |
로컬 개발 | DEBUG=true, 로컬 DB, 더미 API 키 |
staging |
QA, 검증 | 프로덕션과 유사한 설정, 실제에 가까운 데이터 |
prod |
실제 서비스 | DEBUG=false, 실제 DB, 실제 API 키 |
.env 파일 예시
# 새 파일: .env.devAPP_ENV=developmentAPP_PORT=3000DEBUG=trueDB_HOST=localhostDB_PORT=5432DB_NAME=myapp_devDB_USER=postgresDB_PASSWORD=devpasswordREDIS_HOST=localhostREDIS_PORT=6379API_KEY=dev-fake-api-key-12345SMTP_HOST=mailhogSMTP_PORT=1025
# 새 파일: .env.stagingAPP_ENV=stagingAPP_PORT=3000DEBUG=falseDB_HOST=staging-db.internalDB_PORT=5432DB_NAME=myapp_stagingDB_USER=myappDB_PASSWORD=REPLACE_WITH_ACTUAL_PASSWORDREDIS_HOST=staging-redis.internalREDIS_PORT=6379API_KEY=REPLACE_WITH_STAGING_KEYSMTP_HOST=smtp.sendgrid.netSMTP_PORT=587
# 새 파일: .env.prodAPP_ENV=productionAPP_PORT=3000DEBUG=falseDB_HOST=prod-db.myapp.comDB_PORT=5432DB_NAME=myapp_prodDB_USER=myappDB_PASSWORD=REPLACE_WITH_PROD_PASSWORDREDIS_HOST=prod-redis.myapp.comREDIS_PORT=6379API_KEY=REPLACE_WITH_PROD_KEYSMTP_HOST=smtp.sendgrid.netSMTP_PORT=587
실제 비밀번호와 API 키는 REPLACE_WITH_... 플레이스홀더로 두고, 서버 배포 시 실제 값으로 교체합니다.
환경 변수 검증 스크립트
앱 실행 전에 필수 환경 변수가 모두 설정되어 있는지 확인합니다.
#!/bin/bash# 새 파일: scripts/validate_env.sh# validate_env.sh: 필수 환경 변수 존재 확인set -euo pipefailRED='\033[0;31m'GREEN='\033[0;32m'NC='\033[0m'# 필수 변수 목록REQUIRED_VARS=( "APP_ENV" "APP_PORT" "DB_HOST" "DB_PORT" "DB_NAME" "DB_USER" "DB_PASSWORD" "API_KEY")MISSING=0echo "환경 변수 검증 중..."echo ""for var in "${REQUIRED_VARS[@]}"; do if [ -z "${!var:-}" ]; then echo -e "${RED} 누락: $var${NC}" MISSING=$((MISSING + 1)) else # 값을 보여주되 민감한 변수는 마스킹 if [[ "$var" == *"PASSWORD"* ]] || [[ "$var" == *"KEY"* ]] || [[ "$var" == *"SECRET"* ]]; then echo -e "${GREEN} 설정됨: $var = ****${NC}" else echo -e "${GREEN} 설정됨: $var = ${!var}${NC}" fi fidoneecho ""if [ $MISSING -gt 0 ]; then echo -e "${RED}${MISSING}개 필수 환경 변수가 없습니다.${NC}" exit 1fiecho -e "${GREEN}모든 필수 환경 변수가 설정되어 있습니다.${NC}"exit 0
envsubst로 설정 파일 생성
설정 파일 템플릿에 환경 변수 플레이스홀더를 쓰고, envsubst로 실제 값을 채웁니다. nginx, 데이터베이스 접속 설정 등에 유용합니다.
# 새 파일: config/nginx.conf.templateserver { listen ${APP_PORT}; server_name ${SERVER_NAME}; location / { proxy_pass http://localhost:${BACKEND_PORT}; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } error_log /var/log/nginx/${APP_ENV}_error.log; access_log /var/log/nginx/${APP_ENV}_access.log;}
# 환경 변수를 템플릿에 적용export APP_PORT=80export SERVER_NAME=myapp.comexport BACKEND_PORT=3000export APP_ENV=productionenvsubst < config/nginx.conf.template > /etc/nginx/conf.d/myapp.conf# 결과 확인cat /etc/nginx/conf.d/myapp.conf
server {
listen 80;
server_name myapp.com;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
error_log /var/log/nginx/production_error.log;
access_log /var/log/nginx/production_access.log;
}
특정 변수만 치환하고 싶을 때는 변수 이름을 지정합니다.
# APP_PORT와 SERVER_NAME만 치환 (나머지 $변수는 그대로 유지)envsubst '${APP_PORT} ${SERVER_NAME}' < template.conf > output.conf
env_setup.sh: 통합 환경 설정 스크립트
#!/bin/bash# 새 파일: scripts/env_setup.sh# env_setup.sh: 환경 감지 → 설정 로드 → 검증set -euo pipefailGREEN='\033[0;32m'YELLOW='\033[1;33m'RED='\033[0;31m'CYAN='\033[0;36m'NC='\033[0m'PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"# ─── 환경 감지 ────────────────────────────────────────────────────────────────detect_environment() { # 1. 명시적으로 지정된 경우 if [ -n "${APP_ENV:-}" ]; then echo "$APP_ENV" return fi # 2. CI 환경 감지 (GitHub Actions, GitLab CI 등) if [ -n "${CI:-}" ]; then echo "staging" return fi # 3. Git 브랜치로 감지 local branch branch=$(git -C "$PROJECT_ROOT" symbolic-ref --short HEAD 2>/dev/null || echo "unknown") case "$branch" in main|master) echo "prod" ;; develop) echo "dev" ;; staging) echo "staging" ;; *) echo "dev" ;; esac}ENV=$(detect_environment)ENV_FILE="$PROJECT_ROOT/.env.$ENV"echo -e "${CYAN}환경 설정을 시작합니다...${NC}"echo " 감지된 환경: $ENV"# ─── .env 파일 로드 ───────────────────────────────────────────────────────────if [ ! -f "$ENV_FILE" ]; then echo -e "${YELLOW} 경고: $ENV_FILE 파일이 없습니다. .env 파일을 사용합니다.${NC}" ENV_FILE="$PROJECT_ROOT/.env"fiif [ -f "$ENV_FILE" ]; then # shellcheck disable=SC1090 set -a source "$ENV_FILE" set +a echo -e "${GREEN} 설정 로드 완료: $ENV_FILE${NC}"else echo -e "${RED} 오류: 설정 파일을 찾을 수 없습니다.${NC}" exit 1fi# ─── 환경 변수 검증 ───────────────────────────────────────────────────────────bash "$PROJECT_ROOT/scripts/validate_env.sh"# ─── 설정 파일 생성 (템플릿 있을 때) ──────────────────────────────────────────if [ -f "$PROJECT_ROOT/config/nginx.conf.template" ]; then envsubst < "$PROJECT_ROOT/config/nginx.conf.template" \ > "$PROJECT_ROOT/config/nginx.conf" echo -e "${GREEN} nginx 설정 파일 생성 완료${NC}"fiecho ""echo -e "${GREEN}환경 설정 완료 (환경: $ENV)${NC}"
시크릿 관리 주의사항
.env 파일에는 비밀번호, API 키 같은 민감 정보가 담깁니다. 반드시 .gitignore에 추가해야 합니다.
# .gitignore에 추가cat >> .gitignore << 'EOF'# 환경 변수 파일 (절대 커밋하지 말 것).env.env.*!.env.exampleEOF
.env.example 파일은 커밋합니다. 실제 값 없이 변수 이름과 설명만 담은 템플릿입니다.
# 새 파일: .env.example# 이 파일을 .env로 복사하고 실제 값을 입력하세요.# cp .env.example .envAPP_ENV=development # development | staging | productionAPP_PORT=3000DB_HOST=localhostDB_PORT=5432DB_NAME=myapp_devDB_USER=postgresDB_PASSWORD=your_password_here # ← 실제 비밀번호로 교체API_KEY=your_api_key_here # ← 실제 API 키로 교체
프로덕션 시크릿은 AWS Secrets Manager, HashiCorp Vault, 또는 GitHub Actions Secrets에 저장하고 배포 시 주입하는 방식을 사용합니다. 비밀번호를 파일에 평문으로 저장하지 않는 것이 원칙입니다.