본문으로 건너뛰기
Advertisement

환경 변수 관리

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 조합이 현재 파이썬 생태계의 표준입니다.

Advertisement