python/Django

ValidationError – API/Form 검증

Balang 2025. 4. 28. 14:07
728x90

API 개발하면서, Form이나 API Serializer를 통해 입력 값을 검증해야합니다.

 

근데 사용자가 잘못된 데이터를 보내거나, 빠뜨리거나, 이상한 형식으로 보내면 ValidationError가 발생합니다.

rest_framework.exceptions.ValidationError:

 

문제는, 서버에서 ValidationError를 제대로 잡지 않으면 500에러가 발생하고, 

API 클라이언트(앱/프론트앤드)에서는 "서버 오류" 처럼 오해할 수 있습니다.

→ 사용자 탓인데 서버 오류처럼 보이게 되는 셈이죠.

 

 


원인 분석

  • Django Form, ModelForm, DRF Serializer 모두 내장 검증(validation) 기능을 가지고 있습니다.

이 검증 과정에서 문제가 발생하면 ValidationError 예외가 발생합니다.

이 예외를 적절히 처리하지 않으면, Django는 그냥 500 에러를 반환해버립니다.

 

즉, 사용자의 입력 오류가 서버 내부 오류(500) 처럼 보이는 것이죠.

 


해결 방법

  • Form/ Serializer의 is_valid()를 반드시 검사
    • form.is_valid() , serializer.is_valid() 호출 후 예외처리
  • ValidationErrortry-except로 명시적으로 핸들링
    • ValidationError를 캐치해서 클라이언트에 400 Bad Request로 응답
  • 에러 메세지를 깔끔하게 정리해서 보내기
    • 검증 실패 시 어느 필드가, 어떤 이유로 실패 했는지 반환
  • API 레벨에서는 400 (Bad Request)로 처리
    • 절대 서버 500이 아니라 요청 오류로 분리해야 한다.

 

Django Form 기반 View 예시

from django.http import JsonResponse
from django.views import View
from .forms import SignupForm
from django.core.exceptions import ValidationError

class SignupView(View):
    def post(self, request):
        form = SignupForm(request.POST)
        if form.is_valid():
            # 정상 처리
            user = form.save()
            return JsonResponse({'message': '가입 성공'}, status=201)
        else:
            # 입력 오류 처리
            return JsonResponse({'errors': form.errors}, status=400)

 

DRF Serializer 기반 API 예시

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .serializers import SignupSerializer
from rest_framework.exceptions import ValidationError

class SignupAPIView(APIView):
    def post(self, request):
        serializer = SignupSerializer(data=request.data)
        try:
            serializer.is_valid(raise_exception=True)
            serializer.save()
            return Response({'message': '가입 성공'}, status=status.HTTP_201_CREATED)
        except ValidationError as e:
            return Response({'errors': e.detail}, status=status.HTTP_400_BAD_REQUEST)
            
"""
 DRF에서는 raise_exception=True 옵션을 주면 알아서 400 오류로 변환해준다.
e.detail은 필드별 에러를 dict 형태로 꺼낼 수 있다.
"""

 

ValidationError를 커스텀 핸들링

"""
공통 예외 처리로 ValidationError를 관리하고 싶을 때는, 
Django의 middleware 또는 DRF의 exception handler를 이용할 수 있다.
"""

from rest_framework.views import exception_handler

def custom_exception_handler(exc, context):
    response = exception_handler(exc, context)

    if isinstance(exc, ValidationError):
        # ValidationError는 400 응답
        response.data = {
            'success': False,
            'errors': response.data
        }
    return response
    
    
    
# setting.py
REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'myproject.exceptions.custom_exception_handler'
}

 

 

위에서도 말했지만 ValidationError사용자 입력 오류이지, 서버 오류가 아닙니다.

올바른 HTTP 상태코드(400)를 보내야 API UX가 좋아집니다.

정확한 필드별 오류를 알려주면 프론트엔드나 앱 개발자도 훨씬 편해집니다.

보안 측면에서도 통제된 오류 메세지만 노출할 수 있습니다.

 

단, 여기서 주의할 점이 

  • form.errors, serializer.errorsdict 형태입니다.
    • 바로 JSON으로 반환할 수 있습니다.
  • API 응답 포맷을 통일하는게 좋습니다.
    • 예를 들어 항상 {success: False, errors: {error_message}}

 

정리

Django에서 ValidationError를 만났다면, 사용자 입력 오류로 분류하고,
반드시 400 Bad Request로 클라이언트에 명확한 오류 메세지를 반환해야 한다.

728x90
반응형