Java Category/Spring

[Spring MVC] 메시지, 국제화

ReBugs 2024. 3. 10.

이 글은 인프런 김영한님의 Spring 강의를 바탕으로 개인적인 정리를 위해 작성한 글입니다.


메시지

스프링 부트에서 메시지를 관리하는 기능은 애플리케이션의 국제화(i18n)를 지원하며, 애플리케이션에서 사용되는 문자열을 손쉽게 관리할 수 있게 해준다. 이 기능을 사용하면, 다양한 언어와 지역에 맞춰 동적으로 메시지를 변경할 수 있으며, 코드 내에 하드코딩된 문자열을 줄임으로써 유지보수성을 높일 수 있다. 스프링 부트에서 메시지를 사용하는 방법에 대해 자세히 알아보자.



1. 메시지 소스 파일 준비

메시지 관리의 첫 단계는 src/main/resources 디렉토리 아래에 프로퍼티 파일 형태로 메시지 소스 파일을 준비하는 것이다. 기본적으로 messages.properties 파일을 사용하지만, 다국어 지원을 위해 국가 코드나 언어 코드를 접미사로 추가한 파일들(messages_en.properties, messages_ko.properties 등)을 함께 준비할 수 있다.

 

예를 들어서 messages.properties 라는 메시지 관리용 파일을 만들고 

item=상품
item.id=상품 ID
item.itemName=상품명
item.price=가격
item.quantity=수량

각 HTML들은 다음과 같이 해당 데이터를 key 값으로 불러서 사용하는 것이다.

<label for="itemName" th:text="#{item.itemName}"></label>
  • messages.properties :기본 값으로 사용(한글)

  • messages_en.properties : 영어 국제화 사용

 

/resources/messages.properties (기본, 한국어 설정)

messages.properties

hello=안녕
hello.name=안녕 {0}

{0} 이 부분에 파라미터를 전송하면 파라미터를 포함해서 출력된다.

<p th:text="#{hello.name(${item.itemName})}"></p>

 


/resources/messages_en.properties(영어 설정)

messages_en.properties

 hello=hello
 hello.name=hello {0}

{0} 이 부분에 파라미터를 전송하면 파라미터를 포함해서 출력된다.

<p th:text="#{hello.name(${item.itemName})}"></p>

 

2. application.properties에서 메시지 소스 설정

application.properties 또는 application.yml 파일에 메시지 소스를 위한 설정을 추가해야 한다. 메시지 파일의 기본 이름(basename)을 지정하여 스프링 부트가 메시지 소스를 올바르게 찾을 수 있게 한다.

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

MessageSource 를 스프링 빈으로 등록하지 않고, 스프링 부트와 관련된 별도의 설정을 하지 않으면 messages 라는 이름으로 기본 등록된다. 따라서 messages_en.properties , messages_ko.properties , messages.properties 파일만 등록하면 자동으로 인식된다.

스프링 부트 메시지 소스 기본 값
spring.messages.basename=messages

 

3. MessageSource를 통한 메시지 접근

스프링의 MessageSource 인터페이스를 사용하여 코드에서 메시지를 조회할 수 있다. MessageSource는 @Autowired를 통해 자동으로 주입받을 수 있으며, getMessage 메서드를 사용해 메시지를 가져온다.

@Autowired
MessageSource ms;

 

@Test
void helloMessage() {
    String result = ms.getMessage("hello", null, null);
    assertThat(result).isEqualTo("안녕");
}

ms.getMessage("hello", null, null)

  • code: hello
  • args: null
  • locale: null

가장 단순한 테스트는 메시지 코드로 hello 를 입력하고 나머지 값은 null 을 입력했다.

locale 정보가 없으면 basename 에서 설정한 기본 이름 메시지 파일을 조회한다. basename 으로 messages 를 지정했으므로 messages.properties 파일에서 데이터 조회한다.

 

public String helloMessage(Locale locale) {
    return ms.getMessage("welcome.message", null, locale);
}
  • code: 메시지의 키로 사용될 문자열. 이 경우, "welcome.message"가 해당된다.
  • args: 메시지에서 사용될 가능한 매개변수들의 배열. 메시지 텍스트 내에서 플레이스홀더로 대체될 값들이다. 여기서는 null을 사용하여 추가적인 매개변수가 필요 없음을 나타낸다.
  • locale: 메시지를 조회할 로케일. 이 매개변수를 통해 MessageSource는 주어진 로케일에 맞는 메시지를 반환하려고 시도한다.

 

메시지가 없는 경우, 기본 메시지

@Test
void notFoundMessageCodeDefaultMessage() {
    String result = ms.getMessage("no_code", null, "기본 메시지", null);
    assertThat(result).isEqualTo("기본 메시지");
}

@Test
void argumentMessage() {
    String result = ms.getMessage("hello.name", new Object[]{"Spring"}, null);
    assertThat(result).isEqualTo("안녕 Spring");
}

메시지가 없는 경우에는 NoSuchMessageException 이 발생한다.
메시지가 없어도 기본 메시지( defaultMessage )를 사용하면 기본 메시지가 반환된다.

 

매개변수 사용

 @Test
void argumentMessage() {
    String result = ms.getMessage("hello.name", new Object[]{"Spring"}, null);
    assertThat(result).isEqualTo("안녕 Spring");
}

다음 메시지의 {0} 부분은 매개변수를 전달해서 치환할 수 있다.

hello.name=안녕 {0} → Spring 단어를 매개변수로 전달 → 안녕 Spring

 

4. 컨트롤러에서 메시지 사용하기

스프링 MVC 컨트롤러에서는 @RequestMapping 메서드의 매개변수로 Locale을 받아 해당 사용자의 언어에 맞는 메시지를 반환할 수 있다.

