FastAPI에서 Pydantic 모델 검증 실패 시 커스텀 에러 처리

문제 상황

Express로 작성된 레거시 API를 FastAPI로 전환하는 작업을 진행했다. 기존 프론트엔드는 특정 에러 포맷을 기대하고 있었는데, FastAPI의 기본 Pydantic 검증 에러는 구조가 달랐다.

{
  "detail": [
    {
      "loc": ["body", "email"],
      "msg": "field required",
      "type": "value_error.missing"
    }
  ]
}

프론트는 {"errors": {"email": "필수 항목입니다"}} 형태를 원했다.

해결 방법

FastAPI의 exception handler를 커스터마이징했다.

from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse

app = FastAPI()

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    errors = {}
    for error in exc.errors():
        field = error['loc'][-1]
        msg = error['msg']
        errors[field] = translate_error(msg, error['type'])
    
    return JSONResponse(
        status_code=422,
        content={"errors": errors}
    )

def translate_error(msg: str, error_type: str) -> str:
    error_map = {
        "value_error.missing": "필수 항목입니다",
        "value_error.email": "올바른 이메일 형식이 아닙니다",
    }
    return error_map.get(error_type, msg)

이제 검증 실패 시 프론트가 기대하는 형식으로 응답이 나간다. 기존 코드 수정 없이 API 교체가 가능했다.

추가 고려사항

중첩된 모델의 경우 error['loc']['body', 'user', 'email'] 같은 형태로 오는데, 필요에 따라 점 표기법(user.email)으로 변환하거나 재귀 구조로 만들 수 있다.

Pydantic V2 마이그레이션 계획이 있다면 에러 구조가 변경될 수 있으니 주의가 필요하다.

FastAPI에서 Pydantic 모델 검증 실패 시 커스텀 에러 처리