김영한 님의 스프링 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의 구현체이다
