본 내용은 인프런 김영한님의
"스프링 MVC 2편 - 백엔드 웹 개발 활용 기술" 강의 내용을 정리한 것입니다.
📕 메시지, 국제화 소개
📗 메시지
악덕? 기획자가 화면에 보이는 문구가 마음에 들지 않는다고, 상품명이라는 단어를 모두 상품이름으로
고쳐달라고 하면 어떻게 해야할까?
여러 화면에 보이는 상품명, 가격, 수량 등, label 에 있는 단어를 변경하려면 다음 화면들을 다 찾아가면서
모두 변경해야 한다. 지금처럼 화면 수가 적으면 문제가 되지 않지만 화면이 수십개 이상이라면 수십개의
파일을 모두 고쳐야 한다.
이런 다양한 메시지를 한 곳에서 관리하도록 하는 기능을 메시지 기능이라 한다.
[messages.properties]
item=상품
item.id=상품 ID
item.itemName=상품명
item.price=가격
item.quantity=수량
addForm.html: <label for="itemName" th:text="#{item.itemName}"></label>
📗 국제화
메시지에서 한 발 더 나가보자.
메시지에서 설명한 메시지 파일( messages.properties )을 각 나라별로 별도로 관리하면 서비스를 국제화 할 수 있다.
[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 를 사용하게 개발하면 된다.
이렇게 하면 사이트를 국제화 할 수 있다.
📕 스프링 메시지 소스 설정
📗 직접 등록
@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
- 여러 파일을 한번에 지정할 수 있음
- defaultEncoding: 인코딩 정보를 지정한다. utf-8 을 사용
📗 스프링 부트
스프링 부트를 사용하면 스프링 부트가 MessageSource 를 자동으로 스프링 빈으로 등록
스프링 부트 메시지 소스 설정 스프링 부트를 사용하면 application.properties에서 메시지 소스를 설정
spring.messages.basename=messages,config.i18n.messages
스프링 부트 메시지 소스 기본 값
spring.messages.basename=messages
- MessageSource를 스프링 빈으로 등록하지 않고,
스프링 부트와 관련된 별도의 설정을 하지 않으면 messages라는 이름으로 기본 등록 - messages_en.properties, messages_ko.properties, messages.properties 파일만 등록하면 자동으로 인식
메시지 파일 만들기
[messages.properties]
hello=안녕
hello.name=안녕 {0}
📕 스프링 메시지 소스 사용
[MessageSourceTest]
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
- locale 정보가 없으면 basename에서 설정한 기본 이름 메시지 파일을 조회
- basename으로 messages를 지정 했으므로 messages.properties 파일을 조회
📗 MessageSourceTest 추가 - 메시지가 없는 경우, 기본 메시지
@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 )를 사용하면 기본 메시지가 반환
📗 MessageSourceTest 추가 - 매개변수 사용
@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 순서
📗 MessageSourceTest 추가 - 국제화 파일 선택1
@Test
void defaultLang() {
assertThat(ms.getMessage("hello", null, null)).isEqualTo("안녕");
assertThat(ms.getMessage("hello", null, Locale.KOREA)).isEqualTo("안녕");
}
- ms.getMessage("hello", null, null) : locale 정보가 없으므로 messages 를 사용
- ms.getMessage("hello", null, Locale.KOREA) : locale 정보가 있지만, message_ko 가 없으므로 messages 를 사용
📗 MessageSourceTest 추가 - 국제화 파일 선택2
@Test
void enLang() {
assertThat(ms.getMessage("hello", null, Locale.ENGLISH)).isEqualTo("hello");
}
- ms.getMessage("hello", null, Locale.ENGLISH) : locale 정보가 Locale.ENGLISH 이므로 messages_en 을 찾아서 사용
📕 웹 애플리케이션에 메시지 적용하기
- 타임리프 메시지 표현식: #{...}
- 파라미터 사용
- hello.name=안녕 {0}
- <p th:text="#{hello.name(${item.itemName})}"></p>
[addForm.html]
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link th:href="@{/css/bootstrap.min.css}"
href="../css/bootstrap.min.css" rel="stylesheet">
<style>
.container {
max-width: 560px;
}
</style>
</head>
<body>
<div class="container">
<div class="py-5 text-center">
<h2 th:text="#{page.addItem}">상품 등록 폼</h2>
</div>
<form action="item.html" th:action th:object="${item}" method="post">
<div>
<label for="itemName" th:text="#{label.item.itemName}">상품명</label>
<input type="text" id="itemName" th:field="*{itemName}" class="form-control" placeholder="이름을 입력하세요">
</div>
<div>
<label for="price" th:text="#{label.item.price}">가격</label>
<input type="text" id="price" th:field="*{price}" class="form-control" placeholder="가격을 입력하세요">
</div>
<div>
<label for="quantity" th:text="#{label.item.quantity}">수량</label>
<input type="text" id="quantity" th:field="*{quantity}" class="form-control" placeholder="수량을 입력하세요">
</div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-primary btn-lg" type="submit" th:text="#{button.save}">상품 등록</button>
</div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='items.html'"
th:onclick="|location.href='@{/message/items}'|"
type="button" th:text="#{button.cancel}">취소</button>
</div>
</div>
</form>
</div> <!-- /container -->
</body>
</html>
- 페이지 이름에 적용
- <h2>상품 등록 폼</h2>
- <h2 th:text="#{page.addItem}">상품 등록</h2>
- 레이블에 적용
- <label for="itemName">상품명</label>
- <label for="itemName" th:text="#{label.item.itemName}">상품명</label>
- <label for="price" th:text="#{label.item.price}">가격</label>
- <label for="quantity" th:text="#{label.item.quantity}">수량</label>
- 버튼에 적용
- <button type="submit">상품 등록</button>
- <button type="submit" th:text="#{button.save}">저장</button>
- <button type="button" th:text="#{button.cancel}">취소</button>
📕 웹 애플리케이션에 국제화 적용하기
- 스프링의 국제화 메시지 선택
- 스프링은 언어 선택시 기본으로 Accept-Language 헤더의 값을 사용
- LocaleResolver
- 스프링은 Locale 선택 방식을 변경할 수 있도록 LocaleResolver 라는 인터페이스를 제공
- 스프링 부트는 기본으로 Accept-Language 를 활용하는 AcceptHeaderLocaleResolver 를 사용한다