Spring ๋ฉ์์ง, ๊ตญ์ ํ ๊ธฐ๋ฅ
by JiwonDev๋ฉ์์ง์ ๊ตญ์ ํ ๊ธฐ๋ฅ์ ์คํ๋ง MVC๋ง ์ฌ์ฉํด์ [MessageSource ์คํ๋ง ๋น]์ ์ง์ ๊ตฌํํด๋ ๋์ง๋ง, ์คํ๋ง๊ณผ ํฐ์๋ฆฌํ์์ ์ ๊ณตํ๋ ๊ตญ์ ํ์ ๋ฉ์์ง ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด ์๋นํ ํธ๋ฆฌํ๋ค.
/* messageSource ์ธํฐํ์ด์ค๋ฅผ ์ด์ฉํด์ ์คํ๋ง์ ๋ฉ์์ง ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๋ค. */
@Bean // ์คํ๋ง๋ถํธ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐ๋ก ๋ฑ๋กํ์ง ์์๋ ์๋์ผ๋ก ๋ฑ๋ก๋์ด์๋ค.
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("messages", "errors");
messageSource.setDefaultEncoding("utf-8");
return messageSource;
}
// application.properties
spring.messages.basename=messages // messageSource ๊ฐ์ฒด์ ๋น ์ด๋ฆ. ๊ธฐ๋ณธ๊ฐ์
application.properties ์ต์ ๊ฐ ์์๋ณด๊ธฐ (Spring Boot 2.5.4)
# ๋ฉ์์ง ๊ธฐ๋ฅ์ด๋
HTML ๋ทฐ์ ์ ํ์๋ '์ํ๋ช ' ์ด๋ผ๋ ๊ธ์๋ฅผ ์ ๋ถ '์ํ์ด๋ฆ'์ผ๋ก ๋ฐ๊พธ๋ ค๊ณ ํ๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
์ด๋ฐ ์ด๋ฆ๋ค์ Message๋ผ๊ณ ๋ถ๋ฅด๊ณ , ๋ค์ํ Message๋ค์ ํ๋๋ก ๊ด๋ฆฌํ๋ ๊ธฐ๋ฅ์ ๋ฉ์์ง ๊ธฐ๋ฅ์ด๋ผ๊ณ ํ๋ค.
์๋ฅผ ๋ค์ด message.properteis๋ผ๋ ํ์ผ์ ๋ง๋ค๊ณ ์๋์ ๊ฐ์ด ๋ฉ์์ง๋ฅผ ๊ด๋ฆฌํ ์ ์๋ค.
item=์ํ
item.id=์ํ ID
item.itemName=์ํ๋ช
item.price=๊ฐ๊ฒฉ
item.quantity=์๋
# ๊ตญ์ ํ ๊ธฐ๋ฅ์ด๋
HTTP์ accpet=language ํค๋๊ฐ๋ฑ์ผ๋ก ์ฌ์ฉ์์ ์ธ์ด ์ง์ญ(Locale)์ ์์๋ด์ ์ธ์ด๋ณ๋ก ๋ฉ์์ง ํ์ผ(.properteis)์ ๋ค๋ฅด๊ฒ ์ ์ฉ์ํค๋ ๊ธฐ๋ฅ์ด๋ค.
# ์คํ๋ง, ํ์๋ฆฌํ์ ๋ฉ์์ง ์ ์ฉํ๊ธฐ
์คํ๋ง์ ์๋์ ๊ฐ์ ๊ธฐ๋ณธ์ผ๋ก ๋ฉ์์ง ์ด๋ฆ(messages) ์ค์ ๊ฐ์ ๊ฐ์ง๊ณ ์๋ค.
- resources/ ์์ ์๋ messages.properties, messages_ko.properties ๋ฑ์ ์๋์ผ๋ก ์ธ์ํ๋ค.
// application.properties
spring.messages.basename=messages // messageSource ๊ฐ์ฒด์ ๋น ์ด๋ฆ. ๊ธฐ๋ณธ๊ฐ์
messages ๋ผ๋ ๋น์ ๋ฑ๋กํ๋ค๋ฉด ์๋์ ๊ฐ์ ํ์ผ์ ์๋์ผ๋ก ์ธ์ํ๋ค.
- messages_en.properties
- messages_ko.properties
- messages_๊ตญ๊ฐ์ฝ๋.properties
- messages.properties (default ๋ฉ์์ง ์ค์ ๊ฐ)
- message.properties๋ฅผ ์๋์ ๊ฐ์ด ์์ ํด๋ณด์.
# message.properties
hello=์๋
hello.name=์๋
{0}
- ์คํ๋ง์์๋ MessageSource ์ธํฐํ์ด์ค์ ์คํ๋ง ๋น์ ์ฃผ์ ๋ฐ์ ์ฌ์ฉํ ์ ์๋ค.
@SpringBootTest
public class MessageSourceTest {
@Autowired
MessageSource ms;
@Test
@DisplayName("hello ํ๋กํผํฐ์๋ [์๋
] ๊ฐ์ด ๋ค์ด์๋ค.")
void helloMessage() {
String result = ms.getMessage("hello", null, null);
assertThat(result).isEqualTo("์๋
");
}
@Test
@DisplayName("ํ๋กํผํฐ๊ฐ ์๋ ๊ฒฝ์ฐ NoSuchMessageException์ ๋ฐ์์ํจ๋ค.")
void notFoundMessageCode() {
assertThatThrownBy(() -> ms.getMessage("no_code", null, null))
.isInstanceOf(NoSuchMessageException.class);
}
@Test
@DisplayName("ms.getMessage()๋ฅผ ์ฌ์ฉํ๋ฉด ํ๋กํผํฐ๊ฐ ์๋ ๊ฒฝ์ฐ ๊ธฐ๋ณธ๊ฐ์ ์ง์ ํด์ค ์ ์๋ค.")
void notFoundMessageCodeDefaultMessage() {
String result = ms.getMessage("no_code", null, "๊ธฐ๋ณธ ๋ฉ์์ง", null);
assertThat(result).isEqualTo("๊ธฐ๋ณธ ๋ฉ์์ง");
}
@Test
@DisplayName("๋ฉ์์ง ํ๋ผ๋ฉํ๋ Object[]๋ก ์ค ์ ์๋ค.")
void argumentMessage() {
String message = ms.getMessage("hello.name", new Object[]{"Spring"}, null);
assertThat(message).isEqualTo("์๋
Spring");
}
@Test
@DisplayName("์ง์ญ(Locale)๊ฐ์ ๋ฐ๋ผ ๋ค๋ฅธ ๋ฉ์์ง ํ๋กํผํฐ๋ฅผ ์ฌ์ฉํ๋ค.")
void enLang() {
assertThat(ms.getMessage("hello", null, Locale.ENGLISH)).isEqualTo("hello");
}
@Test
@DisplayName("ํด๋น ์ง์ญ(Locale)์ด ์๊ฑฐ๋ null์ ์ฃผ๋ฉด ๊ธฐ๋ณธ ๋ฉ์์ง ํ๋กํผํฐ๋ฅผ ์ฌ์ฉํ๋ค.")
void defaultLang() {
assertThat(ms.getMessage("hello", null, null)).isEqualTo("์๋
");
assertThat(ms.getMessage("hello", null, Locale.KOREA)).isEqualTo("์๋
");
}
}
- ํ์๋ฆฌํ์์๋ #{hello} #{hello.name('ํ๋ผ๋ฉํ')} #{ hello.name(${name}) } ์ผ๋ก ์ฌ์ฉํ๋ค.
์ค์ ํ์๋ฆฌํ์์ ๋ฉ์์ง ์ฌ์ฉ ์์ ์ดํด๋ณด๊ธฐ
# resources/messages.properties
label.item=์ํ
label.item.id=์ํ ID
label.item.itemName=์ํ๋ช
label.item.price=๊ฐ๊ฒฉ
label.item.quantity=์๋
page.items=์ํ ๋ชฉ๋ก
page.item=์ํ ์์ธ
page.addItem=์ํ ๋ฑ๋ก
page.updateItem=์ํ ์์
button.save=์ ์ฅ
button.cancel=์ทจ์
<div>
<label for="itemId" th:text="#{label.item.id}">์ํ ID</label>
<input class="form-control" id="itemId" name="itemId" readonly th:value="${item.id}"
type="text" value="1">
</div>
<div>
<label for="itemName" th:text="#{label.item.itemName}">์ํ๋ช
</label>
<input class="form-control" id="itemName" name="itemName" readonly th:value="${item.itemName}"
type="text" value="์ํA">
</div>
<div>
<label for="price" th:text="#{label.item.price}">๊ฐ๊ฒฉ</label>
<input class="form-control" id="price" name="price" readonly th:value="${item.price}"
type="text" value="10000">
</div>
<div>
<label for="quantity" th:text="#{label.item.quantity}">์๋</label>
<input class="form-control" id="quantity" name="quantity" readonly th:value="${item.quantity}"
type="text" value="10">
</div>
# ์คํ๋ง, ํ์๋ฆฌํ์ ๊ตญ์ ํ ์ ์ฉํ๊ธฐ
์คํ๋ง์ ์ฌ์ฉํ๋ฉด ๊ทธ๋ฅ message_en.properties๋ฅผ ์ถ๊ฐ๋ก ์์ฑํด์ฃผ๋ฉด ์์ด ๊ตญ์ ํ๊ฐ ์ ์ฉ๋๋ค. ๐
- ์ด๋ ์คํ๋ง์ LocaleResolver๋ผ๋ ์ธํฐํ์ด์ค๊ฐ ์กด์ฌํ๊ธฐ ๋๋ฌธ์ด๋ค.
- ์คํ๋ง ๋ถํธ์์๋ LocaleResolver = new AcceptHeaderLocaleResolver() ๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ์ฌ์ฉํ๋ค.
์ด๋ HTTP์ Accept-Language ํค๋๊ฐ์ ์ฝ์ด์ Locale์ ์ค์ ํ๋ ๊ฐ์ฒด์ด๋ค.
์ฆ, ๋ค๋ฅธ๋ฐฉ์์ผ๋ก ์ฌ์ฉ์์ ์ธ์ด๋ฅผ ํ์ ํ๊ณ ์ถ๋ค๋ฉด LocaleResolver๋ฅผ ๋ณ๊ฒฝํด์ฃผ๋ฉด ๋๋ค. ์น๋ธ๋ผ์ฐ์ ์ ํค๋๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ์ฌ์ฉ์์ Locale ์ ๋ณด๋ฅผ ์ฟ ํค, ์ธ์ ์ ๋ณด๊ดํด์ ์ฌ์ฉํ๊ธฐ๋ ํ๋ค.
์ฌ๋ด์ผ๋ก ์คํ๋ง์์๋ Internationalization ๋ฅผ [i ~18๊ธ์ ~n] ์ด๋ผ๋ ์๋ฏธ๋ก i18n ํจํค์ง๋ฅผ ์ฌ์ฉํ๋ค.
'๐ฑ Spring Framework > Spring MVC' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Spring Bean Validation#2 ์คํ๋ง ๋น ๊ฒ์ฆ (0) | 2021.08.29 |
---|---|
Spring Vaildation#1 ๊ฒ์ฆ (0) | 2021.08.29 |
Thymeleaf#2 Spring๊ณผ HTML Form ์ฒ๋ฆฌ (0) | 2021.08.28 |
Thymeleaf#1 ๊ธฐ๋ณธ๊ธฐ๋ฅ (0) | 2021.08.28 |
#2 HTTP API ์์ฒญ ๋งคํ, ํค๋ ์กฐํ (0) | 2021.08.11 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
JiwonDev
JiwonDev