[Spring] @Valid와 @Validated의 차이, 사용법, Bean Validaion이란?
Bean Validation이란?
Bean Validation이란 어노테이션을 통해 자바 빈(Java Bean)의 유효성을 검증할 수 있는 기술 표준(JSR380)이다.
어노테이션을 사용하여 유효성 검사 규칙을 정의할 수 있어 사용하기 편리하다.
아래와 같이 자바 빈 클래스의 필드나 메서드에 특정 어노테이션을 적용해 유효성 검사를 수행할 수 있다.
@Email
private String email;
@NotNull
private Long id;
@Email
public String getEmail(){
return email;
}
예를 들어 @Email 어노테이션으로 해당 필드에 이메일 형식의 값이 들어오는지, @NotNull 어노테이션을 통해 Null 값을 검사할 수 있다.
이러한 Bean Validation은 검증 어노테이션과 인터페이스의 모음이기 때문에 구현체가 필요하다.
보통 Hibernate-Validator를 많이 사용한다.
그리고 @Valid와 @Validated 어노테이션 둘 모두 주로 Hibernate Validator 구현체를 사용하는 검증 어노테이션이다.
즉, 둘 모두 Bean Validation 스펙의 기술을 사용하는 어노테이션이다.
Bean Validation을 사용하려면 아래와 같이 dependency를 추가해줘야 한다.
gradle
implementation 'org.springframework.boot:spring-boot-starter-validation'
위와 같이 스프링 부트를 통해 dependency를 추가하면 자동으로 Bean Validation을 사용하기 위한 라이브러리들이 모두 추가된다.
스프링 부트는 ValidationAutoConfiguration 클래스를 통해 LocalValidatorFactoryBean, MethodValidationPostProcessor를 자동으로 빈으로 등록한다.
이때 스프링 부트는 LocalValidatorFactoryBean을 글로벌 Validator로 등록한다. 이 LocalValidatorFactoryBean을 Validator로 사용해 여러 제약 조건을 검증하는 것이다.
MethodValidationPostProcessor는 메서드 요청을 가로채 유효성 검사를 진행하는데, 이는 @Validated에서만 사용된다.
자세한 내용은 아래에서 설명하겠다.
참고) 제약 조건 어노테이션
JSR 표준 스펙에서 제공하는 제약 조건 어노테이션에는 아래와 같은 것들이 있다.
- @NotNull : null 값이 아닌지 검증
- @NotEmpty : null, 빈 문자열("")이 아닌지 검증, 공백(" ")은 통과
- @NotBlank : null, 빈 문자열(""), 공백(" ")이 아닌지 검증
- @Min : 최솟값 검증
- @Max : 최댓값 검증
- @Pattern : 패턴 검증
- @Email : 이메일 형식인지 검증
더 자세한 내용은 아래를 참고하자.
https://javaee.github.io/javaee-spec/javadocs/javax/validation/constraints/package-summary.html
@Valid
@Valid는 자바 표준 스펙 기술로, 객체나 메서드 파라미터의 유효성 검사를 위해 주로 사용된다.
@Valid는 컨트롤러(Controller) 단에서만 사용이 가능하며 다른 계층에서는 단독으로 사용이 불가하다.
다른 계층에서 사용하고 싶다면 @Validated와 결합해야 하는데, 이는 아래에서 다루겠다.
@Valid는 아래와 같이 주로 객체의 유효성 검사를 할 때 사용한다.
public class RequestDto{
@Email
private String email;
@NotNull
private Long id;
}
위와 같이 검증이 필요한 객체의 필드에 검증 어노테이션을 붙여주고,
@Controller
public class SomeController{
@GetMapping(...)
public ResponseEntity getSomething(@Valid @RequestBody RequestDto request){
...
}
}
위와 같이 컨트롤러에서 해당 객체를 파라미터로 사용하는 메서드에 @Valid를 붙여주면 유효성 검증을 수행한다.
@Valid 동작 원리
클라이언트로부터 받은 요청은 가장 먼저 프론트 컨트롤러인 디스패처 서블릿(Dispatcher Servlet)이 받아 적절한 컨트롤러에게 요청을 전달한다.
요청을 전달하는 과정에서 컨트롤러 메서드의 @RequestBody가 붙은 파라미터는 Json 메시지를 객체로 변환된다.
이 작업은 ArgumentResolver의 구현체인 RequestResponseBodyMethodProcessor가 처리하는데
이때 내부적으로 @Valid로 시작하는 어노테이션이 붙은 필드의 유효성 검사를 진행한다.
정확히는 RequestResponseBodyMethodProcessor의 resolveArgument() 메서드에서 Validation이 이루어진다.
여기서 LocalValidatorFactoryBean을 Validator로 이용해 Validation을 수행한다.
만약 @ModelAttribute를 사용한다면 ModelAttributeMethodProcessor에서 같은 과정을 거친다.
이때 @Valid 유효성 검증을 통과하지 못한다면 MethodArgumentNotValidException 예외가 발생한다.
어떤 예외가 발생하는지 알아야 해당 예외를 핸들링 할 수 있기 때문에 예외의 종류를 아는 것은 중요하다.
따라서 @Valid는 디스패처 서블릿에서 적절한 컨트롤러에게 요청을 위임할때 동작하므로 컨트롤러에서만 동작한다.
참고)
@Validated
@Validated는 자바 표준 기술이 아닌 스프링에서 제공하는 기능이다.
@Valid가 컨트롤러에서만 사용가능한 한계가 있지만, @Validated는 다른 계층에서도 사용 가능하다.
그 이유는 동작 원리에서 설명하겠다.
유효성 검증은 컨트롤러단에서 처리하고 넘기는 것이 좋지만, 필요에 의해 서비스단이나 다른 계층에서 검증이 필요한 경우도 있다.
이때 사용할 수 있는게 @Validated이다.
@Service
@Validated
public class SomeService{
@GetMapping(...)
public ResponseEntity getSomething(@Valid @RequestBody RequestDto request){
...
}
}
위와 같이 서비스 클래스에 @Validated 어노테이션을 붙여주고 유효성 검증이 필요한 파라미터에 @Valid를 붙여주면 된다.
참고로 @Validated는 groups 옵션을 통해 유효성 검증 그룹을 지정할 수 있다. 하지만 코드가 복잡해지기 때문에 잘 사용하지 않는 기능이므로 설명하진 않겠다
@Validated 동작 원리
@Validated는 AOP를 기반으로 메서드의 요청을 가로채 유효성 검증을 진행한다.
위에서 Bean Validation을 설명하면서 스프링 부트가 ValidationAutoConfiguration을 통해 MethodValidationPostProcessor 클래스를 등록한다고 했다.
이 MethodValidationPostProcessor가 내부적으로 AOP Advice인 MethodValidationInterceptor를 등록하고, MethodValidationInterceptor가 Point Cut으로 메서드 요청을 가로채 유효성 검사를 진행한다.
이때 유효성 검증을 통과하지 못한다면 ConstraintViolationException 예외를 발생시킨다.
따라서 @Validated는 컨트롤러단뿐만 아니라 다른 계층에서도 사용 가능하다.
클래스에 @Validated를 붙이면 유효성 검증 AOP가 적용되기 때문이다.
참고로 ConstraintViolationException을 @ExceptionHandler에서 핸들링하려면 jakarta.validation 패키지를 import 해야 한다.
org.hibernate.exception에도 ConstraintViolationException이 있는데 이는 데이터베이스와의 상호작용을 처리하기 위한 ORM 프레임워크로써의 hibernate이다.
참고)
[Spring] AOP(Aspect Oriented Programming)란? 스프링 AOP란?
정리
@Valid
- 자바 표준 스펙
- 컨트롤러단에서만 사용 가능
- 유효성 검증을 통과하지 못할 경우 MethoArgumentNotValidException 발생
@Validated
- 스프링에서 제공하는 기능
- 다양한 계층에서 사용 가능
- 유효성 검증을 통과하지 못할 경우 ConstraintViolationException 발생
- groups를 통해 검증 대상 지정 가능
참고
[1] https://beanvalidation.org/
[2] https://hibernate.org/validator/
[3] https://mangkyu.tistory.com/174
[4] https://wildeveloperetrain.tistory.com/158
[5] https://medium.com/sjk5766/valid-vs-validated-%EC%A0%95%EB%A6%AC-5665043cd64b