zioinfo-mail/workspace/guardia-itsm/database.py
DESKTOP-TKLFCPR\ython cfe2901a55 refactor(structure): consolidate all projects under workspace/
- itsm/    -> workspace/guardia-itsm/
- manager/ -> workspace/guardia-manager/
- app/     -> workspace/guardia-messenger/
- manual/  -> workspace/guardia-docs/

workspace/zioinfo-web/ unchanged.
git mv preserves full commit history.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 23:50:56 +09:00

93 lines
3.5 KiB
Python

"""
GUARDiA ITSM — 데이터베이스 엔진 설정.
환경변수 DATABASE_URL 로 DB를 자동 선택한다:
- 미설정(기본): SQLite + aiosqlite (개발/단독 실행)
- postgresql+asyncpg://user:pass@host:5432/db: PostgreSQL (운영)
PostgreSQL 사용 시 asyncpg, alembic 패키지가 필요하다:
pip install asyncpg alembic
마이그레이션:
alembic init migrations
alembic revision --autogenerate -m "initial_schema"
alembic upgrade head
"""
from __future__ import annotations
import logging
import os
from sqlalchemy.ext.asyncio import (
AsyncSession,
async_sessionmaker,
create_async_engine,
)
from sqlalchemy.orm import DeclarativeBase
logger = logging.getLogger(__name__)
# ── 환경변수 기반 DB URL 자동 선택 ───────────────────────────────────────────
DATABASE_URL: str = os.getenv(
"DATABASE_URL",
"sqlite+aiosqlite:///./guardia_itsm.db", # 기본: SQLite (개발)
)
# URL 정규화: postgres:// → postgresql+asyncpg:// (Heroku/Gitpod 호환)
if DATABASE_URL.startswith("postgres://"):
DATABASE_URL = DATABASE_URL.replace("postgres://", "postgresql+asyncpg://", 1)
_is_postgres = DATABASE_URL.startswith("postgresql")
# ── 엔진 생성 ────────────────────────────────────────────────────────────────
_engine_kwargs: dict = {
"echo": os.getenv("DB_ECHO", "false").lower() == "true",
"pool_pre_ping": True,
}
if _is_postgres:
# PostgreSQL: MVCC 기반 연결 풀링
_engine_kwargs["pool_size"] = int(os.getenv("DB_POOL_SIZE", "10"))
_engine_kwargs["max_overflow"] = int(os.getenv("DB_MAX_OVERFLOW", "20"))
logger.info("GUARDiA DB: PostgreSQL 모드 (pool_size=%s)", _engine_kwargs["pool_size"])
else:
# SQLite: 파일 잠금 방식, 오버플로 불가
_engine_kwargs["connect_args"] = {"check_same_thread": False}
logger.info("GUARDiA DB: SQLite 모드")
engine = create_async_engine(DATABASE_URL, **_engine_kwargs)
SessionLocal = async_sessionmaker(
engine,
class_=AsyncSession,
expire_on_commit=False,
)
# ── Base ─────────────────────────────────────────────────────────────────────
class Base(DeclarativeBase):
pass
# ── 의존성 주입 ───────────────────────────────────────────────────────────────
async def get_db():
"""FastAPI Depends용 — DB 세션 제공 후 자동 정리."""
async with SessionLocal() as session:
yield session
# ── 테이블 초기화 ─────────────────────────────────────────────────────────────
async def init_db() -> None:
"""
개발 환경에서 테이블 자동 생성.
운영(PostgreSQL)에서는 Alembic migration을 사용하므로
create_all은 이미 존재하는 테이블을 건드리지 않는다.
"""
from models import Base as ModelBase # noqa: F401 — 모델 등록용 임포트
async with engine.begin() as conn:
await conn.run_sync(ModelBase.metadata.create_all)
logger.info("DB 테이블 초기화 완료")