반응형
이전글 : 2024.11.07 - [BE/🍃 Spring] - [Validation] spring boot validation 사용법과 종류
:: Spring Boot :: v3.3.5
@Valid와 @Validated
- Valid와 Validated 검증 실패시 exception 발생
- @Valid
- @Valid는 jakarta.validation.Valid에 속해 있고 RequestBody로 전달된 요청 객체를 검증하는데 사용됨
- @Validated
- @Validated는 org.springframework.validation.annotation.Validated에 속해 있고, AOP 기반으로 동작
- @RequestParam, @PathVariable에서 전달되는 데이터의 유효성을 검증
- Controller가 아닌 곳 (ex Service) 에서 데이터의 검증이 필요한 경우 사용
예제 코드
BookApiController.java
@RestController
@RequestMapping("/api/v1/book")
@Validated
public class BookApiController {
@PostMapping
public String regist(
@RequestBody @Valid RegistBookRequest request
) {
return request.toString();
}
@GetMapping("/{book_name}")
public String detail(
@PathVariable(name = "book_name") @Size(min = 3, max = 20, message = "책의 이름은 3~20 자리로 등록해 주세요.")
String bookName
) {
return bookName;
}
}
RegistBookRequest.java
@Getter
@ToString
@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
public class RegistBookRequest {
@Size(min = 3, max = 20, message = "책의 이름은 3~20 자리로 등록해 주세요.")
private String bookName;
@NotBlank(message = "카테고리 값은 필수 입니다.")
private String category;
@Past(message = "발행일은 과거날짜만 가능합니다. ")
private LocalDate issuedDay;
@Max(value = 100, message = "최대로 등록할 수 있는 수량은 100개 입니다.")
private int amount;
}
Validation시 발생 가능한 Exception
MethodArgumentNotValidException
- MethodArgumentNotValidException은 Controller에서 @RequestBody를 @Valid로 검증시 발생
- BindingResult로 필드별 오류를 확인할 수 있음
// http://localhost:8080/api/v1/book
{
"book_name": "te",
"category": "철학",
"issued_day": "2023-12-12",
"amount": 101
}
book_name과 amount로 유효하지 않은 데이터를 보냈을 때
Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.lang.String cohttp://m.youable.validation.controller.BookApiController.regist(com.youable.validation.dto.request.RegistBookRequest) with 2 errors: [Field error in object 'registBookRequest' on field 'bookName': rejected value [te]; codes [Size.registBookRequest.bookName,Size.bookName,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [registBookRequest.bookName,bookName]; arguments []; default message [bookName],20,3]; default message [책의 이름은 3~20 자리로 등록해 주세요.]] [Field error in object 'registBookRequest' on field 'amount': rejected value [101]; codes [Max.registBookRequest.amount,Max.amount,Max.int,Max]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [registBookRequest.amount,amount]; arguments []; default message [amount],100]; default message [최대로 등록할 수 있는 수량은 100개 입니다.]] ] |
- 검증의 실패한 모든 필드에 대한 데이터가 로그로 출력되는 것을 확인할 수 있음
- 필드명, 어떤 validation이 사용되었는지, default message로 지정한 문자열 확인 가능
- 이를 이용해서 공통 규격의 response를 클라이언트에게 전달해 줄 수 있음
HandlerMethodValidationException
- Controller에 AOP기반의 @Validation이 없는 경우 스프링의 MethodValidator 기능이 사용됨
- org.springframework.web.method.annotation.HandlerMethodValidationException이 발생
@RestController
@RequestMapping("/api/v1/book")
public class BookApiController {
@GetMapping("/find")
public String find(
@RequestParam(name = "book_name") @Size(min = 3, max = 20, message = "책의 이름은 3~20 자리로 등록해 주세요.")
String bookName,
@RequestParam(name = "amount") @Max(value = 10, message = "한번에 구매할 수 있는 수량은 최대 10개")
int amount
) {
return bookName + " " + amount;
}
}
org.springframework.web.method.annotation.HandlerMethodValidationException: 400 BAD_REQUEST "Validation failure" |
ConstraintViolationException
- 메서드의 파라미터, 리턴 값에 대한 유효성 검증 실패시 발생
- aop기반으로 검증 수행
- Spring 6.1 미만 버전에서 발생
// http://localhost:8080/api/v1/book/te
@RestController
@RequestMapping("/api/v1/book")
@Validated
public class BookApiController {
@GetMapping("/{book_name}")
public String detail(
@PathVariable(name = "book_name") @Size(min = 3, max = 20, message = "책의 이름은 3~20 자리로 등록해 주세요.")
String bookName
) {
return bookName;
}
}
2024-11-08T10:20:21.490+09:00 ERROR 65319 --- [validation] [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: jakarta.validation.ConstraintViolationException: detail.bookName: 책의 이름은 3~20 자리로 등록해 주세요.] with root cause jakarta.validation.ConstraintViolationException: detail.bookName: 책의 이름은 3~20 자리로 등록해 주세요. |
- 검증에 실패한 [메서드명.필드명]을 알 수 있음
HttpMessageNotReadableException
- validation 검증 로직이 아닌, json 문자열을 객체로 파싱하는 과정에서 타입이 맞지 않아 발생하는 exception
- 검증 로직은 아니지만 request 객체를 전달 받는 과정에서 발생할 수 있는 문제이므로 에러 핸들링 필요
- 아래 예시 코드 참고
// http://localhost:8080/api/v1/book
{
"book_name": "te",
"category": "철학",
"issued_day": "2023-12-12",
"amount": "총수량"
}
int 형인 amount를 유효하지 않은 String으로 보냈을 때
[validation] [nio-8080-exec-6] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `int` from String "총수량": not a valid `int` value] |
- int 타입을 String 데이터로 받아 JSON 파싱에 에러가 발생했다는 것을 확인할 수 있음
- 클라이언트에게 어떤 문제가 발생하는지 확인 시켜주는 공통 규격의 response를 클라이언트에게 전달해줄 수 있음
이 외에도 업무하며 마주한 request 데이터 관련 에러가 몇가지 더 있는데, 이러한 에러를 클라이언트가 알 수 있게 제공하지 않으면 클라이언트는 이 에러가 왜 발생하는지 알 수 없음
- MethodArgumentTypeMismatchException : 클라이언트 request시 전달된 파라미터의 타입이 정의된 Controller 메서드의 파라미터 타입과 일치하지 않을 때 발생 (long 타입으로 정의했으나 파싱 불가능한 문자열이 전달된 경우 등)
@GetMapping("/school")
public String getSchoolById(@RequestParam("id") Long id) {
}
- MissingServletRequestParameterException : 필수 파라미터가 누락된 경우
@GetMapping("/test")
public String getById(@RequestParam(name = "id", required = true) Long id) {
// 생략
}
- MissingServletRequestPartException : multipart/form-data 가 아닌 다른 타입으로 보내거나, 데이터를 누락하는 경우 등에 발생
@PostMapping("/upload")
public String handleFileUpload(@RequestPart("file") MultipartFile file) {
// 생략
}
👉 다음 포스팅에서는 이 에러를 핸들링 하는 방법을 알아보겠습니다.
728x90
반응형
'BE > 🍃 Spring' 카테고리의 다른 글
[Spring Boot] Custom Validation annotation (0) | 2024.11.10 |
---|---|
[Spring Boot] RestControllerAdvice로 Validation Exception 핸들링하기 (1) | 2024.11.09 |
[Validation] spring boot validation 사용법과 종류 (0) | 2024.11.07 |
[Spring Boot] Controller와 RestController 차이와 ResponseBody (2) | 2024.09.22 |
Spring boot 프로젝트에 SSL 인증서 적용하기 (0) | 2024.07.15 |
댓글