Spring Boot Validation check

Validation 이란

Java에서 null 값에 대한 접근이 발생할 때 null pointer exception이 발생하므로, 이를 사전에 방지하기 위해 유효성을 검사하는 과정이 Validation 입니다.

일반적인 java 코드에서 validation을 체크하는 예제 코드를 확인해보겠습니다.
parameter의 값이 null이면 exception이 발생할 수 있으므로, null이 들어오면 return을 하여 이를 막아줍니다.

1
2
3
4
5
6
public void showPrice(int price){
if (price == null) {
return ;
}
// 정상 Logic
...

위 코드에서는 검증해야 할 값이 1개지만, 값이 많아질수록 코드가 길어지고, 재사용성에 한계가 있습니다.

또한 유지 보수 측면에서도 바람직하지 않으며 일반적으로 Service Logic과 분리가 필요합니다..

Spring Boot Validation

build.gradle에 implementation 'org.springframework.boot:spring-boot-starter-validation'을 추가하여 사용할 수 있다.

클래스 혹은 원하는 변수위에 Annotation을 추가하여 사용할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
public class User {
@NotBlank
private String name;
@Max(value = 90)
private int age;
@Email
private String Email;
@YearMonth
private String birthday;
...
}
  • @Size : 문자 길이 설정
    • min, max값을 통해 최소, 최대 문자 길이 설정 가능
  • @NotNull : null 불가
  • @NotEmpty : null 또는 “”(빈문자열) 불가
  • @NotBlank : null 또는 “”(빈문자열), “ “(공백) 불가
  • @Past : 과거 날짜
  • @PastOrPresent : 과거 혹은 오늘 날짜
  • @Future : 미래 날짜
  • @Pattern : 정규식 적용
    • regex = “[정규식]” 과 같이 사용할 수 있다.
  • @Max, @Min : 최대, 최소값 설정
  • @Valid : 해당 object에 대해 validation 실행
    • 아래 예시와 같이 ResponseBody로 들어오는 User객체에 대해 Validation을 실행해줍니다.
    • 객체의 왼쪽, 오른쪽 아무 곳에나 붙여도 상관 없습니다.
      1
      2
      3
      public ResponseEntity post(@Valid @RequestBody User user, BindingResult bindingResult){
      ...
      }

Custom Validation

원하는 Validation이 존재하지 않을 때 custom으로 생성하여 사용하는 예제를 확인해보겠습니다.

먼저 Annotation을 생성해주고, 이를 통작하게 해줄 Validator를 구현해주어야합니다.

저는 아래 예시를 통해 올바른 연(Year), 월(Month) 형식이 들어오는지 체크하는 Annotation을 생성해보겠습니다.

1. Annotation 생성

  • Annotation의 Annotation (Annotation의 범위, 타겟 등 설정)

    • Constraint : 어떤 클래스(validator)에 의해 유효성 검사가 될 것인지 설정
    • Target에는 임의로 메서드, 필드 등을 넣어주었습니다.
    • 실행 중일 때 동작하도록 Retention은 RUNTIME으로 설정하였습니다.
  • 메타 정보 생성

    • message() : 유효하지 않을 시 출력해주는 message를 관리하기 위해 사용됩니다.
    • groups() : Validation을 적용할 그룹을 제한할 수 있습니다.
    • payload() : Custom Annotation에 관련된 메타 정보를 정의합니다.
      • Error가 발생했을 때 심각도를 나타냅니다.
      • payload = Severity.Error.class와 같이 사용합니다.
    • pattern() : 날짜 검증을 위해서 기본 형식을 설정해주었습니다.

img.png

1
2
3
4
5
6
7
8
9
10
11
12
13
@Constraint(validatedBy = {YearMonthValidator.class})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface YearMonth {

String message() default "yyyyMMdd 형식으로 입력하세요";

Class<?>[] groups() default { };

Class<? extends Payload>[] payload() default { };

String pattern() default "yyyyMMdd";
}

2. Validator 생성

위에서 생성한 @YearMonth를 동작시키기 위해서 Validator가 필요합니다.

1. ConstraintValidator<**동작시킬 Annotation**, **검증 객체 타입**>을 implements 해줍니다.

2. 객체 검증 기준 생성

  • 위에서 Annotation을 생성할 때, 검증할 때 기준이 되는 String을 pattern()으로 초기화시켜놓았습니다.
  • 이 패턴을 가져오기 위해 같은 자료형의 변수를 생성해줍니다.

3. initailize(YearMonth constraintAnnotation)메서드를 Override해줍니다.

  • Argument로 들어있는 YearMonth에 Custom Annotation을 넣어주면 됩니다.
  • 2번에서 생성한 변수에 Annotation에서 초기화해주었던 pattern을 넣어줍니다.

4. isValid(String value, ContraintValidatorContext context) 메서드 Override

  • 비교값과, 정해놓은 기준을 비교하여 유효성을 체크해주는 함수입니다.
  • 저는 애초에 연(Year), 월(Month)를 체크하고자 Custom Validator를 만들고 있었습니다.
  • LocalDate로는 연, 월, 일 값만 비교할 수 있기 때문에, 모든 월이 가지고 있는 01일을 비교값에 추가해주었습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class YearMonthValidator implements ConstraintValidator<YearMonth, String> {

private String pattern;

@Override
public void initialize(YearMonth constraintAnnotation) {
this.pattern = constraintAnnotation.pattern();
}

@Override
public boolean isValid(String value, ConstraintValidatorContext context) {

try{
LocalDate localDate = LocalDate.parse(value+"01", DateTimeFormatter.ofPattern(pattern));
} catch (Exception e){

return false;
}
return true;
}
}

Custom Validation 사용

Target에 Field를 입력해주었기 때문에, 변수에도 사용할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

아래와 같이 birthday라는 변수 위에 Annotation을 추가해주었으며,

```java
public class User {
@NotBlank
private String name;
@Max(value = 90)
private int age;
@Email
private String Email;
@YearMonth
private String birthday;
...
}

컨트롤러 단에서 User 객체의 멤버 변수에 설정한 Annotation들이 동작하도록 @Valid라는 Annotation을 입력해주었습니다.

1
2
3
public ResponseEntity post(@Valid @RequestBody User user, BindingResult bindingResult){
...
}
Author

Inwoo Jeong

Posted on

2021-10-25

Updated on

2021-10-26

Licensed under

You need to set install_url to use ShareThis. Please set it in _config.yml.

댓글