2 분 소요


오류 코드와 메시지 처리

required.item.itemName=상품 이름은 필수 입니다.
range.item.price=상품의 가격 범위 오류 입니다.

required=필수 값 입니다.
range=범위 오류 입니다.

오류 코드를 만들 때 자세히 만들 수도 있고, 단순하게 만들 수도 있다.
단순하게 만들면 범용성이 좋아 여러곳에서 사용할 수 있지만, 메시지를 세밀하게 작성하기 어렵다.
반대로 너무 자세하게 만들면 범용성이 떨어지게된다.
가장 좋은 방법은 범용성으로 사용하다가 필요한 경우 세밀한 내용이 적용되도록 메시지에 단계를 두는 방법이다.

required 오류 코드 사용시

#Level1
required.item.itemName: 상품 이름은 필수 입니다.

#Level2
required: 필수 값 입니다.

이 경우 세밀한 메시지(#Level1)을 사용하게 된다.


MessageCodesResolver

MessageCodesResolver codesResolver = new DefaultMessageCodesResolver();

@Test
void messageCodesResolverObject() {

    String[] messageCodes = codesResolver.resolveMessageCodes("required", "item");

    assertThat(messageCodes).containsExactly("required.item", "required");
}

@Test
void messageCodesResolverField() {
     String[] messageCodes = codesResolver.resolveMessageCodes("required", "item", "itemName", String.class);

    assertThat(messageCodes).containsExactly(
                "required.item.itemName",
                "required.itemName",
                "required.java.lang.String",
                "required"
    );
}

MessageCodesResolver

  • 검증 오류 코드로 메시지 코드들을 생성한다.
  • MessageCodesResolver는 인터페이스이고, DefaultMessageCodesResolver는 기본 구현체이다.
  • 주로 ObjectError, FieldError와 함께 사용

메시지 생성 규칙

객체 오류

Ex) 오류 코드: required, object name: item
1.: required.item
2.: required

필드 오류

Ex) 오류 코드: typeMismatch, object name "user", field "age", field type: int
1. "typeMismatch.user.age"
2. "typeMismatch.age"
3. "typeMismatch.int"
4. "typeMismatch"

동작 방식

  • rejectValue(), reject()는 내부에서 MessageCodesResolver를 사용한다.(메시지 코드 생성)
  • FieldError, ObjectError의 생성자를 보면 여러 오류 코드를 가질 수 있다.
    • MessageCodesResolver를 통해서 생성된 순서대로 오류 코드를 보관.

FieldError

rejectValue("itemName", "required")

  • required.item.itemName
  • required.itemName
  • required.java.lang.String
  • required

ObjectError

reject("totalPriceMin")

  • totalPriceMin.item
  • totalPriceMin

오류 메시지 출력
화면을 렌더링 할 때 th:errors가 실행되는데, 이때 오류가 있다면 생성된 오류 메시지 코드에서 순서대로 찾는다.

사용 이유?

모든 오류 코드에 대해서 메시지를 각각 다 정의하는 것은 관리하기 힘들기 때문에 중요하지 않은 메시지는 범용성 있는 required같은 메시지로 끝내고, 중요한 메시지는 필요할 때 구체적으로 적어서 사용하는 방식이 효과적이다.


사용 예시

errors.properties

#==ObjectError==
#Level1
totalPriceMin.item=상품의 가격 * 수량의 합은 {0}원 이상이어야 합니다. 현재 값 = {1}

#Level2 - 생략
totalPriceMin=전체 가격은 {0}원 이상이어야 합니다. 현재 값 = {1}

#==FieldError==
#Level1
required.item.itemName=상품 이름은 필수입니다.
range.item.price=가격은 {0} ~ {1} 까지 허용합니다.
max.item.quantity=수량은 최대 {0} 까지 허용합니다.

#Level2 - 생략

#Level3
required.java.lang.String = 필수 문자입니다.
required.java.lang.Integer = 필수 숫자입니다.
min.java.lang.String = {0} 이상의 문자를 입력해주세요.
min.java.lang.Integer = {0} 이상의 숫자를 입력해주세요.
range.java.lang.String = {0} ~ {1} 까지의 문자를 입력해주세요.
range.java.lang.Integer = {0} ~ {1} 까지의 숫자를 입력해주세요.
max.java.lang.String = {0} 까지의 문자를 허용합니다.
max.java.lang.Integer = {0} 까지의 숫자를 허용합니다.

#Level4
required = 필수 값 입니다.
min= {0} 이상이어야 합니다.
range= {0} ~ {1} 범위를 허용합니다.
max= {0} 까지 허용합니다.

ValidationUtils

ValidationUtils 사용 전

if (!StringUtils.hasText(item.getItemName())) {
    bindingResult.rejectValue("itemName", "required", "기본: 상품 이름은 필수입니다.");
}

ValidationUtils 사용 후

ValidationUtils.rejectIfEmptyOrWhitespace(bindingResult, "itemName", "required");

정리

  • 1.rejectValue() 호출
  • 2.MessageCodesResolver를 사용해서 검증 오류 코드로 메시지 코드를 생성
  • 3.new FieldError()를 생성하면서 메시지 코드들을 보관
  • 4.th:errors에서 메시지 코드로 메시지를 순서대로 찾고 노출.


<출처 : 인프런 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술(김영한)>

댓글남기기