Files
static/conf/db.py
KWON 98d633ead8 fix: Dockerfile chmod 에러 수정 및 환경변수 지원 추가
- Dockerfile: chmod 명령어에 RUN 추가
- .env.example: 모든 설정 항목 및 자세한 주석 추가
- config.yaml: 각 설정 항목에 대한 상세 주석 추가
- config.sample.yaml: 샘플 파일 주석 개선
- conf/db.py: 환경변수 우선 적용 기능 추가
- lib/common.py: load_config에 환경변수 오버라이드 지원
- 환경변수로 모든 설정값 제어 가능 (DB, API, POS 등)
2025-12-26 17:42:20 +09:00

125 lines
3.8 KiB
Python

# db.py
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
config = load_config()
db_cfg = config.get('database', {})
# DB URL 구성
db_url = (
f"mysql+pymysql://{db_cfg.get('user')}:"
f"{db_cfg.get('password')}@{db_cfg.get('host')}/"
f"{db_cfg.get('name')}?charset=utf8mb4"
)
# MySQL 엔진 생성 (재연결 및 연결 풀 설정)
engine = create_engine(
db_url,
poolclass=pool.QueuePool,
pool_pre_ping=True, # 연결 전 핸들 확인
pool_recycle=3600, # 3600초(1시간)마다 재연결
pool_size=10, # 연결 풀 크기
max_overflow=20, # 추가 오버플로우 연결 수
echo=False, # SQL 출력 (디버그용)
connect_args={
'connect_timeout': 10,
'charset': 'utf8mb4'
}
)
# 연결 에러 발생 시 자동 재연결
@event.listens_for(pool.Pool, "connect")
def receive_connect(dbapi_conn, connection_record):
"""DB 연결 성공 로그"""
logger.debug("DB 연결 성공")
@event.listens_for(pool.Pool, "checkout")
def receive_checkout(dbapi_conn, connection_record, connection_proxy):
"""연결 풀에서 체크아웃할 때"""
pass
@event.listens_for(pool.Pool, "checkin")
def receive_checkin(dbapi_conn, connection_record):
"""연결 풀로 반환할 때"""
pass
# 세션 팩토리
Session = sessionmaker(bind=engine)
SessionLocal = scoped_session(Session)
def get_engine():
"""엔진 반환"""
return engine
def get_session():
"""새로운 세션 반환"""
session = Session()
try:
# 연결 테스트
session.execute('SELECT 1')
except exc.DatabaseError as e:
logger.error(f"DB 연결 실패: {e}")
session.close()
raise
return session
def get_scoped_session():
"""스코프 세션 반환 (스레드 안전)"""
return SessionLocal
def close_session():
"""세션 종료"""
SessionLocal.remove()
class DBSession:
"""컨텍스트 매니저를 사용한 세션 관리"""
def __init__(self):
self.session = None
def __enter__(self):
self.session = get_session()
return self.session
def __exit__(self, exc_type, exc_val, exc_tb):
if self.session:
if exc_type:
self.session.rollback()
logger.error(f"트랜잭션 롤백: {exc_type.__name__}: {exc_val}")
else:
self.session.commit()
self.session.close()