Spring Framework에서 제공하는 Validator를 사용하여 로직을 분리하는 방법도 있지만, 본 포스팅에서는 더 편리하고 자주 사용되는 방법인 Bean Validation에 대해 다룬다.
Bean Validation 관련 라이브러리 추가가 필요하다.

spring.application.name=ValidationExample
spring.messages.basename=errors
spring.messages.basename에 errors 추가하여 errors.properties 등록@Data
public class User {
@NotNull
@Min(1)
@Max(100)
private final int age;
@NotNull
private final String name;
}
NotNull, NotEmpty, NotBlank의 차이는 다음과 같다.
NotNull: null값을 허용하지 않는다.NotEmpty: null값과 ""(초기화된 String)을 허용하지 않는다.NotBlank: null값과 "", " "를 모두 허용하지 않는다.NotNull(message = "이름은 null일 수 없습니다") 와 같이 디폴드 메세지를 지정 가능하다.@Range 어노테이션도 있는데, 이는 Hibernate에서 제공한다.@RestController
@RequiredArgsConstructor
public class userController {
private final MessageSource messageSource;
@PostMapping("/test")
public ResponseEntity<Object> userEcho(@Validated @RequestBody User user, BindingResult bindingResult) {
// 특정 필드가 아닌 복합 룰 검증
if(user.getName().equals("Dennis Ritchie") && user.getAge() > 70)
{
bindingResult.rejectValue(null, "InvalidAge", new Object[]{user.getName(), 70}, null);
}
// 에러 발생 시 BadRequest
if(bindingResult.hasErrors()) {
String errMsg;
if(bindingResult.getFieldError() != null) { // 필드 에러가 있다면 우선적으로 보여준다.
errMsg = messageSource.getMessage(bindingResult.getFieldError(), LocaleContextHolder.getLocale());
return ResponseEntity.badRequest().body(errMsg);
}
else { // 필드 에러가 없다면 오브젝트 에러를 반환한다.
errMsg = messageSource.getMessage(bindingResult.getGlobalError(), LocaleContextHolder.getLocale());
return ResponseEntity.badRequest().body(errMsg);
}
}
return ResponseEntity.ok(user);
}
}
MessageSource를 Autowired해주기 위해 @RequiredArgsConstructor 사용User에 @Validated어노테이션을 추가하여 검증 수행BindingResult를 추가하여 오류 발생시에도 함수가 정상적으로 동작
BindingResult는 Errors를 상속하는 인터페이스Min={1} 이상이어야함!!!
Min.age=나이는 {1} 이상이어야함!!!
InvalidAge={0}의 나이는 {1}을 넘을 수 없음
@Min(1)의 1의 경우가 arguments의 예시. 자동으로 매핑해준다.객체 오류
객체 오류의 경우 다음 순서로 2가지 생성 1.: code + "." + object name 2.: code 예) 오류 코드: required, object name: item 1.: required.item 2.: required필드 오류
필드 오류의 경우 다음 순서로4가지 메시지 코드 생성 1.: code + "." + object name + "." + field 2.: code + "." + field 3.: code + "." + field type 4.: code 예) 오류 코드: typeMismatch, object name "user", field "age", field type: int 1. "typeMismatch.user.age" 2. "typeMismatch.age" 3. "typeMismatch.int" 4. "typeMismatch"
검증 순서
위 얘시 코드는
@ModelAttribute가 아닌@RequestBody를 사용하고 있기 때문에 typeMismatch 검출을 위해서는@ExceptionHandler(TypeMismatchException.class)를 정의해줘야 한다.@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(TypeMismatchException.class) public ResponseEntity<Object> handleTypeMismatch(TypeMismatchException ex) { String errorMsg = "Invalid input type for request body"; return ResponseEntity.badRequest().body(errorMsg); } }
@ControllerAdvice는 @Component 어노테이션의 특수한 케이스로, 스프링 부트 애플리케이션에서 전역적으로 예외를 핸들링할 수 있게 해주는 어노테이션이다.ExceptionHandler는 해당 컨트롤러에서만 동작하나, @ControllerAdvice에 정의하면 전역으로 사용 가능.스프링은 MessageCodesResolver로 오류 매세지의 우선순위를 정하는 기능을 지원한다.
승인

거절
