환경 변수 관리
python-dotenv와 Pydantic Settings로 12-factor 앱 원칙에 맞는 설정을 관리합니다.
설치
pip install python-dotenv pydantic-settings
python-dotenv — 기본 .env 로딩
from dotenv import load_dotenv
import os
# .env 파일 로드 (프로젝트 루트)
load_dotenv()
# 환경 변수 읽기
db_url = os.getenv("DATABASE_URL", "sqlite:///default.db")
debug = os.getenv("DEBUG", "false").lower() == "true"
secret_key = os.environ["SECRET_KEY"] # 없으면 KeyError
print(db_url)
print(debug)
# .env 파일
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
SECRET_KEY=super-secret-key-here
DEBUG=false
ALLOWED_HOSTS=localhost,127.0.0.1
REDIS_URL=redis://localhost:6379/0
# .env.example — 버전 관리에 포함 (실제 값 없이 키만)
DATABASE_URL=postgresql://user:pass@host:5432/dbname
SECRET_KEY=change-me
DEBUG=false
ALLOWED_HOSTS=localhost
REDIS_URL=redis://localhost:6379/0
# .gitignore
.env
.env.local
.env.*.local
Pydantic Settings — 타입 안전 설정
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import Field, PostgresDsn, RedisDsn, field_validator
from typing import Literal
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
case_sensitive=False,
extra="ignore", # 미정의 환경 변수 무시
)
# 앱 설정
app_name: str = "My App"
debug: bool = False
environment: Literal["development", "staging", "production"] = "development"
# 데이터베이스
database_url: PostgresDsn
db_pool_size: int = Field(default=10, ge=1, le=100)
db_max_overflow: int = 20
# 보안
secret_key: str = Field(min_length=32)
jwt_algorithm: str = "HS256"
access_token_expire_minutes: int = 30
# Redis
redis_url: RedisDsn = "redis://localhost:6379/0"
# 외부 서비스
smtp_host: str = "smtp.gmail.com"
smtp_port: int = 587
smtp_user: str = ""
smtp_password: str = ""
# 허용 도메인
allowed_hosts: list[str] = ["localhost", "127.0.0.1"]
@field_validator("allowed_hosts", mode="before")
@classmethod
def parse_allowed_hosts(cls, v):
if isinstance(v, str):
return [h.strip() for h in v.split(",")]
return v
@property
def is_production(self) -> bool:
return self.environment == "production"
@property
def database_url_str(self) -> str:
return str(self.database_url)
# 싱글턴 패턴 (앱 시작 시 1회 로드)
from functools import lru_cache
@lru_cache(maxsize=1)
def get_settings() -> Settings:
return Settings()
settings = get_settings()
환경별 설정 분리
from pydantic_settings import BaseSettings, SettingsConfigDict
import os
class BaseConfig(BaseSettings):
"""공통 설정"""
app_name: str = "My App"
api_version: str = "v1"
log_level: str = "INFO"
class DevelopmentConfig(BaseConfig):
model_config = SettingsConfigDict(env_file=".env.development")
debug: bool = True
database_url: str = "sqlite:///dev.db"
log_level: str = "DEBUG"
class StagingConfig(BaseConfig):
model_config = SettingsConfigDict(env_file=".env.staging")
debug: bool = False
database_url: str = "postgresql://..."
class ProductionConfig(BaseConfig):
model_config = SettingsConfigDict(env_file=".env.production")
debug: bool = False
# 프로덕션에서는 실제 환경 변수 사용
def get_config() -> BaseConfig:
env = os.getenv("ENVIRONMENT", "development")
configs = {
"development": DevelopmentConfig,
"staging": StagingConfig,
"production": ProductionConfig,
}
config_class = configs.get(env, DevelopmentConfig)
return config_class()
config = get_config()
FastAPI / Django 통합
# FastAPI
from fastapi import FastAPI, Depends
from functools import lru_cache
app = FastAPI()
@lru_cache
def get_settings() -> Settings:
return Settings()
@app.get("/config")
async def show_config(settings: Settings = Depends(get_settings)):
return {
"app_name": settings.app_name,
"environment": settings.environment,
"debug": settings.debug,
}
# 테스트에서 오버라이드
from fastapi.testclient import TestClient
def test_config():
def override_settings():
return Settings(debug=True, secret_key="test" * 8, database_url="...")
app.dependency_overrides[get_settings] = override_settings
client = TestClient(app)
response = client.get("/config")
assert response.status_code == 200
12-Factor App 원칙 요약
✅ 설정을 코드에서 분리 — .env 또는 OS 환경 변수
✅ 환경별 값을 환경 변수로 관리 — dev/staging/prod
✅ 비밀 값은 버전 관리 제외 — .gitignore에 .env 추가
✅ 기본값 제공 — 개발 환경에서 쉽게 시작
✅ 유효성 검사 — Pydantic으로 타입 + 범위 검증
❌ 하드코딩된 연결 문자열/API 키
❌ 환경별 설정 파일을 코드에 포함
❌ 개발/프로덕션 설정을 같은 파일에 혼재
정리
| 도구 | 역할 | 추천 상황 |
|---|---|---|
os.getenv | 단순 환경 변수 읽기 | 소규모 스크립트 |
python-dotenv | .env 파일 로드 | 모든 프로젝트 기본 |
Pydantic Settings | 타입 안전 + 유효성 검사 | FastAPI/Django 앱 |
Pydantic Settings + .env 조합이 현재 파이썬 생태계의 표준입니다.