""" 애플리케이션 설정 모듈. 환경 변수(.env)를 pydantic-settings 로 로드하며, 모든 설정은 이 모듈의 ``get_settings()`` 함수를 통해 접근합니다. """ from __future__ import annotations from functools import lru_cache from pydantic import Field, field_validator from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): """전역 애플리케이션 설정.""" model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8", case_sensitive=False, extra="ignore", ) # ── 공공데이터포털 (data.go.kr) ───────────────────────── data_go_kr_api_key: str = Field( default="", description="data.go.kr 서비스 인증키", ) data_go_kr_base_url: str = Field( default="https://open.neis.go.kr/hub", description="data.go.kr NEIS Open API 기본 URL", ) # ── 교육데이터 활용·지원 서비스 (data.edmgr.kr) ────────── edmgr_api_key: str = Field( default="", description="data.edmgr.kr 서비스 인증키", ) edmgr_base_url: str = Field( default="https://open.neis.go.kr/hub", description="NEIS 학교기본정보 API 기본 URL", ) # ── 행정안전부 주민등록 인구통계 ───────────────────────── mois_api_key: str = Field( default="", description="행안부 주민등록 인구통계 서비스 인증키", ) mois_base_url: str = Field( default="https://jumin.mois.go.kr/ageStatMonth.do", description="행안부 인구통계 API URL", ) # ── Hugging Face ───────────────────────────────────────── huggingface_hub_token: str = Field( default="", description="Hugging Face Hub 액세스 토큰", ) granite_model_id: str = Field( default="ibm-granite/granite-timeseries-ttm-r2", description="Granite TTM 모델 식별자", ) # ── 애플리케이션 ───────────────────────────────────────── app_env: str = Field(default="development") log_level: str = Field(default="INFO") api_host: str = Field(default="0.0.0.0") api_port: int = Field(default=8000, ge=1, le=65535) api_workers: int = Field(default=1, ge=1) # ── 캐시 ───────────────────────────────────────────────── redis_url: str = Field(default="") cache_ttl_seconds: int = Field(default=3600, ge=0) # ── 데이터 수집 ─────────────────────────────────────────── ingest_page_size: int = Field(default=1000, ge=1, le=5000) ingest_max_retries: int = Field(default=3, ge=0, le=10) ingest_retry_delay: float = Field(default=2.0, ge=0.0) http_timeout: int = Field(default=30, ge=5, le=120) # ── 예측 ───────────────────────────────────────────────── prediction_context_length: int = Field(default=60, ge=12, le=120) prediction_horizon_months: int = Field(default=60, ge=12, le=120) @field_validator("log_level") @classmethod def _validate_log_level(cls, v: str) -> str: allowed = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"} upper = v.upper() if upper not in allowed: raise ValueError(f"log_level 은 {allowed} 중 하나여야 합니다.") return upper @field_validator("app_env") @classmethod def _validate_app_env(cls, v: str) -> str: allowed = {"development", "production", "test"} lower = v.lower() if lower not in allowed: raise ValueError(f"app_env 는 {allowed} 중 하나여야 합니다.") return lower @lru_cache(maxsize=1) def get_settings() -> Settings: """설정 싱글톤 반환 (최초 호출 시 .env 파일을 읽음).""" return Settings()