https://eckrin.tistory.com/entry/Spring-MVC-%EA%B2%80%EC%A6%9D
앞서 요청 검증방법에 대해서 알아봤다.
하지만 검증 기능을 지금처럼 매번 코드로 작성하는 것은 매우 번거롭다. 따라서 어노테이션을 이용한 검증방법인 Bean Validation에 대해서 알아보자. Bean Validation은 검증 어노테이션과 인터페이스들의 모음이며, 일반적으로 하이버네이트 Validator를 사용한다.
Bean Validation
Bean Validation을 사용하면 다음과 같은 검증 어노테이션들을 지원한다.
@NotBlank : 빈값 + 공백만 있는 경우를 허용하지 않는다.
@NotNull : null 을 허용하지 않는다.
@Range(min = 1000, max = 1000000) : 범위 안의 값이어야 한다.
@Max(9999) : 최대 9999까지만 허용한다.
...
https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#section-builtin-constraints
스프링 부트는 Bean Validation 설정을 보고 글로벌 Validator를 설정해준다. 이 Validator는 @NotNull같은 어노테이션을 보고 검증을 수행한다. 이렇게 글로벌 Validator가 적용되어 있기 때문에, @Validated만 적용하면 동작하게 된다. 이러한 Validator는 어노테이션을 보고 오류가 발생하면 FieldError, ObjectError를 자동으로 생성하여 BindingResult에 담아서 오류를 처리해준다. (저번에 설명한 내용들을 시스템 내부에서 수행해준다고 생각하면 된다)
@PostMapping("/add")
public String addItem(@Validated @ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
//검증에 실패하면 다시 입력 폼으로
if(bindingResult.hasErrors()) {
log.info("errors = {}", bindingResult);
return "validation/v3/addForm";
}
//성공 로직
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
return "redirect:/validation/v3/items/{itemId}";
}
1. @ModelAttribute 어노테이션을 보고 스프링 프레임워크는 @RequestParam 호출시와 같이 자동으로 item객체에 form태그 input값을 받아와서 주입 시도한다.
2. 만약 주입이 실패한다면 원인을 찾아(typeMismatch) BindingResult에 FieldError를 추가한다.
3. 주입이 성공했다면 값을 확인하여 BeanValidation을 적용한다.
- 에러메세지 관리
BeanValidation을 적용하고 bindingResult에 등록된 검증 오류 코드를 보면, 오류 코드가 errors.properties에 저장되는 형태로 나타난다.
1. Range.item.price // 에러명.객체명.필드명
2. Range.price // 에러명.필드명
3. Range.java.lang.Integer // 에러내용.타입
4. Range //에러명
> 구체적인 것부터 추상적인 것 순으로 사용된다.
이러한 형태로 자동으로 에러메세지의 형태와 우선순위가 정해져있으므로, 적절하게 개발자가 원하는 설정대로 에러 메세지를 미리 설정해줄 수 있다.
- 오브젝트 오류 관리
지금까지 어노테이션의 위치를 보면 필드명에 어노테이션이 들어간다. 그러면 오브젝트 에러는 어떤 방법으로 관리할 수 있을까? DTO에 @ScriptAssert를 사용하여 명시해주어 해결하는 방법도 존재하지만, 실무에서 객체의 범위를 넘어서는 오류들도 많기 때문에 기능에 제약이 있는데다 script기능도 곧 deprecated될 예정이라고 하니, 복합 검증에 한해서는 저번에 했던 BindingResult를 이용한 검증 방법을 사용하자.
- 한계: 검증 요구사항이 조건에 따라서 차이가 있다면..
1. Bean Validation Group
지금까지는 어노테이션을 이용하여 필드에 제약을 적용했다. 그런데 만약 Item이라는 DTO에 대해서 별도의 검증 기준을 적용해야 한다면 어떻게 해야할까? 새로운 기준에 맞추어 어노테이션을 변경하면 기존 기준에 side effect가 발생할 수 있다.
먼저 해결을 위하여 Bean Validation은 Group이라는 기능을 제공한다.
groups 사용을 위해서 필요한 상황에 대한 Interface를 생성하자. 여기서는 저장시와 수정시에 필요한 인터페이스를 만들어두었다고 하자(인터페이스는 그룹을 구분하기 위할뿐 내용은 존재하지 않아도 됨). 그러면 어노테이션에 groups={}로 클래스명을 명시하고,
@NotNull(groups = {UpdateCheck.class})
private Long id;
@NotNull
@Max(value = 9999, groups = {SaveCheck.class}) //수정 요구사항 추가
private Integer quantity;
컨트롤러의 @Validated 어노테이션의 value에 명시하고 싶은 인터페이스를 명시해주면 된다.
@PostMapping("/add")
public String addItemV2(@Validated(SaveCheck.class) @ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
2. Form전송 객체 분리
실무에서는 groups 를 잘 사용하지 않는데, 이는 실무에서는 회원 등록시 회원과 관련된 데이터만 전달받는 것이 아니라 약관 정보도 추가로 받는 등, DTO 객체와 관계없는 부가 데이터들이 넘어오는 등의 이유로 다루는 데이터가 완전히 차이날때도 많다. 그래서 보통 DTO를 직접 전달받는 것이 아니라, 복잡한 폼의 데이터를 컨트롤러까지 전달할 별도의 객체(Form 객체)를 만들어서 전달한다. 이를 통해 컨트롤러에서는 폼 데이터를 전달 받고, 이후 컨트롤러에서 필요한 데이터를 사용해서 DTO를 생성한다.
'[ Backend ] > Spring Web' 카테고리의 다른 글
[Spring MVC] 쿠키+세션을 사용한 로그인 (0) | 2022.08.09 |
---|---|
[Spring MVC] 쿠키를 사용한 로그인 (0) | 2022.08.08 |
[Spring MVC] 검증 - BindingResult, @InitBinder (0) | 2022.07.08 |
[Spring MVC] 메세지, 국제화 (0) | 2022.07.08 |
[Spring MVC] URL 매핑, 데이터 전송/조회 (0) | 2022.02.19 |