(2) 검증

CJY·2023년 4월 1일
0

스프링

목록 보기
7/14

저번 포스트에서 Bean Validation을 제외한 검증 방법들을 살펴보았다. 앞선 방법들로만 검증 코드를 작성한다면 생각보다 검증에 대한 개발 시간과 자원이 많이 소비될 것 같다.

생각해보면 데이터가 빈 값으로 들어왔는지 혹은 최솟값을 넘었는지 최댓값보다 작게 들어왔는지 에 대한 검증이 대부분일 것 같다.

이런 검증 로직을 모든 프로젝트에 적용할 수 있게 공통화하고, 표준화 한 것이 바로 Bean Validation이다.
Bean Validation을 잘 활용하면, 애노테이션 하나로 검증 로직을 매우 편리하게 적용할 수 있다.

Bean Validation

특정 구현체가 아닌 기술 표준

bean validation은 기술 표준이다.

Hibernate Validator

일반적으로 사용하는 구현체는 hibernate validator이다.

사용하려면?

프로젝트 생성시 dependency에 validation을 추가하거나

build.gradle에 아래 내용을 추가하면 된다.
implementation 'org.springframework.boot:spring-boot-starter-validation'

사용방법

사용하고자 하는 도메인 필드 위에 어노테이션을 추가하면 된다.
예를 들어,

@Data
public class Product {
 	
   private Long id;
   
   @NotBlank
   private String productName;
   
   @NotNull
   @Range(min = 10000, max = 1000000)
   private Integer price;
   
   @NotNull
   @Max(150)
   private Integer quantity;
   
   public Item() {}
   
   public Item(String productName, Integer price, Integer quantity) {
     this.productName = productName;
     this.price = price;
     this.quantity = quantity;
   }
}

검증 애노테이션
@NotBlank : 빈값 + 공백만 있는 경우를 허용하지 않는다.
@NotNull : null 을 허용하지 않는다.
@Range(min = 10000, max = 1000000) : 범위 안의 값이어야 한다.
@Max(150) : 최대 150까지 허용한다.

스프링 MVC는 어떻게 Bean Validator를 사용?

스프링 부트가 spring-boot-starter-validation 라이브러리를 넣으면 자동으로 Bean Validator를 인지하고 자동으로 글로벌 Validator로 등록한다.
LocalValidatorFactoryBean 을 글로벌 Validator로 등록한다. 이 Validator@NotNull 같은 애노테이션을 보고 검증을 수행한다. 이렇게 글로벌 Validator가 적용되어 있기 때문에, 검증하고자 하는 객체 앞에 @Valid , @Validated 만 적용하면 된다.
검증 오류가 발생하면, FieldError , ObjectError 를 생성해서 BindingResult 에 담아준다.

검증 순서

  1. @ModelAttribute객체의 각 필드에 타입 변환 시도. 실패시 typeMismatchFieldError 추가.
  2. 타입 변환 성공시 Validator 적용.

당연히 타입 변환이 안되면 검증 로직을 실행하는게 의미 없어 보인다.

오류 코드

근데 typeMismatch로 추가된다는게 무슨 말인가? 앞선 포스터에서 언급했던 오류 코드가 typeMismatch라는 것이다. (errors.properties에 작성한 오류코드.)
Bean Validation을 통한 오류코드는 어노테이션 이름과 같다. 즉, @NotNull이 붙은 필드에서 검증 오류가 생기면 그 오류 코드는 NotNull이 된다.
정확하게는 MessageCodeResolver에 의해 NotNull.클래스.필드라는 이름의 오류코드 우선순위가 가장 높고 NotNull.필드, NotNull.java.lang.타입, NotNull 순으로 우선순위가 높을 것이다.

errors.properties에
NotNull={0}에는 값이 있어야 합니다.
Range={2}~{1} 범위 확인.

위와 같이 작성했을 때 {0}에는 필드명이 들어간다. 그리고 Range에서 {1}에는 최댓값, {2}에는 최솟값으로 작성한 값이 들어간다. 언제 넣어준 값? 어노테이션 작성했을 때 넣어준 그 값.

한계

글로벌 Validator로 등록되는데 만약 새로 추가 등록 시의 검증과 수정 시의 검증을 다르게 하고싶다면 굉장히 난처해진다.

해결책

  • groups: Bean Validation 어노테이션 안에 groups 이용.
  • 폼 전용 객체 선언.

groups

  1. 인터페이스 생성.
  2. 검증 어노테이션 속 groups에 인터페이스 추가.
  3. @Validated에 인터페이스 추가. (@Valid에는 적용불가)

예를 들어, 수정 전용 검증을 위해 EditCheck라는 인터페이스를 새로 생성하고 나면

@NotNull(groups = EditCheck.class)
private 필드

다음과 같이 어노테이션에 추가.
마지막으로

@PostMapping("")
public String aa(@Validated(EditCheck.class) @ModelAttribute 필드 ...)

검증 대상에 추가하면 된다.

폼 전용 객체 선언

실무에서는 주로 이 방법이 사용된다고 한다.
도메인의 클래스에 Bean Validation을 모두 지워주고 폼에서 입력받는 데이터를 따로 클래스로 만들어주면 된다. 앞선 방식과 같이 데이터로 취급하고 각 필드에 원하는 검증 어노테이션을 붙이면 된다. 다만 폼 객체로 입력받았으니 데이터를 추출하고 저장할 때는 도메인의 클래스로 변환하는 추가적인 과정이 있다.

정리

검증에 대해 꽤 깊이 있게 공부해봤다. Bean Validation을 먼저 배웠다면 구체적인 동작 원리를 이해하는게 불가능해 보인다. BindingResult( rejectValue(), reject() ), Validator, MessageCodeResolver, @Validated, 폼 전용 객체 등 유용한 기능들에 대해 알아보았다. hibernate validation에는 email, 신용카드 등 다양한 검증 어노테이션들이 있다고 하니 추가적인 공부를 하는데 많은 도움이 될 것 같다.

profile
열심히 성장 중인 백엔드

0개의 댓글