DevLog ๐Ÿ˜ถ

[Spring] ๋ฉ”์‹œ์ง€์™€ ๊ตญ์ œํ™”๋ฅผ ํ†ตํ•ด ์–ธ์–ด ์„ค์ • ์ปค์Šคํ…€ํ•˜๊ธฐ ๋ณธ๋ฌธ

Back-end/Spring

[Spring] ๋ฉ”์‹œ์ง€์™€ ๊ตญ์ œํ™”๋ฅผ ํ†ตํ•ด ์–ธ์–ด ์„ค์ • ์ปค์Šคํ…€ํ•˜๊ธฐ

dolmeng2 2022. 8. 21. 16:53

๊น€์˜ํ•œ ๋‹˜์˜ '์Šคํ”„๋ง MVC 2ํŽธ - ๋ฐฑ์—”๋“œ ์›น ๊ฐœ๋ฐœ ํ™œ์šฉ ๊ธฐ์ˆ '์„ ๋ณด๊ณ  ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค ๐Ÿ˜Š

 

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

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

www.inflearn.com


- ์ง€๋‚œ ํฌ์ŠคํŒ…๊ณผ ์ด์–ด์ง‘๋‹ˆ๋‹ค :D

 

[Spring] Thymeleaf๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ƒํ’ˆ ๋“ฑ๋ก/์ˆ˜์ •/์กฐํšŒ ๊ตฌํ˜„ํ•˜๊ธฐ

 ๊น€์˜ํ•œ ๋‹˜์˜ '์Šคํ”„๋ง MVC 2ํŽธ - ๋ฐฑ์—”๋“œ ์›น ๊ฐœ๋ฐœ ํ™œ์šฉ ๊ธฐ์ˆ '์„ ๋ณด๊ณ  ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค ๐Ÿ˜Š ์Šคํ”„๋ง MVC 2ํŽธ - ๋ฐฑ์—”๋“œ ์›น ๊ฐœ๋ฐœ ํ™œ์šฉ ๊ธฐ์ˆ  - ์ธํ”„๋Ÿฐ | ๊ฐ•์˜ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์— ํ•„์š”ํ•œ ๋ชจ๋“  ์›น ๊ธฐ์ˆ ์„

cl8d.tistory.com


 

| ๋ฉ”์‹œ์ง€, ๊ตญ์ œํ™” ์ฒ˜๋ฆฌํ•˜๊ธฐ

- ์ง€๋‚œ ํฌ์ŠคํŒ…์—์„œ ๋งŒ๋“ค์—ˆ๋˜ ์ƒํ’ˆ ๊ด€๋ฆฌ ํผ์„ ๋ณด๋ฉด, ์ƒํ’ˆ ID์˜ ๊ฒฝ์šฐ ID, ์ด๋ฆ„์€ Name, ๊ฐ€๊ฒฉ์€ Price, ์ˆ˜๋Ÿ‰์€ Quantity๋กœ ํ‘œํ˜„ํ•˜์˜€๋‹ค.

- ๋งŒ์•ฝ ๊ธฐํš์ž๊ฐ€ itemName, itemPrice, itemQuantity๋กœ ์ผ๊ด„ ๋ณ€๊ฒฝํ•˜๊ธฐ๋ฅผ ์›ํ•œ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ?

- ์ง์ ‘ ๊ฐ ํŒŒ์ผ์—์„œ ์ˆ˜์ •ํ•˜์ง€ ๋ง๊ณ , ๋ฉ”์‹œ์ง€๋ฅผ ํ•˜๋‚˜์˜ ๊ณต๊ฐ„์—์„œ ๊ด€๋ฆฌํ•ด๋ณด์ž.

- ๋˜ํ•œ, ์˜์–ด-ํ•œ๊ธ€๋„ ์ž์œ ๋กญ๊ฒŒ ์ง€์›ํ•˜๋„๋ก ๋งŒ๋“ค์–ด๋ณด์ž!

 

