Back-End/[Spring]

[Spring] @Valid와 @Validated의 차이, 사용법, Bean Validaion이란?

연구소장 J 2024. 4. 18. 22:44

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
[그림 1] ValidationAutoConfiguration

 

 

스프링 부트는 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

 

javax.validation.constraints (Java(TM) EE 8 Specification APIs)

Enum Summary  Enum Description Pattern.Flag Possible Regexp flags. Annotation Types Summary  Annotation Type Description AssertFalse The annotated element must be false. AssertFalse.List Defines several AssertFalse annotations on the same element. Assert

javaee.github.io

 

@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)이 받아 적절한 컨트롤러에게 요청을 전달한다.

RequestResponseBodyMethodProcessor
[그림 2] RequestResponseBodyMethodProcessor

 

요청을 전달하는 과정에서 컨트롤러 메서드의 @RequestBody가 붙은 파라미터는 Json 메시지를 객체로 변환된다.

이 작업은 ArgumentResolver의 구현체인 RequestResponseBodyMethodProcessor가 처리하는데 

이때 내부적으로 @Valid로 시작하는 어노테이션이 붙은 필드의 유효성 검사를 진행한다.

 

정확히는 RequestResponseBodyMethodProcessor의 resolveArgument() 메서드에서 Validation이 이루어진다.

여기서 LocalValidatorFactoryBean을 Validator로 이용해 Validation을 수행한다.

 

만약 @ModelAttribute를 사용한다면 ModelAttributeMethodProcessor에서 같은 과정을 거친다.

 

이때 @Valid 유효성 검증을 통과하지 못한다면 MethodArgumentNotValidException 예외가 발생한다.

어떤 예외가 발생하는지 알아야 해당 예외를 핸들링 할 수 있기 때문에 예외의 종류를 아는 것은 중요하다.

 

따라서 @Valid는 디스패처 서블릿에서 적절한 컨트롤러에게 요청을 위임할때 동작하므로 컨트롤러에서만 동작한다.

 

참고)

 

 

[Spring] 디스패처 서블릿(Dispatcher Servlet)이란?

프론트 컨트롤러(Front Controller) 디스패처 서블릿에 대해 알기 위해선 프론트 컨트롤러(Front Controller)에 대해 알아야 한다. 프론트 컨트롤러는 말 그대로 모든 컨트롤러 앞에 있는 컨트롤러이다.

code-lab1.tistory.com

 

 

@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
[그림 3]  ConstraintViolationException

 

참고로 ConstraintViolationException을 @ExceptionHandler에서 핸들링하려면 jakarta.validation 패키지를 import 해야 한다.

org.hibernate.exception에도 ConstraintViolationException이 있는데 이는 데이터베이스와의 상호작용을 처리하기 위한 ORM 프레임워크로써의 hibernate이다.

 

 

참고)

[Spring] AOP(Aspect Oriented Programming)란? 스프링 AOP란?

 

[Spring] AOP(Aspect Oriented Programming)란? 스프링 AOP란?

AOP (Aspect Oriented Programming)란? AOP는 Aspect Oriented Programming의 약자로 관점 지향 프로그래밍이라고 불린다. 관점 지향은 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그

code-lab1.tistory.com

 

정리

@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

반응형