FastAPI 프로젝트에서 설정을 대충 시작하면 보통 이렇게 된다.
- 코드 여기저기에
os.getenv(...)난사 - 로컬에서는 되는데 도커에서 안 됨
- 테스트에서 설정 바꾸기 귀찮아서 결국 전역 변수로 땜빵
나도 예전에 다 해봤고… 그래서 이번 프로젝트에서는 처음부터 Settings 클래스로 통제했다.
핵심: Settings(BaseSettings) 하나로 끝내기
pydantic-settings의 BaseSettings를 쓰면, 환경변수 로딩/타입 검증/기본값을 한 번에 잡을 수 있다.
내가 쓴 구조는 이런 느낌.
class Settings(BaseSettings):
app_name: str = "Doubles API"
api_v1_prefix: str = "/api/v1"
log_level: str = "info"
database_url: str = Field(default_factory=...)
gemini_api_key: str | None = None
weather_api_key: str | None = None
model_config = SettingsConfigDict(
env_file=".env",
env_prefix="DOUBLES_",
case_sensitive=False,
extra="ignore",
)
여기서 학습 포인트는 3개.
1) .env는 “로컬 개발 편의”용이다
env_file=".env" 덕분에, 로컬에서는 .env만 만들어두면 된다.
예시 템플릿은 .env.example에 있다.
DOUBLES_APP_NAME=Doubles API
DOUBLES_API_V1_PREFIX=/api/v1
DOUBLES_LOG_LEVEL=info
DOUBLES_DATABASE_URL=sqlite:////Users/your-name/.doubles/server/doubles.db
DOUBLES_GEMINI_API_KEY=your-gemini-api-key
DOUBLES_WEATHER_API_KEY=your-weatherapi-key
포인트는 딱 하나: 키 이름에 규칙(prefix)을 줘라.
2) env_prefix="DOUBLES_"는 팀 작업할 때 진짜 편하다
환경변수가 프로젝트마다 섞이면 진짜 멘붕 오는데, prefix를 통일하면 된다.
DOUBLES_DATABASE_URLDOUBLES_GEMINI_API_KEYDOUBLES_WEATHER_API_KEY
나중에 CI나 서버 배포할 때도 “이 프로젝트 변수가 뭔지”가 너무 선명해진다.
3) Settings는 캐시해두는 게 깔끔하다
Settings는 보통 “프로세스 시작할 때 한 번” 로드하면 끝이다.
그래서 lru_cache로 캐싱.
@lru_cache
def get_settings() -> Settings:
return Settings()
이러면 코드 어디서든 get_settings()만 부르면 같은 인스턴스를 계속 쓰게 된다.
실습: 일부러 API 키를 빼보고 503 확인하기
이 프로젝트는 API 키가 없으면 서비스를 꺼버리는 형태로 설계돼 있다.
- Gemini 키 없으면 AI 일기 생성 기능은 503
- Weather 키 없으면 날씨 조회는 503
.env에서 DOUBLES_WEATHER_API_KEY를 지우고,
curl -sS "http://127.0.0.1:8000/api/v1/weather?lat=37.57&lng=126.98"
503이 떨어지면 “설정 → DI → 라우터” 흐름이 제대로 붙었다는 뜻이다.
마무리
설정은 초반에 대충 하면, 후반에 무조건 갚는다. (이건 진리임)pydantic-settings로 타입/기본값/환경변수 규칙을 초기에 잡아두면, 프로젝트가 커질수록 편해진다.
다음 편에서는 라우팅 구조를 본다. root 라우터랑 /api/v1를 왜 나눴는지.