@Valid는 제약조건을 달아놓은 속성에 대해 유효성 검사를 하는 어노테이션이다.
개인적으로 Controller 클래스 내의 메소드에서 DTO를 인자로 받을 때 아래와 같이 적용하곤 하였다.
ex)
// getter, setter 생략
class UserDto {
Long id;
String name;
}
...
class UserController {
...
public Response<String> create(@RequestBody @Valid UserDto user) {
...
}
}
하지만, 여기서 지금까지 몰랐던 사실이 있었다.
JPA를 사용하면서 @Embedded와 @Embedable을 사용할 일이 생긴다.
ex)
// getter, setter 생략
class UserDto {
Long id;
String name;
@Embedded
Address address;
}
// getter, setter 생략
@Embeddable
class Address {
Long id;
String city;
...
}
이 상태에서 Address의 city 속성에 @NotBlank와 길이 제한을 두고 싶다면 아래와 같이 속성에 어노테이션을 달아줄 수 있다.
// getter, setter 생략
@Embeddable
class Address {
Long id;
@NotBlank
@Size(min = 1, max = 20)
String city;
...
}
이렇게 적용하면 Address 객체의 city에 대해서도 유효성을 검사해줄 것이라고 생각했는데 그렇지 않다. UserController의 인자로 받는 UserDto에 대한 @Valid의 입장에서 보았을 때, Address 자체를 속성으로 보는 것 같다.
즉,
// getter, setter 생략
class UserDto {
Long id;
String name;
@NotNull
@Embedded
Address address;
}
이렇게 써주게 되면 address 속성 자체가 없는 경우에는 예외가 발생한다는 뜻이다.
그렇다면, Address 내의 속성들에 대해서는 어떻게 검증을 하면 될지 생각해보자.
간단하다.
// getter, setter 생략
class UserDto {
Long id;
String name;
@Valid
@NotNull
@Embedded
Address address;
}
address 위에 @Valid를 붙여주면 된다. 그렇다면 Access 객체의 속성에 대해서도 유효성 검사를 하겠다는 뜻이 된다.
유효성 검사가 안되는 이유에 대해서 알아보다가, 또 다른 사실을 알게 되어 정리하게 되었다. primitive type에 @NotNull을 적용하였을 때, 적용이 안된다는 글을 우연히 접하게 되었다.
// getter, setter 생략
class UserDto {
Long id;
String name;
@NotNull
int age;
}
적용이 안되는 이유는, primitive type인 int는 자동으로 0으로 초기화 되므로, @NotNull에 걸리지 않게 되기 때문이다. 이럴 때는, 아래와 같이 적용하면 된다.
// getter, setter 생략
class UserDto {
Long id;
String name;
@NotNull
Integer age;
}
Reference
Spring Boot not validating Embedded object on Entity
보통 Entity에 @NotNull 등의 어노테이션을 달아 검증을 시도하는데,
맨 위 코드처럼 DTO 검증을 시도하려면, 해당 Entity의 DTO 또한 @NotNull 등의 어노테이션을 달아줘야 하나요?