Bean Validation을 사용하려면 의존관계를 추가
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-validation'
@NotBlank
: 빈값 + 공백만 있는 경우 허용 X@NotNull
: null을 허용 X@Range(min = 1000, max = 1000000)
: 범위 안의 값@Max(9999)
: 최대 9999까지만 허용스프링과 통합하면 검증기를 생성하는 코드를 작성 할 필요는 없음
spring-boot-starter-validation
라이브러리를 넣으면 자동으로 Bean Validator를 인지하고 스프링에 통합@Validated
나 @Valid
를 사용하면 애너테이션 기반으로 검증하는 검증기가 자동으로 검증하고 결과를 bindingResult에 넣어줌public String addItem(@Validated @ModelAttribute Item item,
BindingResult bindingResult, RedirectAttributes redirectAttributes) {
...
}
바인딩에 성공한 필드만 Bean Validation 적용
@NotBlank
errors.properties
에 메세지를 등록
#Bean Validation 추가
NotBlank={0} 공백X
Range={0}, {2} ~ {1} 허용
Max={0}, 최대 {1}
@NotBlank(message = "공백! {0}")
공백일 수 없습니다.
@ScriptAssert()
를 사용해서 처리@ScriptAssert(lang="javascript",script="_this.price * _this.quantity >= 10000 ",
message = "총합이 10000원 넘게 입력해주세요.")
//특정 필드가 아닌 복합 룰 검증
if(item.getPrice()!=null&& item.getQuantity()!=null){
int resultPrice = item.getPrice() * item.getQuantity();
if (resultPrice<10000){
bindingResult.reject(
"totalPriceMin",new Object[]{10000,resultPrice},null);
}
}
동일한 모델 객체를 등록할 때와 수정할 때 각각 다르게 검증하는 방법
그룹을 체크하기 위한 체크 인터페이스(SaveCheck, UpdateCheck) 생성후 적용
item.class
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;
...
}
ValidationItemControllerV3.class
public String addItemV2(@Validated(SaveCheck.class) @ModelAttribute Item item,
BindingResult bindingResult, RedirectAttributes redirectAttributes)
사실 groups 기능은 실제 잘 사용되지는 않음!!
실무에서는 주로 등록용 폼 객체와 수정용 폼 객체를 분리해서 사용
데이터 전달에 Item 도메인 객체 사용
HTML Form -> Item -> Controller -> Item -> Repository
폼 데이터 전달을 위한 별도의 객체 사용
HTML Form -> ItemSaveForm -> Controller -> Item 생성 -> Repository
ItemSaveForm.class
@Data
public class ItemSaveForm {
@NotBlank
private String itemName;
@NotNull
@Range(min=1000, max=1000000)
private Integer price;
@NotNull
@Max(value = 9999)
private Integer quantity;
}
ItemUpdateForm.class
@Data
public class ItemUpdateForm {
@NotNull
private Long id;
@NotBlank
private String itemName;
@NotNull
@Range(min=1000, max=1000000)
private Integer price;
//수정에서는 수량은 자유롭게 변경할 수 있다.
private Integer quantity;
}
ValidationItemControllerV3.class
public String addItem(@Validated @ModelAttribute("item") ItemSaveForm form,
BindingResult bindingResult, RedirectAttributes redirectAttributes) {
...
// 폼 객체를 Item으로 변환
Item item=new Item();
item.setItemName(form.getItemName());
item.setPrice(form.getPrice());
item.setQuantity(form.getQuantity());
Item savedItem = itemRepository.save(item);
...
}
@Validated
로 검증도 수행하고, BindingResult
로 검증 결과를 받음@ModelAttribute("item")
에 item을 넣어주지 않으면 규칙에 의해 itemSaveForm 이라는 이름으로 model에 저장됨 > 뷰 템플릿 수정해야함@Valid
, @Validated
는 HttpMessageConverter(@RequestBody
)에도 적용 가능
@ResponseBody
public Object addItem(@RequestBody @Validated ItemSaveForm form,
BindingResult bindingResult){
log.info("API 컨트롤러 호출");
if(bindingResult.hasErrors()){
log.info("검증 오류 발생 errors={}",bindingResult);
return bindingResult.getAllErrors();
//Field error, Object error 모두 반환
}
log.info("성공 로직 실행");
return form;
}
API의 3가지 경우
@ModelAttribute vs @RequestBody