# ๋ฉ”์‹œ์ง€, ๊ตญ์ œํ™” ๊ธฐ๋Šฅ
Study Repository

๋ฉ”์‹œ์ง€, ๊ตญ์ œํ™” ๊ธฐ๋Šฅ

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 ์— ์˜์กด๊ด€๊ณ„ ์ฃผ์ž…

์ž๋™ ๋นˆ ๋“ฑ๋ก ๋ฐฉ์‹ ์‚ฌ์šฉ ์‹œ ResourceBundleMessageSource๋ฅผ ์‚ฌ์šฉ

 

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๋ฅผ #{...}๋กœ ์ง€์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๋ทฐ ํ…œํ”Œ๋ฆฟ์— ์ ์šฉ

messages.properties ์‚ฌ์šฉ

 

messages_en.properties ์‚ฌ์šฉ

์˜๋ฏธ๊ถŒ์œผ๋กœ Accept-locale ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ• (Chrome ๊ธฐ์ค€)

์„ค์ • - ์–ธ์–ด์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€๊ฒฝํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

์Šคํ”„๋ง์€ ์–ธ์–ด ์„ ํƒ์‹œ ๊ธฐ๋ณธ์œผ๋กœ Accept-Language ํ—ค๋”์˜ ๊ฐ’์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. 

์„ค์ •์—์„œ ๊ธฐ๋ณธ์–ธ์–ด ์šฐ์„ ์ˆœ์œ„๋ฅผ ์˜์–ด๋ฅผ ์ตœ์šฐ์„ ์œผ๋กœ ์ง€์ •ํ•˜๋ฉด Accept-Language ํ—ค๋”๊ฐ€ ์ž๋™์œผ๋กœ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.

 

์ฐธ๊ณ ๋กœ ์Šคํ”„๋ง์€ Locale ์„ ํƒ ๋ฐฉ์‹์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋„๋ก LocaleResolver๋ผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณต, ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์œผ๋ฉด LocaleResolver์˜ ๊ตฌํ˜„์ฒด๋ฅผ ๋ณ€๊ฒฝํ•ด์„œ ์ฟ ํ‚ค๋‚˜ ์„ธ์…˜ ๊ธฐ๋ฐ˜์˜ Locale ์„ ํƒ ๊ธฐ๋Šฅ์œผ๋กœ ๋ณ€๊ฒฝ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

<์ฐธ๊ณ ์ž๋ฃŒ>

 

 

์Šคํ”„๋ง MVC 2ํŽธ - ๋ฐฑ์—”๋“œ ์›น ๊ฐœ๋ฐœ ํ™œ์šฉ ๊ธฐ์ˆ  - ์ธํ”„๋Ÿฐ | ๊ฐ•์˜

์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์— ํ•„์š”ํ•œ ๋ชจ๋“  ์›น ๊ธฐ์ˆ ์„ ๊ธฐ์ดˆ๋ถ€ํ„ฐ ์ดํ•ดํ•˜๊ณ , ์™„์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. MVC 2ํŽธ์—์„œ๋Š” MVC 1ํŽธ์˜ ํ•ต์‹ฌ ์›๋ฆฌ์™€ ๊ตฌ์กฐ ์œ„์— ์‹ค๋ฌด ์›น ๊ฐœ๋ฐœ์— ํ•„์š”ํ•œ ๋ชจ๋“  ํ™œ์šฉ ๊ธฐ์ˆ ๋“ค์„ ํ•™์Šตํ•  ์ˆ˜ ์žˆ

www.inflearn.com

 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

Study Repository

rlaehddnd0422

ํ™œ๋™ํ•˜๊ธฐ