본문 바로가기
스프링/스프링 웹 개발 활용

[Spring] BindingResult의 rejectValue(), reject() 를 사용하여 FieldError, ObjectError를 직접 생성하지 않고 검증하기

by drCode 2023. 9. 27.
728x90
반응형

FieldError, ObjectError를 다루려면 여러 가지 파라미터를 세팅해야하기 때문에 불편한 부분이 많다.

 

컨트롤러에서 BindingResult는 검증해야할 객체인 target 바로 다음에 온다.

따라서 BindingResult는 이미 본인이 검증해야할 객체인 target을 알고 있다.

 

log.info("objectName={}", bindingResult.getObjectName());
log.info("target={}", bindingResult.getTarget());

를 실행해보면

getObjectName, getTarget

와 같이 나오는 걸 볼 수 있다.

 

rejectValue(), reject()

BindingResult가 제공하는 rejectValue(), reject()를 사용하면 FieldError, ObjectError를

직접 생성하지 않고, 깔끔하게 검증 오류를 다룰 수 있다.

 

rejectValue(), reject()를 사용해서 기존 코드를 단순화해보면 아래와 같다.

 

.ValidationItemControllerV2 - addItemV4 추가

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

    log.info("objectName={}", bindingResult.getObjectName());
    log.info("target={}", bindingResult.getTarget());

    // 검증 로직
    if(!StringUtils.hasText(item.getItemName())) {
        bindingResult.rejectValue("itemName", "required");
    }

    if(item.getPrice() == null || item.getPrice() < 1000 || item.getPrice() > 1000000) {
        bindingResult.rejectValue("price", "range", new Object[]{1000, 1000000}, null);
    }

    if(item.getQuantity() == null || item.getQuantity() > 9999) {
        bindingResult.rejectValue("quantity", "max", new Object[] {9999}, null);
    }

    // 특정 필드가 아닌 복합 룰 검증
    if(item.getPrice() != null && item.getQuantity() != null) {
        int resultPrice = item.getPrice() * item.getQuantity();
        if(resultPrice < 10000) {
//                bindingResult.addError(new ObjectError("item", new String[] {"totalPriceMin"}, new Object[] {10000, resultPrice}, null));
            bindingResult.reject("totalPriceMin", new Object[]{10000, resultPrice}, null);
        }
    }

    // 검증에 실패하면 다시 입력 폼으로
    // 부정의 부정을 하면 읽기 복잡하다
    if(bindingResult.hasErrors()) {
        log.info("errors = {} ", bindingResult);
        // bindingResult 는 model Attribute에 안담아도 자동으로 view로 넘겨준다
        return "validation/v2/addForm";
    }


    // 성공로직

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

    // 여기까지 하고 한번 돌려보고 REsponse 보기
}

 

이를 실행해보면 아래와 같이 정상적으로 나온다.

그런데 errors.properties에 있는 코드를 직접 입력하지 않았는데 어떻게 될 수 있었을까?

 

바로 rejectValue() 때문이다

 

rejectValue()

void rejectValue(@Nullable String field, String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage);

 - field : 오류 필드명

 - errorCode : 오류 코드(이 오류 코드는 메시지에 등록된 코드가 아니다. - messageResolver를 위한 오류코드)

 - errorArgs : 오류 메시지에서 {0}을 치환하기 위한 값

 - defaultMessage : 오류 메시지를 찾을 수 없을 때 사용하는 기본 메시지

 

bindingResult.rejectValue("price", "range", new Object[]{1000, 1000000}, null);

앞에서 BindingResult는 어떤 객체를 대상으로 검증하는지 target을 이미 알고 있다고 했다.

따라서 target(item)에 대한 정보는 없어도 된다.

오류 필드명은 동일하게 price를 사용했다.

 

rejectValue는 축약된 오류 코드를 사용한다.

FieldError()를 직접 다룰 때는 오류 코드를 range.item.price와 같이 모두 입력했다.

그런데 rejectValue() 를 사용하고부터는 오류 코드를 range로 간단하게 입력했다.

그래서 오류 메시지를 잘 찾아서 출력한다. 무언가 규칙이 있는 것처럼 보이는데,

MessageCodeResolver 가 이렇게 할 수 있도록 도와준다.

 

errors.properties

range.item.price=가격은 {0} ~ {1} 까지 허용합니다.

 

reject()

void reject(String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage);

 - field : 오류 필드명

 - errorCode : 오류 코드(이 오류 코드는 메시지에 등록된 코드가 아니다. - messageResolver를 위한 오류코드)

 - errorArgs : 오류 메시지에서 {0}을 치환하기 위한 값

 - defaultMessage : 오류 메시지를 찾을 수 없을 때 사용하는 기본 메시지

 

728x90
반응형

댓글