😐서론
애플리케이션 개발을 진행할 때 검증은 가장 중요한 작업이다.
클라이언트에서 데이터가 제대로 넘어왔는지,
비지니스 로직에서 인수가 제대로 된 값이 넘어왔는지,
db에 값이 저장 혹은 수정되기 전에 제대로 된 값이 맞는지 등
정말 다양한 곳에서 데이터 검증이 필요하며 놓쳐서는 안되는 중요한 작업이다.
이 검증을 쉽게 할 수 있는 @Valid 에 대해서 알아보았다.
목차
- @Valid 는 뭐지?
- @Valid 왜 사용하는거지?
- @Valid 어떻게 사용하는거지?
- Jakarta Validation API 살펴보기
- Jakarta Validation API 구현체 찾기 - Hibernate Validator
- Spring 에서 @Valid 동작 원리
🙄본론
@Valid 는 뭐지?
JSR 303부터 현재 JSR 380 버전으로, 자바 표준 스펙으로 지정되어 있는 Bean Validation 기능 입니다.
Jakarta Bean Validation 2.0 - Java 8 이상
Jakarta Bean Validation 3.0 - Java 11 이상
Jakarta Bean Validation 에 가시면 더욱 상세한 정보를 확인할 수 있습니다.
@Valid 왜 사용하는거지?
1. 간단한 검증을 하더라도 검증관련 로직이 길어지게 됩니다.
if (itemDTO == null) {
throw new IllegalArgumentException("아이템 정보가 존재하지 않습니다.");
}
if (itemDTO.getItemName() == null || itemDTO.getItemName().isEmpty()) {
throw new IllegalArgumentException("아이템 명은 필수 입니다.");
}
if (itemDTO.getQuantity() == null) {
throw new IllegalArgumentException("아이템 개수는 필수 입니다.");
}
if (itemDTO.getQuantity() < 1) {
throw new IllegalArgumentException("아이템 개수는 1 이상 이어야 합니다.");
}
if (itemDTO.getQuantity() > 100) {
throw new IllegalArgumentException("아이템 개수는 100 이하여야 합니다.");
}
2. 검증 로직이 중복으로 여러 Layer에 존재하게 됩니다.
3. Layer에 검증 로직이 섞여있기 때문에 추적이 어렵고, 애플리케이션이 복잡해집니다.
@Valid 사용 시
1. 검증 로직을 통합하고, Layer에서 분리하여 관리할 수 있게 됩니다.
public ResponseEntity<String> save(@Valid @RequestBody ItemDTO itemDTO) {
itemService.save(itemDTO);
return new ResponseEntity<>(HttpStatus.CREATED);
}
Hibernate Validator 에 가시면 더욱 상세한 정보를 확인할 수 있습니다.
@Valid 어떻게 사용하는거지?
Spring Boot 사용 시 아래 의존성을 추가해주시면 됩니다.
// Gradle
implementation 'org.springframework.boot:spring-boot-starter-validation'
// Maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Hibernate Validator 는 Jakarta Bean Validation의 구현체 입니다.
Jakarta Bean Validation 는 명세이기 때문에 따로 구현체를 연결해서 사용하고 있고,
Hibernate Validator 를 기본으로 사용하고 있습니다.
Hibernate Validator만 추가해주어도, Jakarta Bean Validation API 에 대한 의존성이 추가가 됩니다.
Jakarta Validation API 살펴보기
기본적으로 정의해둔 어노테이션 목록을 확인할 수 있습니다.
여기서 살펴보아야 하는 것은 @Constraint 어노테이션 입니다.
@Constraint 어노테이션에 있는 validateBy 정보에 구현체를 넣어서 검증이 진행되는 방식입니다.
하지만, jakarta validation 에 기본 구성으로 나와있는 어노테이션들은 모두 구현체 정보가 존재하지 않습니다.
그러면 어디서 구현체를 찾아가 보도록 하겠습니다.
Jakarta Validation API 구현체 찾기 - Hibernate Validator
맨 처음 의존성을 추가할 때 말씀드렸다시피 hibernate-validator 가 구현체 입니다.
그러면, 구현체는 어디에 정의되어 있을까요??
org.hibernate.validator.internal.constraintvalidators.bv
아까 jakarta validation 에서 살펴본 @NotNull 검증 로직이 여기에 있는 것을 확인할 수 있습니다.
ConstraintValidator 인터페이스에 isValid를 구현하여 검증 로직을 만들 수 있게 됩니다.
그렇다면, jakarta validation 어노테이션과 hibernate 검증 클래스를 어디서 연결해줄까요??
org.hibernate.validator.internal.metadata.core.ConstraintHelper.java
구현체와 어노테이션을 연결해주는 부분을 찾았습니다. (다른 어노테이션 정보들도 여기서 연결하고 있습니다.)
결국, jakarta.validation 에서는 표준 어노테이션을 정의만 해두고, 해당 표준 어노테이션을 가져다가
hibernate.validator 에서 구현체를 만들어서 연결을 해주고 있는 걸 볼 수 있었습니다.
그럼 이제 Spring boot 에서는 해당 어노테이션을 어떻게 사용하고 있는지 확인해보도록 하겠습니다.
Spring 에서 @Valid 동작 원리
Controller 로 요청이 들어오면, Dispatcher Servlet을 걸쳐서 Controller가 호출이 됩니다.
이 과정에서 Dispatcher Servlet 에서 @Valid 를 찾아서 검증을 진행하게 됩니다.
간략하게 살펴보면 아래와 같은 흐름을 띄웁니다.
1. 클라이언트에서 컨트롤러 호출
2. Dispatcher Servlet 에서 요청에 맞는 컨트롤러 탐색
3. RequestResponseBodyMethodProcessor 클래스의 resolverArgument 메소드 호출
4. AbstractMessageConverterMethodArgumentResolver 클래스의 validateIfApplicable 메소드 호출
5. ValidationAnnotationUtils 클래스의 determineValidationHints 메소드 호출
5-1. 어노테이션이 java.validation.Valid 인지, Validated인지, Valid 로 시작하는지 확인 후 Object Array 리턴
6. DataBinder -> ValidatorAdapter -> SpringValidatorAdapter 를 통해
hibernate.validator의 ValidatorImpl 클래스 호출
6-1. ValidatorImpl 에서 검증 후 결과 리턴
7. 검증 결과 Errors 에 값이 있을 시 MethodArgumentNotValidException 발생
그래서 여기서 알 수 있는 것은?
- @Valid 은 Dispatcher Servlet 에서 사용하고 있다.
- @Valid 은 컨트롤러에서 밖에 사용할 수 없다.
이후 글 - [SPRING] @Valid @Validated 사용하기 - java bean validation
참고
https://jyami.tistory.com/55
https://kapentaz.github.io/spring/Spring-Boo-Bean-Validation-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90/#
https://meetup.toast.com/posts/223
https://stackoverflow.com/questions/36477544/javax-validation-implementation
https://gompangs.tistory.com/entry/Spring-Valid-Collection-Validation-%EA%B4%80%EB%A0%A8
https://mangkyu.tistory.com/174
https://en.wikipedia.org/wiki/Bean_Validation
'JVM > Spring' 카테고리의 다른 글
[SPRING] 스프링 MVC 구조? (DispatcherServlet?) (0) | 2022.04.22 |
---|---|
[SPRING] @Valid @Validated 사용하기 - java bean validation (0) | 2022.03.26 |
[SPRING] 스프링의 컨트롤러는 어떻게 여러 작업을 처리할까? (0) | 2021.11.15 |
[SPRING] @Transactional readOnly 성능향상 이유 (0) | 2021.09.05 |
[SPRING] synchronized와 @Transactional 을 동시에 사용 시 문제점 (5) | 2021.08.16 |