본문 바로가기
Spring|Spring-boot

Spring 메시지, 국제화

by oncerun 2022. 1. 6.
반응형

최근 진행한 프로젝트가 전 세계에서 접속하는 상황이다. 우선적으로 한국어는 물론이고 영어로 번역을 해야 하는 상황이 있어, 현지분들이 접속 시 기존 화면의 단어를 데이터베이스에서 현지 언어로 번역하여 제공하는 것으로 알고 있다. 

그래서 퇴근 후 메시지, 국제화하는 방법에 대해 공부하다가 관련 내용을 정리하려고 한다.

 

 

 

메시지

 

신규 기능이나 유지보수를 진행하다 보면 규정이 변경되거나, 기존 용어의 어감 등으로 화면상에 공통적으로 들어있는 문구를 변경하는 경우가 상당히 많다.  여러 화면에 있는 모든 단어를 모두 찾아서 변경하는 것은 매우 불편한 작업이고 시간도 오래 걸린다. 

 

이러한 다양한 메시지를 한 곳에서 관리되도록 하는 기능을 메시지 기능이라고 한다.

 

예를 들면 message.properties라는 메시지 관리용 파일을 생성하고 

company.name=회사이름
company.dept=부서이름
company.duty=직무
..

 

각 HTML에서 다음과 같이 해당 데이터를 key값으로 불러서 사용하는 기능이다.

<label for="companyName" th:text="#{company.name}"></label>

 

 

국제화

 

메시지라는 개념에 글로벌을 더한 서비스이다. 이러한 메시지 파일인 message.properteis를 각 나라별로 별도로 관리하면 서비스를 국제화할 수 있다.

 

message_en.properties

 

message_ko.properties

 

두 개의 파일을 설정하고 웹 브라우저가 전송하는 local정보를 통해 view를 제공해주면 된다.

보통 HTTP Accept-language 헤더 값을 사용하거나 사용자의 선택을 통해 해당 정보를 쿠키에 담아 놓고 사용할 수 있다.

 

크롬에서는 설정에서 언어 설정에서 한국어를 en_us보다 낮게 설정하고 서버로 요청을 보내면 

Accept-Language가 en_us, ko 순서로 서버에 도착하기 때문에 서버에서

우선순위 여부 (q 의 값이 클수록 높고 없으면 1)를 통해 국제화를 손쉽게 적용할 수 있다.

 

Locale에 대한 자세한 설정은 마지막에 정리한다.

 

Spring Message Source Setting

스프링은 기본적인 메시지 관리 기능을 제공한다. 

또 스프링 부트를 사용한다면 기본 설정이 다되어서 제공됩니다.

 

 

메시지 관리 기능을 사용하려면 스프링이 제공하는 MessageSource를 스프링 빈으로 등록하면 되는데, MessageSource는 인터페이스이기에 구현체인 ResourceBundleMessageSource를 스프링 빈으로 등록하면 된다.

 

@Bean
public MessageSource messageSource() {
 ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
 messageSource.setBasenames("messages");
 messageSource.setDefaultEncoding("utf-8");
 return messageSource;
}

 

 