- ๊ธฐ๋ณธ์ ์œผ๋กœ, ๋ฉ”์‹œ์ง€ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” MessageSource๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด์•ผ ํ•œ๋‹ค.

- ์ด๋•Œ, MessageSource๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ตฌํ˜„์ฒด์ธ ResourceBundleMessageSource๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜์ž.

    - ๊ตฌํ˜„์ฒด๋กœ๋Š” ๊ทธ์™ธ์—๋„ ReloadableResourceBundleMessageSource, StaticMessageSource๊ฐ€ ์žˆ๋‹ค.

- ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” ์ž๋™์œผ๋กœ ResourceBundleMessageSource๋ฅผ ๋“ฑ๋กํ•ด์ค€๋‹ค!

    - application.properties์— ๋ฉ”์‹œ์ง€ ์†Œ์Šค๋ฅผ ์„ค์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

    - ๋””ํดํŠธ๋กœ๋Š” 'messages'๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ๊ธฐ๋ณธ ๋“ฑ๋ก๋œ๋‹ค.

 

 

โœ” ๋ฉ”์‹œ์ง€ ์„ค์ • ํŒŒ์ผ

- /src/main/resources ๋””๋ ‰ํ† ๋ฆฌ์— [file_name]_[lang_code]_[country_code].properties ํ˜•์‹์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค.

- ๋””ํดํŠธ๊ฐ€ 'messages'์ด๊ธฐ ๋•Œ๋ฌธ์—, messages_en.properties, messages_en_UK.properties ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค.

- ๊ธฐ๋ณธ์œผ๋กœ๋Š” messages.properties์ด๋‹ค.

 

[messages.properties]

hello=์•ˆ๋…•
hello.name=์•ˆ๋…• {0}

[messages_en.properties]

hello=hello
hello.name=hello {0}

- ๊ธฐ๋ณธ์ ์œผ๋กœ key-value ํ˜•ํƒœ๋กœ ์ž‘์„ฑํ•œ๋‹ค.

- {0}์˜ ๊ฒฝ์šฐ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋‹ด๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉํ•œ๋‹ค.

 

- ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•ด๋ณด์ž.

- ์ฐธ๊ณ ๋กœ, intellij ๊ธฐ์ค€ ํ•œ๊ธ€ ์‚ฌ์šฉ ์‹œ file encoding์—์„œ UTF-8๋กœ ๋ณ€๊ฒฝํ•ด์ค˜์•ผ ํ•œ๋‹ค.

- ์ด๊ฑฐ ์„ค์ •ํ•œ ๋‹ค์Œ ํ”„๋กœํผํ‹ฐ ํŒŒ์ผ์—์„œ ํ•œ๊ธ€ ๊นจ์กŒ๋Š”์ง€ ๊ผญ ํ™•์ธํ•˜๊ธฐ!

 

[MessageSourceTest.java]

@SpringBootTest
public class MessageSourceTest {
    @Autowired
    MessageSource ms;

    @Test
    public void hello() throws Exception {
        String result = ms.getMessage("hello", null, null);
        String result2 = ms.getMessage("hello.name",  new String[]{"spring"}, null);

        assertThat(result).isEqualTo("์•ˆ๋…•");
        assertThat(result2).isEqualTo("์•ˆ๋…• spring");
    }
}

- ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ๋Š” code, args, locale ์ •๋ณด๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค.

- locale ์ •๋ณด๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ๋””ํดํŠธ์ธ 'messages' -> messages.properties์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•œ๋‹ค.

 

@Test
public void noMessage() throws Exception {
    assertThatThrownBy(() -> ms.getMessage("hihi", null, null))
        .isInstanceOf(NoSuchMessageException.class);
    String result = ms.getMessage("hihi", null, "hello!", null);
    assertThat(result).isEqualTo("hello!");
}

- ๋ฉ”์‹œ์ง€๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ์—๋Š” NoSuchMessageException์ด ๋ฐœ์ƒํ•˜์ง€๋งŒ, defaultMessage๋ฅผ ์„ค์ •ํ•ด์ฃผ๋ฉด ํ•ด๋‹น ๋ฉ”์‹œ์ง€๋กœ ์น˜ํ™˜๋œ๋‹ค.

