Bean Validation
특정 필드 검증의 경우 빈값, 길이, 크기, 형식 과 같은 간단한 로직이다. 이러한 로직들을 모든 프로젝트에 적용할 수 있도록 표준화 한 것이 Bean Validation이다.
@PostMapping("/v3/member")
public String createMemberV3(
// 1. @ModelAttribute 뒤에 2. BindingResult가 위치한다.
@ModelAttribute MemberCreateRequestDto request,
BindingResult bindingResult,
Model model
) {
System.out.println("/V3/member API가 호출되었습니다.");
// 3. Validation
if (request.getAge() == null || request.getAge() < 0) {
// BindingResult FieldError 추가
bindingResult.addError(
new FieldError("request", "age", "age 필드는 필수이며 0 이상의 값이어야 합니다.")
);
}
// error 처리
if (bindingResult.hasErrors()) {
System.out.println("Error를 처리하는 로직");
// error 페이지 반환
return "error";
}
// Model에 저장
model.addAttribute("point", request.getPoint());
model.addAttribute("name", request.getName());
model.addAttribute("age", request.getAge());
return "complete";
}

BingdingResult 예시처럼 코드로 구현하는것은 번거로운 일이다.Bean Validation 적용 예시
@Getter
public class SignUpRequestDto {
@NotBlank
private String name;
@NotNull
@Range(min = 1, max = 120)
private Integer age;
}
@Controller
public class BeanValidationController {
@PostMapping("/model-attribute")
public String beanValidationV1(
@Validated @ModelAttribute SignUpRequestDto dto
) {
// 로직
// ViewName
return "complete";
}
}
@RestController
public class BeanValidationRestController {
@PostMapping("/request-body")
public String beanValidationV2(
@Validated @RequestBody SignUpRequestDto dto
) {
// 로직
// 문자 Data 반환
return "회원가입 완료";
}
}
@RequestBody에도 사용할 수 있다.Field Error
특정 필드 검증의 경우 빈값, 길이, 크기, 형식 과 같은 간단한 로직이다. 이러한 로직들을 모든 프로젝트에 적용할 수 있도록 표준화 한 것이 Bean Validation이다.
의존성 추가(build.gradle)
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 뒤에 version이 따로 적혀있지 않은 이유 → Spring Boot 기능
External Libraries

jakarta.validation-api-${version}.jarjakarta.validation.constraints
org.hibernate.validator:hibernate-validator:${version}
jakarta.validation-api : Interfaceorg.hibernate.validator:hibernate-validator : 구현체
@Range Annotation은 Hibernate Validator 에서만 동작하는것
@NotBlank, @NotNull 은 validation 표준 인터페이스
사용된 Annotation 정리
@Data
public class TestDto {
// 테스트 하고싶은 Annotation으로 변경 가능
@NotBlank
private String stringField;
@NotNull
@Range(min = 1, max = 9999)
private Integer integerField;
}
import static org.assertj.core.api.Assertions.assertThat;
public class BeanValidationTest {
@Test
void beanValidation() {
// Spring과 통합하면 아래 두줄의 코드는 사용하지 않는다.
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
// Test 하고싶은 상황을 만들어서 검증 가능
TestDto dto = new TestDto();
dto.setStringField(" ");
dto.setIntegerField(1);
// DTO를 검증
Set<ConstraintViolation<TestDto>> violations = validator.validate(dto);
// 검증 결과가 예상대로 발생했는지 확인
// 검증에 걸린 필드가 있어야 함
assertThat(violations).isNotEmpty();
// 2개의 제약 위반 발생
assertThat(violations.size()).isEqualTo(2);
// Validation에 걸린 내역을 출력
for(ConstraintViolation<TestDto> violation : violations) {
// 아래의 결과에 Message가 있으면 Validation에 걸린것.
// Default Message가 있기 때문에 출력됨
// Message를 수정하고싶다면 Annotation 속성값(message="입력")으로 설정할 수 있다.
// 결과가 비어있으면 Validation에 걸리지 않은것.
System.out.println("violation = " + violation.getMessage());
}
}
}
출력결과

import가 org.hibernate.validator로 시작하면 하이버네이트를 사용할 때만 제공되는 검증 기능으로 다른 구현체로 validator를 교체하였을 경우 동작하지 않는다. 하지만 org.hibernate.validator를 대부분 사용한다.
Validator
단순히 Annotation을 선언해주면 검증이 완료되는 이유는 Validator(Validation을 사용하는것)가 존재하기 때문이다. Spring Boot는 validation 라이브러리를 설정하면 'org.springframework.boot:spring-boot-starter-validation'자동으로 Bean Validator를 Spring에 통합되도록 설정해준다.
LocalValidatorFactoryBean 을 Global Validator로 등록한다.
주의사항 : Global Validator를 수동으로 등록하면LocalValidatorFactoryBean 를 등록하지 않는다.
@Valid, @Validated 만 적용하면 된다.Bean Validation Annotation이 있으면 검증을 수행한다.
ex) @NotNull, @NotBlank, @Max 등등..
Validation Error가 발생하면 FieldError, ObjectError를 생성하여 BindingResult에 담아준다.
@Valid, @Validated 차이점
@Valid 는 JAVA 표준이고 @Validated 는 Spring 에서 제공하는 Annotation이다.@Validated 를 통해 Group Validation 혹은 Controller 이외 계층에서 Validation이 가능하다.@Valid 는 MethodArgumentNotValidException 예외를 발생시킨다.@Validated 는 ConstraintViolationException 예외를 발생시킨다.Validator 적용
@ModelAttribute 각각의 필드 타입에 맞추어 바인딩(변환) 시도TypeMismatch FieldError 발생@ModelAttribute → 각 필드 바인딩 → 성공한 필드만 Bean Validation 적용bindingResult에 TypeMismatch FieldError 추가 → 바인딩에 실패한 필드는 값이 없음(null) → Bean Validation 적용하지 않음Bean Validator는 바인딩에 실패한 필드는 Bean Validation을 적용하지 않는다. 바인딩(변환)에 성공한 필드만이 Bean Validation을 적용하는 의미가 있다.