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

[Spring] erros.properties 를 이용한 오류 코드와 메시지 처리

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

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 : 기본 오류 메시지

 

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

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

 

errors 메시지 파일 생성

messages.properties 를 사용해도 되지만, 오류 메시지를 구분하기 쉽게 errors.properties 라는 별도의 파일로 관리한다.

 

먼저 스프링 부트가 해당 메시지 파일을 인식할 수 있게 다음 설정을 추가한다.

이렇게하면 messages.properties , errors.properties 두 파일을 모두 인식한다.

(생략하면 messages.properties 를 기본으로 인식한다.)

 

스프링 부트 메시지 설정 추가

application.properties

spring.messages.basename=messages,config.i18n.messages, errors

 

errors.properties 추가

src/main/resources/errors.properties

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

※ 참고: errors_en.properties 파일을 생성하면 오류 메시지도 국제화 처리를 할 수 있다.

 

이제 errors 에 등록한 메시지를 사용하도록 코드를 변경해보자.

 

ValidationItemControllerV2 - addItemV3() 추가

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

    // 검증 로직
    if(!StringUtils.hasText(item.getItemName())) {
        bindingResult.addError(new FieldError("item", "itemName", item.getItemName(), false, new String[] {"required.item.itemName"}, null, null));
    }

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

    if(item.getQuantity() == null || item.getQuantity() > 9999) {
        bindingResult.addError(new FieldError("item", "quantity", item.getQuantity(), false, new String[] {"max.item.quantity"}, 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));
        }
    }

    // 검증에 실패하면 다시 입력 폼으로
    // 부정의 부정을 하면 읽기 복잡하다
    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 보기
}

addItemV2()의 @PostMapping 부분을 주석처리하고 진행하면 된다.

 

//range.item.price=가격은 {0} ~ {1} 까지 허용합니다.
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} 로 치환할 값을 전달한다.


실행해보면 메시지, 국제화에서 학습한 MessageSource 를 찾아서 메시지를 조회하는 것을 확인할 수 있다

 

참고로, 기본 오류 메시지를 적용할 수 있는데,

 

ValidationItemControllerV2.java - addItemV3 중

bindingResult.addError(new FieldError("item", "itemName", item.getItemName(), false, new String[] {"required.item.itemName", "required.default"}, null, null));

위의 에러 코드 중, 배열에 항목을 넣은 것들을 순차적으로 탐색하는데,

 

이때 아래에 errors.properties를 살펴보면

 

errors.properties

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

 

위의 항목 중 required.item.itemName 이 주석처리되어 있을 때

 

두번째 항목인  required.default가 적용되는걸 볼 수 있다.

 

기본 오류 메시지 적용

 

근데, 에러 코드와 메시지가 없는 것은 어떻게 뜰까?

 

상품명을 지정하지 않은 상태로 등록을 실행 시

상품명을 지정하지 않은 상태로 등록을 실행 시 

500 에러를 확인할 수 있다.

500 에러를 확인할 수 있다.

 

No message found under code 'required.default2'

 No message found under code 'required.default2'

 

메시지를 찾을 수 없다는 내용으로, 등록되지 않은 메시지일 경우에는 위처럼 에러를 떨군다.

728x90
반응형

댓글