@Valid 와 @Validated

Minjae An·2023년 12월 13일
0

Spring Web

목록 보기
1/9

@Valid 는 자바 표준 스펙이고 @Validated 는 스프링에서 제공하는 어노테이션이다.
@Validated 를 통해 그룹 유효성 검사나 Controller 가 아닌 다른 계층에서 유효성 검증 가능
@ValidMethodArgumentNotValidException 예외를
@ValidatedConstraintViolationExecption 을 발생시킨다.

Valid의 사용

Valid 어노테이션은 주로 request body를 검증하는데 많이 사용된다. 아래와 같은 User dto가
존재할 때, 유효성 검사 어노테이션을 적용했다.

public class UserRequest {
    @Email
    private String email;

    @NotBlank
    private String password;
    
    @NotNull
    private Address address;
}

Address 는 다음과 같이 구성되어 있다.

public class Address {
    @NotBlank
    private String city;

    @NotBlank
    private String zipCode;
}

Controller 내의 User 회원가입 함수 파라미터 부분에 Valid 어노테이션을 적어야 검증 기능이 정상 동작한다.

@PostMapping()
public String signUp(@Valid @RequestBody UserRequest request) {
    return "ok";
}

하지만 실제 동작시 Address 의 변수에 대한 유효성 검증이 실행되지 않는다. 아래와 같이 요청이 예외 처리되지 않고 정상적으로 들어간다.

{
    "email": "test@gmail.com",
    "password": "password",
    "address": {
        "city": "",       // 빈 문자열
        "zipCode": null   // null
    }
}

위와 같이 nested dto를 검증하려면 AddressNotNull 대신 Valid 어노테이션을 붙여야
정상적으로 동작한다.

public class UserRequest {
    @Email
    private String email;

    @NotBlank
    private String password;
    
    @Valid // @NotNull 대신 @Valid를 사용해야함
    private Address address;
}

Validated의 사용

검증 입력 값에는 크게 다음 3가지 유형이 있다.

  1. query string, ex) /users?teamId=1
  2. query parameter, ex) /users/1
  3. request body

request body의 경우 위와 같이 Valid 어노테이션으로 처리하면 되고 만약 쿼리 스트링이나
파라미터를 검증해야 하는 경우는 어떻게 해야 할까?

쿼리 파라미터로 특정 사용자를 조회하는 API가 존재한다고 가정할 시, id는 보통 DB의 auto_increment로 1부터 시작하니 이걸 검증한다고 하면, 아래와 같이 Validated 어노테이션을
클래스 레벨에 선언 후 유효성 검사를 위한 어노테이션을 추가하면 된다.

@RestController
@RequestMapping("/users")
@Validated
public class UserController {
    
    @GetMapping("/{id}")
    public String find(@PathVariable @Min(1)  Long id) {
        return "ok";
    }
}

만약 위 코드에서 Valiated 어노테이션이 없다면 Min 이 동작하지 않는다.

모종의 이유로 요청을 controller가 아닌 service에서 검증하다고 가정해보자. Service class 단에 Valiated 어노테이션을 선언하고 파라미터에 Valid 어노테이션을 적용하면 된다.

@Transactional(readOnly = true)
@Validated   // Validated 어노테이션 추가
@Service
public class UserService {
  @Transactional
  public void signUp(@Valid UserRequest request) { // 파라미터에 Valid 추가
    // 회원가입 로직
  }
}

입력 파라미터의 유효성 검증은 컨트롤러에서 최대한 처리하는 것이 적합하다. 하지만 불가피하게 다른 곳에서 파라미터를 검증해야 할 수 있다. Spring에서는 이를 위해 AOP 기반으로 메서드의
요청을 가로채서 유효성 검증을 진행해주는 @Validated 를 제공하고 있다. 이 어노테이션은 JSR
표준 기술이 아니며 Spring 프레임워크에서 제공하는 어노테이션 및 기능이다.

예외 처리

Valid 를 통해 검증을 수행하는 과정에서는 MethodArgumentNotValidException 이 발생할 수 있으며 이는 Exception Handler를 통해 핸들링할 수 있다.

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseMessage<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
    return ResponseMessage.badRequest("");
}

만약 Validated 방법을 사용했다면 ConstraintViolationException 이 발생한다.

@ExceptionHandler(ConstraintViolationException.class)
public ResponseMessage<String> handleMethodConstraintViolationException(ConstraintViolationException e) {
    return ResponseMessage.badRequest("");
}

참고

profile
내가 쓴 코드가 남의 고통이 되지 않도록 하자

0개의 댓글