예외처리를 하는데
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);
}
}
@ExceptionHandler(ConstraintViolationException.class)
@Validated
@RestController
public class UserController {
public void createUser(@PathVariable @Min(1) Long id) { ... }
}
@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();
공통점
내친김에..
@PostMapping("/users")
public User createUser(@Valid @RequestBody UserDto userDto) {
// userDto 내부 필드들의 유효성 검사
}
public class UserDto {
@NotBlank
private String name;
@Valid // 중첩된 객체도 검증 가능
private AddressDto address;
}
@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 사용