- defaultMessage๋Š” ์„ธ ๋ฒˆ์งธ ์ธ์ž๋กœ ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค. 

@Test
public void international() throws Exception {
    assertThat(ms.getMessage("hello", null, Locale.KOREA)).isEqualTo("์•ˆ๋…•");
    assertThat(ms.getMessage("hello", null, Locale.ENGLISH)).isEqualTo("hello");
}

๊ตญ์ œํ™” ์ ์šฉ์ด๋‹ค. Locale.KOREA๋ฅผ ํ•˜๋ฉด messages_ko.properties๋ฅผ ์ฐพ์ง€๋งŒ, ์—†๊ธฐ ๋•Œ๋ฌธ์— default์ธ messages_properties์—์„œ ์ฐพ๋Š”๋‹ค. Locale.ENGLISH๋ฅผ ํ•˜๋ฉด messages_en.properties๋ฅผ ์ฐพ๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ๊ฐ€ hello="hello", key๊ฐ’์ด hello์ธ value๊ฐ’ = hello๊ฐ€ ๋‚˜์˜จ๋‹ค.


 

| ์‹ค์ œ๋กœ ์ ์šฉํ•˜๊ธฐ

- ์ง€๋‚œ ํฌ์ŠคํŒ…์—์„œ ๋งŒ๋“ค์—ˆ๋˜ ์ƒํ’ˆ ๊ด€๋ฆฌ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋ฉ”์‹œ์ง€, ๊ตญ์ œํ™”๋ฅผ ์ ์šฉํ•ด๋ณด์ž.

[message.properties]

label.item=Item
label.item.id=ItemID
label.item.itemName=Name
label.item.price=Price
label.item.quantity=Quantity

page.items=Item List
page.item=Item Detail
page.addItem=Item Save Form
page.updateItem=Item Edit Form

start.message=๐Ÿ‘‹๐ŸปHello {0}!

 

โœ” ํƒ€์ž„๋ฆฌํ”„ - ๋ฉ”์‹œ์ง€ ์ ์šฉํ•˜๊ธฐ

- ํƒ€์ž„๋ฆฌํ”„์˜ #{...}์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ”์‹œ์ง€ ์กฐํšŒ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

- th:text="#{...}" ์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ ์šฉํ•ด์ฃผ์—ˆ๋‹ค.

 

[addForm.html]

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  ...
</head>
<body>
<div class="container">
  <div class="py-5 text-center">
    <h2 th:text="#{page.addItem}" class="font-monospace fw-bold">Item Save Form</h2>
  </div>

  <form action="item.html" th:action th:object="${item}" method="post">
    <div class="row fw-bold font-monospace">
      <div class="col text-center">
        <label for="itemName"
               th:text="#{label.item.itemName}">Name</label>
        <input type="text"
               th:field="*{itemName}"
               class="form-control text-center"
               placeholder="Please Enter an Item Name.">
      </div>
    </div>

    <div class="row mt-2 fw-bold font-monospace">
      <div class="col text-center">
        <label for="price"
               th:text="#{label.item.price}">Price</label>
        <input type="text"
               th:field="*{price}"
               class="form-control text-center"
               placeholder="Please Enter an Item Price.">
      </div>
      <div class="col text-center">
        <label for="quantity"
               th:text="#{label.item.quantity}">Quantity</label>
        <input type="text"
               th:field="*{quantity}"
               class="form-control text-center"
               placeholder="Please Enter an Item Quantity.">
      </div>
    </div>
    ...
</body>
</html>

