콘텐츠로 이동

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)와 메시지 컨버터

섹션 제목: “요청 본문 바인딩(@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) {
// ...
}
}

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

섹션 제목: “동작 원리(클래스 레벨에 선언한 경우)”

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 예외 발생

마지막 업데이트:

Spring