본문 바로가기
C.W.K.
Stream
Lesson 07 of 08 · published

애플리케이션 config — pydantic, dataclass, env override

~10 min · yaml, config, pydantic, dataclass

Level 0평문
0 XP0/64 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

앱의 YAML, 위에 env var

요즘 service 는 config 를 층으로 쌓아. YAML 파일이 기본값을 들고, 환경 변수가 배포마다 그 위를 덮고, 실행 인자가 호출마다 또 그 위를 덮어. 방식은 이래. YAML 을 타입이 잡힌 config 객체로 파싱한 다음, env var 가 이름을 보고 그 필드를 갈아끼우게 두는 거야.

Pydantic Settings (Python)

Pydantic V2 는 pydantic_settings 를 따로 내놨어. BaseSettings 를 상속한 클래스를 하나 만들고 거기에 YAML 파일을 물려주면 (직접 만든 source 를 끼워서), env var 가 알아서 필드를 덮어써. 타입이 안 맞으면 파싱하는 그 자리에서 에러가 나니까, 오타가 배포까지 안 따라가.

'설정당 한 타입' 원칙

타입을 미리 못 박아둬. port: int = 8000, log_level: Literal["debug","info","warn","error"] = "info" 이렇게. 그러면 안 맞는 값은 파서가 걷어내. JSON Schema 랑 발상이 같아. 다만 그 일을 언어의 타입 시스템이 대신 해주는 거지.

원칙: YAML 을 그냥 dict 로 읽어놓고 문자열 키로 파고드는 짓 (config["server"]["port"]) 은 하지 마. 키를 잘못 썼든, 키가 빠졌든, 타입이 어긋났든 전부 실행 중에 아무 말 없이 터져. 타입이 잡힌 config 객체를 두면 그 셋이 전부 파싱 시점의 에러로 바뀌어.

Code

Pydantic Settings + YAML·python
from pathlib import Path
from typing import Literal
import yaml
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource

class YamlConfigSource(PydanticBaseSettingsSource):
    def get_field_value(self, *args, **kwargs):
        return None  # __call__ 에 위임
    def __call__(self):
        path = Path('config.yaml')
        if path.exists():
            return yaml.safe_load(path.read_text()) or {}
        return {}

class Settings(BaseSettings):
    port: int = 8000
    host: str = 'localhost'
    log_level: Literal['debug', 'info', 'warn', 'error'] = 'info'
    database_url: str

    @classmethod
    def settings_customise_sources(cls, settings_cls, init_settings, env_settings, dotenv_settings, file_secret_settings):
        return (init_settings, env_settings, YamlConfigSource(settings_cls), dotenv_settings, file_secret_settings)

settings = Settings()
print(settings.port)  # YAML 에서 8000, 또는 PORT env var 로 override
config.yaml·yaml
port: 8000
host: 0.0.0.0
log_level: info
database_url: postgresql://postgres:dev@localhost/pippa
Runtime 환경 override·bash
# 한 필드 override — env var 가 YAML 이김
LOG_LEVEL=debug PORT=9000 python -m myapp

# Dockerfile / k8s ConfigMap 에서
env:
  - name: LOG_LEVEL
    value: debug
  - name: DATABASE_URL
    valueFrom:
      secretKeyRef:
        name: pippa-secrets
        key: database-url

External links

Exercise

config 를 읽는 작은 앱을 하나 골라. 지금 yaml.safe_load 로 읽고 dict 로 파고드는 방식이면, 타입이 잡힌 config 객체로 옮겨 (pydantic 이든 attrs 든 dataclass + cattrs 든). 그리고 필드를 하나 빼거나 타입을 틀리게 넣고 실행해봐. 앱이 뜨는 그 순간 파싱에서 죽을 거야. 그게 바로 얻는 것이고.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.