스프링 MVC 2편 - 메시지, 국제화

링딩·2022년 8월 12일
0

스프링 MVC

목록 보기
10/18

김영한 강사님 강의를 참고하여 작성했습니다.

메시지와 국제화

만약 기획자,,, 악덕? 어쨌든 화면에 나오는 문구를 개발 중간에 상품명 에서 상품이름으로 고쳐달라면,, 우린 어떻게 해야할까?

특히나 그 고쳐야 할 문구들이 수십개를 넘어선다면,,,?

우리는 지금까찌 html을 하드코딩 했고 이들을 한 곳에서 관리하도록 하기 위해 '메시지' 기능이 필요하다.

...

그리고 메시지에서 한 발 더 나가보면? 우리는 생각보다 해외 사이트에서 내가 쓰는 혹은 지원하고 있는 언어로 서비스를 받고 있는 경우가 대다수였다..

왜일까? 그건 바로 앞에서 나온 메시지messages.properties 라는 파일을 이용해 각 나라별로 별도 관리가 가능해서 이다.



1. 메시지

스프링은 메시지 관리 기능을 제공해준다. 어떻게?
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/ 폴더 아래에 두 파일을 만들었다고 해보자.

경로 /resources/messages.properties


hello=안녕
hello.name=안녕 {0}

경로 /resources/messages_en.properties

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;
  • 코드를 포함한 일부 파라미터로 메시지를 읽어오는 기능을 제공한다

메시지 소스 사용

1. 기본형태

@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: hello
  • args: null
  • locale: 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");
    }
   }
  • 다음 메시지의 {0} 부분은 매개변수를 전달해서 치환할 수 있다.
  • hello.name=안녕 {0} -> Spring 단어를 매개변수로 전달 -> 안녕 Spring

국제화 파일을 선택한 경우

  • locale 정보를 기반으로 국제화 파일을 선택.

그래서 만약 Locale이 en_US 의 경우 messages_en_US 를 이게 없다면 messages_en messages 순서로 찾는다.

  • Locale 에 맞추지만 만일 없으면 디폴트를 찾는다고 이해하면 된다.
//국제화 파일 선택 시, 디폴트 값의 경우 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("안녕");}

  • locale 정보가 있지만, 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 헤더의 값을 기본으로 세팅하여 국제화를 시켜준다.


LocalResolver

스프링은 Locale 선택 방식을 변경할 수 있도록 LocaleResolver 라는 인터페이스를 제공하는데,
스프링 부트는 기본으로 Accept-Language 를 활용하는 AcceptHeaderLocaleResolver 를 사용한다.

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

이 LocaleResolver는 변경은 못하나요?

가능하다. 쿠키나 세션 기반의 locale 선택 기능을 이용할 수도 있을 것이다.
예를 들면 애플 사이트 등에서 고객이 직접 locale을 선택하는 방법도 있을 것이고...

허나 요새는 국제화를 지원하는 사이트의 경우가 아니면 많이 쓰이거나 제공하진 않는 듯 하다.

profile
초짜 백엔드 개린이

0개의 댓글