MethodArgumentNotValidException 발생 조건부터 핸들링까지

2026. 5. 30. 20:16·Spring

방탈출 사용자 예약 미션에서 요청 DTO 검증과 전역 예외 처리를 구현하며 MethodArgumentNotValidException을 @ExceptionHandler로 처리한 경험이 있다. 당시에는 이 예외가 왜 발생하는지, 내부적으로 어떻게 동작하는지 제대로 이해하지 못한 채 구현에 임했다. 미션이 끝난 지금, 동작 원리를 제대로 정리해두고자 한다.


MethodArgumentNotValidException이란 무엇인가?

BindException to be thrown when validation on an argument annotated with @Valid fails. Spring Docs - MethodArgumentNotValidException

 

@Valid가 붙은 파라미터의 Bean Validation 검증이 실패했을 때 Spring이 던지는 예외다. BindException을 상속하기 때문에, 검증에 실패한 모든 오류 정보를 BindingResult를 통해 꺼낼 수 있다.

BindingResult에는 두 종류의 에러가 담긴다.

  • FieldError: 특정 필드에서 발생한 검증 실패 (예: name이 비어있음)
  • ObjectError: 객체 전체 수준의 검증 실패 (Cross-field validation)

즉, MethodArgumentNotValidException은 @Valid 검증 실패 시 발생하며, 어떤 필드가 왜 실패했는지 상세 정보를 담고 있는 예외다.


MethodArgumentNotValidException 예외는 언제 발생할까?

MethodArgumentNotValidException은 @RequestBody 파라미터에 @Valid가 선언된 상태에서 Bean Validation 검증이 실패했을 때 발생한다.

내부적으로는 RequestResponseBodyMethodProcessor의 resolveArgument()에서 다음 순서로 동작한다.

Object arg = readWithMessageConverters(...)             // 1. JSON → Java 객체 변환
WebDataBinder binder = binderFactory.createBinder(...) // 2. DataBinder 생성
validateIfApplicable(binder, parameter)                // 3. Validator에게 검증 위임

if (binder.getBindingResult().hasErrors()
        && isBindExceptionRequired(binder, parameter)) {
    throw new MethodArgumentNotValidException(...)     // 4. 에러 있으면 예외 던짐
}
  1. HttpMessageConverter를 통해 JSON을 Java 객체로 변환
  2. DataBinder를 생성하고 validateIfApplicable()로 Validator에게 검증 위임
  3. 검증 완료 후 아래 두 조건을 동시에 확인
    • binder.getBindingResult().hasErrors() → 검증 실패 에러가 존재하는가
    • isBindExceptionRequired() → 컨트롤러 메서드 파라미터에 BindingResult가 없는가
  4. 두 조건이 모두 참이면 MethodArgumentNotValidException을 던짐
    • 둘 중 하나라도 거짓이면 예외를 던지지 않음

주목할 조건은 isBindExceptionRequired()다. 컨트롤러 메서드 파라미터 목록에 Errors 또는 BindingResult가 함께 선언되어 있으면 false를 반환한다. 이 경우 예외를 던지지 않고, 검증 실패 정보를 메서드가 직접 받아 처리할 수 있도록 넘긴다.

 

즉, MethodArgumentNotValidException은 검증 실패 결과를 메서드가 직접 받지 않는 경우에만 발생하며, 예외를 던지는 주체는 Spring MVC의 RequestResponseBodyMethodProcessor다.


MethodArgumentNotValidException 발생했을 때 어떻게 적절한 에러 응답을 반환하는가?

MethodArgumentNotValidException은 두 가지 방식으로 처리할 수 있다.

 

1. 전역 처리 — @RestControllerAdvice

애플리케이션 전체에서 발생하는 MethodArgumentNotValidException을 한 곳에서 처리한다.

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handle(MethodArgumentNotValidException e,
                                                HttpServletRequest request) {
        List<FieldErrorResponse> fieldErrors = e.getBindingResult().getFieldErrors().stream()
                .map(fe -> new FieldErrorResponse(fe.getField(), fe.getDefaultMessage()))
                .toList();
        ErrorResponse errorResponse = new ErrorResponse(
                "INVALID_CONSTRAINT", request.getRequestURI(), "요청 값이 유효하지 않습니다.", fieldErrors
        );
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
    }
}

 

getFieldErrors()로 꺼낸 FieldError에서 getField()(실패한 필드명)와 getDefaultMessage()(제약 조건에 선언한 메시지)를 사용해 에러 응답을 구성한다.

