Files
tk-factory-services/tkeg/api/app/utils/error_handlers.py
2026-03-16 15:41:58 +09:00

140 lines
4.5 KiB
Python

"""
에러 처리 유틸리티
표준화된 에러 응답 및 예외 처리
"""
from fastapi import HTTPException, Request
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
from sqlalchemy.exc import SQLAlchemyError
from typing import Dict, Any
import traceback
from .logger import get_logger
logger = get_logger(__name__)
class TKMPException(Exception):
"""TK-MP 프로젝트 커스텀 예외"""
def __init__(self, message: str, error_code: str = "TKMP_ERROR", status_code: int = 500):
self.message = message
self.error_code = error_code
self.status_code = status_code
super().__init__(self.message)
class ErrorResponse:
"""표준화된 에러 응답 생성기"""
@staticmethod
def create_error_response(
message: str,
error_code: str = "INTERNAL_ERROR",
status_code: int = 500,
details: Dict[str, Any] = None
) -> Dict[str, Any]:
"""표준화된 에러 응답 생성"""
response = {
"success": False,
"error": {
"code": error_code,
"message": message,
"timestamp": "2025-01-01T00:00:00Z" # 실제로는 datetime.utcnow().isoformat()
}
}
if details:
response["error"]["details"] = details
return response
@staticmethod
def validation_error_response(errors: list) -> Dict[str, Any]:
"""검증 에러 응답"""
return ErrorResponse.create_error_response(
message="입력 데이터 검증에 실패했습니다.",
error_code="VALIDATION_ERROR",
status_code=422,
details={"validation_errors": errors}
)
@staticmethod
def database_error_response(error: str) -> Dict[str, Any]:
"""데이터베이스 에러 응답"""
return ErrorResponse.create_error_response(
message="데이터베이스 작업 중 오류가 발생했습니다.",
error_code="DATABASE_ERROR",
status_code=500,
details={"db_error": error}
)
@staticmethod
def file_error_response(error: str) -> Dict[str, Any]:
"""파일 처리 에러 응답"""
return ErrorResponse.create_error_response(
message="파일 처리 중 오류가 발생했습니다.",
error_code="FILE_ERROR",
status_code=400,
details={"file_error": error}
)
async def tkmp_exception_handler(request: Request, exc: TKMPException):
"""TK-MP 커스텀 예외 핸들러"""
logger.error(f"TK-MP 예외 발생: {exc.message} (코드: {exc.error_code})")
return JSONResponse(
status_code=exc.status_code,
content=ErrorResponse.create_error_response(
message=exc.message,
error_code=exc.error_code,
status_code=exc.status_code
)
)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
"""검증 예외 핸들러"""
logger.warning(f"검증 오류: {exc.errors()}")
return JSONResponse(
status_code=422,
content=ErrorResponse.validation_error_response(exc.errors())
)
async def sqlalchemy_exception_handler(request: Request, exc: SQLAlchemyError):
"""SQLAlchemy 예외 핸들러"""
logger.error(f"데이터베이스 오류: {str(exc)}", exc_info=True)
return JSONResponse(
status_code=500,
content=ErrorResponse.database_error_response(str(exc))
)
async def general_exception_handler(request: Request, exc: Exception):
"""일반 예외 핸들러"""
logger.error(f"예상치 못한 오류: {str(exc)}", exc_info=True)
return JSONResponse(
status_code=500,
content=ErrorResponse.create_error_response(
message="서버 내부 오류가 발생했습니다.",
error_code="INTERNAL_SERVER_ERROR",
status_code=500,
details={"error": str(exc)} if logger.level <= 10 else None # DEBUG 레벨일 때만 상세 에러 표시
)
)
def setup_error_handlers(app):
"""FastAPI 앱에 에러 핸들러 등록"""
app.add_exception_handler(TKMPException, tkmp_exception_handler)
app.add_exception_handler(RequestValidationError, validation_exception_handler)
app.add_exception_handler(SQLAlchemyError, sqlalchemy_exception_handler)
app.add_exception_handler(Exception, general_exception_handler)
logger.info("에러 핸들러 등록 완료")