Bean Validation
- 수정에 적용상품 수정에도 빈 검증(Bean Validation
)을 적용해보자!
edit()
: Item 모델 객체에 @Validated
를 추가하자.editForm
으로 이동하는 코드 추가Bean Validation
- 한계데이터를 등록할 때와 수정할 때는 요구사항이 다를 수 있다.
package hello.itemservice.domain.item;
@Data
public class Item {
@NotNull //수정 요구사항 추가
private Long id;
@NotBlank
private String itemName;
@NotNull
@Range(min = 1000, max = 1000000)
private Integer price;
@NotNull
//@Max(9999) //수정 요구사항 추가
private Integer quantity;
...
}
수정 요구사항을 적용하기 위해 다음을 적용했다.
id
: @NotNull
추가quantity
: @Max(9999)
제거Bean Validation
- groups
동일한 모델 객체를 등록할 때와 수정할 때 각각 다르게 검증하는 방법에 대해 알아보자!
BeanValidation
의 groups
기능을 사용한다.Item
을 직접 사용하지 않고, ItemSaveForm
, ItemUpdateForm
같은BeanValidation - groups
기능 사용이런 문제를 해결하기 위해 Bean Validation
은 groups
라는 기능을 제공한다.
이 groups
기능을 이용해서 등록 시에 검증할 기능과 수정 시에 검증할 기능을 각각 그룹으로 나누어 적용할 수 있다.
Item - groups
적용@Data
public class Item {
@NotNull(groups = UpdateCheck.class) //수정시에만 적용
private Long id;
@NotBlank(groups = {SaveCheck.class, UpdateCheck.class})
private String itemName;
@NotNull(groups = {SaveCheck.class, UpdateCheck.class})
@Range(min = 1000, max = 1000000, groups = {SaveCheck.class, UpdateCheck.class})
private Integer price;
@NotNull(groups = {SaveCheck.class, UpdateCheck.class})
@Max(value = 9999, groups = SaveCheck.class) //등록시에만 적용
private Integer quantity;
...
}
📌
groups
기능 정리
groups
기능을 사용해서 등록과 수정 시에 각각 다르게 검증할 수 있었다.- 하지만 사실
groups
기능은 잘 사용하지 않는다.
- 사용하기 복잡하고,
- 실무에서는 등록용 폼 객체와 수정용 폼 객체를 분리해서 사용하기 때문에
groups
를 적용할 일이 없기 때문이다!
Form
전송 객체 분리 - 프로젝트 준비 V4
Form
전송 객체 분리 - 소개실무에서는 groups
를 잘 사용하지 않는다.
등록 시 폼에서 전달하는 데이터가 Item
도메인 객체와 딱 맞지 않기 때문이다.
그래서 보통 Item
을 직접 전달받는 것이 아니라,
복잡한 폼의 데이터를 컨트롤러까지 전달할 별도의 객체를 만들어서 전달한다.
Item
도메인 객체 사용
HTML Form → Item → Controller → Item → Repository
Item
도메인 객체를 컨트롤러, 리포지토리까지 직접 전달해서 중간에 Item을 만드는 과정이 없어서 간단하다.groups
를 사용해야 한다.
HTML Form → ItemSaveForm → Controller → Item 생성 → Repository
Item
도메인 객체를 폼 전달 데이터로 사용하고, 그대로 쭉 넘기면 편리하겠지만,
실무에서는 Item
의 데이터만 넘어오는 것이 아니라 무수한 추가 데이터가 넘어온다.
그리고 더 나아가서 Item
을 생성하는데 필요한 추가 데이터를 데이터베이스나 다른 곳에서 찾아와야 할 수도 있다.
따라서 이렇게 폼 데이터 전달을 위한 별도의 객체를 사용하고, 등록, 수정용 폼 객체를 나누면 등록, 수정이
완전히 분리되기 때문에 groups
를 적용할 일은 드물다.
Form
전송 객체 분리 - 개발Item
대신 ItemSaveForm
을 전달받는다.
@Validated
로 검증을 수행하고, BindingResult
로 검증 결과를 받는다.
Bean Validation
- HTTP
메시지 컨버터@Valid
, @Validated
는 HttpMessageConverter(@RequestBody)
에도 적용할 수 있다.
Postman
을 이용해서 테스트📌
API
- 성공 요청: 성공
- 실패 요청: JSON을 객체로 생성하는 것 자체가 실패함.
- 검증 오류 요청: JSON을 객체로 생성하는 것은 성공했고, 검증에서 실패함.
price
값에 숫자가 아닌 문자를 전달해서 실패하도록 만들자.
HttpMessageConverter
에서 요청 JSON을 Item
객체를 생성하지 못한다.
이 경우는, 객체가 생성되지 않았기 때문에 컨트롤러 자체가 호출되지 않고 그 전에 예외가 발생한다. 물론 Validator
도 실행되지 않는다.
이번에는 수량(quantity
) 검증 오류가 발생하도록 해보자.
return bindingResult.getAllErrors();
는 ObjectError
와 FieldError
를 반환한다.
스프링이 이 객체를 JSON으로 변환해서 클라이언트에 전달했다.
@ModelAttirbute vs. @RequestBody
@ModelAttribute
는 필드 단위로 정교하게 바인딩이 적용된다.
특정 필드가 바인딩 되지 않아도 나머지 필드는 정상 바인딩 되고,Validator
를 사용한 검증도 적용할 수 있다.
@RequestBody
는HttpMessageConverter
단계에서 JSON 데이터를 객체로 변경하지 못하면 이후 단계 자체가 진행되지 않고 예외가 발생한다.
컨트롤러도 호출되지 않고,Validator
도 적용할 수 없다.