결론부터 말하자면 @Valid
와 @Validation
의 차이는 다음과 같다.
@Valid
- 자바 표준 스펙으로,
Bean Validator
라는 검증기를 통해 객체의 유효성을 검증한다.- Spring에서는
LocalValidatorFactorBean
이 제약 검증을 한다.- 유효성에 어긋날 시
MethodArgumentNotValidException
을 발생한다.
@Validated
- Spring에서 제공하는 기능으로, AOP 기반으로 메소드의 요청을 가로채서 유효성을 검증하는 어노테이션이다.
- 유효성 검증을 위한 AOP의
Advice
가 등록되고,PointCut
으로 요청을 가로채서 유효성을 검증한다.- 제약 조건에 어긋날 시
ConstraintViolationException
을 발생한다.- 클래스에는
@Validated
, 메소드에는@Valid
를 붙여주어야 한다.
@Valid
는 주로 request body를 검증하는데 사용된다.
@NotNull, @NotEmpty, @NotBlank 차이🔍
- @NotNull :
null
만 허용하지 않음- @NotEmpty :
null
""(empty)
허용하지 않음 (""는 길이가 0인 String임)- @NotBlank :
null
""(empty)
" "(blank)
허용하지 않음
@Getter
@NoArgumentsConstructor
@AllAgrumentsConstructor
public class UserRequest {
@Email
private String email;
@NotBlank
private String password;
@NotNull
private Address address;
}
@Getter
@NoArgumentsConstructor
@AllAgrumentsConstructor
public class Address {
@NotBlank
private String city;
@NotBlank
private String zipCode;
}
다음과 같이 UserRequest
와 UserRequest
의 필드 값 중 하나인 Address
가 만들어 준다.
@PostMapping()
public String signUp(@Valid @RequestBody UserRequest request) {
return "ok";
}
다음 Controller에 signUp
이라는 함수에서 @Valid
를 하면 유효성 검사가 작동된다.
하지만 주의할 점은 UserReqeust
의 address의 유효성 검사는 이루어 지지 않았다는 것이다.
{
"email": "test@gmail.com",
"password": "password",
"address": {
"city": "", // 빈 문자열 <- 유효성 검사 X
"zipCode": null // null < - 유효성 검사 X
}
}
UserRequest
클래스의 nested 클래스인 Address
의 유효성 검사까지 잘 작동되려면 다음과 같이 @Valid
를 설정해주면 된다.
public class UserRequest {
@Email
private String email;
@NotBlank
private String password;
@Valid // @NotNull 대신 @Valid를 사용해야함!!
private Address address;
}
맨 처음 언급했듯이 유효성 검증에서 오류가 있다면
MethodArgumentNotValidException
와 함께 404 BadRequest
에러가 발생한다.
@Valid
는 기본적으로 컨트롤러에서만 동작하며 기본적으로 다른 계층에서는 검증이 되지 않는다. 다른 계층에서 파라미터를 검증하기 위해서는 @Validated
와 결합되어야 하는데, 아래에서 @Validated
와 함께 자세히 살펴보도록 하자.
(Service, Repostiroy와 같은 계층에서 유효성 검사를 위해서는 @Valid
@Validated
둘 다 사용)
기존의 @Valid
는 reqeust body만 유효성 검증이 가능하지만, @Validated
는 url의 Path Variable(쿼리 파라미터)
까지 유효성 검증이 가능하다.
이것도 Valid
Validated
의 차이라고 볼수 있다.
@RestController
@RequestMapping("/users")
@Validated //@Valid 대신 @Validated 사용
public class UserController {
.
.
@GetMapping("/{id}")
public String find(@PathVariable @Min(1) Long id) {
return "ok";
}
}
처음 언급했듯이, @Validated
는 클래스 레벨에 붙이면 된다. 위 코드에서 만약 @Validated
이 없다면 @Min
은 동작하지 않는다.
.
.
다음은 @Validated
으로 Service
와 같은 다른 계층에서 유효성 검증을 어떻게 처리할 수 있는지 알아보겠다.
@Validated // @Validated 추가
@Service
public class UserService {
@Transactional
public void signUp(@Valid UserRequest request) { // 파라미터에 Valid 추가
// 회원가입 로직
}
}
class 레벨에 @Validated
, 파라미터에 @Valid
을 추가하면 된다.
마찬가지로 유효성 검증에서 오류가 있다면
ConstraintViolationException
가 발생한다.
@Validated
의 또 다른 기능은 유효성 검증 그룹을 지정할 수 있다는 것이다.
예를 들어, 사용자의 요청과 관리자의 요청이 1개의 클래스에서 처리될 때, 사용자와 관리자에게 다르 조건으로 유효성 검증을 적용할 수 있다.
방법은 아래와 같다.
public interface UserValidationGroup {}
public interface AdminValidationGroup {}
다음과 같이 내용이 없는 마커 인터페이스
를 만든다.
@Getter
@NoArgumentsConstructor
@AllAgrumentsConstructor
public class ReqeustDto {
@NotEmpty(groups = {UserValidationGroup.class, AdminValidationGroup.class} )
private String name;
@NotEmpty(groups = UserValidationGroup.class)
private String userId;
@NotEmpty(groups = AdminValidationGroup.class)
private String adminId;
}
다음과 같이 그룹을 지정할 수 있다.
그리고 Controller에서 다음과 같이 메소드의 파라미터에 @Validated
를 추가하면 된다.
@PostMapping("/users")
public ResponseEntity<Void> addUser(
@RequestBody @Validated(UserValidationGroup.class) RequestDto requestDto
) {
...
}
}
위 코드에서는 @Validated
의 파라미터로 UserValidationGroup.class
를 넣었기 때문에, UserValidationGroup에 해당하는 name
userId
의 유효성 검증만 작동된다.
만약 @Validated
에 특정 마커를 지정해주지 않았거나, groups가 지정되어 있는데 이를 무시하고 @Valid
를 붙이면 다음과 같이 처리된다.
@Validated
에 특정 마커를 지정❌ 또는 @Valid
사용 : groups가 없는 속성들만 유효성 검증@Validated
에 특정 클래스를 지정한 경우: 지정된 클래스를 groups로 가진 제약사항만 처리@Validated
의 그룹 지정 기능은 코드가 복잡해져서 거의 사용되지 않으므로 이러한 기능이 있음을 참고하고 넘어가도록 하자.