@RestControllerAdvice 유효성 검사 예외처리

minjun kim·2025년 1월 4일

예외처리를 하는데

exceptionAdviceHandle에서 공통적으로 처리하는 부분중에

2개의 ExceptionHandle에 대해 언제 쓰이는가를 정리보려고한다.


@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<ErrorMessage> handleConstrainViolatedException(
            ConstraintViolationException ex
    ) {
        // 예외에 대한 처리
        // 유의미한 응답 바디를 주기위함
        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
        List<String> errors = constraintViolations.stream()
                .map(
                        constraintViolation ->
                                constraintViolation.getPropertyPath() + ", " +
                                        constraintViolation.getMessage()
                )
                .toList();

        ErrorMessage errorMessage = new ErrorMessage(errors);
        return new ResponseEntity(errorMessage, HttpStatus.BAD_REQUEST);


    }

    // 컨틀롤러에서 유효성 검사에 실패한경우
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorMessage> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
        List<String> errors = fieldErrors.stream()
                .map(
                        fieldError ->
                            fieldError.getField() + ", " + fieldError.getDefaultMessage()
                )
                .toList();


        ErrorMessage errorMessage = new ErrorMessage(errors);
        return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST);
    }


}

1. ConstraintViolationException

@ExceptionHandler(ConstraintViolationException.class)

  • 주로 @Validated와 함께 사용되는 제약조건 검증에서 발생
  • 메서드 파라미터나 경로 변수 (@PathVariable)등의 유효성 검증 시 발생
    예시 :
@Validated
@RestController
public class UserController {
    public void createUser(@PathVariable @Min(1) Long id) { ... }
}

2. MethodArgumentNotValidException

@ExceptionHandler(MethodArgumentNotValidException.class)

주로 @Valid를 사용한 요청 본문(Request Body) 검증에서 발생
@ReqeustBody로 받는 객체의 유효성 검증 시 발생

@RestController
public class UserController {
    public void createUser(@RequestBody @Valid UserDto userDto) { ... }
}

실제 사용 예시 :

// ConstraintViolationException이 발생하는 경우
@GetMapping("/users/{id}")
public User getUser(@PathVariable @Min(1) Long id) {
    return userService.getUser(id);
}

// MethodArgumentNotValidException이 발생하는 경우
@PostMapping("/users")
public User createUser(@RequestBody @Valid UserDto userDto) {
    return userService.createUser(userDto);
}

추출 방식도 다르다.

// ConstraintViolationException
Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();

// MethodArgumentNotValidException
List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();

공통점

  • 둘다 필드명과 에러 메시지 조합
  • ErrorMessage 객체에 담아서 반환
  • BAD_Reqeust 상태코드 반환

내친김에..

@Valid와 @Valided 차이

@Valid

  • Java 표준 스펙(JSR-303)
  • 특정 객체의 필드를 검증할 때 사용
  • 중첩된 객체의 유효성 검사 가능
@PostMapping("/users")
public User createUser(@Valid @RequestBody UserDto userDto) {
    // userDto 내부 필드들의 유효성 검사
}

public class UserDto {
    @NotBlank
    private String name;
    
    @Valid  // 중첩된 객체도 검증 가능
    private AddressDto address;
}

@Validated

  • Spring 프레임워크의 기능
  • 그룹화된 유효성 검사 가능
  • 메서드 레벨 유효성 검사 가능
  • 클래스 레벨에 적용 가능
@Validated  // 클래스 레벨에 적용
@RestController
public class UserController {
    // 메서드 파라미터 검증 가능
    @GetMapping("/users/{id}")
    public User getUser(@PathVariable @Min(1) Long id) {
        return userService.getUser(id);
    }

    // 그룹 지정 가능
    @PostMapping("/users")
    public User createUser(@Validated(CreateGroup.class) @RequestBody UserDto userDto) {
        return userService.createUser(userDto);
    }
}

그룹검증예시

public interface BasicGroup {}
public interface AdvancedGroup {}

public class UserDto {
    @NotBlank(groups = {BasicGroup.class, AdvancedGroup.class})
    private String name;
    
    @NotBlank(groups = AdvancedGroup.class)
    private String address;
}

// 기본 정보만 검증
@PostMapping("/users/basic")
public void createBasicUser(@Validated(BasicGroup.class) @RequestBody UserDto user) {}

// 모든 정보 검증
@PostMapping("/users/advanced")
public void createAdvancedUser(@Validated(AdvancedGroup.class) @RequestBody UserDto user) {}

주요 차이점 정리:

@Valid는 객체의 필드 검증에 중점
@Validated는 더 넓은 범위의 기능 제공:

  • 그룹 검증
  • 메서드 파라미터 검증
  • 클래스 레벨 적용 가능

보통의 경우:

@RequestBody로 받는 DTO 검증 → @Valid 사용
메서드 파라미터나 그룹 검증이 필요한 경우 → @Validated 사용

profile
배움의 흔적을 남기고 싶습니다.

0개의 댓글