[Spring Boot] Validation

wujin·2024년 2월 18일
post-thumbnail

Validation

Spring Boot에서 데이터의 유효성을 검사하기 위해 Validation 기능을 사용한다. 이 기능은 데이터 전송 객체(DTO)에 주석을 달아 유효성 검사를 정의할 수 있다. 유효성 검사가 필요한 부분에서는 주로 컨트롤러 메소드에서 검사를 수행하고, 유효하지 않은 경우 예외를 통해 처리한다.

의존성 추가

  • Maven
<dependency>
  <groupId>org.springframework.boot</groupId>
  	<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
  • Gradle
dependencies {
	...
    
	implementation 'org.springframework.boot:spring-boot-starter-validation'
}

@Valid, @Validated

Spring에서 유효성 검사를 수행할 때 사용하는 두 가지 어노테이션인 @Valid@Validated가 존재한다. 두 어노테이션은 유사한 목적을 가지고 있지만, 적용되는 범위와 기능에 약간의 차이가 있다.

@Valid

  • Java EE에서 지원해주는 Java Bean Validaion 표준에 포함된 어노테이션으로, Java EE 환경뿐만 아니라 Spring에서도 이 어노테이션을 유효성 검증에 활용한다.

  • 일반적으로 컨트롤러 메소드의 인자 또는 엔티티 클래스에서 유효성 검사를 적용할때 사용한다.

  • @Valid는 메소드 인자에 적용될 때 해당 인자가 유효한지 검증한다. 만약 유효성 검사를 통과하지 못하면 org.springframework.web.bind.MethodArgumentNotValidException 예외가 발생한다.

@Valid의 동작 원리
모든 요청은 프론트 컨트롤러인 디스패처 서블릿을 통해 컨트롤러로 전달된다. 전달 과정에서는 컨트롤러 메소드의 객체를 만들어주는 ArgumentResolver가 동작하는데, @Valid 역시 ArgumentResolver에 의해 처리가 된다.
대표적으로 @RequestBody는 Json 메세지를 객체로 변환해주는 작업이 ArgumentResolver의 구현체인  
RequestResponseBodyMethodProcessor가 처리하며, 이 내부에서 @Valid로 시작하는 어노테이션이 있을 경우에 유효성 검사를 진행한다. (이러한 이유로 @Valid가 아니라 커스톰 어노테이션인 @ValidMangKyu여도 동작한다.) 만약 @ModelAttribute를 사용중이라면 ModelAttributeMethodProcessor에 의해 @Valid가 처리된다.
출처: https://mangkyu.tistory.com/174 [MangKyu's Diary:티스토리]

예시

import javax.validation.Valid;
import org.springframework.web.bind.annotation.*;

@RestController
public class UserController {
    @PostMapping("/users")
    public User createUser(@RequestBody @Valid User user) {
        // 유효성 검사 통과 후 로직
        return user;
    }
}

import javax.validation.constraints.*;
public class User {
    @NotBlank
    private String name;

    @Min(18)
    private int age;

    // getters, setters, etc.
}

@Validated

  • @Validated는 JSR 표준 기술이 아닌 Spring에서 제공하는 어노테이션으로, AOP 기반으로 메소드의 요청을 가로채서 유효성 검증을 진행해준다.

  • @Validated는 클래스, 인터페이스, 메소드에 적용할 수 있다. 특정 유효성 검사 그룹을 지정할 수 있으므로, 다양한 상황에 맞춰 유효성 검사를 유연하게 조정할 수 있다.

  • 인터페이스에 적용하면 해당 인터페이스의 모든 구현 클래스에 유효성 검사가 적용된다.

  • 유효성 검증에 실패하면 javax.validation.ConstraintViolationException 예외가 발생한다.

@Validated의 동작 원리
특정 ArgumnetResolver에 의해 유효성 검사가 진행되었던 @Valid와 달리, @Validated는 AOP 기반으로 메소드 요청을 인터셉터하여 처리된다. @Validated를 클래스 레벨에 선언하면 해당 클래스에 유효성 검증을 위한 AOP의 어드바이스 또는 인터셉터(MethodValidationInterceptor)가 등록된다. 그리고 해당 클래스의 메소드들이 호출될 때 AOP의 포인트 컷으로써 요청을 가로채서 유효성 검증을 진행한다.
이러한 이유로 @Validated를 사용하면 컨트롤러, 서비스, 레포지토리 등 계층에 무관하게 스프링 빈이라면 유효성 검증을 진행할 수 있다. 대신 클래스에는 유효성 검증 AOP가 적용되도록 @Validated를, 검증을 진행할 메소드에는 @Valid를 선언해주어야 한다.
이러한 이유로 @Valid에 의한 예외는 MethodArgumentNotValidException이며, @Validated에 의한 예외는  ConstraintViolationException이다. 이를 알고 있으면 나중에 예외 처리를 할 때 도움이 된다.
출처: https://mangkyu.tistory.com/174 [MangKyu's Diary:티스토리]

예시

import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.*;
import org.springframework.stereotype.Service;

@Service
@Validated
public class UserService {
    public void createUser(@Validated(UserChecks.class) User user) {
        // 유효성 검사 통과 후 로직
    }
}

import javax.validation.constraints.*;
public class User {
    @NotBlank(groups = UserChecks.class)
    private String name;

    @Min(value = 18, groups = UserChecks.class)
    private int age;

    // getters, setters, etc.
}

public interface UserChecks {
    // 유효성 검사 그룹 정의
}

제약조건 Annotation

  • @NotNull : 해당 값이 null이 아닌지 검증함
  • @NotEmpty : 해당 값이 null이 아니고, 빈 스트링("") 아닌지 검증함(" "은 허용됨)
  • @NotBlank : 해당 값이 null이 아니고, 공백(""과 " " 모두 포함)이 아닌지 검증함
  • @Size : 해당 값이 주어진 값 사이에 해당하는지 검증함(String, Collection, Map, Array에도 적용 가능)
  • @Min : 해당 값이 주어진 값보다 작지 않은지 검증함
  • @Max : 해당 값이 주어진 값보다 크지 않은지 검증함
  • @Pattern : 해당 값이 주어진 패턴과 일치하는지 검증함
  • @Email : 문자열 값이 유효한 이메일 형식인지 검증함
  • @AssertTrue : 해당 값이 true인지 검증함

이 외에도 다양한 어노테이션이 존재한다.

0개의 댓글