목차
1. 메세지, 국제화 소개
메세지
화면에서 공통으로 사용되는 다양한 메세지를 한 곳에서 관리하도록 하는 기능을 메세지 기능이라 한다.
메세지 기능을 사용하지 않을 경우 우리는 모든 메세지를 각각 하드코딩으로 입력해야한다.
<div>
<label for="itemId">상품 ID</label>
<input type="text" id="itemId" name="itemId" class="form-control" value="1" th:value="${item.id}" readonly>
</div>
<div>
<label for="itemName">상품명</label>
<input type="text" id="itemName" name="itemName" class="form-control" value="상품A" th:value="${item.itemName}" readonly>
</div>
<div>
<label for="price">가격</label>
<input type="text" id="price" name="price" class="form-control" value="10000" th:value="${item.price}" readonly>
</div>
<div>
<label for="quantity">수량</label>
<input type="text" id="quantity" name="quantity" class="form-control" value="10" th:value="${item.quantity}" readonly>
</div>
HTML
복사
아이템 상세를 보여주는 위 HTML 태그들에는 각각 하드코딩으로 상품ID, 상품명, 가격, 수량이 적혀있다.
또한 해당 명칭들은 나머지 다른 많은 페이지에서 공통으로 쓰이고 있다고 할 때 기획 변경으로 상품명을 제품명으로 바꿔야 한다면 어떨까? 메세지 기능을 사용하지 않았다면 모든 소스파일에서 하나하나 상품명이라 적혀있는 곳을 찾아 제품명이라 바꿔줘야한다.
하지만 만약 메세지 기능을 사용했다면 다음과 같이 메세지를 변수화 하여 key 값으로 불러 사용하 수 있다.
item.itemName= 제품명
YAML
복사
<label for="itemName" th:text="#{item.itemName}"></label>
HTML
복사
국제화
이제 메세지를 한 곳에 모아서 기획자의 기획변경에도 어려움없이 메세지를 바꿀 수 있다.
그다음으로 이제 서비스가 제공되야하는 곳이 다른 언어권이라면 어떨까? 메세지 내용을 바꾸기엔 기존 서비스 언어권 국가에는 그대로 제공이 되야한다. 이럴 경우 메세지 파일(message.properties)를 각 나라별로 별도로 관리하여 국제화 기능을 제공할 수 있다.
예를 들어 한국과 영어 두 언어가 제공되야 한다면 다음과 같이 두 개의 파일을 만들어 분류할 수 있다.
•
messages_en.properties
item=Item
item.id = Item ID
item.itemName= Item Name
item.price= price
item.quantity= quantity
YAML
복사
•
messages_ko.properties
item= 상품
item.id = 상품 ID
item.itemName= 상품명
item.price= 가격
item.quantity= 수량
YAML
복사
이제 영어를 사용하는 사용자는 messages_en.properties, 한국어를 사용하는 사용자는 messages_ko.properties를 사용하도록 개발하면 된다.
참고 - HTTP의 accept-language
그럼 서버에서는 사용자가 한국어를 원하는지 영어를 원하는지 어떻게 알 수 있을까? 보통 한국에서 접속하면 한글, 외국에서 접속하면(ex: 미국) 영어를 제공하도록 하면되는데 이는 HTTP에서 배우던 accept-language 헤더 값을 이용하거나 사용자가 직접 언어를 선택하게 한 뒤 이를 쿠키등을 사용해 처리하면 된다.
정리
•
메세지를 properties로 설정해 공통 메세지를 변수화하여 제공할 수 있다.
•
여러 언어에 맞게 messages를 제공할 수 있으며 이를 국제화 기능이라 한다.
2. 스프링에서 메세지 소스 설정하기
소개
스프링은 기본적인 메세지 관리 기능을 제공하는데, 스프링 부트는 프레임워크 자체적으로 MessageSource를 자동으로 스프링 빈으로 등록해준다.
자신이 사용하는 프레임워크(스프링, 스프링부트)에 맞는 설정방식으로 설정하면 된다.
스프링일 경우 빈 등록 방법
스프링부트일 경우 메세지 소스 설정
사용해보기
•
basename 설정은 기본값인 messages를 사용한다.
•
messages.properties
hello=안녕
hello.name= 안녕 {0}
Plain Text
복사
•
messages_en.properties
hello= hello
hello.name= hello {0}
Plain Text
복사
•
(만약 IntelliJ를 사용한다면) 여러 messages 설정이 묶여서 Resource Bundle 'messages'로 뭉쳐질 것이다. 그리고 해당 경로를 보면 다음과 같이 나온다.
등록된 언어별로 속성의 값을 볼 수 있다.
2. 스프링에서 메세지 소스 사용해보기
소개
설정은 했으니 이제 메세지 소스를 실제로 읽어 사용해보자.
스프링 프로젝트는 구동시 자동(혹은 수동)으로 스프링 빈으로 MessageSource가 등록된다. 이 빈을 통해 메세지 기능을 사용할 수 있는데, 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;
}
Java
복사
•
code: 메세지에서 불러올 값의 key
•
args: 메세지에 {0}과 같은 포맷이 있는경우 넘겨줄 인수이며 Object[] 타입니다.
⇒ Ex: new Object[]{"Spring"}
•
defaultMessage: 만약 인수로 전달한 key에 해당하는 값이 없는경우 반환할 기본 값
•
locale: Locale정보(ex: Locale.KOREA, Locale.ENGLISH, ...)
사용해보기
@SpringBootTest
public class MessageSourceTest {
@Autowired
MessageSource ms;
@Test
void helloMessage() {
String hello = ms.getMessage("hello", null, null);
Assertions.assertThat(hello).isEqualTo("안녕");
}
@Test
void notFoundMessageCode() {
/*assertThatThrownBy(() -> ms.getMessage("no_content", null, null))
.isInstanceOf(NoSuchMessageException.class);*/
String message = ms.getMessage("no_content", null, "기본 메세지", null);
assertThat(message).isEqualTo("기본 메세지");
}
@Test
void argumentMessage() {
String message = ms.getMessage("hello.name", new Object[]{"Spring"}, null);
assertThat(message).isEqualTo("안녕 Spring");
}
@Test
void defaultLang() {
assertThat(ms.getMessage("hello", null, null)).isEqualTo("안녕");
assertThat(ms.getMessage("hello", null, Locale.KOREA)).isEqualTo("안녕");
}
@Test
void enLang() {
assertThat(ms.getMessage("hello", null, Locale.ENGLISH)).isEqualTo("hello");
}
}
Java
복사
•
@SpringBootTest
: MessageSource가 스프링 빈으로 등록해야 하기 때문에 SpringBootTest 애노테이션을 사용한다.
3. 웹 애플리케이션에 메세지 적용해보기
웹페이지에 메세지를 적용하는건 어렵지 않다.
우선 다음과 같은 메세지가 있다고 하자.
label.item=상품
label.item.id=상품 ID
label.item.itemName=상품명
label.item.price=가격
label.item.quantity=수량
page.items=상품 목록
page.item=상품 상세
page.addItem=상품 등록
page.updateItem=상품 수정
button.save=저장
button.cancel=취소
Plain Text
복사
위와 같은 메세지가 등록되어있다고하면 타임리프에서는 메세지 표현식(#{...})을 사용하면 스프링의 메세지를 편리하게 조회할 수 있다. 예를들어 등록된 메세지 중에서 상품이라는 값을 사용하려면 ${label.item}을 사용하면 된다.
•
렌더링 전
<div th:text="#{label.item}"> <div>
HTML
복사
•
렌더링 후
<div>상품</div>
HTML
복사
파라미터가 있는 메세지
•
메세지의 값이 상황에따라 변경이 되야하는 경우가 있는데 이 경우 파라미터를 사용하면 된다.
•
메세지 : hello.name= 안녕 {0}
•
사용 예제
<p th:text="#{hello.name(${item.itemName})}"></p>
HTML
복사
4. 웹 애플리케이션에 국제화 적용하기
메세지 기능을 사용해 웹 애플리케이션의 유연한 메세지를 추가했다면 이번엔 국제화를 적용해 다른 언어로도 서비스가 제공되도록 할 수 있다.
우선 위에서 제공했던 messages.properties를 기반으로 messages_en.properties을 작성해보자.
label.item=Item
label.item.id=Item ID
label.item.itemName=Item Name
label.item.price=price
label.item.quantity=quantity
page.items=Item List
page.item=Item Detail
page.addItem=Item Add
page.updateItem=Item Update
button.save=Save
button.cancel=Cancel
Plain Text
복사
웹으로 확인하기
이렇게 언어별로 메세지를 작성해줬다.
그럼 이를 어떻게 확인할 수 있을까?
위에서 이미 얘기했지만 요청시 헤더에 Accept-Language의 값을 변경해서 국제화를 테스트할 수 있는데,
크롬기준으로 웹 브라우저 언어 설정 값을 변경하면 페이지 요청시 Accept-Language의 값이 변경된다.
•
크롬 - 설정 - 고급 - 언어에가면 언어를 정렬할 수 있다.
여기서 영어를 가장 위로 올리면 Accept-Language가 영어로 바뀌게 된다.
naver.com의 accept-language
스프링의 국제화 메세지 선택
메세지 기능은 Locale 정보를 알아야 언어를 선택할 수 있기에 결국 스프링도 Locale정보를 알아야 언어를 선택할 수 있다. 스프링에서는 언어 선택시 기본으로 Accept-Lanuage 헤더의 값을 사용한다.
LocaleResolver
스프링에서 Locale선택 방식을 변경할 수 있도록 제공하는 인터페이스
스프링 부트는 기본으로 Accept-Language를 활용하는 AcceptHeaderLocaleResolver를 사용한다.
public interface LocaleResolver {
/**
* Resolve the current locale via the given request.
* Can return a default locale as fallback in any case.
* @param request the request to resolve the locale for
* @return the current locale (never {@code null})
*/
Locale resolveLocale(HttpServletRequest request);
/**
* Set the current locale to the given one.
* @param request the request to be used for locale modification
* @param response the response to be used for locale modification
* @param locale the new locale, or {@code null} to clear the locale
* @throws UnsupportedOperationException if the LocaleResolver
* implementation does not support dynamic changing of the locale
*/
void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);
}
Java
복사
LocaleResolver 인터페이스
•
만약 Locale선택 방식을 변경하려면 LocaleResolver의 구현체를 변경해서 쿠키나 세션 기반의 Locale 선택 기능을 사용할 수 있다. 예를들어 고객이 직접 Locale을 선택하도록 하는 경우를 말한다.