티스토리 뷰

에러 메시지 처리를 공통으로 관리하고 필요에 따라 국제화까지 적용할 수 있도록 애플리케이션 개발을 진행한 경험을 바탕으로 해당 내용을 정리해보도록 하겠습니다.

 

먼저 SpringBoot에서는 메시지 처리 및 국제화를 위해 자동으로 MessageSource 객체를 빈으로 등록하게 됩니다.

 

따라서 기본적인 설정을 사용한다면 별도의 의존성 추가나 빈 등록을 할 필요가 없고 resources 디렉토리 하위에 messages.properties 파일만 만들어서 사용하면 됩니다. 기본적인 basename이 messages 이기 때문입니다.

 

그외 추가적인 설정을 하고 싶다면 역시나 별도의 의존성 추가나 빈 등록을 하지 않고 application.yml 파일에 다음과 같이 커스텀한 설정 정보를 추가해주면 됩니다.

spring:
  messages:
    basename:message/messages
    encoding:UTF-8
    fallbackToSystemLocale:false
    alwaysUseMessageFormat:true

 

SpringBoot에서 어떻게 자동으로 MessageSource가 빈으로 등록되고 messages.properties 파일을 읽어오는지 간단하게 살펴보면 다음과 같습니다.

 

  • SpringBoot 프로젝트는 @SpringBootApplication 이라는 애노테이션을 붙여서 사용하게 됩니다.
  • 해당 애노테이션 내부에는 @EnableAutoConfiguration 이라는 애노테이션이 있고 해당 애노테이션은 자동 설정 파일들을 읽어 설정을 진행합니다. 또한 @ComponentScan 이라는 애노테이션도 있는데 이는 컴포넌트들을 읽어 빈으로 등록하는 역할을 합니다.
  • 자동 설정 파일 중 MessageSourceAutoConfiguration.java 라는 파일이 있고 이 파일 내부에서 MessageSource를 빈으로 등록하고 있습니다.

이제 제가 프로젝트에서 사용하고 테스트한 코드 예시를 살펴보겠습니다.

 

1. application.yml 에 messages.properties 경로 추가

spring:
  ...

  messages:
    basename: messages, errors
    encoding: UTF-8

이렇게 basename을 추가하면 resources 디렉토리 하위에 messages.properties와 errors.properties를 메시지 처리 properties로 인식하겠다고 지정하는 것입니다. 기본적으로 messages.properties는 지원하기 때문에 별도의 추가 파일이 없다면 굳이 지정할 필요는 없습니다.

이제 국제화를 위해서는 messages_ko.properties, messages_en.properties 등과 같이 기본 파일명 뒤에 언더바(_) 하고 locale 명을 지정하면 해당 파일을 사용할 수 있게 됩니다.

 

2. 사용할 곳에서 바로 적용

기본적으로 스프링에서 제공하는 MessageSource라는 것을 생성하거나 주입 받아서 해당 클래스의 getMessage() 메서드를 통해 properties 파일에 지정된 메시지를 읽어와 사용할 수 있습니다.

 

저는 에러 메시지들을 다루기 위해 별도 클래스를 지정하여 사용하였지만 이번 글에서는 단순하게 테스트 코드 예제로 해당 내용을 살펴보겠습니다.

먼저 각 메세지들을 properties 파일에 등록합니다.

// messages.properties
greeting.hello=안녕하세요.
greeting.hi={0}님, 안녕!

// messages_en.properties
greeting.hello=Hello~
greeting.hi=Hi, {0}!

그리고 나서 다음과 같이 테스트를 통해 국제화까지 확인해보겠습니다.

getMessage() 메서드는 code와 Locale을 통해 맞는 메세지를 찾아옵니다. 중간에 args와 default message는 null이 가능합니다. Locale에 해당하는 파일이 없는 경우에는 기본 properties를 사용하게 됩니다.

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.MessageSource;

import java.util.Locale;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
public class MessageTest {
    @Autowired
    MessageSource messageSource;

    @Test
    @DisplayName("기본 locale")
    public void message_default() {
        // given
        String codeHello = "greeting.hello";
        String codeHi = "greeting.hi";
        String name = "hanbee";

        // when
        String hello = messageSource.getMessage(codeHello, null, null, Locale.getDefault());
        String hi = messageSource.getMessage(codeHi, new Object[]{name}, null, Locale.getDefault());

        // then
        assertThat(hello).isEqualTo("안녕하세요.");
        assertThat(hi).isEqualTo(name + "님, 안녕!");
    }

    @Test
    @DisplayName("영어 locale")
    public void message_en() {
        // given
        String codeHello = "greeting.hello";
        String codeHi = "greeting.hi";
        String name = "hanbee";

        // when
        String hello = messageSource.getMessage(codeHello, null, "Hello", Locale.ENGLISH);
        String hi = messageSource.getMessage(codeHi, new Object[]{name}, "Hi", Locale.ENGLISH);
        String notFoundCode = messageSource.getMessage("notFound.code", null, "Hello Hello", Locale.ENGLISH);

        // then
        assertThat(hello).isEqualTo("Hello~");
        assertThat(hi).isEqualTo("Hi, " + name + "!");
        assertThat(notFoundCode).isEqualTo("Hello Hello");
    }
}

 

추가. MessageCodesResolver

MessageCodesResolver는 code와 objectName, fieldName, fieldType 등을 바탕으로 구체적인 메시지 코드에서 범용적인 메시지 코드까지 코드 목록을 만들어냅니다.

 

SpringBoot 의 BindingResult가 rejectValue 할 때 MessageCodesResolver라는 것을 활용하게 되고 이때 만들어진 코드 목록을 바탕으로 구체적인 코드에서 범용적인 코드 순으로 맞는 메시지가 있는지 확인하게 됩니다.

 

기본적인 생성 규칙은 다음과 같습니다.

// 오브젝트 에러
code.objectName
code

// 필드 에러
code.objectName.fieldName
code.fieldName
code.fieldType
code

 

스프링의 Bean Validation 처리를 할 수 있는 @Valid, @Validated 를 사용한 경우에도 에러 처리 시 위와 같은 코드들이 만들어지고 그에 맞는 메시지를 등록하면 스프링이 제공하는 기본 메시지 대신 원하는 에러 메시지를 제공할 수 있습니다.

 

예를 들어 item이라는 객체 내 itemName 필드에 @NotBlank 라는 애노테이션을 적용한 뒤, 해당 부분에서 에러가 발생했다면 스프링 부트는 메시지 코드를 다음과 같이 만들게 됩니다.

 

  • NotBlank.item.itemName
  • NotBlank.itemName
  • NotBlank.java.lang.String
  • NotBlank

 

따라서 위와 같은 형태로 에러 메시지 코드를 지정하여 사용하는 것이 유용합니다.


지금까지 SpringBoot의 메시지 처리에 대해 알아보았습니다. 프로젝트의 공통 부분을 작업하다보니 기존에는 가볍게 넘겼던 에러 처리나 메시지 처리에 대해 자세히 알 수 있어서 좋은 시간이었던 것 같습니다. 앞으로도 프로젝트할 때 더 꼼꼼하게 공통 부분을 작업해야할 것 같습니다. 🤣

 

추가적으로 @Valid, @Validated 같은 애노테이션이 어떤 식으로 동작하고 검증기를 어떻게 실행하게 되는지에 대해서는 조금 더 공부하고 정리할 수 있도록 하겠습니다.

 

참고

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함