[TIL] #4. 검증1 - Validation ②

kiteB·2021년 9월 25일
0

TIL-Spring4

목록 보기
7/17
post-thumbnail

BindingResult1

스프링이 제공하는 검증 오류 처리 방법BindingResult에 대해 알아보자!

ValidationItemControllerV2 - addItemV1

BindingResult bindingResult 파라미터의 위치

public String addItemV1(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes) { }

BindingResult bindingResult 파라미터의 위치는 @ModelAttribute Item item 다음에 와야 한다!


✔ 필드 오류 - FieldError

if (!StringUtils.hasText(item.getItemName())) {
    bindingResult.addError(new FieldError("item", "itemName", "상품 이름은 필수입니다."));
}
  • 필드에 오류가 있으면 FieldError 객체를 생성해서 bindingResult에 담아두면 된다.
  • public FieldError(String objectName, String field, String defaultMessage) {}
    • objectName: @ModelAttribute 이름
    • field: 오류가 발생한 필드 이름
    • defaultMessage: 오류 기본 메시지

✔ 글로벌 오류 - ObjectError

bindingResult.addError(new ObjectError("item", "가격 * 수량의 합은 10,000원 이상이어야 합니다. 현재 값 = " + resultPrice));
  • 특정 필드를 넘어서는 오류가 있으면 ObjectError 객체를 생성해서 bindingResult에 담아두면 된다.
  • public ObjectError(String objectName, String defaultMessage) {}
    • objectName: @ModelAttribute의 이름
    • defaultMessage: 오류 기본 메시지

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

타임리프는 스프링의 BindingResult를 활용해서 편리하게 검증 오류를 표현하는 기능을 제공한다.

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

코드 확인하기

🔗 전체 코드 확인하기

실행 결과


BindingResult2

BindingResult가 있으면 @ModelAttribute에 데이터 바인딩 시 오류가 발생해도 컨트롤러가 호출된다!

예를 들어, 바인딩 시 타입 오류가 발생하면?

  • BindingResult400 오류 발생 → 컨트롤러 호출 ❌, 오류 페이지로 이동한다!
  • BindingResult→ 오류 정보(FieldError)를 BindingResult에 담아서 컨트롤러를 정상 호출한다!

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

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

BindingResultErrors

org.springframework.validation.Errors
org.springframework.validation.BindingResult

  • BindingResult는 인터페이스이고, Errors 인터페이스를 상속받고 있다.
  • 실제 넘어오는 구현체는 BeanPropertyBindingResult인데,
    두 인터페이스 모두 구현하고 있으므로 BindingResult 대신에 Errors를 사용해도 된다!
  • Errors 인터페이스는 단순한 오류 저장과 조회 기능을 제공하며, BindingResult는 여기에 더해서 추가적인 기능까지 제공한다.
    • 우리가 사용한 addError()BindingResult만 제공하므로 여기서는 BindingResult를 사용해야 한다.
    • 주로 관례상 BindingResult를 많이 사용한다.

FieldError, ObjectError

📌 목표

  • 사용자 입력 오류 메시지가 화면에 표시되도록 하자.
  • FieldError, ObjectError에 대해서 더 자세히 알아보자.

ValidationItemControllerV2 - addItemV2

FieldError 생성자

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

✔ 오류 발생시 입력 값 유지

new FieldError("item", "price", item.getPrice(), false, null, null, "가격은 1,000 ~ 1,000,000 까지 허용합니다.")
  • FieldError: 오류 발생시 사용자 입력 값을 저장하는 기능을 제공한다.
    • rejectedValue: 오류 발생시 사용자 입력 값을 저장하는 필드
  • bindingFailure: 타입 오류 같은 바인딩이 실패했는지 여부 저장.

✔ 타임리프의 사용자 입력 값 유지

th:field="*{price}"

타임리프의 th:field 동작은 다음과 같다.

  • 정상 상황 → 모델 객체의 값을 사용
  • 오류 발생 → FieldError에서 보관한 값을 사용해서 값을 출력한다.

✔ 스프링의 바인딩 오류 처리

타입 오류로 바인딩에 실패하면 스프링은 FieldError를 생성하면서 사용자가 입력한 값을 넣어둔다.

그리고 해당 오류를 BindingResult에 담아서 컨트롤러를 호출한다.

→ 타입 오류 같은 바인딩 실패시에도 사용자의 오류 메시지를 정상 출력할 수 있다.


코드로 확인하기

🔗 전체 코드 확인하기

실행 결과

이제 오류가 발생해도 입력 값이 유지된다!


오류 코드와 메시지 처리 1

FieldError, ObjectError의 생성자는 errorCode, arguments를 제공한다.

이것은 오류 발생시 오류 코드로 메시지를 찾기 위해 사용된다!


error 메시지 파일 생성

오류 메시지를 저장하기 위해 errors.properties 파일을 만들자!

required.item.itemName=상품 이름은 필수입니다.
range.item.price=가격은 {0} ~ {1} 까지 허용합니다.
max.item.quantity=수량은 최대 {0} 까지 허용합니다.
totalPriceMin=가격 * 수량의 합은 {0}원 이상이어야 합니다. 현재 값 = {1}

이렇게 파일을 따로 빼두면, 하드코딩을 할 필요가 없어서 유지보수가 쉬워진다!


ValidationItemControllerV2 - addItemV3

new FieldError("item", "price", item.getPrice(), false, new String[]{"range.item.price"}, new Object[]{1000, 1000000}
  • codes
    • required.item.itemName를 사용해서 메시지 코드를 지정한다.
    • 메시지 코드는 배열로 여러 값을 전달할 수도 있다. 순서대로 매칭해서 처음 매칭되는 메시지가 사용된다.
  • arguments: Object[]{1000,1000000}를 사용해서 코드의 {0}, {1}로 치환할 값을 전달한다.

실행 결과

🔗 전체 코드 확인하기


오류 코드와 메시지 처리 2

바로 위에서 배운 FieldError, ObjectError 보다 더 편리한 방법에 대해 알아보자!

rejectValue(), reject()

BindingResult가 제공하는 rejectValue(), reject()를 사용하면
FieldError, ObjectError를 직접 생성하지 않고, 깔끔하게 검증 오류를 다룰 수 있다!


ValidationItemControllerV2 - addItemV4

rejectValue()

void rejectValue(@Nullable String field, String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage);
  • field: 오류 필드명
  • errorCode: 오류 코드 (messageResolver를 위한 오류 코드)
  • errorArgs: 오류 메시지에서 {0}을 치환하기 위한 값
  • defaultMessage: 오류 메시지를 찾을 수 없을 때 사용하는 기본 메시지

✔ 축약된 오류 코드

  • FieldError()를 직접 사용했을 때는,
    range.item.price와 같이 오류 코드를 모두 입력해야 했다.

  • rejectValue()를 사용했을 때는,
    range처럼 간단하게 입력해도 오류 메시지를 잘 찾아서 출력해준다!

실행 결과

🔗 전체 코드 확인하기

profile
🚧 https://coji.tistory.com/ 🏠

0개의 댓글