Spring Validation이란?


Spring Validation은 Java Bean Validation(JSR-380)을 기반으로 입력값 검증을 어노테이션으로 선언적으로 처리하는 프레임워크입니다. 컨트롤러에서 DTO 유효성 검사를 간단하게 수행할 수 있습니다.



언제 사용하나요?



  • 회원가입, 로그인 폼에서 필수값/이메일/비밀번호 형식 검증

  • API 요청 파라미터 유효성 검사

  • 비즈니스 로직 수행 전 데이터 무결성 확보



의존성 추가


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>


주요 어노테이션


public class UserDto {
@NotBlank(message = "이름은 필수입니다")
private String name;

@Email(message = "이메일 형식이 아닙니다")
private String email;

@Size(min = 8, max = 20, message = "비밀번호는 8-20자")
private String password;

@Min(value = 1, message = "나이는 1 이상")
@Max(value = 150, message = "나이는 150 이하")
private int age;

@Pattern(regexp = "^01[016789]-\\d{3,4}-\\d{4}$",
message = "휴대폰 번호 형식이 아닙니다")
private String phone;
}


컨트롤러에서 검증


@PostMapping("/users")
public ResponseEntity<?> createUser(
@Valid @RequestBody UserDto dto,
BindingResult result) {

if (result.hasErrors()) {
List<String> errors = result.getFieldErrors()
.stream()
.map(e -> e.getField() + ": " + e.getDefaultMessage())
.collect(Collectors.toList());
return ResponseEntity.badRequest().body(errors);
}

return ResponseEntity.ok(userService.create(dto));
}


커스텀 Validator 만들기


// 커스텀 어노테이션
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
String message() default "유효하지 않은 전화번호";
Class<?>[] groups() default {};
Class<?>[] payload() default {};
}

// Validator 구현
public class PhoneValidator
implements ConstraintValidator<Phone, String> {
@Override
public boolean isValid(String value,
ConstraintValidatorContext ctx) {
if (value == null) return true;
return value.matches("^01[016789]-\\d{3,4}-\\d{4}$");
}
}


그룹별 검증


// 그룹 인터페이스
public interface OnCreate {}
public interface OnUpdate {}

// DTO에서 그룹 지정
public class UserDto {
@Null(groups = OnCreate.class)
@NotNull(groups = OnUpdate.class)
private Long id;

@NotBlank(groups = {OnCreate.class, OnUpdate.class})
private String name;
}

// 컨트롤러에서 그룹 적용
@PostMapping
public void create(@Validated(OnCreate.class) @RequestBody UserDto dto) {}

@PutMapping
public void update(@Validated(OnUpdate.class) @RequestBody UserDto dto) {}


글로벌 예외 처리


@RestControllerAdvice
public class ValidationExceptionHandler {

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidation(
MethodArgumentNotValidException ex) {

Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(e ->
errors.put(e.getField(), e.getDefaultMessage())
);
return ResponseEntity.badRequest().body(errors);
}
}


자주 쓰는 어노테이션 정리











어노테이션설명
@NotNullnull 불가
@NotEmptynull, "" 불가
@NotBlanknull, "", " " 불가
@Size문자열/컬렉션 크기
@Email이메일 형식
@Past/@Future과거/미래 날짜
@Positive양수만