[editForm.html]

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    ...
</head>
<body>
<div class="container">
    <div class="py-5 text-center">
        <h2 class="font-monospace fw-bold"
            th:text="#{page.updateItem}">Item Edit Form</h2>
    </div>

    <form action="item.html" th:action th:object="${item}" method="post">
        <div class="row fw-bold font-monospace">
            <div class="col text-center">
                <label for="id"
                       th:text="#{label.item.id}">Id</label>
                <input type="text"
                       th:field="*{id}"
                       class="form-control text-center"
                       readonly>
            </div>
            <div class="col text-center">
                <label for="itemName"
                       th:text="#{label.item.itemName}">Name</label>
                <input type="text"
                       th:field="*{itemName}"
                       class="form-control text-center">
            </div>
        </div>

        <div class="row mt-2 fw-bold font-monospace">
            <div class="col text-center">
                <label for="price"
                       th:text="#{label.item.price}">Price</label>
                <input type="text"
                       th:field="*{price}"
                       class="form-control text-center">
            </div>
            <div class="col text-center">
                <label for="quantity"
                       th:text="#{label.item.quantity}">Quantity</label>
                <input type="text"
                       th:field="*{quantity}"
                       class="form-control text-center">
            </div>
        </div>
        ...
</div>
</body>
</html>

[item.html]

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
...
<body>
<div class="container">
    <div class="py-5 text-center">
        <h2 class="font-monospace fw-bold"
            th:text="#{page.item}">Item Detail</h2>
    </div>
    <h2 class="font-monospace fw-bold text-center text-danger"
        th:if="${param.status}" th:text="'Save Success!'"></h2>

    <div class="row fw-bold font-monospace">
        <div class="col text-center">
            <label for="itemId"
                   th:text="#{label.item.id}">ID</label>
            <input type="text" id="itemId" name="itemId"
                   class="form-control text-center"
                   value="1" th:value="${item.id}" readonly>
        </div>
        <div class="col text-center">
            <label for="itemName"
                   th:text="#{label.item.itemName}">Name</label>
            <input type="text" id="itemName" name="itemName"
                   class="form-control text-center"
                   value="์ƒํ’ˆA" th:value="${item.itemName}" readonly>
        </div>
    </div>

    <div class="row mt-2 fw-bold font-monospace">
        <div class="col text-center">
            <label for="price"
                   th:text="#{label.item.price}">Price</label>
            <input type="text" id="price" name="price"
                   class="form-control text-center"
                   value="10000" th:value="${item.price}" readonly>
        </div>
        <div class="col text-center">
            <label for="quantity"
                   th:text="#{label.item.quantity}">Quantity</label>
            <input type="text" id="quantity" name="quantity"
                   class="form-control text-center"
                   value="10" th:value="${item.quantity}" readonly>
        </div>
    </div>
    ...
</body>
</html>

[items.html]

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
...
<body>
<div class="container" style="max-width: 600px">
    <div class="py-5 text-center">
        <h1 class="font-monospace fw-bold mb-3"
            th:text="#{start.message(${userName})}">๐Ÿ‘‹๐Ÿป Hello</h1>
        <hr class="my-4">
        <h2 class="font-monospace fw-bold"
            th:text="#{page.items}">Item List</h2>
        ...
    </div>
    <div class="mt-1 font-monospace">
        <table class="table table-hover table-bordered">
            <thead>
            <tr class="text-center">
                <th th:text="#{label.item.id}">ID</th>
                <th th:text="#{label.item.itemName}">์ƒํ’ˆ๋ช…</th>
                <th th:text="#{label.item.price}">๊ฐ€๊ฒฉ</th>
                <th th:text="#{label.item.quantity}">์ˆ˜๋Ÿ‰</th>
            </tr>
            </thead>
            ...
        </table>
    </div>
</div>
</body>
</html>

- ์ƒ๋‹จ์— ์‚ฌ์šฉ์ž์—๊ฒŒ ์ธ์‚ฌํ•˜๋Š” ๋ฌธ๊ตฌ๊ฐ€ ์žˆ์œผ๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์•„์„œ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค.

- ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด #{message()}๋กœ ์ž…๋ ฅ์ด ๊ฐ€๋Šฅํ•˜๋‹ค!

 

[SItemController.java] - ์ˆ˜์ •

