ABAP DUMP ERROR 24시

BindingResult를 활용한 Validation 처리 [전 vs 후(version1,2) ] 본문

[WEB]Back-end/Spring MVC

BindingResult를 활용한 Validation 처리 [전 vs 후(version1,2) ]

이운형 2022. 2. 19. 21:41
반응형

# 인프런 김영한의 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 개인적으로 정리한 글입니다.

 

정리


Q. 과거 Validation 처리는 어떻게 했어?

Post방식으로 Error validation을 처리할때

 

1.Map<String, String> 형식으로 HashMap을 만든다

2.errors.put을 통해 errors들을 모은다 

3.model.addAttribute(errors", errors)를 통해 model에 넘긴다.

4. 에러 메세지를 thymeleaf를 통해 내보낸다.

 @PostMapping("/add")
    public String addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes, Model model) {

//        타입 검증
//        가격, 수량에 문자가 들어가면 검증 오류 처리

//        필드 검증
//        상품명: 필수, 공백X

//        가격: 1000원 이상, 1백만원 이하
//        수량: 최대 9999
//        특정 필드의 범위를 넘어서는 검증
//        가격 * 수량의 합은 10,000원 이상


        //검증 오류 결과를 보관
        Map<String, String> errors = new HashMap<>();

        //검증 로직
        if (!StringUtils.hasText(item.getItemName())){
            errors.put("itemName", "상품 이름은 필수입니다.");
        }
        if (item.getPrice() == null || item.getPrice() < 1000 || item.getPrice()>1000000){
            errors.put("price", "가격은 1,000 ~ 1,000,000 까지 허용합니다.");
        }
        if(item.getQuantity()== null || item.getQuantity() >= 9999){
            errors.put("quantity", "수량은 최대 9,999까지 허용합니다.");
        }
        if(item.getPrice() != null && item.getQuantity() != null){
            int resultPrice = item.getPrice() * item.getQuantity();
            if(resultPrice < 10000){
                errors.put("globalError" , "가격 * 수량의 합은 10,000원 이상이어야 합니다. 현재 값 =" + resultPrice);
            }
        }
        //검증에 실패하면 다시 입력 폼으로 가자.
        if(!errors.isEmpty()){
            log.info("errors = {}", errors);
            model.addAttribute("errors", errors);
            return "validation/v1/addForm";
        }

<thymeleaf를 활용하여 조건식을 개발>

 

Error 시 오류 메세지를 날리는 방식으로 설계되었다.

<input type="text" id="itemName" th:field="*{itemName}"
       th:class="${errors?.containsKey('itemName')} ? 'form-control field-error' : 'form-control'"
       class="form-control" placeholder="이름을 입력하세요">
<div class="field-error" th:if="${errors?.containsKey('itemName')}"
     th:text="${errors['itemName']}">

 

Q. 현재 Validation 처리는 어떻게 했어?

다음과 같이 BindingResult를 추가해!

@ModelAttribute 뒤에 꼭 BindingResult가 와야한다. 순서가 변경되면 오류가 난다.

@PostMapping("/add")
public String addItem(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes, Model model) {
@PostMapping("/add")
public String addItem(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes, Model model) {


    //검증 로직
    if (!StringUtils.hasText(item.getItemName())){
        bindingResult.addError(new FieldError("item", "itemName", "상품 이름은 필수 입니다."));
    }
    if (item.getPrice() == null || item.getPrice() < 1000 || item.getPrice()>1000000){
        bindingResult.addError(new FieldError("item", "price","가격은 1,000~ 1,000,000 까지 허용합니다." ));
    }
    if(item.getQuantity()== null || item.getQuantity() >= 9999){
        bindingResult.addError(new FieldError("item", "quantity", "수량은 최대 9,999까지 허용됩니다."));
    }
    if(item.getPrice() != null && item.getQuantity() != null){
        int resultPrice = item.getPrice() * item.getQuantity();
        if(resultPrice < 10000){
            bindingResult.addError(new ObjectError("item", "가격 * 수량의 합은 10,000원 이상이어야 합니다. 현재 값 = " + resultPrice));
        }
    }
    //검증에 실패하면 다시 입력 폼으로 가자.
    if(bindingResult.hasErrors()){
        log.info("errors= {}", bindingResult);
        return "validation/v2/addForm";

    }
    Item savedItem = itemRepository.save(item);
    redirectAttributes.addAttribute("itemId", savedItem.getId());
    redirectAttributes.addAttribute("status", true);
    return "redirect:/validation/v1/items/{itemId}";

- bindingResult.addError(new FieldError())

- bindingResult.addError(new ObjectError())

 

그리고 bindinResult.hasErrors() 하면 validation/v2/addForm을 다시 보여주도록 설계하면 된다.

 

 

/** 추가내용 2022-02-20 **/

 

public String addItemV2(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes){
    if(!StringUtils.hasText(item.getItemName())){
        bindingResult.addError(new FieldError("item", "itemName", item.getItemName(),
                false, null, null, "상품 이름은 필수입니다."));
    }
    if(item.getPrice() == null || item.getPrice()<1000 || item.getPrice()> 1000000){
        bindingResult.addError(new FieldError("item", "price", item.getPrice(),
                false, null, null, "가격은 1,000~ 1,000,000 까지 허용합니다."));
    }
    if (item.getQuantity() == null || item.getQuantity() > 10000){
        bindingResult.addError(new FieldError("item", "quantity", "item.getQuantity", false, null, null , "수량은 최대 9,999까지 허용합니다."));
    }

    if (item.getPrice() != null && item.getQuantity() != null){
        int resultPrice = item.getPrice() * item.getQuantity();
        if (resultPrice < 10000){
            bindingResult.addError(new ObjectError("item", null,null,"가격 + 수량의 합은 10,000원 이상이어야 합니다. 현재값 = " + resultPrice));
        }
    }

bindingResult 에는 다음과 같은 추가 기능이 존재한다.

 

bindingResult.addError(new FieldError(String objectName, String field, @Nullable Object rejectedValue, boolean bindingFailure, @Nullable String[] codes, @Nullable Object[] arguments, @Nullable String defaultMessage))

 

여기서 눈여겨야 봐야할 점은 @Nullable Object rejectedValue, boolean bindingFailure 이다 .

rejectedValue 가 바로 오류 발생시 사용자 입력 값을 저장하는 필드

bindingFailure : 타입 오류 같은 바인딩 실패인지, 검증 실패인지 구분 값

 

따라서

 

new FieldError("item", "price", item.getPrice(), false, null, null, "가격은 1,000 ~ 1,000,000 까지 허용합니다.")

 

처럼 파라미터를 넣어주면 오류 발생시에도 데이터가 날아가지 않고 남아있다.

반응형
Comments