메시지, 국제화

CJY·2023년 3월 25일
0

스프링

목록 보기
5/14

메시지

지금까지 나는 뷰 템플릿에서 동적으로 할당할 필요가 없는 문구는 있는 그대로 태그 사이에 적었다. 그냥 하드코딩 한 것이다. 그런데 만약 이 문구를 바꿔야 하는 일이 생기면 어떻게 될까 ? IDE의 힘을 빌려 replace를 파일마다 적용해야할까 ? 지금 나 혼자 해보는 토이 프로젝트에서는 별 에너지 소모 없이 할 수는 있어도 규모가 점점 커진다면 분명 힘든 일이 될 것이다.

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

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

student=학생
student.id=학번
student.studentName=학생이름
student.departure=학생전공
student.age=학생나이

hello.student.studentName= 안녕하세요 {0}님

이렇게 작성해놓고 HTML파일에서 태그에 th:text="#{...}"를 사용하는 것이다.

<label for="studentName" th:text="#{student.studentName}"></label>

만약 매개변수가 있는 경우에는 다음과 같이 사용한다.

<label for="studentName" th:text="#{hello.student.studentName(${student.studentName})}"></label>

국제화

여기서 messages.properties의 코드에 한글을 매칭시켰는데 이걸 다양한 언어로 할당하면 그게 국제화가 된다. 예를 들어 영어를 넣고 싶으면 파일은 messages_en.properties로 생성하고 아래와 같이 작성하면 된다.

student=student
student.id=student ID
student.studentName=student name
student.departure=student departure
student.age=student age

뭐 영어로 대충 작성해봤다.

이렇게 하면 사이트를 국제화 할 수 있다.

한국에서 접근한 것인지 영어에서 접근한 것인지는 인식하는 방법은 HTTP accept-language 해더 값을 사용하거나 사용자가 직접 언어를 선택하도록 하고, 쿠키 등을 사용해서 처리하면 된다.
메시지와 국제화 기능을 직접 구현할 수도 있겠지만, 스프링은 기본적인 메시지와 국제화 기능을 모두 제공한다. 그리고 타임리프도 스프링이 제공하는 메시지와 국제화 기능을 편리하게 통합해서 제공한다.
지금부터 스프링이 제공하는 메시지와 국제화 기능을 알아보자.

스프링 메시지 소스 설정

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

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

@Bean
public MessageSource messageSource() {
	ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
 	messageSource.setBasenames("messages", "errors");
 	messageSource.setDefaultEncoding("utf-8");
 	return messageSource;
}
  • basenames : 설정 파일의 이름을 지정한다.
    • 위에서처럼 messages, errors로 지정하면 messages.propertieserrors.properties 파일을 읽어서 사용한다.
    • 그럼 messages_en.properties를 읽으려면 베이스네임에 하나하나 다 적용해야 하나 ? 그건 아니다. 기본적으로 브라우저에서 설정한 언어가 없으면 messages.properties를 default값으로 읽고 설정한 언어가 있으면 messages_언어.properties를 읽어준다.
    • 파일의 위치는 /resources에 두면 된다.
    • 한번에 여러 파일을 지정할 수 있다. 여기서는 messageserrors를 지정했다.
  • defaultEncoding: 인코딩 정보를 지정한다.

스프링 부트

스프링 부트를 사용하면 편리하게도 MessageSource를 자동으로 스프링 빈으로 등록한다.

application.properties

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

이렇게 써놓기만 하면 되지만 스프링 부트 메시지 소스 기본 값이 있다.
spring.messages.basename=messages
따라서 위와 같이 설정하고 싶다면
spring.messages.basename=messages,errors라고 설정하면 된다.

예시

/resources/messages.properties

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

/resources/messages_en.properties

hello=hi
hello.name=hi {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)  hrows NoSuchMessageException;

테스트로 가서

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 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("안녕");
 }
}
  • ms.getMessage("hello", null, null)
    code: "hello"
    args: null
    locale: null

다음과 같은 의미를 가지므로 테스트는 통과한다.
만약 코드에 우리가 설정하지 않은 코드를 넣어준다면 NoSuchMessageException이 터질 것이다.

assertThatThrownBy(() -> ms.getMessage("noSuchCode", null, null)).isInstanceOf(NoSuchMessageException.class);

아니면 defaultMessage를 이용해서

String result = ms.getMessage("noSuchCode", null, "기본 메시지", null);
assertThat(result).isEqualTo("기본 메시지");

변수를 활용할 수도 있다.

String result = ms.getMessage("hello.name", new Object[]{"Spring"}, null);
assertThat(result).isEqualTo("안녕 Spring");

messages.properties에서 {0}부분이 매개변수가 들어갈 자리이다. 0부터 차례대로 1, 2, 3 .. 이런식으로 매개변수의 개수를 추가할 수 있다.

locale에는 Locale.KOREA, Locale.ENGLISH 등을 사용할 수 있다.
Locale정보가 없다면 Locale.getDefault()를 호출해서 시스템의 기본 로케일을 사용한다. 우리나라에서 시스템을 기본적으로 ko_KR을 사용하므로 로케일 정보가 없는 요청이 들어온다면 우선 messages_ko.properties를 찾아보고 해당 파일이 없다면 default로 messages.properties를 조회하게 된다.

profile
열심히 성장 중인 백엔드

0개의 댓글