@GetMapping
public String items(Model model) {
    List<SItem> items = repository.findAll();
    model.addAttribute("items", items);
    model.addAttribute("userName", "Spring");
    return "basic/items";
}

- ๊ฐ„๋‹จํ•˜๊ฒŒ ์‚ฌ์šฉ์ž์˜ ์ด๋ฆ„์„ ๋„˜๊ฒจ์ฃผ์—ˆ๋‹ค.

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

 

- ์ด๋ฒˆ์—๋Š” ๊ตญ์ œํ™”๋ฅผ ์ ์šฉํ•ด๋ณด์ž! ์ผ๋ณธ์–ด๋ฅผ ์ ์šฉํ•ด๋ณผ ์˜ˆ์ •์ด๋‹ค. (ํ•œ๊ธ€์€ ๋ป”ํ•˜๋‹ˆ๊นŒ...ใ…Žใ…Ž)

[messages_ja.properties]

label.item=้ …็›ฎ
label.item.id=้ …็›ฎID
label.item.itemName=ๅ“ๅ
label.item.price=ไพกๆ ผ
label.item.quantity=ๆ•ฐ้‡

page.items=ใ‚ขใ‚คใƒ†ใƒ ใƒชใ‚นใƒˆ
page.item=ใ‚ขใ‚คใƒ†ใƒ ่ฉณ็ดฐ
page.addItem=ใ‚ขใ‚คใƒ†ใƒ ไฟๅญ˜ใƒ•ใ‚ฉใƒผใƒ 
page.updateItem=ใ‚ขใ‚คใƒ†ใƒ ็ทจ้›†ใƒ•ใ‚ฉใƒผใƒ 

start.message=ใ“ใ‚“ใซใกใฏ {0}<(๏ผฟ๏ผฟ)>!

- ํŒŒํŒŒ๊ณ ์˜ ๋„์›€์„ ๋ฐ›์•„ ์ž‘์„ฑํ•˜์˜€๋‹ค.

- ํฌ๋กฌ์—์„œ ์–ธ์–ด ์„ค์ •์„ ์ผ๋ณธ์–ด๋กœ ๋ฐ”๊พผ ๋‹ค์Œ์— ํ•œ ๋ฒˆ ๋Œ๋ ค๋ณด์ž.

    - ์ด๋Ÿฌ๋ฉด ์š”์ฒญ ์‹œ Accept-Language ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๊ณ , ์Šคํ”„๋ง์€ Locale ์ •๋ณด๋ฅผ ์ด ํ—ค๋”์—์„œ ๊ฐ€์ ธ์™€ ์ฐธ๊ณ ํ•œ๋‹ค.

- ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ LocaleResolver ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด์ธ AccpetHeaderLocaleResolver๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

 

- ์–ด๋”˜๊ฐ€ ์ด์งˆ๊ฐ์ด ๋“ค์ง€๋งŒ ์•„๋ฌดํŠผ ์ž˜ ์ ์šฉ๋˜์—ˆ๋‹ค.

 

- ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์–ธ์–ด๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋„๋ก ์กฐ๊ธˆ ๋” ์ปค์Šคํ…€ํ•ด๋ณด์•˜๋‹ค.

[messages.properties]

select.lang.en=ENGLISH
select.lang.ja=JAPANESE
lang=LANGUAGE

[messages_ja.properties]

select.lang.en=่‹ฑ่ชž
select.lang.ja=ๆ—ฅๆœฌ่ชž
lang=่จ€่ชž

[Items.html]

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
...
<body>
<div class="container" style="max-width: 600px">
    <div class="py-5 text-center">

        <div class="float-end fw-bold" th:text="#{lang}">์–ธ์–ด ์„ ํƒ</div>

        <select id="lang" name="lang" class="form-select mb-4"
                onchange="if(this.value) location.href=(this.value);">
            <option value="">== SELECT LANGUAGE TYPE ==</option>
            <option value="?lang=en" th:text="#{select.lang.en}">์˜์–ด</option>
            <option value="?lang=ja" th:text="#{select.lang.ja}">์ผ๋ณธ์–ด</option>
        </select>
        <hr class="my-4">

        <h1 class="font-monospace fw-bold mb-3"
            th:text="#{start.message(${userName})}">๐Ÿ‘‹๐Ÿป Hello</h1>
        <hr class="my-4">

        ...
    </div>
    ...

