๋ฉ์์ง & ๊ตญ์ ํ (Message & Internationalization)
1. ๊ฐ๋ ๐
1. ๋ฉ์์ง (Message)
ํ๋ฉด์ ๋ณด์ด๋ '์ํ๋ช '์ด๋ผ๋ ๋จ์ด๋ฅผ '์ํ์ด๋ฆ'์ผ๋ก ๋ฐ๊พธ๊ณ ์ถ์ ๊ฒฝ์ฐ ์ด๋ป๊ฒ ํด์ผ ํ ๊น? ๊ฐ๋จํ๊ฒ๋ item.html์์ ํด๋น ๋จ์ด๋ฅผ ๋ฐ๊พธ๋ฉด ๋๋ค.
ํ์ง๋ง ์ํ๋ช ์ด๋ผ๋ ๋จ์ด๊ฐ item.html, addItem.html, updateItem.html, deleteItem.html, ... ์์ ์ฌ์ฉ๋๊ณ ์๋ค๋ฉด ๋ชจ๋ ํ์ผ์ ๊ณ ์ณ์ผ ํ๋ค๋ ๋ฌธ์ ๊ฐ ์๋ค.
์ด๋ ๊ฒ HTML ํ์ผ์ ๋ฉ์์ง๋ฅผ ํ๋์ฝ๋ฉ๋์ด ์๋๋ก ํ๋ ๊ฒ ์๋๋ผ, ๋ฉ์์ง๋ฅผ ํ ๊ณณ์์ ๊ด๋ฆฌํ๋๋ก ํ ์ ์๋๋ฐ ์ด๊ฒ์ด ๋ฐ๋ก ๋ฉ์์ง ๊ธฐ๋ฅ์ด๋ค.
2. ๊ตญ์ ํ (Internationalization)
ํ๊ตญ์ด๋ฅผ ์ฌ์ฉํ๋ ์ฌ๋์๊ฒ๋ '์ํ๋ช ', ์์ด๋ฅผ ์ฌ์ฉํ๋ ์ฌ๋์๊ฒ๋ 'Item Name'์ผ๋ก ๋ณด์ฌ์ฃผ๊ณ ์ ํ๋ค. ์ด๋ฅผ ๊ฐ๋ฅํ๊ฒ ํ๋ ๊ฒ์ด ๊ตญ์ ํ ๊ธฐ๋ฅ์ด๋ค.
2. ๋ฉ์์ง ์์ค ์ค์ ๐
๋ฉ์์ง์ ๊ตญ์ ํ๋ /resources/messages.properties์ ์๋ ๋ด์ฉ์ ์ฝ์์ผ๋ก์จ ๋์ํ๋ค. (๋ฌผ๋ก messsages.properties๊ฐ ์๋ ๋ค๋ฅธ ํ์ผ์ ์ฝ์ ์๋ ์๋ค.) messages.properties๋ฅผ ์ฝ์ ์ ์๋๋ก ์ค์ ํ๋ ๋ฐฉ๋ฒ์ผ๋ก๋ ์ง์ ๋ฑ๋ก(์คํ๋ง ๋น)๊ณผ ์๋ ๋ฑ๋ก(์คํ๋ง ๋ถํธ)์ด ์๋ค.
1. ์ง์ ๋ฑ๋ก (์คํ๋ง ๋น)
์คํ๋ง์ด ์ ๊ณตํ๋ MessageSource๋ฅผ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํ์ฌ ๋ฉ์์ง ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๋ค.
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setDefaultEncoding("utf-8");
return messageSource;
}
MessageSourc๋ ์ธํฐํ์ด์ค์ด๋ฏ๋ก ResourceBundleMessageSource๋ผ๋ ๊ตฌํ์ฒด๋ฅผ ์ด์ฉํด์ผ ํ๋ค.
- basename
๊ฐ์ผ๋ก messages๋ก ์ง์ ํ๋ฉด messages.properties, messages_ko.properties, messages_en.properties ๋ฑ์ ์ฝ์ ์ ์๋ค.
์ด๋ ์ฃผ์ํ ์ ์ ์ด๋ฆ์ '.'์ ๋ฃ์ง ์๋๋ก ํ์. '.'์ path๊ตฌ๋ถ์๋ก ๋ณํํ๊ธฐ ๋๋ฌธ์ด๋ค. (ex. error.message) - defaultEncoding
์ธ์ฝ๋ฉ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ค.
ํ์ผ์ ์์น๋ /resources/messages.properties์ด๋ค.
2. ์๋ ๋ฑ๋ก (์คํ๋ง ๋ถํธ)
์คํ๋ง ๋ถํธ๋ฅผ ์ฌ์ฉํ๋ฉด ์คํ๋ง ๋ถํธ๊ฐ MessageSource๋ฅผ ์๋์ผ๋ก ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํ๋ค. ๋ฉ์์ง ์์ค๋ฅผ application.properties์ ์๋์ ๊ฐ์ด ์ค์ ํ ์ ์๋ค.
spring.messages.basename=messages
๊ธฐ๋ณธ ๊ฐ์ด spring.messages.basename=messages ๋ผ์ ๋ณ๋๋ก ์ค์ ์ ํ์ง ์์๋ ๋๊ธด ํ๋ค. ๊ทธ๋ฌ๋ ์ถ๊ฐ๋ก ๋ฉ์์ง ๊ฐ์ ๊ด๋ฆฌํ๊ณ ์ถ์ ๊ฒฝ์ฐ์๋ ๋ค์๊ณผ ๊ฐ์ด ์ค์ ์ ํ ์ ์๋ค.
#์์
spring.messages.basename=messages, config.i18n.messages
3. ๋ฉ์์ง ํ์ผ ์ค์ ๐ฅ
์๋์ ๊ฐ์ด /resources์ 'messages.properties', 'messages_en.properties'์ ์ค์ ํ ์ ์๋ค.
# /resources/messages.properties
label.item=์ํ
label.item.id=์ํ ID
label.item.itemName=์ํ๋ช
page.addItem=์ํ ๋ฑ๋ก
page.updateItem=์ํ ์์
hello.name=์๋ {0}
# /resources/messages_en.properties
label.item=Item
label.item.id=Item ID
label.item.itemName=Item Name
page.addItem=Item Registration
page.updateItem=Item Modification
hello.name=hello {0}
4. ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฉํ๊ธฐ (ํ์๋ฆฌํ) ๐
๊ทธ๋ฆฌ๊ณ 'xxx.html'ํ์ผ์ ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํ ์ ์๋ค.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<title>Title</title>
</head>
<body>
<div th:text="#{label.item}"></div>
<div th:text="#{label.item.id}"></div>
<div th:text="#{label.item.itemName}"></div>
<br>
<div th:text="#{page.addItem}"></div>
<div th:text="#{page.updateItem}"></div>
<br>
<div th:text="#{hello.name(${userName})}"></div> // ํ๋ผ๋ฏธํฐ ์ฌ์ฉ
</body>
</html>
1. ๋ฉ์์ง
์์ HTML์ ์ด์ฉํ๋ฉด ํ๋ฉด์๋ ๋ฉ์์ง ํ์ผ(messages.properties)์ ์๋ ๊ฐ๋ค์ด ์ถ๋ ฅ๋๋ค. (ํ๊ธ์ด ๋ฌผ์ํ๋ก ๋์ฌ ๋์๋ '[Settings] - [File Encodings] - Default encoding for properties files : UTF-8'๋ก ์ค์ ํ๋๋ก ํ์. ์ด๋ ๊ฒ ์ค์ ํ๋ฉด messages.properties๊ฐ ๋ค์ ๋ฌผ์ํ๋ก ๋ฐ๊พธ์ด ์์ ์ ์๋๋ฐ ๊ทธ๊ฒ๋ ํ์ธํ์.)

