Bean Validation

  • 검증 애너테이션과 인터페이스의 모음

  • Bean Validation 2.0(JSR-380)이라는 기술 표준

    • 특정 구현체가 아니다.

  • 대표적인 구현체는 하이버네이터 Validator가 있다.

    • 이름은 하이버네이트이지만 ORM과는 관련이 없다.

하이버네이트 Validator

의존 관계 추가

implementation 'org.springframework.boot:spring-boot-starter-validation'

테스트 코드 작성

import lombok.Data;
// 하이버네이트 validator 구현체를 사용할 때만 제공된다. 실무에서 대부분 사용하므로 자유롭게 쓰면 된다.
import org.hibernate.validator.constraints.Range;

// 특정 구현에 관계없이 제공되는 표준 인터페이스
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

@Data
public class Item {

    private Long id;

    @NotBlank
    private String itemName;

    @NotNull
    @Range(min = 1000, max = 1_000_000)
    private Integer price;

    @NotNull
    @Max(9999)
    private Integer quantity;

    public Item() {
    }

    public Item(String itemName, Integer price, Integer quantity) {
        this.itemName = itemName;
        this.price = price;
        this.quantity = quantity;
    }
}

애너테이션 하나로 간단하게 검증할 수 있다. 스프링은 빈 검증기를 통합해뒀기 때문에 실제로 직접 빈 검증기를 쓸 일은 없다.

스프링 적용

기존 Validator 코드를 제거해도 실행해보면 검증기가 적용된다. validation 의존성을 추가하면 스프링이 자동으로 처리해주기 때문이다.

작동 원리

  1. LocalValidatorFactoryBean을 글로벌 Validator로 등록한다.

  2. 애너테이션을 보고 검증을 수행한다.

따라서 @Valid, Validated 애너테이션만 적용하면 된다. 검증 오류가 발생하면 FieldError, ObjectError를 생성해 BindingResult에 담는다.

만약 이전에 만들었던 글로벌 검증기를 적용하고 있다면 스프링 부트가 자동으로 Validator를 등록하지 않기 때문에 제거해준다.

검증 순서

  1. @ModelAttribute가 각 필드에 타입 변환을 시도한다.

    • requestParam을 각 필드에 넣어준다.

  2. 성공하면 Validatior를 적용한다.

  3. 실패하면 typeMismatch로 FieldError를 추가한다.

    • Validator는 적용하지 않는다.

즉, 바인딩에 성공한 필드만 Bean Validation을 적용한다. 일단 모델 객체에서 바인딩 받는 값이 정상으로 들어와야 검증도 의미가 있기 때문이다.

에러 코드

각 애너테이션은 그 이름을 오류 코드로 해서 메시지 코드를 생성한다.

@NotBlank

  • NotBlank.item.itemName

  • NotBlank.itemName

  • NotBlank.java.lang.String

  • NotBlank

@Range

  • Range.item.price

  • Range.price

  • Range.java.lang.Integer

  • Range

따라서 메시지를 등록하면 Bean Validation의 기본 기능의 메시지를 수정할 수 있다.

메시지 검색 우선순위

  1. 생성된 메시지 코드의 순서대로 messageSource에서 메시지 검색

  2. 애너테이션에 정의한 message 속성

    • @NotBlank(message = "공백은 넣을 수 없습니다. {0}")

  3. 라이브러리가 제공하는 기본값

오브젝트 오류

@ScriptAssert

Bean Validation 애너테이션은 필드에 선언한다. 오브젝트 에러를 만들고 싶다면 어떻게 해야할까?

  • 메시지 코드

    • ScriptAssert.item

    • ScriptAssert

이 방식은 제약 조건이 많고 복잡하다. 실무에서 나오는 다양한 조건을 만족하기도 어렵다.

자바 코드 작성

따라서 그냥 자바 코드로 풀어내는 것을 권장한다.

Groups

데이터 등록과 수정의 요구 사항이 다를 수 있다. 만약 수정 기능에선 id가 @NotNull이라면 같은 객체를 사용하는 등록 기능에 문제가 발생한다.

그래서 Bean Validation은 groups라는 기능을 제공한다. 예를 들어 등록과 수정을 다른 그룹으로 나눠 적용할 수 있다.

@Validated에 groups 기능을 적용하면 된다. @Valid에는 이 기능이 없다.

하지만 복잡도가 올라가고 실무에서는 다른 방법을 쓰기 때문에 잘 쓰지 않는다.

Last updated

Was this helpful?