본문 바로가기

Develop/Spring

Spring Validation

728x90

데이터를 믿고 쓰기 위해서는 `Validation`이 필요합니다.

 

Validation

  • 유효성 검증
  • 주로 사용자 또는 타 서버의 요청(Http Request) 내용에서 잘못된 내용이 있는지 확인하는 행위

Validation의 종류

학문적으로는 여러 세부적인 단계들도 있지만 실제로 개발자가 주로 챙겨야하는 검증은 크게 두 종류로 나뉩니다.

 

데이터 검증

  • 데이터에 필수적인 데이터가 있는지 검증
  • 문자열의 길이나 숫자형 데이터의 경우 값의 범위
  • e-mail, 신용카드 번호 등 특정 형식에 맞춘 데이터

비즈니스 검증

  • 서비스의 정책에 따라 데이터를 확인하여 검증
  • 예) 배달 앱인 경우 배달 요청을 할 때 해당 주문건 이 결제 완료 상태인지 확인 등
  • 경우에 따라 외부 API를 호출하거나 DB의 데이터까지 조회하여 검증하는 경우도 존재

Spring의 Validation

스프링은 웹만 사용되지 않기 때문에 웹 레이어에 종속적이지 않은 방법으로 벨리데이션 하려고 의도하고 있으며

주로 아래 두 가지 방법을 활용하여 벨리데이션을 진행합니다. (둘 다 데이터 검증에 가까움)

Java Bean Validation

JavaBean 기반으로 간편하게 개별 데이터를 검증합니다.

요즘에 가장 많이 활용되는 방법 중 하나이며

아래 코드처럼 JavaBean 내에 어노테이션으로 검증 방법을 명시합니다.

public class MemberCreationRequest {
    @NotBlank(message="이름을 입력해주세요.")
    @Size(max=64, message="이름의 최대 길이는 64자 입니다.")
    private String name;
    @Min(0, "나이는 0보다 커야 합니다.")
    private int age;
    @Email("이메일 형식이 잘못되었습니다.")
    private int email;
    
    // the usual getters and setters...
}

위처럼 요청 dto에 어노테이션으로 명시 후 아래처럼 @Valid 어노테이션을 해당 @RequestBody에 달게 되면

Java Bean Validation을 수행한 후 문제가 없을 때만 메서드 내부로 진입이 됩니다.

  • 검증 중 실패가 발생하면 MethodArgumentNotValidException이 발생
@PostMapping(value = "/member")
public MemberCreationResponse createMember (
    @Valid @RequestBody final MemberCreationRequest memberCreationRequest) {
    // member creation logics here...
}

Spring Validator 인터페이스 구현을 통한 validation

public class Person {
    private String name;
    private int age;
    
    // the usual getters and setters...
}

위처럼 Person이라는 JavaBean 객체가 있을 때 아래는 해당 인스턴스에서만 활용되는 validator입니다.

인터페이스에 있는 두 개의 메서드는 아래와 같은 역할을 합니다.

  • supports: 이 validator가 동작할 조건을 정의, 주로 class의 타입을 비교합니다.
  • validate: 원하는 검증을 진행합니다.
public class PersonValidator implements Validator {

    /**
     * This Validator validates only Person instances
     */
    public boolean supports(Class clazz) {
        return Person.class.equals(clazz);
    }
    
    public void validate(Object obj, Errors e) {
        ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
        Person p = (Person) obj;
        if (p.getAge() < 0) {
            e.rejectValue("age", "negativevalue");
        } else if (p.getAge() > 110) {
            e.rejectValue("age", "too.darn.old");
        }
    }
}

Validation 수행 시 주의사항 및 패턴

주의사항

validation이 너무 여러 곳에 흩어져 있으면 테스트 및 유지 보수성이 떨어집니다.

  • 중복된 검증: 정책 변경 시에 모든 중복 코드를 수정해야 할 수 있습니다.
  • 다른 검증: 여러 곳에서 다른 정책을 따르는 검증이 수행될 수 있습니다.

가능한 validation은 로직 초기에 수행 후 실패 시에는 exception을 던지는 것이 처리가 편리합니다.

 

실무 활용 패턴

주로 사용되는 패턴

  • 요청 DTO에서 Java Bean Validation으로 단순 데이터(유무, 범위, 형식 등)을 1차로 검증
  • 로직 초기에 2차로 비즈니스 검증 수행 후 실패 시에는 Custom Exception(ErrorCode, ErrorMessage 를 입력)해서
    예외를 던지도록 하고 예외 처리하여 응답을 생성합니다.

Spring validator의 장점

  • Java Bean Validation에 비해 조금 더 복잡한 검증이 가능합니다.

Spring validator의 단점

  • Validation을 수행하는 코드를 찾기가 상당히 어렵습니다.
  • 완전히 데이터만 검증하는 것이 아니기 때문에 일부 비즈니스적인 검증이 사용되는 경우가 있습니다.
    이 경우 비즈니스 검증 로직이 여러 곳으로 흩어지기 때문에 잘못된 검증(중복 검증, 다른 정책을 따르는 검증) 등
    을 수행할 확률이 높아집니다.

 

이러한 검증 방법이 정답은 아니며
팀 내에서 주로 사용하는 검증 패턴을 따르는 것이 좋습니다.

'Develop > Spring' 카테고리의 다른 글

(작성 중) Spring web.xml  (0) 2022.11.16
Spring SpEL  (0) 2022.11.16
Spring DataBinding  (0) 2022.11.16
Spring AOP  (0) 2022.11.16
Spring Resource  (0) 2022.11.16