MVC2 3rd Step

최보현·2022년 8월 12일
0

MVC

목록 보기
10/18
post-thumbnail

스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - sec03
출처 : 스프링 MVC 2편

메세지 & 국제화

Message?

우리는 여태까지 화면에 보이는 문구를 일일히 하나씩 다 쳐주었다. 넘나 귀찮
만약에 이름을 다 바꿔야하는 일이 생기면? 우리는 label에 있는 단어들을 몇 십개의 파일을 일일히 찾아가서 고쳐줘야함 WHY? 우리가 하드코딩 해놔서
이런 메시지를 한 곳에서 관리하도록 하는 것을 메시지 기능이라고 한다.
주로 messages.properties라는 메시지 관리용 파일을 만들어서 다음과 같이 작성해둔다.

item=상품
item.id=상품 ID
item.itemName=상품명

그래서 우리는 이렇게 된 것을 어떻게 불러오나?!
각 HTML에서 th:text="#{}"안에 key값을 불러와서 사용함
<label for="itemName" th:text="#{item.itemName}"></label>

국제화?

보통 웹사이트에는 한국인만 들어오는 것이 아니라, 외국인들도 들어오는 경우 생김
그럴 때 메시지를 영어로 전달하기 위해 국제화를 진행한다.
보통 영어가 많이 쓰이니깐, 영어로 지정한다고 했을 때 우리는 messages_en.properties라는 파일을 따로 하나 생성한다

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

어떤 언어권에서 접근한 것인지는 인식하는 방법은 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 로 지정하면 messages.properties 파일을 읽어서 사용
    - 추가로 국제화 기능을 적용하려면 messages_en.properties , messages_ko.properties 와 같이 파일명 마지막에 언어 정보를 주면 됨
    • 만약 찾을 수 있는 국제화 파일이 없으면 messages.properties (언어정보가 없는 파일명)를 기본으로 사용
      - 파일의 위치는 /resources/messages.properties 에 두면 됨
      - 여러 파일을 한번에 지정할 수 있음 ex) messages , errors
  • defaultEncoding : 인코딩 정보를 지정한다. utf-8 을 사용하면 된다.

=> 이건 직접 설정하는 방법이고, 스프링 부트를 사용하면 자동으로 스트링 빈으로 등록됨
스프링 부트를 사용하면 appllication.properties부분에 한 마디만 적어주면 됨
spring.messages.basename=messages,config.i18n.messages
얘도 디폴트로 messages.properties가 자동으로 인식됨

그리고 메시지 부분에 매개변수를 받을 수 있는데 hello.name=hello {0}이렇게 {}를 사용해주면 됨

스프링 메시지 소스 사용

MessageSource 인터페이스를 보면 코드를 포함한 일부 파라미터로 메시지를 읽어오는 기능을 제공함

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

ms.getMessage("hello", null, null) => 코드, 매개변수, 언어 순
언어 정보가 null이니깐 디폴트인 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 발생함
+) 예외가 발생하는 코드를 테스트할 때는 asserThatThrownBy를 쓰고 isInstanceOf를 통해 검사
메시지가 없어도 기본 메시지(defaultMessage)를 사용하면 기본 메시지가 반환됨

매개변수를 사용 할 경우

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

{}부분안에 매개변수를 전달해서 치환이 가능하다
위와 같은 경우는 Spring을 넣어줌으로써 "안녕 Spring"이 출력되게 된다

국제화 파일 선택

locale 정보를 기반으로 국제화 파일을 선택한다.
ex) Locale이 en_US 의 경우 messages_en_US messages_en messages 순서로 찾는다.
=> Locale 에 맞추어 구체적인 것이 있으면 구체적인 것을 찾고, 없으면 디폴트를 찾는다고 이해하면 된다.

@Test
void defaultLang() {
	assertThat(ms.getMessage("hello", null, null)).isEqualTo("안녕");
    //locale 정보가 없으므로 messages 를 사용
	assertThat(ms.getMessage("hello", null, Locale.KOREA)).isEqualTo("안녕");
    //정보가 있지만, message_ko 가 없으므로 messages 를 사용
    assertThat(ms.getMessage("hello", null, Locale.ENGLISH)).isEqualTo("hello");
    //정보가 Locale.ENGLISH 이므로 messages_en 을 찾아서 사용
}

웹 애플리케이션에 메시지 적용하기

총 정리를 해보자면, 메시지는 messages.properties 파일을 만들고 안에 메시지 내용을 적고 타임리프에서는 이를 사용하기 위해 #{...}를 사용한다.

label.item=상품
label.item.id=상품 ID
label.item.itemName=상품명
page.items=상품 목록
page.item=상품 상세
button.save=저장
button.cancel=취소

메시지의 이름을 작성할 때 page, label, button을 구분해서 작성을 했다. 그래서 각 구분별로 적용하면 됨

파라미터를 사용하고자 하면
hello.name=안녕 {0}
=><p th:text="#{hello.name(${item.itemName})}"></p>

근데 국제화 적용은 어떻게 해?
우리가 타임리프에서 #{...}를 통해 가져오도록 해두었기 때문에 일일히 만들어야할 필요는 없다!
스프링의 국제화 메시지 선택
앞서 MessageSource 테스트에서 보았듯이 메시지 기능은 Locale 정보를 알아야 언어를 선택할 수 있음
결국 스프링도 Locale 정보를 알아야 언어를 선택할 수 있는데, 스프링은 언어 선택시 기본으로 Accept-Language 헤더의 값을 사용함

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

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

LocaleResolver 변경
만약 Locale 선택 방식을 변경하려면 LocaleResolver 의 구현체를 변경해서 쿠키나 세션 기반의 Locale 선택 기능을 사용할 수 있음
=> ex) 고객이 직접 Locale 을 선택하도록 하는 것이다.

근데 보통 국제화는 잘 안쓴다고 하더이다...

profile
Novice Developer's Blog

0개의 댓글