Skip to content

Validation

Bean Validation은 특정한 구현체가 아니라 Bean Validation 2.0(JSR-380) 기술 표준

검증 로직을 모든 프로젝트에 적용할 수 있게 공통화 및 표준화 한 것으로, 이를 잘 활용하면, 애노테이션 하나(@Valid / @Validated)로 검증 로직을 매우 편리하게 적용할 수 있다.

public class Example {
@NotNull
@Min(0)
Integer example;
}
public class Example {
@PostMapping("/example")
public String example(@Valid @ModelAttribute Example example, BindingResult bindingResult) {
// ...
return "example";
}
}

JSR-303(자바) 표준 스펙으로, 빈 검증기(Bean Validator)를 이용해 제약 조건을 검증하며, 요청 데이터 소스에 따라 검증 흐름이 달라진다.

  1. 프론트 컨트롤러가 컨트롤러로 전달
  2. ArgumentResolver가 요청 파라미터를 바인딩하여 객체 생성
  3. 파라미터에 @Valid가 있으면 Bean Validator로 검증 수행
  4. 검증 실패 시 BindingResult가 있으면 오류를 담고 계속 진행(없으면 MethodArgumentNotValidException 예외 발생)

요청 본문 바인딩(@RequestBody)와 메시지 컨버터

Section titled “요청 본문 바인딩(@RequestBody)와 메시지 컨버터”
  1. HttpMessageConverter가 요청 본문(JSON 등)을 읽어 객체로 역직렬화
  2. 파라미터에 @Valid가 있으면 역직렬화된 객체에 대해 검증 수행
  3. 검증 실패 시 MethodArgumentNotValidException 예외 발생
구분바인딩 소스검증 시점예외 처리
@ModelAttribute쿼리 파라미터, 폼 데이터데이터 바인딩 직후BindingResult 존재 시 오류 저장(없으면 MethodArgumentNotValidException)
@RequestBodyJSON, XML 등 본문메시지 컨버터 역직렬화 직후MethodArgumentNotValidException

Spring 프레임워크에서 제공하는 애노테이션으로, 컨트롤러가 아닌 곳에서도 검증을 수행할 수 있다.

@Service
@Validated
public class ExampleService {
public void example(@Valid Example example) {
// ...
}
}

사용하기 위해선 두 가지 선언이 필요하다.

  • 컨트롤러가 아닌 곳에서 검증을 수행하려면 @Validated 클래스 레벨에 선언
  • 검증을 수행하고자 하는 메서드에 파라미터에 @Valid 선언

제약에 그룹을 부여해 상황별 규칙을 분리할 수 있다. 컨트롤러 · 서비스에서 @Validated(Create.class)처럼 그룹을 명시해 적용한다.

public interface Create {
}
public interface Update {
}
public class Article {
@NotNull(groups = Update.class)
private Long id;
@NotBlank(groups = {Create.class, Update.class})
private String title;
}
@RestController
@RequestMapping("/articles")
public class ArticleController {
@PostMapping
public ResponseEntity<?> create(@Validated(Create.class) @RequestBody Article article) {
// ...
}
@PutMapping("/{id}")
public ResponseEntity<?> update(@PathVariable Long id, @Validated(Update.class) @RequestBody Article article) {
// ...
}
}

동작 원리(클래스 레벨에 선언한 경우)

Section titled “동작 원리(클래스 레벨에 선언한 경우)”

Validated는 AOP 기반으로 메서드 요청을 인터셉터하여 처리하게 된다.

  1. 해당 클래스에 유효성 검증을 위한 인터셉터(MethodValidationInterceptor) 등록
  2. 해당 클래스의 메서드들이 호출될 때 요청을 가로채서 유효성 검증 수행
  3. 검증에 실패하면 ConstraintViolationException 예외 발생

컨트롤러 단에서는 BindingResult로 처리하거나, 전역 예외 처리에서 일관된 응답 포맷을 구성한다.

@RestControllerAdvice
public class ApiExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?> handleInvalid(MethodArgumentNotValidException e) {
List<Map<String, String>> errors = e.getBindingResult().getFieldErrors().stream()
.map(fe -> Map.of("field", fe.getField(), "message", fe.getDefaultMessage()))
.toList();
return ResponseEntity.badRequest().body(Map.of("errors", errors));
}
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<?> handleViolation(ConstraintViolationException e) {
List<Map<String, String>> errors = e.getConstraintViolations().stream()
.map(v -> Map.of("property", v.getPropertyPath().toString(), "message", v.getMessage()))
.toList();
return ResponseEntity.badRequest().body(Map.of("errors", errors));
}
}
@Valid@Validated
JSR-303 표준 스펙Spring 프레임워크에서 제공
ArgumentResolver에서 검증을 수행AOP 기반으로 검증을 수행
컨트롤러에서만 동작컨트롤러가 아닌 모든 곳에서 동작
MethodArgumentNotValidException 예외 발생ConstraintViolationException 예외 발생

Last updated:

Spring