1. setBasenames를 통해 이름을 등록할 수 있다. 이러면 messages.properties라는 파일을 자동으로 읽습니다.(, 로 다중 설정이 가능하다.

 

2. 추가로 국제화 기능을 적용하려면 ( message_en | message_ko ). properties와 같이 파일명에 마지막 언어정보를 주면 된다. 만약 찾을 수 없다면 message.properties를 기본으로 사용한다.

 

3. 파일의 위치는 /resource/message.properties로 두면 된다.

 

4. defaultEncoding으로 인코딩 정보를 제공한다. 

 

 

* TIP 스프링 부트 사용 시 appliaction.properties의  속성에 대해 궁금하다면 다음 페이지에 접속하면 된다.

https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties

 

Common Application Properties

 

docs.spring.io

 

 

MessageSoruce 인터페이스

public interface MessageSource {
String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
String getMessage(String code, @Nullable Object[] args, Locale locale) throwsNoSuchMessageException;

 

첫 번째 인자인 code는 properties에 설정해준 key값이다. 이 key값을 넘기면 설정한 값이 반환된다.

그 이외에도 3가지의 인자가 더 있는 것을 확인할 수 있는데

하나는 defaultMessage의 String타입이고,

Locale은 아시다시피 국가정보

args의 인자를 받을 수 있는 Object타입을 받는 배열이 있다.

 

String message = messageSource.getMessage("message.key", new Object[]{"first args", "second args"}, null)

위와 같이 해당 메시지 프로퍼티에서 값을 가져온다면 프로퍼티 설정과 그의 결과는 다음과 같다.

message.key=안녕 {0} {1}


--------------------

안녕 first args second args

 

 

만약 메시지 프로퍼티의 code값이 존재하지 않는다면 Exception이 던져진다.

  @Test
    void notFoundMessageCode() {
        assertThatThrownBy(() -> ms.getMessage("not exists code", null, null))
                .isInstanceOf(NoSuchMessageException.class);
    }

 

그래서 String defaultMessage가 존재하며 해당하는 code값을 찾지 못하면 defaultMessage의 값이 출력된다.

 

 

Local의 인자 값에 따라 국제화 파일을 선택한다.

 Locale이 en_US의 경우 messages_en_US messages_en messages 순서로 찾는다. Locale에 맞추어 구체적인 것이 있으면 구체적인 것을 찾고, 없으면 디폴트를 찾는다고 이해하면 된다.

 

    @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");
    }

 

 

타임리프, JSP에서는 이러한 메시지 기능을 손쉽게 이용할 수 있도록 Spring과의 통합이 잘되어있다.

 

JSP는 <title><spring:message code="page.title" /></title>

타임리프는 <title><th:text="#{page.title}" /></title>

 

사용하는 템플릿에 따라 관련 내용을 찾아보면 대부분 지원하여 관련 Doc를 제공해 줄 것이다.

 

 

* TIP 메시지 프로퍼티의 code 값이다. 대부분의 메시지들은 변하지 않는 문자들을 관리한다. 이러한 타이틀과 같은 것들의 네이밍을 할 때 관련 tag + id + vale로 지어주는 것이 효과적이고 통일성 있다.

message.properties

form.main.h2.title   // form 태그의 name는 main이고 내부태그는 h2이고 용도는 title이다

이경우 타임리프를 통해 생성해준다면 다음과 같이 할 수 있다.

<form name="main">
    <h2 th:text="#{form.main.input.name}"></h2>
</form>

 

tag + name + tag + role... 이렇게 회사마다 규칙을 정하면 유지보수에 많은 도움이 될 것이다.

참고로 파라미터는 다음과 같이 사용할 수 있다.

hello.name=안녕 {0}

<h2 th:text="#{hello.name(${item.name})}"></h2>

 

 

스프링도 Locale정보를 사용하는데 기본적으로 HTTP 헤더의 Acception-Language 방식을 채택한다. 하지만 사용자의 선택에 따라 국제화를 적용하는 경우도 있기 때문에 스프링은 LocaleResolver라는 인터페이스를 제공함으로써 다양한 Locale 선택 방식을 변경할 수 있다.

 

LocalResolver 인터페이스

public interface LocaleResolver {
Locale resolveLocale(HttpServletRequest request);
void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);
}

 

이 인터페이스는 HttpServletRequest객체에서 Locale 정보를 반환하는 메서드와, Locale정보를 수정할 수 있는 메서드를 규약으로 정한 여러 Reslover를 사용할 수 있음을 내포한다.

 

해당 인터페이스를 implements 받아서 구현하여 LocaleResolver의 구현체로 넣고 빈으로 등록하여 동작하게 하여도 되지만 웬만한 것들은 스프링이 지원해주기 때문에 스프링이 제공하는 클래스를 통해 빠르게 개발이 가능하다.

 

● 스프링이 제공하는 LocaleResolver 구현 클래스

 클래스  설 명
 AcceptHeaderLocaleResolver  웹 브라우저가 전송한 Accept-Language 헤더로부터 Locale 선택한다. setLocale() 메서드를 지원  하지 않는다.
 CookieLocaleResolver  쿠키를 이용해서 Locale 정보를 구한다. setLocale() 메서드는 쿠키에 Locale 정보를 저장한다.
 SessionLocaleResolver  세션으로부터 Locale 정보를 구한다. setLocale() 메서드는 세션에 Locale 정보를 저장한다.
 FixedLocaleResolver  웹 요청에 상관없이 특정한 Locale로 설정한다. setLocale() 메서드를 지원하지 않는다.

 

 

반응형

'Spring|Spring-boot' 카테고리의 다른 글

Spring Web Validation (2)  (0) 2022.01.08
Spring Web Validation (1)  (0) 2022.01.08
Spring MVC HTTP Header 처리와 Arguments, Return  (0) 2021.12.29
뷰 리졸버  (0) 2021.12.25
핸들러 매핑과 핸들러 어댑터  (0) 2021.12.25

댓글