[Spring MVC 2편] 3. 메세지, 국제화

HJ·2023년 1월 15일
0

Spring MVC 2편

목록 보기
3/13

김영한 님의 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 강의를 보고 작성한 내용입니다.
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard


1. 메세지, 국제화 기능

  • 다양한 메세지를 한 곳에서 관리하도록 하는 기능을 메세지 기능이라 한다

    • ex> messages.properties라는 파일을 만들고 데이터를 key value 형식으로 저장하고 필요한 부분에서 데이터의 key값으로 불러서 사용하는 방식
  • 메세지 기능에서 설명한 파일을 각 나라 별로 만들어 관리하면 서비스를 국제화 할 수 있다

    • ex> messages_en.properties, messages_ko.properties 라는 파일을 만들어서 분류
  • HTTP 헤더의 accept-language를 보고 그에 맞는 파일을 사용해서 HTML을 렌더링 하도록 한다

    • 사용자가 직접 언어를 선택하도록 하고, 쿠키 등을 사용해서 처리할 수도 있다
  • 스프링은 기본적인 메세지와 국제화 기능을 제공하며, 타임리프 역시 스프링이 제공하는 메세지, 국제화 기능을 통합해서 제공한다




2. 스프링 메세지 소스 설정

2-1. 스프링 빈 생성

@Bean
public MessageSource messageSource() {
	ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
	messageSource.setBasenames("messages", "errors");
	messageSource.setDefaultEncoding("utf-8");
	return messageSource;
}
  • 메세지 관리 기능을 사용하기 위해 MessageSource를 스프링 빈으로 등록해야한다

    • MessageSource는 인터페이스이기 때문에 구현체인 ResourceBundleMessageSource를 스프링 빈으로 등록한다
  • setBasenames()

    • 설정 파일의 이름을 지정

    • resources 디렉터리에서 해당 파일을 찾는다

    • 위의 코드의 경우 resources에 있는 messages.properties, errors.properties를 읽어들인다

  • 정리하자면 MessageSource가 setBasenames()에 지정된 이름의 메세지 파일들을 읽어서 가지고 있고, 스프링 빈으로 등록된다

2-2. 메세지 소스 설정

  • 스프링부트를 사용하면 MessageSource를 자동으로 스프링 빈으로 등록해주고, 필요한 경우 application.properties에 별도의 메세지 소스를 설정한다

    • application.properties에 별도의 설정을 하지 않으면 메세지 소스 기본값은 아래와 같다

    • spring.messages.basename=messages

  • MessageSource를 스프링 빈으로 등록하지 않고, 스프링부트와 관련된 설정을 하지 않으면 messages 라는 이름으로 기본 등록된다

    • messages로 시작하는 messagesXXX.properties파일만 등록하면 자동으로 인식된다
  • 정리하자면, 스프링부트를 사용하고 별도로 메세지 소스 설정을 하지 않은 경우 MessageSource가 messages로 시작하는 메세지 파일을 읽어서 가지고 있고, 스프링부트에 의해 자동으로 스프링 빈으로 등록된다
  • ex> messages.properties, messages_en.properties라는 파일이 있고, 별도의 설정 정보를 입력하지 않았다고 가정했을 때

    • 한국에서 들어오면 messages.properties, 미국에서 들어오면 messages_en.properties를 사용한다

    • 만약, 한국도 아니고 미국도 아닌 경우에는 디폴트인 messages.properties를 불러와서 사용한다




3. MessageSource 사용

3-1. MessageSource 인터페이스

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;
}

3-2. MessageSource 메세지 기능 테스트

< 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 정보를 주고 싶으면 Locale.KOREA와 같이 작성하면 된다
  • locale 정보가 없으면 basename에서 설정한 기본 이름 메세지 파일을 조회한다

    • application.properties에 basename을 messages로 설정했으므로 messages.properties파일에서 데이터를 조회

@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("기본 메세지");
}
  • 위 인터페이스의 첫 번째 메서드를 보면, code가 존재하지 않았을 때 기본 메세지를 설정한 경우 기본 메세지를 반환하는 것을 알 수 있다

@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()를 호출

    • 안녕 {0} ➜ 안녕 Spring 이라는 메세지가 반환
  • 두 번째 테스트는 임의로 진행했는데 Object 배열에 두 개의 문자열을 넣고 테스트를 진행

    • {0}이 배열의 0번째 항목으로 치환되어 안녕 Test 라는 메세지가 반환

3-3. MessageSource 국제화 기능 테스트

@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 = nullLocale.getDefault()messages_ko.properties 조회 ➜ 조회 실패 ➜ messages.properties 조회



4. 웹 어플리케이션에 메세지 기능 적용

<!-- Thymeleaf -->
<h2 th:text="#{page.addItem}">상품 등록 폼</h2>
<!-- 페이지 소스 보기 -->
<h2>상품 등록</h2>
  • 타임리프에서 #{ ... } 라는 메세지 표현식을 사용해서 스프링의 메세지를 조회한다

  • 메세지 표현식으로 작성된 것들이 렌더링 될 때, 메세지 파일에 있는 내용으로 바뀌게 된다

  • 나중에 상품 등록을 다른 말로 변경할 때, 모든 파일에서 변경하는 것이 아닌 메세지 파일의 내용만 변경하면 된다

  • 파라미터 사용하는 경우

    • hello.name=안녕 {0}의 경우 메세지 표현식을 아래처럼 사용

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

  • 템플릿에서 메세지 표현식을 적용하면 국제화 기능은 웹 브라우저에서 언어만 변경하면 쉽게 확인할 수 있음



5. 스프링 국제화 메세지 선택

5-1. 설명

  • 스프링의 메세지 기능은 Locale 정보를 알아야 언어를 선택할 수 있다

  • 스프링은 언어 선택 시, 기본적으로 HTTP 요청 메세지의 헤더에 있는 Accept-Language 정보를 사용해 Locale 정보를 알아낸다

  • Locale 선택 방식으로 Accept-Language를 활용할 수도 있고, 사용자가 언어를 선택하도록 하고 사용자의 선택을 쿠키나 세션같은 곳에 저장하여 계속 사용하도록 할 수 있다

  • 스프링은 Locale 선택 방식을 변경할 수 있도록 LocaleResolver 라는 인터페이스를 제공한다

    • Accept-Language 를 사용하면 AcceptHeaderLocaleResolver 가 사용된다

5-2. LocaleResolver

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의 구현체이다

profile
공부한 내용을 정리해서 기록하고 다시 보기 위한 공간

0개의 댓글