[스프링] 메시지, 국제화

gyeol·2023년 11월 19일

스프링

목록 보기
29/50
post-thumbnail

김영한 님의 '스프링 MVC 2편 - 백엔드 웹 개발 핵심 기술'을 듣고 적은 글입니다.

메시지, 국제화 소개

메시지

화면에 보이는 문구 상품명이라는 단어를 모두 상품 이름으로 바꿔야 한다면 어떻게 해야할 까?
여러 화면에 보이는 상품명, 가격 등 label에 있는 단어를 변경하려면 다음 화면들을 다 찾아가면서 모두 변경해야 한다. 프로그램이 크면 클수록 귀찮은 작업이다.

왜냐하면 해당 HTML 파일에 메시지가 하드코딩 되어있기 때문이다.

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

예를 들어 message.properties라는 메시지 관리용 파일을 만들고

item=상품
item.id=상품 ID
item.itemName=상품명
item.price=가격
item.quantity=수량

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

<label for="itemName" th:text="#{item.itemName}"></label>

이렇게되면 훨씬 관리하기 편리해질 것이다.

국제화

미국에서 사이트를 열면 영어로 보이고, 한국에서 사이트를 열면 한국어로 보이게 하려면 어떻게 해야할 까?

메시지에서 설명한 메시지 파일 messages.properties을 각 나라별로 별도로 관리하면 서비스를 국제화할 수 있다.

예를들어 다음과 같이 2개의 파일을 만들어 분류한다.

# messages_en.properties

item=Item
item.id=Item ID
item.itemName=Item Name
item.price=price
item.quantity=quantity

#messages_ko.properties

item=상품
item.id=상품 ID
item.itemName=상품명
item.price=가격
item.quantity=수량

영어를 사용하는 사람이면 messages_en.properties를 사용하고, 한국어를 사용하는 사람이면 messages_ko.properties를 사용하게 하면 된다.
이렇게 하면 사이트를 국제화할 수 있다.

한국에서 접근한 것인지 영어에서 접근한 것인지 인식하는 방법은 HTTP accept-language 헤더 값을 사용하거나 사용자가 직접 언어를 선택하도록 하고, 쿠키나 세션 등을 이용해 처리하면 된다.

메시지와 국제화 기능을 직접 구현할 수도 있겠지만 스프링은 기본적인 메시지와 국제화 기능 모두 제공한다. 그리고 타임리프도 스프링이 제공하는 메시지와 국제화 기능을 편리하게 통합해 제공한다.

스프링 메시지 소스 설정

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

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

하지만 스프링 부트를 사용하면 자동으로 등록하기에 위의 코드가 필요하지 않다.

스프링 부트 메시지 소스 설정

application.properties

spring.messages.basename=messages,config.i18n.messages

or

spring.messages.basename=messages

이렇게 적어 디폴트 파일을 정의해준다. 나는 messages.properties파일을 따로 만들어 디폴트값으로 지정해준 것이다.

그리고 messages.propertiesmessages_en.properties 파일을 만들어준다.

#messages.properties
hello=안녕
hello.name=안녕 {0}

#messages_en.properties
hello=hello
hello.name=hello {0}

스프링 메시지 소스 사용

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;

위 코드의 메서드 이름은 동일하지만 매 개변수가 다르다.

테스트 코드를 만들어 테스트를 해보자. message.MessageSourceTest.java 파일을 만들어주자.

package hello.itemservice.message;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;

import java.util.Locale;

import static org.assertj.core.api.Assertions.*;
@SpringBootTest
public class MessageSourceTest {
    @Autowired
    MessageSource ms;

    @Test
    void helloMessage() {
        String result = ms.getMessage("hello", null, null);
        assertThat(result).isEqualTo("안녕");
    }

    @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("기본 메시지");

    }

    @Test
    void argumentMessage(){
        String message = ms.getMessage("hello.name", new Object[]{"spring"}, null);
        assertThat(message).isEqualTo("안녕 spring");
    }

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

helloMessage()

메시지 코드로 hello를 입력하고 나머지 값은 null값을 입력했다. locale 정보가 없기에 기본 이름 메시지 파일을 조회한다.

처음 코드를 돌렸을 때 안녕 한글이 깨져서 오류가 났는데 이때, 한글 인코딩 관련 질문 페이지를 보고 해결했다!

notFoundMessageCode(), notFoundMessageCodeDefaultMessage()

메시지가 없는 경우 NoSuchMessageException이 발생한다. 메시지가 없어도 기본 메시지를 사용하면 기본 메시지가 반환된다.

argumentMessage()

다음 메시지 {0} 부분은 매개변수를 전달받을 수 있다.
hello.name=안녕 {0} -> Spring 단어 매개변수로 전달 -> 안녕 Spring

웹 애플리케이션 적용

이를 웹 애플리케이션에 적용하기 위해서는 타임리프의 메시지 표현식 #{...}를 사용해 스프링 메시지를 편리하게 조회한다.

예를 들어 앞서 등록한 상품이라는 이름을 조회하려면 #{label.item} 이라고 하면 된다.

렌더링 전
<div th:text="#{label.item}"></h2>

렌더링 후
<div>상품</h2>

웹 애플리케이션에 국제화 적용하기

웹으로 확인하기

웹 브라우저의 언어 설정 값을 변경해 국제화 적용을 확인해볼 수 있다. 웹 브라우저의 언어 설정 값을 변경하면 요청 시 Accept-Language의 값이 변경된다.

Accept-Language는 클라이언트가 서버에 기대하는 언어 정보를 담아 요청하는 HTTP 요청 헤더이다.

메시지 기능은 Locale 정보를 알아야 언어를 선택할 수 있다. 결국 스프링도 Locale 정보를 알아야 언어를 선택할 수 있는데, 스프링은 언어 선택 시 기본으로 Accept-Language 헤더 값을 사용한다.

LocaleResolver

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

만약 Locale 선택 방식을 변경하려면 LocaleResolver의 구현체를 변경해 쿠키나 세션 기반의 Locale 선택 기능을 사용할 수 있다.
예를 들어 alert 창에 고객이 언어를 선택할 수 있도록 하고 이를 쿠키나 세션에 저장해 사용할 수 있도록 하는 것이다.

profile
공부 기록 공간 '◡'

0개의 댓글