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";
}
}
@Valid 동작 원리
JSR-303(자바) 표준 스펙으로, 빈 검증기(Bean Validator
)를 이용해 제약 조건을 검증하며, 요청 데이터 소스에 따라 검증 흐름이 달라진다.
ModelAttribute 바인딩(@ModelAttribute)
프론트 컨트롤러가 컨트롤러로 전달
ArgumentResolver가 요청 파라미터를 바인딩하여 객체 생성
파라미터에 @Valid가 있으면 Bean Validator로 검증 수행
검증 실패 시
BindingResult가
있으면 오류를 담고 계속 진행(없으면MethodArgumentNotValidException
예외 발생)
요청 본문 바인딩(@RequestBody)와 메시지 컨버터
HttpMessageConverter가 요청 본문(JSON 등)을 읽어 객체로 역직렬화
파라미터에 @Valid가 있으면 역직렬화된 객체에 대해 검증 수행
검증 실패 시
MethodArgumentNotValidException
예외 발생
검증 대상과 바인딩 소스 요약
@ModelAttribute
쿼리 파라미터, 폼 데이터
데이터 바인딩 직후
BindingResult 존재 시 오류 저장(없으면 MethodArgumentNotValidException
)
@RequestBody
JSON, XML 등 본문
메시지 컨버터 역직렬화 직후
MethodArgumentNotValidException
@Validated
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 기반으로 메서드 요청을 인터셉터하여 처리하게 된다.
해당 클래스에 유효성 검증을 위한 인터셉터(MethodValidationInterceptor) 등록
해당 클래스의 메서드들이 호출될 때 요청을 가로채서 유효성 검증 수행
검증에 실패하면
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 vs @Validated
JSR-303 표준 스펙
Spring 프레임워크에서 제공
ArgumentResolver에서 검증을 수행
AOP 기반으로 검증을 수행
컨트롤러에서만 동작
컨트롤러가 아닌 모든 곳에서 동작
MethodArgumentNotValidException
예외 발생
ConstraintViolationException
예외 발생
참고자료
Last updated
Was this helpful?