10. 메시지 국제화
정리
메시지
악덕? 기획자가 화면에 보이는 문구가 마음에 들지 않는다고, 상품명이라는 단어를 모두 상품이름으로 고쳐달라고 한다. “상품명”이라는 메시지가 하드코딩되어 있다면, 여러 HTML 파일에 흩어져있는 “상품명”을 직접 일일히 찾아서 “상품이름”으로 수정해야 할 것이다.
이런 다양한 메시지를 한 곳에서 관리하도록 하는 기능을 메시지 기능이라 한다.
예를 들어서 messages.properties
라는 메시지 관리용 파일을 만들고
item=상품
item.id=상품 ID
item.itemName=상품명
item.price=가격
item.quantity=수량
각 HTML들은 다음과 같이 해당 데이터를 key 값으로 불러서 사용하는 것이다.
국제화
메시지의 기능을 조금 더 확장해서 생각해보면, 메시지 파일을 나라별로 관리하면 서비스 대상 국가에 맞춰 국제화할 수 있다.
messages_en.properties
item=Item
item.id=Item ID
item.itemName=Item Name
item.price=price
item.quantity=quantity
한국에서 접근한 것인지 영어에서 접근한 것인지는 인식하는 방법은 HTTP Accept-Language
헤더 값을 사용하거나 사용자가 직접 언어를 선택하도록 하고, 쿠키 등을 사용해서 처리하면 된다.
메시지와 국제화 기능을 직접 구현할 수도 있겠지만, 스프링은 기본적인 메시지와 국제화 기능을 모두 제공한다. 그리고 타임리프도 스프링이 제공하는 메시지와 국제화 기능을 편리하게 통합해서 제공한다.
스프링 메시지 소스 설정
스프링은 기본적인 메시지 관리 기능을 제공한다.
메시지 관리 기능을 사용하려면 스프링이 제공하는 MessageSource
를 스프링 빈으로 등록하면 되는데, MessageSource
는 인터페이스이다. 따라서 구현체인 ResourceBundleMessageSource
를 스프링 빈으로 등록하면 된다.
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("messages", "errors");
messageSource.setDefaultEncoding("utf-8");
return messageSource;
}
basenames
: 설정 파일의 이름을 지정한다.messages
로 지정하면messages.properties
파일을 읽어서 사용한다. 추가로 국제화 기능을 적용하려면messages_en.properties
,messages_ko.properties
와 같이 파일명 마지막에 언어 정보를 주면 된다. 만약 찾을 수 있는 국제화 파일이 없으면messages.properties
(언어정보가 없는 파일명)를 기본으로 사용한다. 파일의 위치는/resources/messages.properties
에 두면 된다. 여러 파일을 한번에 지정할 수 있다. 여기서는messages
,errors
둘을 지정했다.defaultEncoding
: 인코딩 정보를 지정한다.utf-8
을 사용하면 된다.
스프링 부트
스프링 부트를 사용하면 스프링 부트가 MessageSource
를 자동으로 스프링 빈으로 등록해준다.MessageSource
를 스프링 빈으로 등록하지 않고, 스프링 부트와 관련된 별도의 설정을 하지 않으면 messages
라는 이름으로 기본 등록된다. 따라서 messages_en.properties
, messages_ko.properties
, messages.properties
파일만 등록하면 자동으로 인식된다.
주의!
massage
가 아니라messages
다! 마지막s
에 주의하자
메시지소스를 설정하려면 application.properties
에서 직접 설정해주면 된다.
application.properties
spring.messages.basename=messages,config.i18n.messages
스프링 메시지 소스
스프링 메시지 소스를 이용해 아래 메시지 프로퍼티 파일을 적용한다고 해보자.
/resources/messages.properties
hello=안녕
hello.name=안녕 {0}
/resources/messages_en.properties
hello=hello
hello.name=hello {0}
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;
}
스프링 메시지소스 인터페이스를 보면 코드를 포함한 일부 파라미터로 메시지를 읽어오는 기능을 제공한다.
@SpringBootTest
public class MessageSourceTest {
@Autowired
MessageSource ms;
@Test
void helloMessage() {
String result = ms.getMessage("hello", null, null);
assertThat(result).isEqualTo("안녕");
}
}
code
code에는 “hello”가, 나머지 args와 locale에는 null을 입력하면, 위에서 설정했던 “안녕”이라는 결과를 얻게 된다.
인터페이스 구현체 내부에서 locale
정보가 없으면 basename
에서 설정한 기본 이름 메시지 파일을 조회한다. 그래서 messages.properties
에 가서 “hello”에 해당하는 “안녕”을 조회하여 반환해준 것이다.
defaultMessage
위 예시에는 설정 파일에 hello=안녕
이 존재하였기 때문에 정상적으로 작동하였지만,
만약 존재하지 않는다면?
- 메시지가 없는 경우에는
NoSuchMessageException
이 발생한다. - 메시지가 없어도 기본 메시지(
defaultMessage
)를 사용하면 기본 메시지가 반환된다.
@Nullable Object[] args
- 매개변수
위에서 메시지 프로퍼티 파일에 {0}
가 존재하는 것을 확인할 수 있었다.
MessageSource의 args에 값을 전달하면, {0}
자리에 args값이 매개변수로 전달되어 치환된다.
- 예를 들어 “Spring”을 전달한다면
hello.name=안녕 {0}
→ Spring 단어를 매개변수로 전달 →안녕 Spring
localelocale
정보를 기반으로 국제화 파일을 선택한다. Locale
이 en_US
의 경우 messages_en_US
, messages_en
, messages
순서으로 찾는다. Locale
에 맞추어 구체적인 것이 있으면 구체적인 것을 찾고, Locale
정보가 없는 경우 Locale.getDefault()
을 호출해서 시스템의 기본 locale
을 사용한다.
타임리프
스프링 MessageSource를 통해 메시지를 조회하는 동작에 대해서 살펴봤다. 실제 웹 Application의 HTML 문서에 적용하기 위해선 랜더링을 거쳐야 하는데, 타임리프가 스프링 메시지를 편하게 적용할 수 있는 기능을 제공해준다.
타임리프 메시지 적용
타임리프의 메시지 표현식 #{...}
를 사용하면 스프링의 메시지를 편리하게 조회할 수 있다.
프로퍼티 파일에 label.item=상품
이라고 지정되어 있다면
렌더링 전
<div th:text="#{label.item}"></h2>
렌더링 후
<div>상품</h2>
파라미터 적용
hello.name=안녕 {0}
<p th:text="#{hello.name(${item.itemName})}"></p>
타임리프로 전달된 변수${item.itemName}
는 메시지의 플레이스홀더 {0}
를 대체한다.
주의사항
- 파라미터 순서
{0}
,{1}
처럼 인덱스를 기반으로 파라미터가 대체된다.- 여러 파라미터가 필요한 경우
#{hello.name(${param1}, ${param2})}
형태로 작성합니다.
- 메시지 키가 없을 경우
- 메시지 키가 없으면 예외가 발생하거나 기본값을 사용할 수 있다.
- 기본값 설정:
#{hello.name:Default Message}
스프링의 국제화 메시지 선택
앞서 MessageSource
테스트에서 보았듯이 메시지 기능은 Locale
정보를 알아야 언어를 선택할 수 있다. 결국 스프링도 Locale
정보를 알아야 언어를 선택할 수 있는데, 스프링은 언어 선택 시 기본으로 Accept-Language
헤더의 값을 사용한다.
LocaleResolver
스프링은 Locale
선택 방식을 변경할 수 있도록 LocaleResolver
라는 인터페이스를 제공하는데,
스프링 부트는 기본으로 AcceptLanguage
를 활용하는 AcceptHeaderLocaleResolver
를 사용한다.
LocaleResolver 인터페이스
public interface LocaleResolver {
Locale resolveLocale(HttpServletRequest request);
void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);
}
LocaleResolver 변경
만약 Locale
선택 방식을 변경하려면 LocaleResolver
의 구현체를 변경해서 쿠키나 세션 기반의 Locale
선택 기능을 사용할 수 있다. Apple, Amazon 같은 세계적인 빅테크 기업의 페이지를 가보면 고객이 직접 Locale
을 선택할 수 있는 기능을 제공하는데, 이런 식으로 동작한다고 생각하면 된다. 이와 관련해서 LocaleResolver
를 검색하면 수 많은 예제가 나온다.
'Spring > MVC' 카테고리의 다른 글
[Spring/MVC] 12. Bean Validation (0) | 2025.01.19 |
---|---|
[Spring/MVC] 11. Validation (0) | 2025.01.19 |
[Spring/MVC] 09. 타임리프 - 스프링 통합과 폼 (0) | 2024.12.28 |
[Spring MVC] 08. 타임리프 기본기능 (0) | 2024.12.27 |
[Spring MVC] 07. 스프링 MVC - 웹 페이지 만들기(타임리프, PRG, RedirectAttribute) (0) | 2024.12.26 |