@NotNull, @NotEmpty, @NotBlank 모두 Bean Validation에 속한 어노테이션들이다.
@NotNull : null만 허용하지 않는다.
=> " "(공백), ""(초기화된 String) 는 허용한다.
@NotEmpty : null과 ""를 둘다 허용하지 않는다.
=> " "(공백) 은 허용한다.
@NotBlank : null과 ""(초기화된 String), " "(공백) 모두 허용하지 않는다.
@Documented
@Constraint(validatedBy = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
public @interface NotEmpty {
...
}
위의 @Target을 보면 여러 위치에 올 수 있다.
이 글에서는 Entity의 필드와 DTO의 필드에 어느 위치에 3가지 어노테이션을 달아야 하는지 알아보자.
참고) 스프링과 Bean Validation을 통합해서 사용하면 스프링이 LocalValidatorFactoryBean를 글로벌 Validator로 등록해 검증 코드를 작성하지 않고, @Valid나 @Validated 를 통해 검증할 수 있다. (검증 오류가 발생하면, FieldError나 ObjectError를 생성해 BindingResult에 담아준다.)
=> 상품 등록 요청을 하는 DTO와 상품 수정 요청하는 DTO가 있다고 가정해보자.
상품 등록의 경우 DTO의 필드에 itemId값은 필요 없는 반면, 상품 수정 요청 DTO에는 itemId값이 꼭 필요하다.
이때, 상품 ID 값에 @NotNull을 넣는 순간, 상품 생성이 불가능해진다.
이런 문제는 Bean Validation의 groups기능을 사용하거나,
별도의 DTO를 만들어서 해결할 수 있다.
그러나 groups기능의 경우 엔티티가 굉장히 복잡해 지는 문제점을 가지고 있다. 또한 전달하는 데이터가 상품 엔티티와 스펙이 동일하지 않아, 비즈니스 로직에 필요없는 추가 정보들도 제공되는 문제가 존재한다.
우선 엔티티에 필요없는 애노테이션을이 붙지 않는다.
또한 필요한 데이터에 딱 맞는 데이터 형태로 데이터를 전달할 수 있으며, 불필요한 검증이 생기지 않는다.
하지만 전달하려는 데이터마다 DTO를 달리 만들어야 하는 단점도 존재한다.