refactor: config.yaml 제거 및 환경변수 전용 설정으로 전환
- config.yaml 파일 삭제 (모든 설정을 .env로 이관) - conf/db.py: 환경변수에서 직접 DB 설정 로드 - lib/common.py: load_config()를 환경변수 기반으로 완전히 재작성 - .env 파일에 모든 설정값 추가 (API, GA4, POS, 예측 가중치 등) - YAML 의존성 제거, 환경변수만으로 전체 시스템 설정 가능 - 12-factor app 원칙 준수 (설정을 환경변수로 관리)
This commit is contained in:
37
conf/db.py
37
conf/db.py
@ -3,41 +3,22 @@ import os
|
|||||||
import logging
|
import logging
|
||||||
from sqlalchemy import create_engine, event, exc, pool
|
from sqlalchemy import create_engine, event, exc, pool
|
||||||
from sqlalchemy.orm import sessionmaker, scoped_session
|
from sqlalchemy.orm import sessionmaker, scoped_session
|
||||||
import yaml
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# 프로젝트 루트 경로 설정
|
# 프로젝트 루트 경로 설정
|
||||||
BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
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):
|
def get_db_config():
|
||||||
"""설정 파일 로드 (환경변수 우선)"""
|
"""환경변수에서 데이터베이스 설정 로드"""
|
||||||
try:
|
return {
|
||||||
# config.yaml 로드
|
'host': os.getenv('DB_HOST', 'localhost'),
|
||||||
with open(path, 'r', encoding='utf-8') as f:
|
'user': os.getenv('DB_USER', 'firstgarden'),
|
||||||
config = yaml.safe_load(f)
|
'password': os.getenv('DB_PASSWORD', 'Fg9576861!'),
|
||||||
if not config:
|
'name': os.getenv('DB_NAME', 'firstgarden')
|
||||||
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
|
|
||||||
|
|
||||||
config = load_config()
|
db_cfg = get_db_config()
|
||||||
db_cfg = config.get('database', {})
|
|
||||||
|
|
||||||
# DB URL 구성
|
# DB URL 구성
|
||||||
db_url = (
|
db_url = (
|
||||||
|
|||||||
127
lib/common.py
127
lib/common.py
@ -39,87 +39,64 @@ def get_logger(name: str) -> logging.Logger:
|
|||||||
|
|
||||||
def load_config(config_path: str = None) -> dict:
|
def load_config(config_path: str = None) -> dict:
|
||||||
"""
|
"""
|
||||||
conf/config.yaml 파일을 UTF-8로 읽어 파이썬 dict로 반환
|
환경변수에서 설정 로드 (config.yaml 대체)
|
||||||
환경변수가 있으면 우선 적용
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config_path: 설정 파일 경로 (없으면 기본값 사용)
|
config_path: 하위 호환성을 위해 유지 (사용 안 함)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
설정 딕셔너리
|
설정 딕셔너리 (환경변수 기반)
|
||||||
|
|
||||||
Raises:
|
|
||||||
FileNotFoundError: 설정 파일을 찾을 수 없을 때
|
|
||||||
yaml.YAMLError: YAML 파싱 실패 시
|
|
||||||
"""
|
"""
|
||||||
if config_path is None:
|
config = {
|
||||||
config_path = os.path.join(os.path.dirname(__file__), '..', 'conf', 'config.yaml')
|
'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:
|
return config
|
||||||
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'
|
|
||||||
|
|
||||||
def retry_on_exception(max_retries: int = 3, delay: float = 1.0, backoff: float = 2.0):
|
def retry_on_exception(max_retries: int = 3, delay: float = 1.0, backoff: float = 2.0):
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user