</div>

</body>
</html>

- select box์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ•œ ์˜ต์…˜์„ onchange๋กœ ๊ฐ์ง€ํ•˜์—ฌ ๊ฐ์ง€๋œ ๊ฐ’์˜ value ๊ฐ’์œผ๋กœ ์ด๋™ํ•˜๋„๋ก ํ•˜์˜€๋‹ค.

- ์ด๋Ÿฌ๋ฉด ์˜์–ด ์„ ํƒ ์‹œ /basic/items?lang=en, ์ผ๋ณธ์–ด ์„ ํƒ ์‹œ /basic/items?lang=ja๋กœ ๋„˜์–ด๊ฐ„๋‹ค.

 

[WebMvcConfig.java]

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Bean
    public LocaleResolver localeResolver() {
        CookieLocaleResolver resolver = new CookieLocaleResolver();
        resolver.setDefaultLocale(Locale.getDefault());
        resolver.setCookieName("lang");
        return resolver;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
        localeChangeInterceptor.setParamName("lang"); // lang={country_code}
        registry.addInterceptor(localeChangeInterceptor);
    }
}

- ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ • ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

- ๋จผ์ €, CookieLocaleResolver๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Cookie๋ฅผ ์ด์šฉํ•ด Locale ์ •๋ณด๋ฅผ ์ €์žฅํ•ด์ฃผ์—ˆ๋‹ค.

    - ๊ธฐ๋ณธ์€ accept-header์—์„œ ์ฝ์–ด์˜ค์ง€๋งŒ, ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด cookie์—์„œ ๊ฐ’์„ ์ฝ์–ด์˜จ๋‹ค.

- ์ฟ ํ‚ค ์ด๋ฆ„์€ lang์œผ๋กœ ์„ค์ •์œผ๋กœ ์„ค์ •ํ•˜์˜€์œผ๋ฉฐ, ์ปค์Šคํ…€ ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค.

- LocaleChangeInterceptor๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ lang ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ๋™์ž‘ํ•˜๋„๋ก ์ง€์ •ํ•˜์˜€๋‹ค.

- ์ฆ‰, ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ†ตํ•ด ๋“ค์–ด์˜จ ์–ธ์–ด๊ฐ’์— ๋”ฐ๋ผ์„œ ์–ธ์–ด ์„ค์ •์„ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

- ๋‚ด๊ฐ€ ์ƒ๊ฐํ•œ ๊ฒŒ ๋งž๋‹ค๋ฉด ์ด๋Ÿฐ ๋Š๋‚Œ...

- ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ -> ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ ์ถ”๊ฐ€ -> ์ธํ„ฐ์…‰ํ„ฐ ๋™์ž‘ -> ์ฟผ๋ฆฌํŒŒ๋ผ๋ฏธํ„ฐ lang์— ์žˆ๋Š” ์–ธ์–ด ์ •๋ณด๋ฅผ ํ™•์ธํ•˜๊ณ  ์–ธ์–ด ์„ค์ •

-> ํ•ด๋‹น ์–ธ์–ด ์ •๋ณด๋Š” ์ฟ ํ‚ค์— ์ €์žฅ, ๊ทธ๋ฆฌ๊ณ  ์ฟ ํ‚ค์— ์žˆ๋Š” ๊ฐ’ ๊ณ„์† ์‚ฌ์šฉ... ์ด๋Ÿฐ ๋กœ์ง์ธ ๊ฒƒ ๊ฐ™๋‹ค!

 

- ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ๊ฒ€์ฆ ์ฒ˜๋ฆฌ๋ฅผ ์ง„ํ–‰ํ•ด๋ณด์ž.

Comments