홈으로 돌아가기

Python에서 Pydantic으로 TOML 설정 로드하기

모듈이 tomllib를 통해 settings.toml을 로드하고 lru_cache로 캐시하며 Pydantic 모델을 검증합니다. 모든 설정 클래스에 TypeVar 타이핑 지원. 로직과 매개변수 분리를 위한 프로덕션 애플리케이션에 이상적.

Python에서 settings.toml 파싱 및 Pydantic 검증
Advertisement 728x90

Python 앱에서 Pydantic으로 settings.toml 읽기와 검증하기

이 모듈은 settings.toml 파일을 읽고, tomllib로 파싱하며, lru_cache로 결과를 캐싱하고, Pydantic으로 섹션을 검증합니다. 설정 로직을 앱 파라미터와 분리해 중복 디스크 읽기를 줄이고 타입 안전성을 보장합니다.

from functools import lru_cache
from pathlib import Path
from tomllib import load
from typing import Any, Type, TypeVar

from pydantic import BaseModel, SecretStr

ConfigType = TypeVar("ConfigType", bound=BaseModel)

class BotConfig(BaseModel):
    token: SecretStr

@lru_cache
def parse_config_file() -> dict[str, Any]:
    config_path = Path(__file__).resolve().parent.parent.joinpath("settings.toml")

    if not config_path.exists():
        error = "설정 파일을 찾을 수 없습니다"
        raise ValueError(error)

    with open(config_path, "rb") as file:
        config_data = load(file)

    return config_data

def get_config(model: Type[ConfigType], root_key: str) -> ConfigType:
    config_dict = parse_config_file()

    if root_key not in config_dict:
        error = f"키 {root_key}를 찾을 수 없습니다"
        raise ValueError(error)

    return model.model_validate(config_dict[root_key])

왜 TOML과 Pydantic을 설정에 사용할까

TOML 파일은 사람이 읽기 쉽고 Python 딕셔너리로 깔끔하게 파싱됩니다. [bot] 같은 섹션은 중첩 딕셔너리로 변환됩니다. Pydantic은 구조를 검증하고 타입 변환을 처리하며, SecretStr로 비밀 값을 마스킹합니다.

TOML 예시:

Google AdInline article slot
[bot]
token = "123456:ABCDEF"

파싱 결과는 {"bot": {"token": "123456:ABCDEF"}}가 됩니다. 검증으로 BotConfig 객체가 생성됩니다.

@lru_cache로 캐싱하면 반복 읽기를 건너뜁니다: 첫 호출에서 파싱하고 이후는 캐시를 사용합니다. parse_config_file.cache_clear()로 재로드할 수 있습니다.

pathlib로 경로 처리하기

Path(__file__).resolve().parent.parent.joinpath("settings.toml")은 프로젝트 상위 디렉터리의 파일에 대한 절대 경로를 생성합니다. exists()로 존재 여부를 확인하고 parent 속성으로 검증을 간소화합니다.

Google AdInline article slot
  • Path.joinpath(): 플랫폼 간 경로 세그먼트를 추가합니다.
  • resolve(): 심볼릭 링크를 절대 경로로 해결합니다.
  • "rb"로 열기: tomllib.load()가 바이너리 읽기를 요구합니다.

TypeVar와 Pydantic으로 타입 힌트

ConfigType = TypeVar("ConfigType", bound=BaseModel)으로 get_config이 어떤 Pydantic 모델과도 작동하며 전달된 타입의 인스턴스를 반환합니다.

  • Type[ConfigType]: 모델 클래스를 기대합니다. 인스턴스가 아닙니다.
  • model_validate(): 딕셔너리를 검증된 객체로 변환하며, 실패 시 ValidationError를 발생시킵니다.
  • 딕셔너리에 Any: TOML은 str, int, bool, dict 등 타입이 섞여 있습니다.

다른 섹션으로 확장:

class DatabaseConfig(BaseModel):
    host: str
    port: int
    user: str

config = get_config(DatabaseConfig, "database")

캐싱과 데코레이터

@lru_cache는 인수로 결과를 저장합니다(여기서는 인수 없어 단순). LRU는 한도 초과 시 오래된 항목을 제거하지만, 설정에는 하나의 슬롯으로 충분합니다.

Google AdInline article slot

장점:

  • I/O 감소: 디스크 읽기 한 번.
  • 멱등성: 여러 get_config 호출이 하나의 파싱을 공유.
  • 스레드 안전: 표준 라이브러리 데코레이터가 처리.

단점: TOML 파일 변경 시 cache_clear()까지 무시됩니다.

모듈 구조

  • 모델(BotConfig): 로더와 독립적.
  • parse_config_file(): 전체 TOML 파싱.
  • get_config(): 섹션 추출 및 검증.

이 관심사 분리로 테스트 용이성 향상: 경로나 TOML 문자열 모킹 가능.

주요 팁:

  • tomllib(Python 3.11+) 사용, 이전 버전은 tomli 대체.
  • 사용 전 Pydantic으로 설정 검증.
  • 파싱 캐싱하되 재로드 지원 추가.
  • 로그/디버그에서 마스킹 위해 SecretStr에 비밀 저장.
  • 플랫폼 호환 위해 pathlib로 경로 구성.

— Editorial Team

Advertisement 728x90

다음 읽기