📋 Bean Validation이란?
- 웹 애플리케이션을 만들 때, 사용자가 입력한 값이 유효한지 검증(Validation)이 필요
- 이 검증을
Annotation기반으로 간단하게 처리할 수 있도록 도와주는 표준 기술
🛠️ Bean Validation 등장 배경
⚠️ 기존 방식의 문제
if (request.getAge() == null || request.getAge() < 0) {
// BindingResult FieldError 추가
bindingResult.addError(
new FieldError("request", "age", "age 필드는 필수이며 0 이상의 값이어야 합니다.")
);
}
Bean Validation📦 Bean Validation 핵심 요소
| 구성 요소 | 설명 |
|---|---|
jakarta.validation-api | Bean Validation의 인터페이스, 즉 표준 |
hibernate-validator | 이를 실제로 구현한 구현체 |
@Validated or @Valid | 컨트롤러에서 유효성 검증을 실행하게 하는 어노테이션 |
| 다양한 Annotation들 | @NotBlank, @NotNull, @Range 등 |
→ Bean Validation(인터페이스) 구현체인 Hibernate Validator 사용
💡 Hibernate Validator 공식 문서
> Hibernate Validator → 모든 기술의 정확한 사용법은 공식 문서에 존재
🔍 Bean Validation 적용 예시
✍ DTO 클래스
@Getter
public class SignUpRequestDto {
@NotBlank
private String name;
@NotNull
@Range(min = 1, max = 120)
private Integer age;
}
💻 Controller
@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 "회원가입 완료";
}
}
→ @ModelAttribute 방식은 HTML form에서
→ @RequestBody 방식은 JSON 형태 요청에서 사용
📏 Field Error
- 특정 필드 검증의 경우, 빈 값, 길이, 크기, 형식과 같은 간단한 로직
- 이러한 로직들을 모든 프로젝트에 적용할 수 있도록 표준화한 것이 Bean Validation
🧩 Bean Validation 적용
implementation 'org.springframework.boot:spring-boot-starter-validation'
Spring Boot에서는 별도 버전 명시 없이 자동 설정됨
외부 라이브러리로 아래 파일이 추가됨:
- jakarta.validation-api
- hibernate-validator
사용된 Annotation 정리
@NotBlank
CharSequence 타입 허용CharSequence(Interface)의 구현체@NotNull
@NotEmpty
CharSequence, Collection, Map, Array 허용🧪 테스트 코드
@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());
}
}
}
> 검증 결과 출력 예시:
violation = 공백일 수 없습니다
→ violation.getMessage()는 어노테이션의 기본 메시지
→ 메시지는 (message="직접 작성")으로 커스터마이징 가능
🏷️ Annotation 총정리
| 어노테이션 | 설명 |
|---|---|
@NotBlank | null ❌, 빈 문자열 ❌, 공백만 ❌ (문자가 반드시 있어야 함) |
@NotNull | null ❌ (빈 문자열은 허용) |
@NotEmpty | null ❌, 빈값 ❌ (컬렉션, 배열 포함) |
@Range | 숫자 범위를 지정 (Hibernate 전용) |
🛡️ Validator
- 단순히 Annotation을 선언해 주면 검증이 완료되는 이유는
Validator(Validation을 사용하는 것)가 존재하기 때문- 자동으로 Bean Validator를 Spring에 통합되도록 설정
🧠 Validator 동작 원리
Spring Boot가 실행되면 LocalValidatorFactoryBean이 글로벌 Validator로 자동 등록됨
@Validated나 @Valid를 붙이기만 하면 됨
필드 바인딩 성공 후, 해당 필드에 Validation Annotation이 붙어 있으면 검증 수행
오류 발생 시 FieldError, ObjectError가 BindingResult에 담김
🔎 @Valid vs @Validated 차이점
| 항목 | @Valid | @Validated |
|---|---|---|
| 소속 | Java 표준 (jakarta.validation) | Spring Framework |
| 그룹 검증 | ❌ 지원 안 함 | ✅ 그룹 지정 가능 |
| 발생 예외 | MethodArgumentNotValidException | ConstraintViolationException |
| 사용 범위 | 주로 Controller | Controller 외 Service 등도 가능 |
⚠️ 바인딩 실패 시 Validation 제외
Integer 필드에 문자열이 들어오면 변환 자체가 실패하여 null 처리🧠 검증은 바인딩(변환)에 성공한 필드에만 의미가 있음