개발을 하다 보면 DTO 또는 객체를 검증해야 하는 경우가 있다. 이럴 때 사용할 수 있는 @Valid와 @Validated 어노테이션이 존재한다. @Valid와 @Validated 어노테이션을 사용하기 위해 아래 의존성을 추가해야 한다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
@Valid은 javax.validation패키지에 존재하여 독립적으로 사용한다면 Controller 계층에서만 동작한다. 그 이유는 ArgumentResolver에 의해 처리가 되기 때문이다.
모든 요청은 DispatcherServlet을 통해 Controller로 전달되는것을 알고 있을 것이다. 그 과정에서 ArgumentResolver가 동작한다. 따라서 독립적으로 사용 시 Controller에서만 동작할 것이다.
Argument Resolver에 대한 내용은 아래글을 참고하기 바란다.
만약 다른 계층에서 사용하길 원한다면 @Validated와 결합되어야한다.
@Validated는 아래에서 다시 정리해 보겠다.
@Valid를 사용하여 검증한 결과 오류가 있다면 MethodArgumentNotValidException 예외가 발생된다.
검증객체
import javax.validation.constraints.NotBlank;
...
@Getter
@Setter
public class UserDto {
private long userId;
private String email;
@NotBlank
private String password;
@NotBlank
private String username;
}
검증하고자 하는 변수에 javax.validation.constraints에 있는 Annotation을 선언한다.
어노테이션 | 설명 | 예시 |
@NotBlank | null이 아니며 공백이 아닌 문자를 하나 이상 포함한다. | |
@NotEmpty | null 이거나 빈 문자열이 아니어야 한다. | |
@NotNull | null이 아닌 값이다. | |
@Null | null값이다. | |
@Max | 해당값보다 작거나 같아야한다. | @Max(value = 100) |
@Min | 해당값보다 크거나 같아야한다. | @Max(value = 1) |
@DecimalMax | 해당값보다 작거나 같아야한다. | @DecimalMax(value = "1000000000") |
@DecimalMin | 해당값보다 크거나 같아야한다. | @DecimalMin(value = "1") |
@Positive | 양수인 값이다. | |
@PositiveOrZero | 0이거나 양수인 값이다. | |
@Negative | 음수인 값이다. | |
@NegativeOrZero | 0이거나 음수인 값이다. | |
올바른 형식의 이메일 주소여야한다. | ||
@Future | Now 보다 미래의 날짜, 시간이어야 한다. | |
@FutureOrPresent | Now 거나 미래의 날짜, 시간이어야 한다. | |
@Past | Now 보다 과거 의의 날짜, 시간이어야 한다. | |
@PastOrPresent | Now 거나 과거의 날짜, 시간이어야 한다. | |
@AssertTrue | 값이 항상 True여야 한다. | |
@AssertFalse | 값이 항상 False여야 한다. | |
@Size | 값이 min과 max사이에 있어야한다. | @Size(max = 10, min = 1) |
@Pattern | 지정한 정규식과 대응되는 문자열 이어야 한다. Java의 Pattern 패키지의 컨벤션을 따른다 | @Pattern(regexp = "^[가-힣]*$", message = "한글만 입력할 수 있습니다.") |
Controller계층에 검증할 객체에 @Valid 어노테이션 선언
@RequestMapping("/devlog/user")
@RestController
public class UserController {
@PostMapping(value = "/join", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseVo<User> join(@RequestBody @Valid UserDto userDto){
...
}
}
검증하고자하는 객체에 @Valid를 붙여주면 유효성 검증이 진행된다.
입력 파라미터의 유효성 검증은 Controller계층에서 최대한 처리하고 다음 계층으로 넘겨주는 것이 좋다.
하지만 개발을 하다 보면 불가피하게 다른 곳에서 파라미터를 검증해야 할 수 있다.
Spring에서는 이를 위해 AOP 기반으로 메서드의 요청을 가로채서 유효성 검증을 진행해 주는 @Validated를 제공하고 있다.
AOP기반으로 동작하기 때문에 @Validated를 사용하면 아무 계층에서나 스프링 빈이라면 유효성 검증을 진행할 수 있다.
@Validated는 org.springframework.validation.annotation 패키지에 속한다.
@Service
@Validated
public class UserService {
public User join(@Valid UserDto userDto) {
...
}
}
클래스에 @Validated를 붙여주고, 유효성을 검증할 메서드의 파라미터에 @Valid를 붙여주면 유효성 검증이 진행된다.
동일한 클래스에 대해 제약조건이 요청에 따라 달라질 수 있다. 사용자정보 생성과 수정 두 가지 요청에는 서로 다른 제약조건이 설정되어야 한다.
이러한 경우를 위해 검증 그룹을 지정할 수 있는 기능을 제공하고 있다.
검증 그룹을 지정하기 위해서는 마커 인터페이스를 정의해야 한다.
마커 인터페이스는 일반적인 인터페이스와 동일하지만 사실상 아무 메서드도 선언하지 않은 인터페이스를 말한다.
유효성 검증에 실패하면 ConstraintViolationException 예외가 발생한다.
인터페이스정의
public class ValidatorGroup {
public interface CommonGroup {}
public interface CreateGroup {}
public interface UpdateGroup {}
public interface DeleteGroup {}
}
각각의 java파일로 선언해도 무방하다. 관리를 편하게 하기 위해 Class를 생성하여 마커 인터페이스를 선언하였다.
검증 객체에 그룹 속성을 지정
public class UserDto {
@Min(value = 1, groups = {ValidatorGroup.UpdateGroup.class})
private long userId;
@NotBlank(groups = {ValidatorGroup.CreateGroup.class})
private String email;
@NotBlank(groups = {ValidatorGroup.CreateGroup.class, ValidatorGroup.UpdateGroup.class})
private String password;
@NotBlank(groups = {ValidatorGroup.CreateGroup.class, ValidatorGroup.UpdateGroup.class})
private String username;
}
Controller에 검증 그룹 지정
@RequestMapping("/devlog/user")
@RestController
public class UserController {
@PostMapping(value = "/validGroupTest/join", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseVo<User> join(@RequestBody @Validated(ValidatorGroup.CreateGroup.class) UserDto userDto) {
...
}
@PostMapping(value = "/validGroupTest/modify", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseVo<User> modify(@RequestBody @Validated(ValidatorGroup.UpdateGroup.class) UserDto userDto) {
...
}
}
상황에 맞게 검증그룹을 지정한다.
테스트
userId변수는 전송하지 않았지만 통과한 것을 보았을 때 CreateGroup으로 지정한 변수들만 검증한 것을 확인할 수 있다.
email변수는 전송하지 않았지만 통과한 것을 보았을때 UpdateGroup으로 지정한 변수들만 검증한 것을 확인할 수 있다.
javax.validation.constraints에 있는 어노테이션을 선언하여 검증을 진행하였다. 제공되는 어노테이션으로 기본적인 체크는 되겠지만 DB를 조회하여 체크하거나 별도의 로직을 체크해야 하는 상황이라면 @Valid와 @Validated만으로는 유효성 검증을 할 수 없을 것이다. 이러한 경우에는 @Valid와 @Validated를 어떻게 사용할 수 있는지 다음글에서 정리해보겠다.
Custom Annotation 만들어 직접 유효성 검사하기 (@Valid, @Validated 심화)
Custom Annotation 만들어 직접 유효성 검사하기 (@Valid, @Validated 심화) (59) | 2024.11.28 |
---|
댓글 영역