๋งจ ์ฒ์์ ๊ณ ๋ คํ๋ ๊ฒ์ฒ๋ผ '์ํ๋ช '์ '์ํ์ด๋ฆ'์ด๋ผ๊ณ ๋ฉ์์ง ํ์ผ์์ ์์ ํด ๋ณด์.
# /resources/messages.properties
# '์ํ๋ช '์ '์ํ์ด๋ฆ'์ผ๋ก ์์
label.item=์ํ
label.item.id=์ํ ID
label.item.itemName=์ํ์ด๋ฆ #์ฌ๊ธฐ๋ฅผ ์์
page.addItem=์ํ ๋ฑ๋ก
page.updateItem=์ํ ์์
hello.name=์๋ {0}
๊ทธ๋ฌ๋ฉด ๋ค์๊ณผ ๊ฐ์ด '์ํ๋ช '์ด '์ํ์ด๋ฆ'์ด๋ผ๊ณ ๋ณด์ด๊ฒ ๋๋ค. ํ๋์ฉ ๋ฐ๊ฟ์ค ํ์๊ฐ ์๋ค!

2. ๊ตญ์ ํ
๊ตญ์ ํ๋ Locale ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ด๋ฃจ์ด์ง๋ค. Locale์ ๋ง์ถ์ด ๊ตฌ์ฒด์ ์ธ ๊ฒ์ด ์์ผ๋ฉด ๊ตฌ์ฒด์ ์ธ ๊ฒ์ ์ฐพ๊ณ , ์์ผ๋ฉด ๋ํดํธ(messages.properties)๋ฅผ ์ฐพ๋๋ค.
ํ์ฌ์๋ Locale์ด ko์ธ๋ฐ messages_ko.properties๊ฐ ์์ผ๋ฏ๋ก messages.properties๊ฐ ์ฌ์ฉ๋๊ณ ์๋ค. ๊ทธ๋์ ๋ค์๊ณผ ๊ฐ์ด ํ๊ธ ํ๋ฉด์ด ์ถ๋ ฅ๋๋ ๊ฒ์ด๋ค. (Locale์ ๊ฐ๋ฐ์๋๊ตฌ(F12) โ Network โ Request Headers โ Accept-Language์์ ํ์ธ์ด ๊ฐ๋ฅํ๋ค.)

