Spring

스프링 메세지 소스 및 국제화(MessageSource, Locale)

녁이 2023. 12. 14. 18:33
728x90
반응형

Message

기획자가 화면에 보이는 문구를 변경해달라는 상황이다, 상품명이라는 단어를 모두 상품이름으로 고쳐달라고 하면 어떻게 해야할까?

그러면 많은 화면에 해당하는 수십개의 파일을 수정해야 한다...

이를 유연하게 대처할 수 있는 방법은 뭐가 있을까?

 

messages.properties

메세지 관리용 파일을 만드는 것이다!

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

이런식으로 한 곳에 작성해두고, 필요시에 각 HTML 태그에서 해당 데이터를 Key 값으로 불러 사용하는 것이다.

 

그럼 해당 데이터를 어떻게 불러 사용할까?

<label for="itemName" th:text="#{item.itemName}"></label>

위의 예시와 같이 th:text = "#{...}" 형식을 맞춰 사용해줘야 한다.

위와 같이 작성하면 렌더링 후 → 상품명 이 웹에 출력된다.

 


국제화

위에서 설명한 메세지 관리용 파일을 각 나라별로 별도로 관리하면 서비스를 국제화 할 수 있다. 

예를 들어 아래와 같이 한글 파일과 영어 파일을 나누는 것이다.

 

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 헤더 값을 사용하거나 사용자가 직접 언어를 선택하도록 하고, 쿠키, 세션 등을 사용해서 처리하면 된다.


 

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

 

스프링은 Locale 선택 방식을 변경할 수 있도록 LocaleResolver 라는 인터페이스를 제공하는데, 스프링 부트는 기본으로 Accept-Language 를 활용하는 AcceptHeaderLocaleResolver 를 사용한다.

 

만약 Locale 선택 방식을 변경하려면 LocaleResolver 의 구현체를 변경해서 쿠키나 세션 기반의 Locale 선택 기능을 사용할 수 있다.

예 - 고객이 직접 Locale 을 선택


스프링 메시지 소스 설정

메시지 관리 기능을 사용하려면 스프링이 제공하는 MessageSource 를 스프링 빈으로 등록하면 되는데,

MessageSource 는 인터페이스이다. 따라서 구현체인 ResourceBundleMessageSource 를 스프링 빈으로 등록하면 된다.

 

@Bean
public MessageSource messageSource() {
 ResourceBundleMessageSource messageSource = new
ResourceBundleMessageSource();
 messageSource.setBasenames("messages", "errors");
 messageSource.setDefaultEncoding("utf-8");
 return messageSource;
}

 

이는 스프링 빈으로 직접 등록 하는 방법이다.

 

BUT,

스프링 부트를 사용하면 스프링 부트가 MessageSource 를 자동으로 스프링 빈으로 등록한다.

 

 

다음과 같이 application.properties 에 메세지 소스를 설정을 따로 할 수가 있다.

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

 

따로 설정을 하지 않는다면, 아래와 같이 messages가 기본값으로 설정된다.

spring.messages.basename=messages

따라서, messages_en.properties , messages_ko.properties , messages.properties 파일만 등록하면 자동으로 인식된다.

 

 

EX)

messages.properties  경로 : /resources/messages.properties

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

 

messages_en.properties  경로 : /resources/messages_en.properties

hello=hello
hello.name=hello {0}

 


스프링 메시지 소스 사용 - Test

 

MessageSource 인터페이스

public interface MessageSource {
	String getMessage(String code, @Nullable Object[] args, @Nullable String 
	defaultMessage, Locale locale);

	String getMessage(String code, @Nullable Object[] args, Locale locale) throws
	NoSuchMessageException;

 

MessageSource 인터페이스를 보면 코드를 포함한 일부 파라미터로 메시지를 읽어오는 기능을 제공

 

@TEST

@SpringBootTest
public class MessageSourceTest {
 @Autowired
 MessageSource ms;
 @Test
 void helloMessage() {
 String result = ms.getMessage("hello", null, null);
 assertThat(result).isEqualTo("안녕");
 }
}

code는 hello, arg는 없기 때문에 null, locale은 설정하지 않아 null로 했다.

결과는 기본 한국어로 되어 있기 때문에 "안녕" 이 출력된다.

 

@Test - 메세지가 없는 경우, 기본 메세지

@Test
void notFoundMessageCode() {
 assertThatThrownBy(() -> ms.getMessage("no_code", null, null))
 .isInstanceOf(NoSuchMessageException.class);
}

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

해당하는 코드가 없을 때는 NoSuchMessageException 이 발생한다.

만일, 해당하는 코드가 없는데 DefaultMessage를 위와 같이 설정해준다면, DefaultMessage가 출력된다.

위에서는 "기본 메세지" 라고 작성해두었다.

 

@Test - 매개 변수 사용

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

매개 변수는 new Object[]{"..."} 을 통해 전달한다.

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


국제화 파일 선택

locale 정보를 기반으로 국제화 파일을 선택한다.

Locale이 en_US 의 경우 messages_en_US → messages_en → messages 순서로 찾는다.

Locale 에 맞추어 구체적인 것이 있으면 구체적인 것을 찾고, 없으면 디폴트를 찾는다고 이해하면 된다.

 


웹 애플리케이션에 국제화 적용

위에서 말한 방식대로 HTML 파일에서 해당 부분을 변경해주면 된다.

<h2>상품 등록 폼</h2> -> <h2 th:text="#{page.addItem}">상품 등록</h2>

 

만약, 파라미터를 사용해야 한다면?

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

위와 같은 형식으로 작성해줘야 한다. → #{...(${...})} 

 

728x90
반응형