[SpringBoot] spring-boot-starter-validation - 2.Validation

유알·2023년 1월 27일
0

[Spring]

목록 보기
5/17

1. 검증 (@Valid , @Validated)

@Valid와 @Validated

@Valid 어노테이션이 javax.validation 패키지에 속하는 반면, @Validated 어노테이션은 org.springframework.validation.annotation에 속합니다.
https://wildeveloperetrain.tistory.com/158 이 링크 참조

요약

@Valid -> Controller에서 검증가능, MethodArgumentNotValidException 발생
@Validated -> Controller 외부에서 가능 (AOP기반) , ConstraintViolationException발생
왠만하면 컨트롤러 단에서 다 검증해주는 것이 좋다.

@Valid 사용방법

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class Person {

    @NotNull
    @Size(max = 64)
    private String name;
    
    
    /* 생략 */

}

컨트롤러 메서드에 @Valid로 시작하는 어노테이션을 붙여준다.

@PostMapping(value = "/", produces = "application/json")
public ResponseEntity<?> sendSMS(@Valid @RequestBody Person person) {

	/* 생략 */

}

포함된 클래스에 대해 Valid

public class SignUpRequest {
    @Valid private Email email;
    @Valid private Name name;
}

public class Name {
    @NotEmpty private String first;
    private String middle;
    @NotEmpty private String last;
}

public class Email {
    @javax.validation.constraints.Email
    private String value;
}

이런식으로 다른 클래스를 멤버 변수로 가지고 있는 경우
상위 클래스에 @Valid를 붙이고 아래와 같이 호출하면 된다.

@RestController
@RequestMapping("/members")
public class MemberApi {

    private final MemberSignUpService memberSignUpService;

    @PostMapping
    public MemberResponse create(@RequestBody @Valid final SignUpRequest dto) {
        final Member member = memberSignUpService.doSignUp(dto);
        return new MemberResponse(member);
    }
}

자세한 내용

모든 요청은 프론트 컨트롤러인 디스패처 서블릿을 통해 컨트롤러로 전달된다.

전달 과정에서 컨트롤러에서 @ResponseBody 어노테이션을 사용하는 경우 ArgumentResolver의 구현체인 RequestResponseBodyMethodProcessor에 의해 @Valid로 시작하는 어노테이션이 있을 경우 유효성 검사를 진행한다.
(이러한 이유로 커스텀 어노테이션도 @Valid로만 시작한다면 유효성 검사를 진행한다.)

만약 유효성 검사 중 Constrain에 맞지 않는 것이 있다면,
MethodArgumentNotValidException예외가 발생하게 되고, 이는 디스패처 서블릿에 기본으로 등록된 기본 Exception Resolver인 DefaltHandlerExceptionResolver에 의해 400 BadRequest 에러를 발생시킨다.

@Validated 사용 방법

@Valid 는 기본적으로 컨트롤러에서만 동작하며 다른 계층에서는 검증이 되지 않는다.
다른 계층에서 파라미터를 검증하기 위해서는 @Validated 와 결합되어야한다.
JSR 표준 기술이 아니며 Spring 프레임 워크에서 제공하는 어노테이션이다.

다음과 같이 클래스에 @Validated를 붙여주고 유효성 검증할 메서드의 파라미터에 @Valid를 붙여주면 된다.

@Service
@Validated
public class UserService {

	public void addUser(@Valid AddUserRequest addUserRequest) {
		...
	}
}

여기서 @Validated는 @Valid의 MethodArgumentNotValidException이 아닌, ConstraintViolationException을 발생시키며,

ArgumentResolver에 의해 진행되었던 @Valid와는 달리 AOP기반으로 MethodValidationInterceptor를 통해 진행된다.

@Validated의 또 다른 기능( 유효성 검증 그룹 지정)

동일한 클래스에 대해 제약조건이 요청에 따라 달라질 수 있다.
예를 들어 일반 사용자의 요청과 관리자의 요청이 1개의 클래스로 처리 될때, 각각 다른 제약 조건이 적용되어야 할 수 있다.

검증 그룹을 지정하기 위해서는 내용이 없는 마커 인터페이스를 간단히 정의 해야한다.

public interface UserValidationGroup {} 
public interface AdminValidationGroup {}

그리고 제약조건에 groups로 지정해 줄 수 있다.

@NotEmpty(groups = {UserValidationGroup.class, AdminValidationGroup.class} ) 
private String name; 

@NotEmpty(groups = UserValidationGroup.class) 
private String userId; 

@NotEmpty(groups = AdminValidationGroup.class) 
private String adminId;

컨트롤러에도 제약조건 검증을 적용할 클래스를 지정해주면 된다.

@PostMapping("/users") 
public ResponseEntity<Void> addUser(
    @RequestBody @Validated(UserValidationGroup.class) AddUserRequest addUserRequest) {
    
      ...
}

그 다음 @Validated의 파라미터로 그룹을 지정해주면 된다.

  • @Validated에 특정 클래스를 지정하지 않는 경우 : groups가 없는 속성들만 처리
  • @Valid @Validated에 특정 클래스를 지정한 경우 : 지정된 클래스를 group으로 가진 제약사항만 처리

https://mangkyu.tistory.com/174
https://wildeveloperetrain.tistory.com/158
https://gaemi606.tistory.com/entry/Spring-Boot-ResponseBody-%EA%B0%81-%ED%95%AD%EB%AA%A9%EC%97%90-%ED%81%AC%EA%B8%B0-%ED%95%84%EC%88%98-%EA%B0%92-%EC%84%A4%EC%A0%95-spring-boot-starter-validation

profile
더 좋은 구조를 고민하는 개발자 입니다

0개의 댓글