검증 2

JIWOO YUN·2024년 2월 5일
0

SpringMVC2

목록 보기
10/26
post-custom-banner

BindingResult

  • 스프링이 제공하는 검증 오류를 보관하는 객체 -> 오류 발생시 여기에 보관
public FieldError(String objectName,String field,String message){}
  • 필드 오류가 있으면 FieldError객체를 생성해서 bindingReuslt에 담기.
    • objectName : @ModelAttribute 이름
    • field : 오류가 발생하 필드 이름
    • defaultMessage : 오류 기본 메시지

global 오류가 발생시 new ObjectError에 담아준다.

  • 특정 필드에 대한 오류가 아니기 때문에 ObjectError에 담아줌.
public ObjectError(String objectName,String defaultMessage){}
  • obejctName : @ModelAttribute 이름
  • defaultMessage : 오류 기본 메시지

addItem에다가 BindingResult를 추가

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

        //상품명은 필수
        if(!StringUtils.hasText(item.getItemName())){
            bindingResult.addError(new FieldError("item","itemName","상품 이름은 필수입니다."));
        }

        //가격은 1000원이상 1백만원 이하
        if (item.getPrice() == null || item.getPrice() < 1000 || item.getPrice() > 1000000) {
            bindingResult.addError(new FieldError("item","price","가격은 1000 ~ 1000000 까지 허용합니다."));
        }

        //수량은 최대 9999
        if(item.getQuantity() == null || item.getQuantity() > 9999){
            bindingResult.addError(new FieldError("item","quantity","수량은 최대 9999까지 가능합니다."));
        }

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

        if(bindingResult.hasErrors()){
            log.info("errors={}",bindingResult);
            return  "validation/v2/addForm";
        }


        itemRepository.save(item);
        redirectAttributes.addAttribute("itemId",item.getId());
        redirectAttributes.addAttribute("status",true);

        return "redirect:/validation/v2/items/{itemId}";
    }

BindingResult는 ModelAttribute인 item 뒤에다가 해야한다.

  • bindingresult가 무엇을 넣어놓는냐면 item객체의 바인딩 결과를 넣어두기 때문에 순서가 중요하다.

html 부분도 타임리프와 스프링에서 추가로 제공해준다.

 <div th:if="${errors?.containsKey('globalError')}">
 <p class="field-error" th:text="${errors['globalError']}">전체 오류 메시
지</p>

<div th:if="${#fields.hasGlobalErrors()}">
 <p class="field-error" th:each="err : ${#fields.globalErrors()}"
th:text="${err}">글로벌 오류 메시지</p>

위의 코드를 아래의 코드로 변경이 가능하다.

  • 복잡하게 키를 포함하고있는지 체크할 필요없지 글로벌에러가 있는지 체크를 통해서 전체 오류 메시지를 띄우기가 가능하다.

타임리프 스프링 검증 오류 통합 기능

  • #fields : #fields로 BindingResult가 제공하는 검증 오류에 접근 할 수 있다.
  • th:errors : 해당 필드에 오류가 있는 경우 태그에 출력
  • th:errorclass : th:field에서 지정한 필드에 오류가 있으면 class 정보를 추가

  • BindingResult 가 있을시 @ModelAttribute에 데이버 바인딩 시 오류가 발생해도 컨트롤러가 호출됨.
    • @ModelAttribute 에 바인딩 시 타입 오류가 발생한다면
      • BindingResult가 없으면 -> 400 오류가 발생하면서 컨트롤러가 호출되지 않고, 오류페이지 이동
      • BindingResult가 있으면 -> 오류 정보를 BindingResult에 담아서 컨트롤러를 정상호출

검증 오류를 적용하는 3가지 방법

  1. @ModelAttribute의 객체 타입 오류등으로 바인딩이 실패하는 경우 스프링이 Fielderror 를 생성해서 BindingResult에 넣어주기.
  2. 개발자가 직접 넣어주기
  3. Validator 사용

BindingResult 인터페이스는 Errors 인터페이스를 상속받고있음

  • BindingResult 대신에 Errors를 사용해도되지만 Errors의 경우 기능이 단순한 오류 저장및 조회기능만 제공
    • BindingResult는 거기에 추가적인 기능을 제공해준다.
    • 그렇기 때문에 관례상 BindingResult를 사용한다.

FieldError, ObjectError

FieldError에는 2가지 생성자가 존재한다.

public FieldError(String objectName,String field, String defaultMessage);
public FieldError(String objectName,String field, @Nullable Object rejectValue, boolean bindingFailure,@Nullable String[] codes,@Nullable Object[] arguments, @Nullable String defaultMessage)
  • field : 오류 필드
  • rejectValue : 사용자가 입력한 값
  • bindingFailure : 타임오류 같은 바인딩 실패인지 검증실패인지 구분값
  • codes : 메시지 코드
  • argument : 메시지에서 사용하는 인자
  • defaultMessage : 기본 오류 메시지

밑의 생성자를 사용하면 오류가 발생해도 사용자 입력값을 유지할 수있다.

new FieldError("item", "price", item.getPrice(), false, null, null, "가격은 1,000 ~ 
1,000,000 까지 허용합니다.")
profile
열심히하자
post-custom-banner

0개의 댓글