Spring & Spring boot

[Spring Boot] Validation - 2. @Validated 어노테이션 사용방법

jh4dev 2024. 8. 14. 22:50
<목차>

1. @Valid vs @Validated
2. @Validated 검증 그룹 지정하기

 

이전 포스팅에서, JAVA 에서 지원하는 @Valid 어노테이션에 대해 알아보았으며, 이번 포스팅에서는 스프링에서 지원하는 @Validated 어노테이션 사용법에 대해 알아보자.


[@Valid vs @Validated]

@Validated 는 기본적으로 @Valid 의 기능을 포함하고 있으며, 다음과 같은 차이가 있다.

항목 @Valid @Validated
용도 Java Bean Validation을 수행할 때 사용된다. 필드 또는 메서드 파라미터에서 사용된다. Java Bean Validation을 수행하면서 특정 검증 그룹(Group)을 지정할 수 있다.
적용 위치 필드, 메서드 파라미터, 생성자 파라미터 클래스, 메서드 파라미터, 생성자 파라미터
검증 그룹 지정 검증 그룹을 지정할 수 없습니다. 모든 기본 검증 규칙이 적용된다. 검증 그룹을 지정할 수 있어, 특정 그룹에 대한 검증을 수행할 수 있다.
주로 사용하는 곳 JSR-303/JSR-380 Bean Validation에서 사용되며, Spring MVC 및 Spring Data에서도 지원된다. Spring Framework에서 검증 그룹을 필요로 할 때 주로 사용된다.
에러 발생 시 처리 발생한 유효성 검증 에러는 일반적으로 MethodArgumentNotValidException으로 처리된다. 발생한 유효성 검증 에러는 ConstraintViolationException 또는 MethodArgumentNotValidException으로 처리된다.
Spring 지원 여부 Java Bean Validation 표준이므로 Spring 외의 환경에서도 사용 가능합니다. Spring 프레임워크에서 주로 사용되는 어노테이션이다.

 

위와 같은 내용 중, @Validated 의 검증 그룹을 지정하는 기능에 대해 알아보자.


[@Validated 검증 그룹 지정하기]

검증 그룹을 지정하기 위해서는, 마커 인터페이스를 생성하여 사용한다.

마커 인터페이스(Marker Interface) : 메서드를 가지지 않고, 특정 기능이나 속성을 표시하기 위해 사용되는 인터페이스

 

1. 마커 인터페이스 생성

public interface ValidationGroup1 {
}
public interface ValidationGroup2 {
}

 

2. 도메인 모델 내, 그룹 지정

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    @Builder
    public class ValidatedRequestDto {

        @NotBlank
        String name;

        @Email
        String email;

        @Pattern(regexp = "01(?:0|1|[6~9])[.-]?(\\d{3}|\\d{4})[.-]?(\\d{4})$")
        String phoneNumber;

        //Group1
        @Min(value=20, groups = ValidationGroup1.class)
        @Max(value=40, groups = ValidationGroup1.class)
        int age;

        @Size(min = 0, max = 40)
        String description;

        //Group2
        @Positive(groups = ValidationGroup2.class)
        int count;

        @AssertTrue
        boolean booleanCheck;
    }

 

3. @Validated 어노테이션을 활용하여 컨트롤러 작성

    @RestController
    @RequestMapping("/validation")
    @Slf4j
    public class ValidatedController {
        //@VALID
    //    @PostMapping("/valid")
    //    public ResponseEntity<ValidRequestDto> checkValidationByValidAnno(@Valid @RequestBody ValidRequestDto validRequestDto) {
    //
    //        log.info(validRequestDto.toString());
    //
    //        return ResponseEntity.status(HttpStatus.OK).body(validRequestDto);
    //    }

        @PostMapping("/validated/group1")
        public ResponseEntity<ValidatedRequestDto> checkValidationByValidatedAnnoGroup1(
                @Validated(ValidationGroup1.class) @RequestBody ValidatedRequestDto validatedRequestDto
        ) {
            log.info(validatedRequestDto.toString());

            return ResponseEntity.status(HttpStatus.OK).body(validatedRequestDto);
        }

        @PostMapping("/validated/group2")
        public ResponseEntity<ValidatedRequestDto> checkValidationByValidatedAnnoGroup2(
                @Validated(ValidationGroup2.class) @RequestBody ValidatedRequestDto validatedRequestDto
        ) {
            log.info(validatedRequestDto.toString());

            return ResponseEntity.status(HttpStatus.OK).body(validatedRequestDto);
        }

        @PostMapping("/validated/all-group")
        public ResponseEntity<ValidatedRequestDto> checkValidationByValidatedAnnoAllGroup(
                @Validated({ValidationGroup1.class, ValidationGroup2.class})
                @RequestBody ValidatedRequestDto validatedRequestDto
        ) {
            log.info(validatedRequestDto.toString());

            return ResponseEntity.status(HttpStatus.OK).body(validatedRequestDto);
        }
    }

  • ValidationGroup1
    • age 필드를 그룹으로 지정하였다.
    • 따라서, age 필드를 제외한 다른 필드의 값이 유효하지 않더라도 성공으로 처리되는 것을 확인할 수 있다.
      Request
      {
        "name": "", //@NotBlank 어노테이션이 적용되어있으므로 유효하지 않은 상태
        "email": "123", //@Email 어노테이션이 적용되어있으므로 유효하지 않은 상태
        "phoneNumber": "010-1234-1234",
        "age": 40, // MAX 이하이므로 유효한 상태
        "description": "string",
        "count": 0,
        "booleanCheck": true
      }
    • /validated/group1 호출 결과 
      아래 좌측 이미지와 같이, name 과 email 필드가 유효하지 않은 상태임에도 불구하고 200 으로 리턴된걸 확인할 수 있다.
      하지만, group1 로 설정된 age 를 유효하지 않게 호출하면, 우측과 같이 에러가 발생하는 것을 확인할 수 있다.

 


정리하자면, @Validated 어노테이션에 특정 그룹을 설정하는 경우에는, 지정된 그룹으로 설정된 필드에 대해서만 유효성 검사를 수행한다.

all-group 컨트롤러(checkValidationByValidatedAnnoAllGroup())와 같이, 여러 그룹을 지정할 수도 있으며, 마찬가지로 그룹들에 속한 필드들에 대해서만 유효성 검사를 수행한다.