๋ฉ์์ง, ๊ตญ์ ํ ๊ธฐ๋ฅ
by rlaehddnd0422๋ฉ์์ง ๊ธฐ๋ฅ
์๋ฅผ๋ค์ด ์ด์ ํฌ์คํ ์์ ๋ง๋ ์ํ๊ด๋ฆฌ ํ๋ก์ ํธ์์ ์ํ๋ช ์ด๋ผ๋ ๋จ์ด๋ฅผ ๋ชจ๋ ์ํ์ด๋ฆ์ผ๋ก ๊ณ ์น๊ณ ์ถ์ ๋ ์ด๋ป๊ฒ ํด์ผํ ๊น์?
์ํ ๋ฑ๋ก ํผ, ํธ์ง ํผ, ์์ธ ํผ ๋ฑ ์ฌ๋ฌ ํ ํ๋ฆฟ์ ํ๋์ฝ๋ฉ๋ "์ํ๋ช "์ "์ํ์ด๋ฆ"์ผ๋ก ๋ฐ๊พธ๋ ๊ฒ์ ์๋นํ ๊ท์ฐฎ์ ์์ ์ ๋๋ค.
์คํ๋ง์์๋ ๋ค์ํ ๋ฉ์์ง๋ฅผ ํ ๊ณณ์์ ๊ด๋ฆฌํ ์ ์๋๋ก ํ๋ ๋ฉ์์ง ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
์๋ฅผ ๋ค์ด messages.properties๋ผ๋ ๋ฉ์์ง ๊ด๋ฆฌ์ฉ ํ์ผ์ ๋ง๋ค๊ณ
item = ์ํ
item.id = ์ํ ID
item.itemName = ์ํ๋ช
item.price = ๊ฐ๊ฒฉ
item.quantity = ์๋
๊ฐ HTML ๋ค์ ํ๋์ฝ๋ฉ๋ ๋ฐ์ดํฐ๋ค์ th:text="#{item.itemName}"์ผ๋ก ๋ฐ๊พธ๋ฉด ๋ฉ๋๋ค.
๊ตญ์ ํ ๊ธฐ๋ฅ
๋ฉ์์ง์์ ํ์ฅ๋ ๊ธฐ๋ฅ์ผ๋ก ๋ฉ์์ง์์ ์ค๋ช ํ ๋ฉ์์ง ํ์ผ (message.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=์๋
์ด๋ ๊ฒ ํ๋ฉด ์ฌ์ดํธ๋ฅผ ๊ตญ์ ํ ํ ์ ์์ต๋๋ค.
ํ๊ตญ์์ ์ ๊ทผํ์ง ์๋ฏธ๊ถ์์ ์ ๊ทผํ ๊ฒ์ธ์ง ์ธ์ํ๋ ๋ฐฉ๋ฒ์ HTTP Accept-language ํค๋ ๊ฐ์ ์ฌ์ฉํ๊ฑฐ๋ ์ฌ์ฉ์๊ฐ ์ง์ ์ธ์ด๋ฅผ ์ ํํ๋๋ก ํ๊ณ , ์ฟ ํค ๋ฑ์ ์ฌ์ฉํด์ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
๋ฉ์์ง, ๊ตญ์ ํ ๊ธฐ๋ฅ์ ์ง์ ๊ตฌํ๋ ๊ฐ๋ฅํ์ง๋ง ์คํ๋ง์์๋ ์ด๋ฐ ๊ธฐ๋ฅ๋ค์ ๊ฐํธํ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ์ ๊ณตํ๊ณ ์์ต๋๋ค.
Spring Boot์ ๋ฉ์์ง ๊ด๋ฆฌ ๊ธฐ๋ฅ
๋ฉ์์ง ๊ด๋ฆฌ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ธฐ ์ํ ๋ฐฉ๋ฒ์ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
1) ์คํ๋ง์ด ์ ๊ณตํ๋ MessageSource๋ฅผ ์คํ๋ง ๋น์ผ๋ก ์ง์ ๋ฑ๋กํ๊ฑฐ๋
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("messages","errors");
messageSource.setDefaultEncoding("utf-8");
return messageSource;
}
MessageSource(interface)์ ๊ตฌํ์ฒด ResourceBundleMessageSource(implement)์ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก.
- basename : ์ค์ ํ์ผ์ ์ด๋ฆ ์ง์ .
- "messages"๋ก ์ง์ ํ๋ฉด "messages.properties"ํ์ผ์ ์ฝ์ด์ ์ฌ์ฉํฉ๋๋ค.
- ์ถ๊ฐ๋ก ๊ตญ์ ์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ค๋ฉด "ํ์ผ์ด๋ฆ"๋ค์ "_en", "_ko"์ ๊ฐ์ด ํ์ผ๋ช ๋ง์ง๋ง์ ์ธ์ด ์ ๋ณด๋ฅผ ์ฃผ๋ฉด ๋ฉ๋๋ค.
- ํ์ผ์ ์์น๋ /resources ํ์
- ๋ง์ฝ ์ฐพ์ ์ ์๋ ๊ตญ์ ํ๋ ํ์ผ์ด ์์ผ๋ฉด ์ธ์ด์ ๋ณด๊ฐ ์๋ ํ์ผ์ด๋ฆ์ ์ฌ์ฉํ๊ฒ ๋ฉ๋๋ค.
- defaultEncoding : ์ธ์ฝ๋ฉ ์ ๋ณด ์ง์
2) ์ด๋ ๊ฒ ์๋์ผ๋ก ๋น ๋ฑ๋ก์ ํ์ง ์์๋ ์ฌ์ค ์คํ๋ง ๋ถํธ๋ฅผ ์ฌ์ฉํ๋ฉด ์์์ MessageSource๋ฅผ ์๋์ผ๋ก ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํฉ๋๋ค.
์คํ๋ง ๋ถํธ๋ฅผ ์ฌ์ฉํด์ ๋ฉ์์ง ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ฒ ๋ ๊ฒฝ์ฐ์๋ application.properties์ ๋ฉ์์ง ์์ค๋ฅผ ์ค์ ํ๋ฉด ๋ฉ๋๋ค.
spring.messages.basename=messages
์ด๋ ๊ฒ ๋ณ๋์ ์ค์ ์ ํ์ง ์์ผ๋ฉด messages๋ผ๋ ์ด๋ฆ์ผ๋ก ๊ธฐ๋ณธ ๋ฑ๋ก๋ฉ๋๋ค. ๋ฐ๋ผ์ messages_en, messages_ko์ ๊ฐ์ ํ์ผ๋ง ๋ฑ๋กํ๋ฉด ์๋์ผ๋ก ์ธ์ํฉ๋๋ค.
์ ์ด์ ๋ฉ์์ง ํ์ผ์ ๋ง๋ค๊ณ ํ ์คํธ ํด๋ณด๊ฒ ์ต๋๋ค.
messages.properties
hello=์๋
hello.name=์๋
{0}
messages_en.properties
hello=hello
hello.name=hello {0}
TestCode
@SpringBootTest
public class MessageSourceTest {
@Autowired
MessageSource ms;
@Test
void helloMessage()
{
String result = ms.getMessage("hello", null, null);
Assertions.assertThat(result).isEqualTo("์๋
");
}
@Test
void notFoundMessageCode()
{
Assertions.assertThatThrownBy(
()-> ms.getMessage("no_code",null,null)
).isInstanceOf(NoSuchMessageException.class);
}
@Test
void notFoundMessageCodeFeDefaultMessage()
{
String result = ms.getMessage("no_code", null, "๊ธฐ๋ณธ ๋ฉ์์ง", null);
Assertions.assertThat(result).isEqualTo("๊ธฐ๋ณธ ๋ฉ์์ง");
}
@Test
void argumentMessage()
{
String result = ms.getMessage("hello.name", new Object[]{"Spring"}, null);
Assertions.assertThat(result).isEqualTo("์๋
Spring");
}
@Test
void defaultLang()
{
Assertions.assertThat(ms.getMessage("hello", null, null)).isEqualTo("์๋
");
Assertions.assertThat(ms.getMessage("hello", null, Locale.KOREA)).isEqualTo("์๋
");
}
@Test
void enLang() {
Assertions.assertThat(ms.getMessage("hello", null,
Locale.ENGLISH)).isEqualTo("hello");
}
}
@Autowired MessageSource ms;
์คํ๋ง ๋ถํธ๋ก ์๋ ๋น ๋ฑ๋ก๋ MessageSource ms ์ ์์กด๊ด๊ณ ์ฃผ์
String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
/**
* Try to resolve the message. Treat as an error if the message can't be found.
* @param code the message code to look up, e.g. 'calculator.noRateSet'.
* MessageSource users are encouraged to base message names on qualified class
* or package names, avoiding potential conflicts and ensuring maximum clarity.
* @param args an array of arguments that will be filled in for params within
* the message (params look like "{0}", "{1,date}", "{2,time}" within a message),
* or {@code null} if none
* @param locale the locale in which to do the lookup
* @return the resolved message (never {@code null})
* @throws NoSuchMessageException if no corresponding message was found
* @see #getMessage(MessageSourceResolvable, Locale)
* @see java.text.MessageFormat
*/
String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
MessageSource ์ธํฐํ์ด์ค๋ ์ฝ๋๋ฅผ ํฌํจํ ์ผ๋ถ ํ๋ผ๋ฏธํฐ๋ก ๋ฉ์์ง๋ฅผ ์ฝ์ด์ค๋ ๊ธฐ๋ฅ ์ ๊ณตํฉ๋๋ค.
getMessage(code, argument, defaultMessage, locale)
- code : ์ง์ ํ ๋ฉ์์ง
- argument ( Object๋ก ์ค์ , Nullable ) : ์ง์ ํ ๋ฉ์์ง ๋ค์ ํฉ์น ์ ์์
-
๋ค์ ๋ฉ์์ง์ {0} ๋ถ๋ถ์ ๋งค๊ฐ๋ณ์๋ฅผ ์ ๋ฌํด์ ์นํํ ์ ์์ต๋๋ค.
hello.name=์๋ {0} โถ๏ธ Spring ๋จ์ด๋ฅผ ๋งค๊ฐ๋ณ์๋ก ์ ๋ฌ โถ๏ธ ์๋ Spring
-
- defaultMessage : code ์์ ์ง์ ํ ๋ฉ์์ง๊ฐ ์กด์ฌํ์ง ์์ผ๋ฉด ์ฌ์ฉํ ๊ธฐ๋ณธ ๊ฐ ๋ฉ์์ง ์ค์
- locale : ์ง์ ํ locale์ด ์์ผ๋ฉด ๊ธฐ๋ณธ ์ค์ ํ์ผ(basename)์์ ์ค์ ํ ๊ธฐ๋ณธ ์ด๋ฆ ๋ฉ์์ง ํ์ผ ์กฐํ
์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ฉ
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=์ํ ์์ !
page.save = ์ ์ฅ ์๋ฃ!
button.save=์ ์ฅ!
button.cancel=์ทจ์!
messages_en.properties
label.item=Item
label.item.id=Item ID
label.item.itemName=Item Name
label.item.price=price
label.item.quantity=quantity
page.items=Item List
page.item=Item Detail
page.addItem=Item Add
page.updateItem=Item Update
page.save = Item Saved.
button.save=Save
button.cancel=Cancel
<form action="item.html" th:action th:object="${item}" method="post">
<label for="itemName" th:text="#{label.item.itemName}">์ํ๋ช
</label>
<div class="col">
<button class="w-100 btn btn-primary btn-lg"
th:text="#{page.updateItem}"
onclick="location.href='editForm.html'"
th:onclick="|location.href='@{/message/items/{itemId}/edit(itemId=${item.id})}'|"
type="button">์ํ ์์ </button>
</div>
- th:text๋ฅผ #{...}๋ก ์ง์ ํ๋ฉด ๋ฉ๋๋ค.
๋ทฐ ํ ํ๋ฆฟ์ ์ ์ฉ
์๋ฏธ๊ถ์ผ๋ก Accept-locale ์ค์ ํ๋ ๋ฐฉ๋ฒ (Chrome ๊ธฐ์ค)
์ค์ - ์ธ์ด์์ ๋ค์๊ณผ ๊ฐ์ด ๋ณ๊ฒฝํ๋ฉด ๋ฉ๋๋ค.
์คํ๋ง์ ์ธ์ด ์ ํ์ ๊ธฐ๋ณธ์ผ๋ก Accept-Language ํค๋์ ๊ฐ์ ์ฌ์ฉํฉ๋๋ค.
์ค์ ์์ ๊ธฐ๋ณธ์ธ์ด ์ฐ์ ์์๋ฅผ ์์ด๋ฅผ ์ต์ฐ์ ์ผ๋ก ์ง์ ํ๋ฉด Accept-Language ํค๋๊ฐ ์๋์ผ๋ก ๋ณ๊ฒฝ๋ฉ๋๋ค.
์ฐธ๊ณ ๋ก ์คํ๋ง์ Locale ์ ํ ๋ฐฉ์์ ๋ณ๊ฒฝํ ์ ์๋๋ก LocaleResolver๋ผ๋ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณต, ๋ณ๊ฒฝํ๊ณ ์ถ์ผ๋ฉด LocaleResolver์ ๊ตฌํ์ฒด๋ฅผ ๋ณ๊ฒฝํด์ ์ฟ ํค๋ ์ธ์ ๊ธฐ๋ฐ์ Locale ์ ํ ๊ธฐ๋ฅ์ผ๋ก ๋ณ๊ฒฝ ํ ์ ์์ต๋๋ค.
<์ฐธ๊ณ ์๋ฃ>
'๐ Backend > MVC Pattern' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์ค๋ฅ ์ฝ๋์ ๋ฉ์์ง ์ฒ๋ฆฌ (0) | 2023.03.21 |
---|---|
๊ฒ์ฆ(Validation) ์ฒ๋ฆฌ ๋ฐฉ๋ฒ (0) | 2023.03.20 |
Spring MVC ๊ธฐ๋ณธ ๊ธฐ๋ฅ - ์๋ต ๋งคํ @ResponseBody, MessageConverter, ์์ฒญ ๋งคํ ํธ๋ค๋ฌ ์ด๋ํฐ ๊ตฌ์กฐ (0) | 2023.03.13 |
Spring MVC ๊ธฐ๋ณธ ๊ธฐ๋ฅ - ์์ฒญ ๋งคํ @RequestMapping, @RequestParam (0) | 2023.03.12 |
Spring MVC ๊ธฐ๋ณธ ๊ธฐ๋ฅ - Logging (0) | 2023.03.10 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
Study Repository
rlaehddnd0422