๋ง์ฝ Locale์ด ์์ด๋ก ๋ค์ด์จ๋ค๋ฉด messages_en.properties๊ฐ ์ฌ์ฉ๋์ด ์๋์ ๊ฐ์ ์์ด ํ๋ฉด์ด ๋์ฌ ๊ฒ์ด๋ค.
์์ด๋ก ์ค์ ํ๋ ๋ฐฉ๋ฒ์ 'Chrome ๋ง์ถค์ค์ ๋ฐ ์ ์ด โ ์ธ์ด โ ์์ด โ ๊ฐ์ฅ ์๋ก ์ด๋'์ผ๋ก ์ค์ ํ๋ฉด ๋๋ค. ๊ทธ๋ฌ๋ฉด Accept-Language๊ฐ en์ด ๋๋ค.

5. ์คํ๋ง์์ ์ฌ์ฉํ๊ธฐ ๐
@SpringBootTest
public class MessageSourceTest {
@Autowired
MessageSource ms;
@Test
void itemMessage() {
String result = ms.getMessage("label.item", null, null);
Assertions.assertThat(result).isEqualTo("์ํ");
}
// ํ๋ผ๋ฏธํฐ ์ฌ์ฉ
@Test
void argumentMessage() {
String result = ms.getMessage("hello.name", new Object[]{"Amenable"}, null);
Assertions.assertThat(result).isEqualTo("์๋
Amenable");
}
}
ํด๋น ๊ธ์
๊น์ํ ๋์ '์คํ๋ง MVC 2ํธ - ๋ฐฑ์๋ ์น ๊ฐ๋ฐ ํ์ฉ ๊ธฐ์ ',
H.Kwon ๋์ 'Spring Boot(Spring) i18n ์ค์ ์ ์ฃผ์์ฌํญ',
hanker ๋์ 'Spring boot - ๋ค๊ตญ์ด ์ฒ๋ฆฌ ์ ํ๊ธ ๋ฌผ์ํ(?)๋ก ๋์ฌ๋'
์ ์ฐธ๊ณ ํ์์ต๋๋ค.