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

문제 상황

FastAPI 프로젝트에서 Pydantic 모델의 검증 실패 시 반환되는 기본 에러 포맷이 프론트엔드 팀에서 파싱하기 어렵다는 피드백을 받았다. 특히 중첩된 모델 검증 실패 시 에러 위치를 특정하기 힘들다는 문제가 있었다.

기본 에러 응답

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

해결 방법

RequestValidationError에 대한 커스텀 exception handler를 등록했다.

from fastapi import FastAPI, Request, status
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 = ".".join(str(loc) for loc in error["loc"][1:])
        errors[field] = {
            "message": error["msg"],
            "type": error["type"]
        }
    
    return JSONResponse(
        status_code=status.HTTP_400_BAD_REQUEST,
        content={
            "success": False,
            "errors": errors
        }
    )

개선된 응답

{
  "success": false,
  "errors": {
    "email": {
      "message": "field required",
      "type": "value_error.missing"
    }
  }
}

필드명을 키로 사용해 프론트엔드에서 특정 input에 에러 메시지를 바로 매핑할 수 있게 되었다. 상태 코드도 422 대신 400으로 통일해 다른 API와 일관성을 맞췄다.

다음 스프린트에서는 i18n을 적용해 다국어 에러 메시지도 지원할 예정이다.

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