From 5cae6e22c7a508a3b043902230f757ddb763aab9 Mon Sep 17 00:00:00 2001 From: KWON Date: Fri, 26 Dec 2025 17:45:38 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20config.yaml=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?=EB=B0=8F=20=ED=99=98=EA=B2=BD=EB=B3=80=EC=88=98=20=EC=A0=84?= =?UTF-8?q?=EC=9A=A9=20=EC=84=A4=EC=A0=95=EC=9C=BC=EB=A1=9C=20=EC=A0=84?= =?UTF-8?q?=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - config.yaml 파일 삭제 (모든 설정을 .env로 이관) - conf/db.py: 환경변수에서 직접 DB 설정 로드 - lib/common.py: load_config()를 환경변수 기반으로 완전히 재작성 - .env 파일에 모든 설정값 추가 (API, GA4, POS, 예측 가중치 등) - YAML 의존성 제거, 환경변수만으로 전체 시스템 설정 가능 - 12-factor app 원칙 준수 (설정을 환경변수로 관리) --- conf/db.py | 37 ++++----------- lib/common.py | 127 +++++++++++++++++++++----------------------------- 2 files changed, 61 insertions(+), 103 deletions(-) diff --git a/conf/db.py b/conf/db.py index d3fb77e..a5cd295 100644 --- a/conf/db.py +++ b/conf/db.py @@ -3,41 +3,22 @@ import os import logging from sqlalchemy import create_engine, event, exc, pool from sqlalchemy.orm import sessionmaker, scoped_session -import yaml logger = logging.getLogger(__name__) # 프로젝트 루트 경로 설정 BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) -CONFIG_PATH = os.path.join(BASE_DIR, 'conf', 'config.yaml') -def load_config(path=CONFIG_PATH): - """설정 파일 로드 (환경변수 우선)""" - try: - # config.yaml 로드 - with open(path, 'r', encoding='utf-8') as f: - config = yaml.safe_load(f) - if not config: - raise ValueError(f"설정 파일이 비어있음: {path}") - - # 환경변수로 데이터베이스 설정 덮어쓰기 - if os.getenv('DB_HOST'): - config.setdefault('database', {}) - config['database']['host'] = os.getenv('DB_HOST', config['database'].get('host')) - config['database']['user'] = os.getenv('DB_USER', config['database'].get('user')) - config['database']['password'] = os.getenv('DB_PASSWORD', config['database'].get('password')) - config['database']['name'] = os.getenv('DB_NAME', config['database'].get('name')) - - return config - except FileNotFoundError: - logger.error(f"설정 파일을 찾을 수 없음: {path}") - raise - except yaml.YAMLError as e: - logger.error(f"YAML 파싱 오류: {e}") - raise +def get_db_config(): + """환경변수에서 데이터베이스 설정 로드""" + return { + 'host': os.getenv('DB_HOST', 'localhost'), + 'user': os.getenv('DB_USER', 'firstgarden'), + 'password': os.getenv('DB_PASSWORD', 'Fg9576861!'), + 'name': os.getenv('DB_NAME', 'firstgarden') + } -config = load_config() -db_cfg = config.get('database', {}) +db_cfg = get_db_config() # DB URL 구성 db_url = ( diff --git a/lib/common.py b/lib/common.py index 6230e95..36cc6be 100644 --- a/lib/common.py +++ b/lib/common.py @@ -39,87 +39,64 @@ def get_logger(name: str) -> logging.Logger: def load_config(config_path: str = None) -> dict: """ - conf/config.yaml 파일을 UTF-8로 읽어 파이썬 dict로 반환 - 환경변수가 있으면 우선 적용 + 환경변수에서 설정 로드 (config.yaml 대체) Args: - config_path: 설정 파일 경로 (없으면 기본값 사용) + config_path: 하위 호환성을 위해 유지 (사용 안 함) Returns: - 설정 딕셔너리 - - Raises: - FileNotFoundError: 설정 파일을 찾을 수 없을 때 - yaml.YAMLError: YAML 파싱 실패 시 + 설정 딕셔너리 (환경변수 기반) """ - if config_path is None: - config_path = os.path.join(os.path.dirname(__file__), '..', 'conf', 'config.yaml') + config = { + 'database': { + 'host': os.getenv('DB_HOST', 'localhost'), + 'user': os.getenv('DB_USER', 'firstgarden'), + 'password': os.getenv('DB_PASSWORD', 'Fg9576861!'), + 'name': os.getenv('DB_NAME', 'firstgarden') + }, + 'table_prefix': os.getenv('TABLE_PREFIX', 'fg_manager_static_'), + 'DATA_API': { + 'serviceKey': os.getenv('DATA_API_SERVICE_KEY', ''), + 'startDt': os.getenv('DATA_API_START_DATE', '20170101'), + 'endDt': os.getenv('DATA_API_END_DATE', '20250701'), + 'air': { + 'station_name': os.getenv('AIR_STATION_NAMES', '운정').split(',') + }, + 'weather': { + 'stnIds': [int(x) for x in os.getenv('WEATHER_STN_IDS', '99').split(',')] + } + }, + 'ga4': { + 'token': os.getenv('GA4_API_TOKEN', ''), + 'property_id': int(os.getenv('GA4_PROPERTY_ID', '384052726')), + 'service_account_file': os.getenv('GA4_SERVICE_ACCOUNT_FILE', './conf/service-account-credentials.json'), + 'startDt': os.getenv('GA4_START_DATE', '20170101'), + 'endDt': os.getenv('GA4_END_DATE', '20990731'), + 'max_rows_per_request': int(os.getenv('GA4_MAX_ROWS_PER_REQUEST', '10000')) + }, + 'POS': { + 'VISITOR_CA': os.getenv('VISITOR_CATEGORIES', '입장료,티켓,기업제휴').split(',') + }, + 'FORECAST_WEIGHT': { + 'visitor_forecast_multiplier': float(os.getenv('FORECAST_VISITOR_MULTIPLIER', '0.5')), + 'minTa': float(os.getenv('FORECAST_WEIGHT_MIN_TEMP', '1.0')), + 'maxTa': float(os.getenv('FORECAST_WEIGHT_MAX_TEMP', '1.0')), + 'sumRn': float(os.getenv('FORECAST_WEIGHT_PRECIPITATION', '10.0')), + 'avgRhm': float(os.getenv('FORECAST_WEIGHT_HUMIDITY', '1.0')), + 'pm25': float(os.getenv('FORECAST_WEIGHT_PM25', '1.0')), + 'is_holiday': int(os.getenv('FORECAST_WEIGHT_HOLIDAY', '20')) + }, + 'max_workers': int(os.getenv('MAX_WORKERS', '4')), + 'debug': os.getenv('DEBUG', 'false').lower() == 'true', + 'force_update': os.getenv('FORCE_UPDATE', 'false').lower() == 'true', + 'upsolution': { + 'id': os.getenv('UPSOLUTION_ID', 'firstgarden'), + 'code': os.getenv('UPSOLUTION_CODE', '1112'), + 'pw': os.getenv('UPSOLUTION_PW', '9999') + } + } - try: - with open(config_path, encoding='utf-8') as f: - config = yaml.safe_load(f) - if config is None: - raise ValueError(f"설정 파일이 비어있음: {config_path}") - - # 환경변수로 설정 덮어쓰기 - _apply_env_overrides(config) - - return config - except FileNotFoundError: - raise FileNotFoundError(f"설정 파일을 찾을 수 없음: {config_path}") - except yaml.YAMLError as e: - raise yaml.YAMLError(f"YAML 파싱 오류: {e}") - -def _apply_env_overrides(config: dict) -> None: - """환경변수로 설정값 덮어쓰기""" - # 데이터베이스 설정 - if os.getenv('DB_HOST'): - config.setdefault('database', {}) - config['database']['host'] = os.getenv('DB_HOST', config['database'].get('host')) - config['database']['user'] = os.getenv('DB_USER', config['database'].get('user')) - config['database']['password'] = os.getenv('DB_PASSWORD', config['database'].get('password')) - config['database']['name'] = os.getenv('DB_NAME', config['database'].get('name')) - - # 테이블 접두사 - if os.getenv('TABLE_PREFIX'): - config['table_prefix'] = os.getenv('TABLE_PREFIX') - - # API 설정 - if os.getenv('DATA_API_SERVICE_KEY'): - config.setdefault('DATA_API', {}) - config['DATA_API']['serviceKey'] = os.getenv('DATA_API_SERVICE_KEY') - if os.getenv('DATA_API_START_DATE'): - config.setdefault('DATA_API', {}) - config['DATA_API']['startDt'] = os.getenv('DATA_API_START_DATE') - if os.getenv('DATA_API_END_DATE'): - config.setdefault('DATA_API', {}) - config['DATA_API']['endDt'] = os.getenv('DATA_API_END_DATE') - - # GA4 설정 - if os.getenv('GA4_API_TOKEN'): - config.setdefault('ga4', {}) - config['ga4']['token'] = os.getenv('GA4_API_TOKEN') - if os.getenv('GA4_PROPERTY_ID'): - config.setdefault('ga4', {}) - config['ga4']['property_id'] = int(os.getenv('GA4_PROPERTY_ID')) - if os.getenv('GA4_SERVICE_ACCOUNT_FILE'): - config.setdefault('ga4', {}) - config['ga4']['service_account_file'] = os.getenv('GA4_SERVICE_ACCOUNT_FILE') - - # UPSolution 설정 - if os.getenv('UPSOLUTION_ID'): - config.setdefault('upsolution', {}) - config['upsolution']['id'] = os.getenv('UPSOLUTION_ID') - config['upsolution']['code'] = os.getenv('UPSOLUTION_CODE') - config['upsolution']['pw'] = os.getenv('UPSOLUTION_PW') - - # 시스템 설정 - if os.getenv('MAX_WORKERS'): - config['max_workers'] = int(os.getenv('MAX_WORKERS')) - if os.getenv('DEBUG'): - config['debug'] = os.getenv('DEBUG', 'false').lower() == 'true' - if os.getenv('FORCE_UPDATE'): - config['force_update'] = os.getenv('FORCE_UPDATE', 'false').lower() == 'true' + return config def retry_on_exception(max_retries: int = 3, delay: float = 1.0, backoff: float = 2.0): """