# =================================================================== # core/config.py # FGTools 통합 설정 관리 모듈 # =================================================================== # 환경변수(.env)를 기반으로 모든 설정을 통합 관리합니다. # python-dotenv를 사용하여 .env 파일을 자동으로 로드합니다. # =================================================================== """ FGTools 설정 관리 모듈 환경변수 기반의 통합 설정 관리를 제공합니다. .env 파일에서 설정을 로드하고, 기본값 및 타입 변환을 지원합니다. 사용 예시: from core.config import get_config config = get_config() db_host = config.database['host'] """ import os import sys from typing import Any, Dict, List, Optional from functools import lru_cache # .env 파일 로드 try: from dotenv import load_dotenv load_dotenv() except ImportError: pass class Config: """ 환경변수 기반 설정 관리 클래스 모든 설정은 환경변수에서 로드되며, 기본값을 제공합니다. 민감한 정보는 .env 파일에 저장하고 .gitignore에 추가하세요. """ def __init__(self): """설정 초기화 및 환경변수 로드""" self._load_all_configs() def _get_env(self, key: str, default: str = '', required: bool = False) -> str: """ 환경변수 조회 Args: key: 환경변수 키 default: 기본값 required: 필수 여부 (True면 없을 시 에러) Returns: 환경변수 값 Raises: SystemExit: 필수 환경변수가 없을 경우 """ value = os.getenv(key, default) if required and not value: print(f"[ERROR] 필수 환경변수가 설정되지 않았습니다: {key}") sys.exit(1) return value def _get_bool(self, key: str, default: bool = False) -> bool: """환경변수를 불리언으로 변환""" return self._get_env(key, str(default)).lower() in ('true', '1', 'yes') def _get_int(self, key: str, default: int = 0) -> int: """환경변수를 정수로 변환""" try: return int(self._get_env(key, str(default))) except ValueError: return default def _get_float(self, key: str, default: float = 0.0) -> float: """환경변수를 실수로 변환""" try: return float(self._get_env(key, str(default))) except ValueError: return default def _get_list(self, key: str, default: str = '', separator: str = ',') -> List[str]: """환경변수를 리스트로 변환 (구분자로 분리)""" value = self._get_env(key, default) return [item.strip() for item in value.split(separator) if item.strip()] def _get_int_list(self, key: str, default: str = '') -> List[int]: """환경변수를 정수 리스트로 변환""" str_list = self._get_list(key, default) result = [] for item in str_list: try: result.append(int(item)) except ValueError: continue return result def _load_all_configs(self): """모든 설정 로드""" # 공통 설정 self.debug = self._get_bool('DEBUG', False) self.log_level = self._get_env('LOG_LEVEL', 'INFO') self.max_workers = self._get_int('MAX_WORKERS', 4) # 데이터베이스 설정 self.database = { 'host': self._get_env('DB_HOST', 'localhost'), 'user': self._get_env('DB_USER', 'firstgarden'), 'password': self._get_env('DB_PASSWORD', ''), 'name': self._get_env('DB_NAME', 'firstgarden'), 'charset': self._get_env('DB_CHARSET', 'utf8mb4'), } self.table_prefix = self._get_env('TABLE_PREFIX', 'fg_manager_static_') # 공공데이터포털 API 설정 self.data_api = { 'service_key': self._get_env('DATA_API_SERVICE_KEY', ''), 'start_date': self._get_env('DATA_API_START_DATE', '20170101'), 'end_date': self._get_env('DATA_API_END_DATE', '20250701'), 'air_stations': self._get_list('AIR_STATION_NAMES', '운정'), 'weather_station_ids': self._get_int_list('WEATHER_STN_IDS', '99'), } # GA4 설정 self.ga4 = { 'api_token': self._get_env('GA4_API_TOKEN', ''), 'property_id': self._get_int('GA4_PROPERTY_ID', 0), 'service_account_file': self._get_env('GA4_SERVICE_ACCOUNT_FILE', './conf/service-account-credentials.json'), 'start_date': self._get_env('GA4_START_DATE', '20170101'), 'end_date': self._get_env('GA4_END_DATE', '20990731'), 'max_rows_per_request': self._get_int('GA4_MAX_ROWS_PER_REQUEST', 10000), } # POS 설정 self.pos = { 'visitor_categories': self._get_list('VISITOR_CATEGORIES', '입장료,티켓,기업제휴'), } # UPSolution 설정 self.upsolution = { 'id': self._get_env('UPSOLUTION_ID', ''), 'code': self._get_env('UPSOLUTION_CODE', ''), 'pw': self._get_env('UPSOLUTION_PW', ''), } # 예측 가중치 설정 self.forecast_weight = { 'visitor_multiplier': self._get_float('FORECAST_VISITOR_MULTIPLIER', 0.5), 'min_temp': self._get_float('FORECAST_WEIGHT_MIN_TEMP', 1.0), 'max_temp': self._get_float('FORECAST_WEIGHT_MAX_TEMP', 1.0), 'precipitation': self._get_float('FORECAST_WEIGHT_PRECIPITATION', 10.0), 'humidity': self._get_float('FORECAST_WEIGHT_HUMIDITY', 1.0), 'pm25': self._get_float('FORECAST_WEIGHT_PM25', 1.0), 'holiday': self._get_int('FORECAST_WEIGHT_HOLIDAY', 20), } self.force_update = self._get_bool('FORCE_UPDATE', False) # Weather 서비스 설정 self.weather_service = { 'service_key': self._get_env('SERVICE_KEY', ''), } # FTP 설정 self.ftp = { 'host': self._get_env('FTP_HOST', ''), 'user': self._get_env('FTP_USER', ''), 'password': self._get_env('FTP_PASSWORD', ''), 'upload_dir': self._get_env('FTP_UPLOAD_DIR', ''), } # 게시판 설정 self.board = { 'id': self._get_env('BOARD_ID', ''), 'ca_name': self._get_env('BOARD_CA_NAME', ''), 'content': self._get_env('BOARD_CONTENT', ''), 'mb_id': self._get_env('BOARD_MB_ID', ''), 'nickname': self._get_env('BOARD_NICKNAME', ''), } # Mattermost 설정 self.mattermost = { 'url': self._get_env('MATTERMOST_URL', ''), 'token': self._get_env('MATTERMOST_TOKEN', ''), 'channel_id': self._get_env('MATTERMOST_CHANNEL_ID', ''), 'webhook_url': self._get_env('MATTERMOST_WEBHOOK_URL', ''), } # Telegram 설정 self.telegram = { 'bot_token': self._get_env('TELEGRAM_BOT_TOKEN', ''), 'chat_id': self._get_env('TELEGRAM_CHAT_ID', ''), } # Synology Chat 설정 self.synology = { 'chat_url': self._get_env('SYNOLOGY_CHAT_URL', ''), 'chat_token': self._get_env('SYNOLOGY_CHAT_TOKEN', ''), } # Notion 설정 self.notion = { 'api_secret': self._get_env('NOTION_API_SECRET', ''), } # Flask 설정 self.flask = { 'secret_key': self._get_env('FLASK_SECRET_KEY', 'dev-secret-key'), 'host': self._get_env('FLASK_HOST', '0.0.0.0'), 'port': self._get_int('FLASK_PORT', 5000), } def get(self, key: str, default: Any = None) -> Any: """ 설정값 조회 (딕셔너리 스타일) Args: key: 설정 키 (점 표기법 지원, 예: 'database.host') default: 기본값 Returns: 설정값 """ keys = key.split('.') value = self for k in keys: if hasattr(value, k): value = getattr(value, k) elif isinstance(value, dict) and k in value: value = value[k] else: return default return value # 싱글톤 인스턴스 _config_instance: Optional[Config] = None @lru_cache(maxsize=1) def get_config() -> Config: """ 설정 싱글톤 인스턴스 반환 처음 호출 시 Config 인스턴스를 생성하고 캐시합니다. 이후 호출에서는 캐시된 인스턴스를 반환합니다. Returns: Config 인스턴스 """ return Config() def reload_config() -> Config: """ 설정 다시 로드 (캐시 무효화) 환경변수가 변경된 후 설정을 다시 로드할 때 사용합니다. Returns: 새로운 Config 인스턴스 """ get_config.cache_clear() return get_config()