DOing
[Spring] Request를 어떻게 검증할 것인가? 본문
이전 프로젝트에서는 Request로 오는 객체를 모두 Service단에서 검증했었다.
그러다보니 Service단이 너무 뚱뚱해지는 기분이 들었다. Service단은 핵심 비지니스 로직에만 집중할 수 있게 만들어야한다고 생각하며 다른 방법을 알아보던 중 Bean validation이라는 것을 알게되었다.
🌱 Bean validation
Bean validation은 클래스의 필드에 annotation을 이용하여 필드가 갖는 제약 조건을 정의하는 구조로 이루어진 검사다.
validator가 그 클래스로 생성된 객체의 유효성 여부를 확인한다.
어떠한 비즈니스적 로직에 대한 검증이 아닌, 객체 자체의 필드에 대한 검증을 한다.
🌱 build.gradle에 의존성 추가
// Spring Boot Bean Validation
implementation("org.springframework.boot:spring-boot-starter-validation")
🌱 RequestDto에서 validation
어노테이션을 이용해서 validation을 해준다.
어떤 어노테이션이 있는지는 공식 문서를 확인하면 된다.
package com.example.highlightbackend.dto.highlight.request;
import lombok.Getter;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Getter
public class HighlightCreateRequestDto {
// 유저의 ID
@NotNull(message = "userId 는 필수 값입니다.")
Long userId;
// 하이라이트한 문장이 있는 페이지의 URL
@NotBlank(message="pageUrl 는 필수 값입니다.")
String pageUrl;
// 하이라이트한 색상값
@NotBlank(message="colorHex 는 필수 값입니다.")
String colorHex;
// 하이라이트한 문장
@NotBlank(message="text 는 필수 값입니다.")
@Size(max=10, message = "text 는 최대 10자까지 지원합니다")
String text;
}
🌱 에러 반환
현재 과정까지만 해준다면 다음과 같은 에러가 출력된다.
콘솔 창에서는 에러 로그가 다음과 같이 출력된다.
2021-09-13 01:42:09.542 WARN 11424 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException:
Validation failed for argument [0] in public com.example.highlightbackend.dto.highlight.response.HighlightCreateResponseDto
com.example.highlightbackend.controller.HighlightController.createHighlight(com.example.highlightbackend.dto.highlight.request.HighlightCreateRequestDto):
[Field error in object 'highlightCreateRequestDto' on field 'text': rejected value [일이삼사오육칠팔구십일];
codes [Size.highlightCreateRequestDto.text,Size.text,Size.java.lang.String,Size];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [highlightCreateRequestDto.text,text];
arguments [];
default message [text],10,0];
default message [text 는 최대 6000자까지 지원합니다]]]
이를 보았을 때 validation을 어기면 400 HTTP status가 반환되며, MethodArgumentNotValidException라는 에러가 발생하는 것을 알 수 있다.
🌱 예외처리
: 예외에 관련되어서 원하는 응답 형태로 주고 싶다면, ExceptionHandler를 만들어 처리해주면된다.
[ ApiException.java ]
package com.example.highlightbackend.exception;
import lombok.Builder;
import lombok.Getter;
import org.springframework.http.HttpStatus;
@Getter
public class ApiException {
private final String message;
private final HttpStatus httpStatus;
@Builder
public ApiException(String message, HttpStatus httpStatus){
this.message = message;
this.httpStatus = httpStatus;
}
}
[ ApiException.java ]
package com.example.highlightbackend.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class ApiExceptionHandler {
@ExceptionHandler(value = {MethodArgumentNotValidException.class})
public ResponseEntity<Object> handle(MethodArgumentNotValidException ex) {
ApiException apiException = ApiException.builder()
.httpStatus(HttpStatus.BAD_REQUEST)
.message(ex.getFieldErrors().get(0).getDefaultMessage())
.build();
return new ResponseEntity<>(apiException, HttpStatus.BAD_REQUEST);
}
}
🌱 @RequestPram와 @PathVariable에서 validation
더보기
아직 조금 불확실!!!
수정중!
@Validated
@RestController
public class TestRestController {
@GetMapping(value = "/test")
public String search(
@Min(1) @RequestParam(value = "number") int number,
@Range(min = 1, max = 10) @RequestParam(value = "keyword") String keyword) {
// number는 최소 1이상
// keyword는 글자수 1~10 사이
return "number: "+number+" keyword: "+keyword;
}
@GetMapping(value = "/test/{number}")
public String getProduct(@Min(1) @PathVariable("number") int number) {
// number 최소 1이상
return "number : "+ number;
}
}
참고 사이트:
https://meetup.toast.com/posts/223
'Spring' 카테고리의 다른 글
[Spring] Spring Data JPA 이해하기 (feat ORM, JPA) (0) | 2021.07.04 |
---|---|
테스트의 중요성과 종류 (0) | 2021.07.03 |
[Spring] POJO란? (1) | 2021.05.29 |
[Spring] Spring Framework의 정의와 목적 (0) | 2021.05.29 |
Domain, Entity, Value(Object) (3) | 2021.05.27 |