@GetMapping("/welcome")
public String welcome(Locale locale, Model model) {
    String welcomeMessage = ms.getMessage("welcome.message", null, locale);
    model.addAttribute("message", welcomeMessage);
    return "welcome";
}

 

 

국제화

스프링 부트에서 국제화(i18n) 기능을 구현하는 것은 애플리케이션을 다양한 언어 및 지역 설정에 맞춰 사용할 수 있게 해주며, 사용자 경험을 향상시키는 데 중요하다.

국제화를 통해 애플리케이션은 사용자의 언어와 지역 설정에 맞는 적절한 메시지, 날짜, 통화 등을 제공할 수 있다. 스프링 부트에서 국제화를 구현하는 기본 단계는 다음과 같다.

 

1. 메시지 소스 파일 생성

src/main/resources 디렉토리에 위치한 프로퍼티 파일들을 통해 다국어 메시지를 관리한다.
파일 이름은 messages.properties로 시작하며, 각 언어 및 지역별로 접미사를 추가한다.

예: messages_en.properties, messages_fr.properties, messages_ko.properties 등.

이 파일들에는 키-값 쌍으로 메시지를 정의한다.

 

예를 들어서 아래와 같이 2개의 파일을 만들어서 분류한다.

messages_en.properties

 item=Item
 item.id=Item ID
 item.itemName=Item Name
 item.price=price
 item.quantity=quantity

 

messages_ko.properties

item=상품
item.id=상품 ID
item.itemName=상품명
item.price=가격
item.quantity=수량


영어를 사용하는 사람이면 messages_en.properties 를 사용하고,
한국어를 사용하는 사람이면 messages_ko.properties 를 사용하게 개발하면 된다.

이렇게 하면 사이트를 국제화 할 수 있다.

한국에서 접근한 것인지 영어에서 접근한 것인지는 인식하는 방법은 HTTP accept-language 해더 값을 사용하거 나 사용자가 직접 언어를 선택하도록 하고, 쿠키 등을 사용해서 처리하면 된다.

메시지와 국제화 기능을 직접 구현할 수도 있겠지만, 스프링은 기본적인 메시지와 국제화 기능을 모두 제공한다.

그리고 타임리프도 스프링이 제공하는 메시지와 국제화 기능을 편리하게 통합해서 제공한다.

 

2. application.properties에서 국제화 설정

application.properties 파일에 국제화 설정을 추가한다.

spring.messages.basename 속성을 사용하여 메시지 소스 파일의 기본 이름을 지정한다. 여러 개의 파일을 쉼표로 구분하여 지정할 수 있다.

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

MessageSource 를 스프링 빈으로 등록하지 않고, 스프링 부트와 관련된 별도의 설정을 하지 않으면 messages 라는 이름으로 기본 등록된다. 따라서 messages_en.properties , messages_ko.properties , messages.properties 파일만 등록하면 자동으로 인식된다.

스프링 부트 메시지 소스 기본 값
spring.messages.basename=messages

 

3. 컨트롤러에서 국제화 메시지 사용

국제화 파일 선택

locale 정보를 기반으로 국제화 파일을 선택한다.
Locale이 en_US 의 경우 messages_en_US → messages_en → messages 순서로 찾는다.Locale 에 맞추어 구체적인 것이 있으면 구체적인 것을 찾고, 없으면 디폴트를 찾는다고 이해하면 된다.

@Autowired를 사용하여 MessageSource를 주입받는다.

getMessage() 메서드를 사용하여 요청된 로케일에 맞는 메시지를 조회한다.

@Autowired
private MessageSource ms;

 

@Test
void defaultLang() {
	assertThat(ms.getMessage("hello", null, null)).isEqualTo("안녕");
	assertThat(ms.getMessage("hello", null, Locale.KOREA)).isEqualTo("안녕");\
}
  • ms.getMessage("hello", null, null) : locale 정보가 없으므로 messages 를 사용
  • ms.getMessage("hello", null, Locale.KOREA) : locale 정보가 있지만, message_ko 가 없으므로 messages 를 사용

 

@Test
void enLang() {
     assertThat(ms.getMessage("hello", null, Locale.ENGLISH)).isEqualTo("hello");
}
  • ms.getMessage("hello", null, Locale.ENGLISH) : locale 정보가 Locale.ENGLISH 이므로 messages_en 을 찾아서 사용
Locale 정보가 없는 경우 Locale.getDefault() 을 호출해서 시스템의 기본 로케일을 사용한다.

예) locale = null 인 경우 시스템 기본 locale 이 ko_KR 이므로 messages_ko.properties 조회 시도 → 조회 실패 → messages.properties 조회

 

스프링의 국제화 메시지 선택

메시지 기능은 Locale 정보를 알아야 언어를 선택할 수 있다.

결국 스프링도 Locale 정보를 알아야 언어를 선택할 수 있는데, 스프링은 언어 선택시 기본으로 Accept-Language 헤더의 값을 사용한다.

LocaleResolver

스프링은 Locale 선택 방식을 변경할 수 있도록 LocaleResolver 라는 인터페이스를 제공하는데, 스프링 부트는 기본으로 Accept-Language 를 활용하는 AcceptHeaderLocaleResolver 를 사용한다.
 public interface LocaleResolver {
     Locale resolveLocale(HttpServletRequest request);
     void setLocale(HttpServletRequest request, @Nullable HttpServletResponse
 response, @Nullable Locale locale);
}​
LocaleResolver 변경

만약 Locale 선택 방식을 변경하려면 LocaleResolver 의 구현체를 변경해서 쿠키나 세션 기반의 Locale 선택 기능을 사용할 수 있다.
예를 들어서 고객이 직접 Locale 을 선택하도록 하는 등

댓글