플레이데이터 - 42일차 Spring-Boot Framework(7)

Kim Hyen Su·2023년 8월 30일

🌟유효성 검사와 예외처리

  • 2009년 부터 Bean Validation이라는 데이터 유효서 검사 프레임워크를 제공.
  • Bean Validation은 어노테이션을 통해서 다양한 데이터 검증 기능을 제공.

✅Hibernate Validator

  • Bean Validation 명세의 구현체.

✅Spring-boot 에서 유효성 검사

  • 원래 스프링 부트의 유효성 검사 기능은 spring-boot-web에 포함되어 있지만, 스프링 부트 2.3 버전 이후로 별도의 라이브러리로 spring-boot-starter-validation을 제공.

1. 유효성 검사 관련 의존성 추가

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

2. 유효성 검사

  • 유효성 검사는 계층으로 데이터가 넘어오는 시점에 해당 데이터에 대한 검사를 실시함.
  • 일반적으로 DTO를 사용하여 계층 간에 데이터 전송에 활용하기 때문에 DTO 객체를 대상으로 유효성 검사를 수행한다.

ValidRequestDto 객체

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

    @NotBlank
    String name;

    @Email
    String email;

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

    @Min(value=20) @Max(value = 40)
    int age;

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

    @Positive
    int count;

    @AssertTrue
    boolean booleanCheck;

}

대표 어노테이션

문자열 검증

최댓값/최솟값 검증

값의 범위 검증

시간에 대한 검증

이메일 검증

자릿수 범위 검증

Boolean 검증

문자열 길이 검증

정규식 검증

3. API Test

ValidationController 생성

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

    private final Logger LOGGER = LoggerFactory.getLogger(ValidationController.class);

    @PostMapping("/valid")
    public ResponseEntity<String> checkValidationByValid(
            @Valid @RequestBody ValidRequestDto validRequestDto){
        LOGGER.info(validRequestDto.toString());
        return ResponseEntity.status(HttpStatus.OK).body(validRequestDto.toString());
    }
}
  • Swagger2 라이브러리를 사용하여 checkValidationByValid() 메서드 호출 및 test

SWAGGER 화면

  • Swagger에서 애플리케이션 호출 시 정상 응답하면 200 code 가 응답.

SWAGGER 화면

  • 유효성 검증에 벗어나는 값으로 변경하여 호출 시, 400 code(Bad Request) 응답.

SWAGGER 화면

오류메시지

[Field error in object 'validRequestDto' on field 'age': rejected value [19];
codes [Min.validRequestDto.age,Min.age,Min.int,Min]; arguments 
[org.springframework.context.support.DefaultMessageSourceResolvable: codes
[validRequestDto.age,age]; arguments []; default message [age],20];
default message [20 이상이어야 합니다]] ]

🎇 @Validated 활용

  • @Valid는 자바에서 지원하는 어노테이션이며, 스프링은 @Validated 라는 어노테이션을 지원한다.
  • @Validated은 @Valid 어노테이션의 긴응을 포함하고 있다.

ValidatedRequestDto 객체

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Builder
public class ValidatedRequestDto {
    @NotBlank
    private String name;

    @Email
    private String email;

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

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

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

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

    @AssertTrue
    private boolean booleanCheck;
}

group/ValidationGroup1 & group/ValidationGroup2 인터페이스 추가

  • 위 경로에 group 설정을 위한 인터페이스 2개를 생성.

ValidationController 수정

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

    private final Logger LOGGER = LoggerFactory.getLogger(ValidationController.class);

    @PostMapping("/valid")
    public ResponseEntity<String> checkValidationByValid(
            @Valid @RequestBody ValidRequestDto validRequestDto){
        LOGGER.info(validRequestDto.toString());
        return ResponseEntity.status(HttpStatus.OK).body(validRequestDto.toString());
    }

    @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({ValidationGroup2.class, ValidationGroup1.class}) @RequestBody ValidatedRequestDto validatedRequestDto){
        LOGGER.info(validatedRequestDto.toString());
        return ResponseEntity.status(HttpStatus.OK).body(validatedRequestDto.toString());
    }

}
  • @Validated 어노테이션에 특정 그룹을 설정하지 않은 경우, groups가 설정되지 않은 필드에 대해 유효성 검사 수행.
  • @Validated 어노테이션에 특정 그룹을 설저한 경우, 지정된 그룹으로 설정된 필드에 대해서만 유효성 검사 수행.

🎇 커스텀 Validation 추가

  • 자바 또는 스프링의 유효성 검사 어노테이션에서 제공하지 않는 기능을 써야할 때 커스텀 Validation을 추가한다.
  • ConstraintValidator와 커스텀 어노테이션을 조합해서 별도의 유효성 검사 어노테이션을 생성할 수 있다.

ConstraintValidator 인터페이스 구현

public class TelephoneValidator implements ConstraintValidator<Telephone,String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if(value == null){
            return false;
        }
        return value.matches("01(?:0|1|[6-9])[.-]?(\\d{3}|\\d{4})[.-]?(\\d{4})$");
    }
}
  • 위 로직에서 false가 반환되면, MethodArgumentNotValidException 예외가 발생.

Telephone 어노테이션 인터페이스 추가

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = TelephoneValidator.class)
public @interface Telephone {
    String message() default "전화번호 형식이 일치하지 않습니다.";

    Class[] groups() default {};

    Class[] payload() default {};
}
  • @Target : 어노테이션을 어디서 선언할 수 있는지 정의.
  • @Retention : 어노테이션이 실제 적용되고 유지되는 범위를 의미.
  • message() : 유효성 검사가 실패할 경우 반환되는 메시지를 의미.
  • groups() : 유효성 검사를 사용하는 그룹으로 설정.
  • payload() : 사용자가 추가 정보를 위해 전달하는 값.

✅예외 처리

🎇예외와 에러

🎇예외 클래스

🎇예외 처리 방법

🎇스프링 부트의 예외 처리 방식

🎇커스텀 예외

🎇커스텀 예외 클래스 생성하기

profile
백엔드 서버 엔지니어

0개의 댓글