BeanValidation

검증 로직을 모든 프로젝트에 적용할 수 있게 공통화하고 표준화 한 것이 바로 Bean Validation 이다.

스프링에서 BeanValidation 을 사용하려면 우선 gradle 에 의존성을 추가해야한다.

implementation 'org.springframework.boot:spring-boot-starter-validation'

 

스프링 MVC는 어떻게 Bean Validator를 사용?

스프링 부트가 spring-boot-starter-validation 라이브러리를 넣으면

자동으로 Bean Validator를 인지하고 스프링에 통합한다.

 

스프링 부트는 자동으로 글로벌 Validator로 등록한다.

LocalValidatorFactoryBean 을 글로벌 Validator로 등록한다.

스프링 부트는 gradle 에 spring-boot-starter-validation 의존성을 추가하면 자동으로

BeanValidator 를 글로벌 Validator로 등록한다.

이렇게 글로벌 Validator가 적용되어 있기 때문에, @Valid , @Validated 만 적용하면 된다.

검증 오류가 발생하면, FieldError , ObjectError 를 생성해서 BindingResult 에 담아준다.

 

PostController

@PostMapping("/posts")
public String post(@RequestBody @Valid PostCreate params) throws Exception {
    log.info("params = {}", params.toString());
    return "Hello";
}

ㄴ @Valid

위처럼 컨트롤러에서 검증할 객체 앞에 애노테이션 @Valid 를 사용한다.

 

PostCreate

DTO 의 검증하고자 하는 필드에 BeanValidation 에서 제공하는 검증 애노테이션을 사용한다.

여기서는 빈값 / 공백(" ")만 있는 경우를 허용하지 않는 @NotBlank 를 사용한다.

@Setter
@Getter
@ToString
public class PostCreate {

    @NotBlank
    private String title;

    @NotBlank
    private String content;

    public PostCreate(String title, String content) {
        this.title = title;
        this.content = content;
    }
}

ㄴ 검증 애노테이션

  • @NotBlank : 빈값 + 공백만 있는 경우를 허용하지 않는다.
  • @NotNull : null 을 허용하지 않는다.
  • @Range(min = 1000, max = 1000000) : 범위 안의 값이어야 한다.
  • @Max(9999) : 최대 9999까지만 허용한다.

 

이제 결과를 살펴보자.

 

 

스프링MVC에서 컨트롤러에 진입하기도 전에 예외가 400 예외가 발생하기 때문에 컨트롤러가 호출되지도 않았다.

BeanValidation 을 사용하여 잘못된 값이 입력되는건 막았지만 어떤 부분이 잘못 됐는지 알 수가 없다.

에러 메시지를 확인할 수 있는 방법은 없을까?

 


BindingResult

BeanValidation 는 스프링이 제공하는 검증 오류를 보관하는 객체이다.

검증 오류가 발생하면, FieldError , ObjectError 를 생성해서 BindingResult 에 담아준다

BindingResult 를 사용하면 요청 데이터를 PostCreate 객체에 바인딩 시 오류가 발생해도 컨트롤러가 호출된다.

그럼 이제 이 BindingResult 를 이용해서 컨트롤러 로직을 수정하고 JSON 을 검증하는 테스트코드도 개선해보자.

 

PostController

@PostMapping("/posts")
public Map<String, String> post(
			@RequestBody @Valid PostCreate params, 
			BindingResult result
			) throws Exception {
            
    if (result.hasErrors()) {
        List<FieldError> fieldErrors = result.getFieldErrors();
        FieldError fieldError = fieldErrors.get(0);
        String fieldName = fieldError.getField();
        String errorMessage = fieldError.getDefaultMessage();

        Map<String, String> error = new HashMap<>();
        error.put(fieldName, errorMessage);
        return error;
    }
    return Map.of();
}

 

PostCreate

@Setter
@Getter
@ToString
public class PostCreate {

    @NotBlank(message = "제목을 입력해주세요.")
    private String title;

    @NotBlank(message = "내용을 입력해주세요.")
    private String content;

    public PostCreate(String title, String content) {
        this.title = title;
        this.content = content;
    }
}

ㄴ @NotBlank(message = "")

괄호 안에 원하는 메시지를 입력하면 defaultMessage 로 설정 가능하다.

 

PostControllerTest

    @DisplayName("/posts 요청 시 title 값은 필수다.")
    @Test
    void post() throws Exception {
        mockMvc.perform(
                MockMvcRequestBuilders.post("/posts")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(
                    "{\"title\" : null, \"content\" : \"글 내용입니다 하하\"}"
                    )
            )
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.title").value("제목을 입력해주세요."))
            .andDo(print());
    }

ㄴ andExpect(jsonPath("$.field").value("value"));

MockMvc의 jsonpath() 를 이용하면 Json 형식의 API Response를 검증할 수 있다.

이번 테스트에서는 title 값이 비었으니 @NotBlank 에서 검증 오류가 발생하고

응답값 중 title 이라는 field 에 "제목을 입력해주세요." 라는 문자열이 들어올것이다.

 

 

테스트를 돌려보면 성공한다.

MockMvc 의 jsonPath 를 이용하면 JSON 을 검증하는 더 풍부한 테스트를 작성할 수 있다.

'백엔드 > Spring' 카테고리의 다른 글

Filter  (0) 2023.09.29
Interceptor  (0) 2023.09.29
@ModelAttribute 와 @RequestBody 써보기  (0) 2023.09.21
DispatcherServlet  (0) 2023.08.01
Thread Pool  (0) 2023.07.25

+ Recent posts