[TIL] #5. 검증2 - Bean Validation ②
Bean Validation
- 수정에 적용
상품 수정에도 빈 검증(Bean Validation
)을 적용해보자!
edit()
: Item 모델 객체에@Validated
를 추가하자.- 검증 오류가 발생하면
editForm
으로 이동하는 코드 추가
Bean Validation
- 한계
데이터를 등록할 때와 수정할 때는 요구사항이 다를 수 있다.
✔ 등록 시 기존 요구사항
- 타입 검증
: 가격, 수량에 문자가 들어가면 검증 오류 처리 - 필드 검증
- 상품명: 필수, 공백 X
- 가격: 1000원 ~ 1백만원
- 수량: 최대 9999
- 특정 필드의 범위를 넘어서는 검증
: 가격 * 수량의 합은 10,000원 이상
✔ 수정 시 요구사항
수정 요구사항 적용
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
의 데이터만 넘어오는 것이 아니라 무수한 추가 데이터가 넘어온다.
그리고 더 나아가서 Item
을 생성하는데 필요한 추가 데이터를 데이터베이스나 다른 곳에서 찾아와야 할 수도 있다.
따라서 이렇게 폼 데이터 전달을 위한 별도의 객체를 사용하고, 등록, 수정용 폼 객체를 나누면 등록, 수정이
완전히 분리되기 때문에 groups
를 적용할 일은 드물다.
Form
전송 객체 분리 - 개발
Item
대신 ItemSaveForm
을 전달받는다.
@Validated
로 검증을 수행하고, BindingResult
로 검증 결과를 받는다.
Bean Validation
- HTTP
메시지 컨버터
@Valid
, @Validated
는 HttpMessageConverter(@RequestBody)
에도 적용할 수 있다.
Postman
을 이용해서 테스트
📌
API
- 성공 요청: 성공
- 실패 요청: JSON을 객체로 생성하는 것 자체가 실패함.
- 검증 오류 요청: JSON을 객체로 생성하는 것은 성공했고, 검증에서 실패함.
1. 성공 요청
2. 실패 요청
price
값에 숫자가 아닌 문자를 전달해서 실패하도록 만들자.
HttpMessageConverter
에서 요청 JSON을 Item
객체를 생성하지 못한다.
이 경우는, 객체가 생성되지 않았기 때문에 컨트롤러 자체가 호출되지 않고 그 전에 예외가 발생한다. 물론 Validator
도 실행되지 않는다.
3. 검증 오류 요청
이번에는 수량(quantity
) 검증 오류가 발생하도록 해보자.
return bindingResult.getAllErrors();
는 ObjectError
와 FieldError
를 반환한다.
스프링이 이 객체를 JSON으로 변환해서 클라이언트에 전달했다.
@ModelAttirbute vs. @RequestBody
@ModelAttribute
는 필드 단위로 정교하게 바인딩이 적용된다.
특정 필드가 바인딩 되지 않아도 나머지 필드는 정상 바인딩 되고,Validator
를 사용한 검증도 적용할 수 있다.
@RequestBody
는HttpMessageConverter
단계에서 JSON 데이터를 객체로 변경하지 못하면 이후 단계 자체가 진행되지 않고 예외가 발생한다.
컨트롤러도 호출되지 않고,Validator
도 적용할 수 없다.
Author And Source
이 문제에 관하여([TIL] #5. 검증2 - Bean Validation ②), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@mmy789/TIL-5.-검증2-Bean-Validation-2ta1ffgg저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)