위 핸들러가 반환하는 에러 응답은 다음과 같다.

{
  "code": "INVALID_CONSTRAINT",
  "path": "/reservations",
  "message": "요청 값이 유효하지 않습니다.",
  "fieldErrors": [
    {
      "field": "name",
      "message": "이름은 필수입니다."
    },
    {
      "field": "date",
      "message": "날짜는 필수입니다."
    }
  ]
}

 

2. 로컬 처리 — 컨트롤러 내 @ExceptionHandler

특정 컨트롤러에서만 다르게 처리하고 싶을 때 사용한다.

@RestController
public class ReservationController {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handle(MethodArgumentNotValidException e) {
        // 이 컨트롤러에서만 적용되는 처리
    }
}

즉, MethodArgumentNotValidException 처리의 핵심은 e.getBindingResult().getFieldErrors()로 실패한 필드 목록을 꺼내 클라이언트에게 어떤 필드가 왜 잘못됐는지 구체적으로 전달하는 것이라고 생각한다.


ResponseEntityExceptionHandler를 통해서 핸들링하는 방식

ResponseEntityExceptionHandler란?

A class with an @ExceptionHandler method that handles all Spring MVC raised exceptions by returning a ResponseEntity with RFC 9457 formatted error details in the body.

 

Spring MVC가 기본으로 제공하는 추상 클래스로, MethodArgumentNotValidException을 포함한 스프링 내부 표준 예외들을 처리하는 @ExceptionHandler 메서드들이 미리 구현되어 있다.

왜 쓰는가? 직접  @ExceptionHandler  작성 방식과의 차이

직접 작성 방식은 @ExceptionHandler(MethodArgumentNotValidException.class)를 선언하고 처리 로직을 처음부터 구현해야 한다. 반면 ResponseEntityExceptionHandler를 상속하면, 이미 등록된 핸들러 메서드를 오버라이드하는 방식으로 응답 형식만 바꿀 수 있다.

또한 MethodArgumentNotValidException 하나만이 아니라 Spring이 내부적으로 발생시키는 수십 개의 표준 예외들을 일관된 구조로 처리할 수 있다는 장점이 있다.

 

어떻게 쓰는가 (상속 + 오버라이드)

@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(
            MethodArgumentNotValidException ex,
            HttpHeaders headers,
            HttpStatusCode status,
            WebRequest request) {

        List<FieldErrorResponse> fieldErrors = ex.getBindingResult().getFieldErrors().stream()
                .map(fe -> new FieldErrorResponse(fe.getField(), fe.getDefaultMessage()))
                .toList();

        ErrorResponse errorResponse = new ErrorResponse(
                "INVALID_CONSTRAINT", "요청 값이 유효하지 않습니다.", fieldErrors
        );

        return ResponseEntity.status(status).body(errorResponse);
    }
}

 

직접 작성 방식과 비교했을 때 두 가지 차이가 눈에 띈다.

  • HttpStatus.BAD_REQUEST를 하드코딩하지 않고, 파라미터로 넘어온 status를 그대로 사용한다.
  • HttpServletRequest 대신 WebRequest를 사용한다.

즉, ResponseEntityExceptionHandler는 응답 형식만 커스터마이징하고 예외 등록은 Spring에 위임하는 방식으로, 표준 예외를 일관되게 처리할 때 유리하다.


자료 조사하면서 더 알아볼 키워드

  • BindingResult
  • FieldError
  • HttpMessageConverter
  • MappingJackson2HttpMessageConverter

'Spring' 카테고리의 다른 글

@Valid vs @Validate 차이는 무엇인가?  (0) 2026.05.24
Bean Validation으로 요청 DTO 검증하기  (0) 2026.05.23
'Spring' 카테고리의 다른 글
  • @Valid vs @Validate 차이는 무엇인가?
  • Bean Validation으로 요청 DTO 검증하기
토리8
토리8
공부한 내용을 나만의 언어로 정리하는 블로그
  • 토리8
    토리의 기록실
    토리8
  • 전체
    오늘
    어제
    • 분류 전체보기 (4) N
      • Spring (3)
      • Java (0)
      • 우아한테크코스 8기 (1) N
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    MethodArgumentNotValidException
    ResponseEntityExceptionHandler
    소프트스킬
    @Valid
    @Validated
    회복탄력성
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
토리8
MethodArgumentNotValidException 발생 조건부터 핸들링까지
상단으로

티스토리툴바