본문 바로가기
IT 개인 공부/Spring

[Spring] Bean Validation

by Libi 2021. 9. 23.
반응형

특정 필드에 대해 검증해야 할 경우가 발생할 수 있다.

예를 들어 회원가입 기능을 구현한다고 해보자. 이름(String)과 나이(Integer)는 값이 무조건 입력되어야 하는 필수요소라고 하자.

 

클라이언트의 실수로 값을 입력하지 않는 경우, 범위를 벗어나거나는 경우, 나이에 Integer가 아닌 String 타입의 값을 입력하는 경우 등 잘못된 상황이 발생할 수 있다.

이때 우리는 검증을 통해 클라이언트에게 잘못된 값을 입력하였다는 것을 알려줄 수 있어야 한다. 그래야 클라이언트는 올바른 값을 입력할 것이기 때문이다.

 

이는 BindingResult를 활용하면 해결할 수 있다. 하지만 FieldError나 ObjectError를 통해 매번 코드로 검증 기능을 작성하는 일은 상당히 번거로울 수 있다.

특히 특정 필드에 대해 타입 검증을 제외한 검증 로직은 대부분 빈 값인지 아닌지, 특정 범위를 벗어나는지 아닌지와 같은 매우 일반적인 로직일 것이다.

이때 Bean Validation을 활용하면 검증 로직을 구현할 필요 없이 어노테이션만으로 보다 편리하게 검증 로직을 적용할 수 있다.

 

Bean Validation은 검증 애노테이션과 여러 인터페이스의 모음이다.

JPA(표준 기술) - 하이버네이트(구현체)의 관계처럼 일반적으로 Bean Validation(표준 기술) - 하이버네이트 Validator(구현체)로 사용된다.

참고로 이름에 하이버네이트가 붙어있지만 ORM과는 관련이 없는 기술이다.

즉, 순수한 Bean Validation을 사용하는 방법도 있지만, JPA - 하이버네이트 관계처럼 Bean Validation 역시 Spring과 통합해서 더욱 간편하게 사용할 수 있도록 지원한다.

 

Bean Validation의 검증 순서는 다음과 같다.

  • 먼저 각각의 필드에 타입 변환을 시도
  • 타입 변환을 성공한 경우에만 추가적으로 Bean Validation 적용

 

위와 같은 회원가입 상황에서 입력 Form을 통해 Member의 정보를 입력받을 경우, 각각의 필드에 검증이 필요하다면 다음과 같이 Bean Validation의 어노테이션을 선언해주면 된다.

@Data
public class Member {

    @NotEmpty
    private String name;

    @Min(1) @Max(100)
    @NotNull
    private Integer age;
}

 

name 필드는 아무런 값을 입력하지 않는 경우 잘못된 입력이라고 판단하고, age 필드는 1~100을 벗어나거나 Null 값을 입력하는 경우 잘못된 입력이라고 판단하게 된다.

 

Controller에서 다음과 같이 검증이 필요한 Member 객체 앞에 @Validated 어노테이션을 선언해주면 잘못된 값이 입력될 경우 BindingResult에 적절한 error를 넣어준다.

이때 주의해야 할 점은 BindingResult의 위치는 반드시 검증하는 Member 객체 뒤쪽에 와야 한다.

@Slf4j
@Controller
public class JoinController {

    @GetMapping("/join")
    public String joinForm(@ModelAttribute Member member) {
        return "joinForm";
    }

    @PostMapping("/join")
    public String join(@Validated @ModelAttribute Member member, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            log.info("검증 오류 발생 errors={}", bindingResult);
            return "joinForm";
        }

        return "join";
    }
}

 

일일이 검증 로직을 구현할 필요 없이 간단한 검증 로직은 Bean Validation 어노테이션을 적용하여 쉽게 해결할 수 있다.

 

Bean Validation은 @NotEmpty, @NotNull, @Min, @Max와 같이 다양한 어노테이션을 지원한다.

 

이들에 대해선 https://docs.jboss.org/hibernate/validator/6.2/reference/en-US/html_single/#section-builtin-constraints 를 참고하면 자세하게 알 수 있다.

 

 

마지막으로 @NotEmpty와 @NotNull의 차이점이다.

개발 초기에 Bean Validation의 정의에 대해 자세히 모른 상태로 사용했다가 실수한 내용이다.

 

두 어노테이션의 의미를 단순하게 생각해보면 빈 값을 검증하는 것으로 착각할 수 있다.

NotEmpty는 빈 값이 아니어야 한다는 의미인데 NotNull도 Null 값이 아니어야 한다는 의미이기 때문에 공백이 아니어야 한다는 동일한 의미로 해석할 수 있기 때문이다.

 

따라서 Integer 타입의 나이 변수에도 빈 값을 입력하면 안된다는 의미로 자연스럽게 @NotEmpty 어노테이션을 적용했지만 오류가 발생하였다.

 

처음에는 NotEmpty는 빈 값이 아니어야 한다는 의미니깐 Reference 타입의 모든 값에 적용돼야 하는 것이 아닌가?라는 의문을 가지면서 이해하지 못하였다.

 

하지만 이는 해당 어노테이션의 정의를 확인하면 이해할 수 있다.

빈 값이라는 의미를 가지는 어노테이션은 @NotBlank, @NotEmpty, @NotNull이 존재하며 이들에 대한 정의는 다음과 같다.

 

즉, @NotEmpty는 빈 값을 검증하긴 하지만, Integer 타입의 값을 지원하지 않기 때문에 오류가 발생하는 것이다.

 

 

 

 

[ Reference ]

· https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard 

· https://docs.jboss.org/hibernate/validator/6.2/reference/en-US/html_single/#section-builtin-constraints

반응형

댓글