김영한 님의 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 강의를 보고 작성한 내용입니다.
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard
다양한 메세지를 한 곳에서 관리하도록 하는 기능을 메세지 기능이라 한다
메세지 기능에서 설명한 파일을 각 나라 별로 만들어 관리하면 서비스를 국제화 할 수 있다
HTTP 헤더의 accept-language를 보고 그에 맞는 파일을 사용해서 HTML을 렌더링 하도록 한다
스프링은 기본적인 메세지와 국제화 기능을 제공하며, 타임리프 역시 스프링이 제공하는 메세지, 국제화 기능을 통합해서 제공한다
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("messages", "errors");
messageSource.setDefaultEncoding("utf-8");
return messageSource;
}
메세지 관리 기능을 사용하기 위해 MessageSource
를 스프링 빈으로 등록해야한다
ResourceBundleMessageSource
를 스프링 빈으로 등록한다setBasenames()
설정 파일의 이름을 지정
resources 디렉터리에서 해당 파일을 찾는다
위의 코드의 경우 resources에 있는 messages.properties
, errors.properties
를 읽어들인다
스프링부트를 사용하면 MessageSource
를 자동으로 스프링 빈으로 등록해주고, 필요한 경우 application.properties
에 별도의 메세지 소스를 설정한다
application.properties에 별도의 설정을 하지 않으면 메세지 소스 기본값은 아래와 같다
spring.messages.basename=messages
MessageSource
를 스프링 빈으로 등록하지 않고, 스프링부트와 관련된 설정을 하지 않으면 messages
라는 이름으로 기본 등록된다
messagesXXX.properties
파일만 등록하면 자동으로 인식된다ex> messages.properties
, messages_en.properties
라는 파일이 있고, 별도의 설정 정보를 입력하지 않았다고 가정했을 때
한국에서 들어오면 messages.properties, 미국에서 들어오면 messages_en.properties를 사용한다
만약, 한국도 아니고 미국도 아닌 경우에는 디폴트인 messages.properties를 불러와서 사용한다
public interface MessageSource {
// Try to resolve the message. Return default message if no message was found.
@Nullable
String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
// Try to resolve the message. Treat as an error if the message can't be found.
String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
/* Try to resolve the message using all the attributes contained within the
{@code MessageSourceResolvable} argument that was passed in */
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
< messages.properties >
hello=안녕
hello.name=안녕 {0}
@Test
void helloMessage() {
String result = ms.getMessage("hello", null, null);
assertThat(result).isEqualTo("안녕");
}
메세지 파일을 보았을 때, hello가 code에 해당하고, 우측이 메세지에 해당
Test에서 hello라는 코드를 주고, args, locale 정보를 모두 null로 설정 후 테스트 진행
Locale.KOREA
와 같이 작성하면 된다locale 정보가 없으면 basename에서 설정한 기본 이름 메세지 파일을 조회한다
@Test
void noFoundMessageCode() {
assertThatThrownBy(() -> ms.getMessage("no_code", null, null))
.isInstanceOf(NoSuchMessageException.class);
}
messages.properties에 no_code라는 code는 존재하지 않는다 ➜ 예외 발생
위 인터페이스의 두 번째 메서드를 보면 NoSuchMessageException
이 발생하는 것을 알 수 있는데 Test 해본 결과 테스트 성공
@Test
void noFoundMessageCodeDeaultMessage() {
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");
}
@Test
void argumentMessageTest() {
String result = ms.getMessage("hello.name", new Object[]{"Test", "Spring"}, null);
assertThat(result).isEqualTo("안녕 Test");
}
messages.properties를 보면 hello.name은 안녕 {0} 로 되어 있는데 {0} 에 매개변수를 전달해 치환할 수 있다
위의 테스트를 보면 Object 배열에 Spring 문자열을 넣고 매개변수로 전달하면서 getMessage()를 호출
두 번째 테스트는 임의로 진행했는데 Object 배열에 두 개의 문자열을 넣고 테스트를 진행
@Test
void dafaultLang() {
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");
}
첫 번째 테스트는 Locale에 아무것도 없는 경우, Locale을 KOREA로 지정한 경우 모두 디폴트인 messages.properties에서 데이터를 조회
두 번째 테스트에서 Locale을 ENGLISH로 지정했는데, 이 경우 messages_en.properties에서 데이터를 조회
Locale 정보가 없는 경우 Locale.getDefault()
을 호출해서 시스템의 기본 로케일을 사용한다
locale = null
➜ Locale.getDefault()
➜ messages_ko.properties
조회 ➜ 조회 실패 ➜ messages.properties
조회<!-- Thymeleaf -->
<h2 th:text="#{page.addItem}">상품 등록 폼</h2>
<!-- 페이지 소스 보기 -->
<h2>상품 등록</h2>
타임리프에서 #{ ... }
라는 메세지 표현식을 사용해서 스프링의 메세지를 조회한다
메세지 표현식으로 작성된 것들이 렌더링 될 때, 메세지 파일에 있는 내용으로 바뀌게 된다
나중에 상품 등록을 다른 말로 변경할 때, 모든 파일에서 변경하는 것이 아닌 메세지 파일의 내용만 변경하면 된다
파라미터 사용하는 경우
hello.name=안녕 {0}
의 경우 메세지 표현식을 아래처럼 사용
<p th:text="#{hello.name(${item.itemName})}"></p>
스프링의 메세지 기능은 Locale 정보를 알아야 언어를 선택할 수 있다
스프링은 언어 선택 시, 기본적으로 HTTP 요청 메세지의 헤더에 있는 Accept-Language 정보를 사용해 Locale 정보를 알아낸다
Locale 선택 방식으로 Accept-Language를 활용할 수도 있고, 사용자가 언어를 선택하도록 하고 사용자의 선택을 쿠키나 세션같은 곳에 저장하여 계속 사용하도록 할 수 있다
스프링은 Locale 선택 방식을 변경할 수 있도록 LocaleResolver
라는 인터페이스를 제공한다
AcceptHeaderLocaleResolver
가 사용된다public interface LocaleResolver {
// Resolve the current locale via the given request.
Locale resolveLocale(HttpServletRequest request);
// Set the current locale to the given one.
void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);
}
Locale 선택 방식을 변경하려면 LocaleResolver
의 구현체를 변경해서 쿠키나 세션 기반의 Locale 선택 기능을 사용할 수 있다
아래에 있는 것들이 LocaleResolver의 구현체이다