[스프링]@Valid vs @Validated

JANG SEONG SU·2023년 8월 30일
0

Sping

목록 보기
5/9

결론부터 말하자면 @Valid@Validation의 차이는 다음과 같다.

@Valid

  • 자바 표준 스펙으로, Bean Validator라는 검증기를 통해 객체의 유효성을 검증한다.
  • Spring에서는 LocalValidatorFactorBean이 제약 검증을 한다.
  • 유효성에 어긋날 시 MethodArgumentNotValidException을 발생한다.

@Validated

  • Spring에서 제공하는 기능으로, AOP 기반으로 메소드의 요청을 가로채서 유효성을 검증하는 어노테이션이다.
    • 유효성 검증을 위한 AOP의 Advice가 등록되고, PointCut으로 요청을 가로채서 유효성을 검증한다.
  • 제약 조건에 어긋날 시 ConstraintViolationException을 발생한다.
  • 클래스에는 @Validated, 메소드에는 @Valid를 붙여주어야 한다.

@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;
}

다음과 같이 UserRequestUserRequest의 필드 값 중 하나인 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 둘 다 사용)


@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의 그룹 지정 기능은 코드가 복잡해져서 거의 사용되지 않으므로 이러한 기능이 있음을 참고하고 넘어가도록 하자.

profile
Software Developer Lv.0

0개의 댓글