개발 공부/Spring

[SpringBoot] Validation 사용하기

journey 2022. 2. 27. 03:27
728x90

01. Spring Boot Validation

  • Validation이란 프로그래밍에 있어서 가장 필요한 부분이다.
  • 특히 Java에서는 null 값에 대해서 접근하려고 할 때 null pointer exception발생 함으로, 이러한 부분을 방지하기 위해서 미리 검증을 하는 과정을 Validation이라고 한다.

단순한 코드

public void run(String account, String pw, int age) {
    if (account == null || pw == null) {
        return
    }
    if (age == 0) {
        return
    }

    // 정상 Logic
}

문제점

  1. 검증해야 할 값이 많은 경우 코드의 길이가 길어진다.
  2. 구현에 따라서 달라 질 수 있지만 Service Logic과의 분리가 필요하다.
  3. 흩어져 있는 경우 어디에서 검증을 하는지 알기 어려우며, 재사용의 한계가 있다.
  4. 구현에 따라 달라질 수 있지만, 검증 Logic이 변경되는 경우 테스트 코드 등 참조하는 클래스에서 Logic이 변경되어야 하는 부분이 발생할 수 있다.

Annotations

     
Annotation 의미 기타
@Size 문자 길이 측정 Int Type 불가
@Notnull null 불가  
@NotEmpty null, “” 불가  
@NotBlank null, “”, “ “ 불가  
@Past 과거 날짜  
@PastOrPresent 오늘이거나 과거 날짜  
@Future 미래 날짜  
@FutureOrPresent 오늘이거나 미래 날짜  
@Pattern 정규식 적용  
@Max 최대값  
@Min 최소값  
@AssertTrue / False 별도 Logic 적용  
@Valid 해당 object validation 실행  

Validation 사용하기

  1. Gradle dependencies
    • implementation 'org.springframework.boot:spring-boot-starter-validation'
  2. bean validation spec
  3. 핸드폰번호 정규식
    • ^\d{2,3}-\d{3,4}-\d{4}$

예제 코드

  • User
public class User {

    private String name;
    private int age;
    private String email;
      private String phoneNumber;

    ... getter / setter ....

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", email='" + email + '\'' +
                ", phoneNumber=" + phoneNumber + '\'' +
                '}';
    }
}
  • Controller
@RestController
@RequestMapping("/api")
public class ApiController {

    @PostMapping("/user")
    public User user(@RequestBody User user) {
        System.out.println(user);

        return user;
    }
}
  • Result

Validation

  • Controller
import com.example.validation.dto.User;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
@RequestMapping("/api")
public class ApiController {

    @PostMapping("/user")
                                                    // Validation에 대한 결과가 바로 에러가 터지지 않고 bindingResult로 값이 들어온다.
    public ResponseEntity user(@Valid @RequestBody User user, BindingResult bindingResult) {

        if (bindingResult.hasErrors()) {
            StringBuilder sb = new StringBuilder();
            bindingResult.getAllErrors().forEach(objectError -> {
                // 어떤 필드에서 어떤 에러가 났는지 값 가져오기.
                FieldError field = (FieldError) objectError;
                // 메세지 가져오기
                String message = objectError.getDefaultMessage();

                System.out.println("field : " + field.getField());
                System.out.println("message : " + message);

                sb.append("field : " + field.getField());
                sb.append("message : " + message);
            });
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(sb.toString());
        }

        // logic

        return ResponseEntity.ok(user);
    }
}

Validation 에러 보기

  • Console 창 결과

    1. 이메일 형식이 ‘asdf@hjkl’이 아닐 때
WARN 1334 --- [nio-9090-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public org.springframework.http.ResponseEntity com.example.validation.controller.ApiController.user(com.example.validation.dto.User): [Field error in object 'user' on field 'email': rejected value [zzanggu]; codes [Email.user.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.email,email]; arguments []; default message [email],[Ljavax.validation.constraints.Pattern$Flag;@35bf8bdf,.*]; default message [올바른 형식의 이메일 주소여야 합니다]] ]
    1. 핸드폰번호 형식이 ‘xxx-xxxx-xxxx’이 아닐 때
WARN 1405 --- [nio-9090-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public org.springframework.http.ResponseEntity com.example.validation.controller.ApiController.user(com.example.validation.dto.User): [Field error in object 'user' on field 'phoneNumber': rejected value [01012345678]; codes [Pattern.user.phoneNumber,Pattern.phoneNumber,Pattern.java.lang.String,Pattern]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.phoneNumber,phoneNumber]; arguments []; default message [phoneNumber],[Ljavax.validation.constraints.Pattern$Flag;@34f826d2,^\d{2,3}-\d{3,4}-\d{4}$]; default message ["^\d{2,3}-\d{3,4}-\d{4}$"와 일치해야 합니다]] ]
  • API 결과