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 |