Book/스프링부트 핵심가이드

@Validated, 커스텀 어노테이션을 이용한 유효성 검사

블로그 주인장 2023. 11. 23.

유효성 검사 활용

스프링부트에서 유효성 검사를 하기 위해 사용하는 Validation 어노테이션을 이용하는 방법에 대해 알아보겠습니다.


@Validated 활용


  • @Valid : 유효성 검사를 수행하는 어노테이션
  • @Validated
    1. 별도의 어노테이션으로 유효성 검사를 지원하는데, @Valid 어노테이션의 기능을 포함한다.
    2. 그룹으로 묶어 대상을 특정할 수 있는 기능이 있다.

@Validated 어노테이션 사용을 위한 검증 그룹 생성

 

[예시] DTO 객체에 그룹 설정

@Data
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ValidatedRequestDto {
    
    //.. 중략
    
    @Min(value = 20, groups = ValidationGroup1.class)
    @Max(value = 40, groups = ValidationGroup1.class)
    int age;
}
  • @Min, @Max 어노테이션의 groups 속성을 사용
  • 해당 설정을 이용하여 어느 그룹에 맞춰서 유효성 검사를 실시할 것인지 지정한 것이다.

 

[예시] @Validated Controller 설정

@RestController
@RequestMapping("/validation")
public class ValidationController {

    private final Logger LOGGER = LoggerFactory.getLogger(ValidationController.class);
    
    @PostMapping("/validated")
    public ResponseEntity<String> checkValidation(
            @Validated @RequestBody ValidatedRequestDto validatedRequestDto
    ) {
        LOGGER.info(validatedRequestDto.toString());
        return ResponseEntity.status(HttpStatus.OK).body(validatedRequestDto.toString());
    }

    @PostMapping("/validated/group1")
    public ResponseEntity<String> checkValidation1(
            @Validated(ValidationGroup1.class)
            @RequestBody ValidatedRequestDto validatedRequestDto
    ) {
        LOGGER.info(validatedRequestDto.toString());
        return ResponseEntity.status(HttpStatus.OK).body(validatedRequestDto.toString());
    }

    @PostMapping("/validated/group2")
    public ResponseEntity<String> checkValidation2(
            @Validated(ValidationGroup2.class)
            @RequestBody ValidatedRequestDto validatedRequestDto
    ) {
        LOGGER.info(validatedRequestDto.toString());
        return ResponseEntity.status(HttpStatus.OK).body(validatedRequestDto.toString());
    }

    @PostMapping("/validated/all-group")
    public ResponseEntity<String> checkValidation3(
            @Validated({ValidationGroup1.class, ValidationGroup2.class})
            @RequestBody ValidatedRequestDto validatedRequestDto
    ) {
        LOGGER.info(validatedRequestDto.toString());
        return ResponseEntity.status(HttpStatus.OK).body(validatedRequestDto.toString());
    }

}
  • @Validated 어노테이션에 특정 그룹을 지정하지 않은 경우 groups 속성을 설정하지 않은 필드에 대해서만 유효성 검사를 실시하게 된다.
  • @Validated 어노테이션에 특정 그룹을 설정하는 경우에는 지정된 그룹으로 설정된 필드에 대해서만 유효성 검사를 진행한다.

 

커스텀 Validation 추가


  • 자바 또는 스프링의 유효성 검사 어노테이션에서 제공하지 않은 기능을 사용할 경우가 있다.
  • 이 경우 ConstraintValidator와 커스텀 어노테이션을 조합해서 별도의 유효성 검사 어노테이션을 생성할 수 있다.
  • ex) 동일한 정규식을 계속 쓰는 @Pattern 어노테이션

 

[예제] 전화번호 형식 일치 확인 어노테이션

public class TelephoneValidator implements ConstraintValidator<Telephone, String> {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        if (value == null) {
            return false;
        }
        return value.matches("^01(?:0|1|[6-9])[.-]?(\\d{3}|\\d{4})[.-]?(\\d{4})$");
    }
}
  • 인터페이스를 선언할 때는 어떤 어노테이션 인터페이스 타입인지 지정해야한다.
  • ConstraintValidator 인터페이스는 inValid( ) 메서드를 정의하고 있다.
  • 해당 메서드를 구현하려면 null에 대한 허용 여부 로직 추가하고, 지정한 정규식과 비교해서 알맞은 형식인지 검사한다.
  • false 가 리턴되면 MethodArgumentNotValidException 예외가 발생한다.

 

[예제] 전화번호  어노테이션 인터페이스

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = TelephoneValidator.class)
public @interface Telephone {
    String message() default "전화번호 형식이 일치하지 않습니다.";
    Class[] groups() default {};
    Class[] payload() default {};
}
  • @Target 어노테이션
    • 이 어노테이션을 어디서 선언할 수 있는지 정의하는 데 사용한다.
    • ex) ElementType.PACKAGE, ElementType.FIELD, ElementType.TYPE, ElementType.METHOD ...
  • @Retention 어노테이션
    • 실제로 적용되고 유지되는 범위를 의미한다.
    • @RetentionPolicy.RUNTIME : 컴파일 이후에도 JVM에 의해 계속 참조된다. 리플렉션이나 로깅에 많이 사용되는 정책이다.
    • @RetentionPolicy.CLASS : 컴파일러가 클래스를 참조할 때까지 유지한다.
    • @RetentionPolicy.SOURCE : 컴파일 전까지만 유지된다. 컴파일 이후에는 사라진다.
  • @Constraint 어노테이션
    • Validator와 매핑하는 작업을 수행한다.
    • message( ) : 유효성 검사가 실패할 경우 반환되는 메시지를 의미
    • groups( ) : 유효성 검사를 사용하는 그룹으로 설정
    • payload( ) : 사용자가 추가 정보를 위해 전달하는 값

 

[예제] 전화번호  어노테이션을 이용한 DTO 클래스

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

    //.. 중략

    @Telephone
    String phoneNumber;
}
반응형

댓글