만약 기획자,,, 악덕? 어쨌든 화면에 나오는 문구를 개발 중간에 상품명 에서 상품이름으로 고쳐달라면,, 우린 어떻게 해야할까?
특히나 그 고쳐야 할 문구들이 수십개를 넘어선다면,,,?
우리는 지금까찌 html을 하드코딩 했고 이들을 한 곳에서 관리하도록 하기 위해 '메시지' 기능이 필요하다.
...
그리고 메시지에서 한 발 더 나가보면? 우리는 생각보다 해외 사이트에서 내가 쓰는 혹은 지원하고 있는 언어로 서비스를 받고 있는 경우가 대다수였다..
왜일까? 그건 바로 앞에서 나온 메시지를 messages.properties
라는 파일을 이용해 각 나라별로 별도 관리가 가능해서 이다.
스프링은 메시지 관리 기능을 제공해준다. 어떻게?
MessageSource
를 스프링 빈으로 등록해서!
-> 그런데 얘는 '인터페이스'다.
=> 구현체인 ResourceBundleMessageSource
를 스프링 빈으로 등록하면 된다. 👍
MessageSource
를 자동으로 스프링 빈에 등록해준다 하지만...@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new
ResourceBundleMessageSource();
messageSource.setBasenames("messages", "errors");
messageSource.setDefaultEncoding("utf-8");
return messageSource;
}
basenames
: 설정 파일의 이름을 지정한다.
-> messages 로 지정하면essages.properties
파일을 읽어서 사용한다.
-> 추가로 국제화 기능을 적용하려면ssages_en.properties
,messages_ko.properties
와 같이
파일명 마지막에 언어 정보를 주면된다.
->messages.properties
(언어정보가 없는 파일명)를 기본으로 사용한다.
-> 파일의 위치/resources/messages.properties
에 두면 된다.
-> 여러 파일을 한번에 지정할 수 있다. 여기서는messages
,errors
둘을 지정했다.- defaultEncoding : 인코딩 정보를 지정한다. utf-8 을 사용하면 된다
MessageSource
를 스프링 빈으로 자동 등록된다. 물론 별도로 등록을 하지 않고 별도의 설정 (application.properties)를 하지 않으면, messages
라는 이름으로 자동 등록
spring.messages.basename=messages
=> 그래서 messages_en.properties
,
messages_ko.properties
, messages.properties
파일만 등록하면 자동으로 인식
/resources/ 폴더 아래에 두 파일을 만들었다고 해보자.
hello=안녕
hello.name=안녕 {0}
hello=hello
hello.name=hello {0}
실제 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;
@SpringBootTest
public class MessageSourceTest {
@Autowired
MessageSource ms;
@Test
void helloMessage() {
String result = ms.getMessage("hello", null, null);
assertThat(result).isEqualTo("안녕");
}
ms.getMessage("hello", null, null)
code
: helloargs
: nulllocale
: null(메시지 코드, 매개변수, 나라(언어)?)
-> 여기서는 locale이 설정이 없어서 기본 messages.properties
파일에서 데이터 조회
@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
)를 사용하면 "기본 메시지"가 반환 //매개변수 사용 {0}
@Test
void argumentMessage() {
String result = ms.getMessage("hello.name", new Object[]{"Spring"}, null);
assertThat(result).isEqualTo("안녕 Spring");
}
}
hello.name=안녕 {0}
-> Spring 단어를 매개변수로 전달 -> 안녕 Spring
그래서 만약 Locale이 en_US
의 경우 messages_en_US
를 이게 없다면 messages_en messages
순서로 찾는다.
//국제화 파일 선택 시, 디폴트 값의 경우 2가지
@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");
}
assertThat(ms.getMessage("hello", null, Locale.KOREA)).isEqualTo("안녕");}
message_ko
가 없으므로 messages
를 사용막상 html의 파일을 보니 상품, 상품ID 등등 겹치는 용어들이 꽤나 많다. 메시지의 본 기능을 이제 써볼 때가 되었다.
그 전에 우리는 알고 갈게 있어야 한다.
📢타임리프의 메시지 표현식 #{...}
=>스프링의 메시지를 편리하게 조회할 수 있다.
그리고 이를 적용하기 위한 메시지들은 모두 messages.properties
에 추가 등록한다.
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=취소
💬참고로 파라미터는 다음과 같이 사용할 수 있다.
hello.name=안녕 {0}
<p th:text="#{hello.name(${item.itemName})}"></p>
결과: 안녕 상품이름
쉽다 우리가 방금 전에 만든 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=Cance
사실 이것으로 국제화 작업은 거의 끝났다. 앞에서 템플릿 파일에는 모두 #{...}
를 통해서 메시지를 사용하도록 적용해두었기 때문이다.
웹 브라우저의 언어 설정 값을 변경하면서 국제화 적용을 확인해보자.
크롬 브라우저 설정 언어를 검색하고, 우선 순위를 변경하면 된다.
우선순위를 영어로 변경하고 테스트해보자.
Accept-Language
의 ⭐ 핵심
Accept-Language
는 클라이언트가 서버에 기대하는 언어 정보를 담아서 요청하는 HTTP 요청 헤더이다.
⭐곧, locale 정보를 알아야 국제화를 시키는데 이 Accept-Language
헤더의 값을 기본으로 세팅하여 국제화를 시켜준다.
스프링은 Locale 선택 방식을 변경할 수 있도록 LocaleResolver
라는 인터페이스를 제공하는데,
스프링 부트는 기본으로 Accept-Language
를 활용하는 AcceptHeaderLocaleResolver
를 사용한다.
public interface LocaleResolver {
Locale resolveLocale(HttpServletRequest request);
void setLocale(HttpServletRequest request, @Nullable HttpServletResponse
response, @Nullable Locale locale);
}
가능하다. 쿠키나 세션 기반의 locale 선택 기능을 이용할 수도 있을 것이다.
예를 들면 애플 사이트 등에서 고객이 직접 locale을 선택하는 방법도 있을 것이고...
허나 요새는 국제화를 지원하는 사이트의 경우가 아니면 많이 쓰이거나 제공하진 않는 듯 하다.