DevLog ๐Ÿ˜ถ

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

Back-end/Spring

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

dolmeng2 2022. 8. 19. 02:04

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

 

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

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

www.inflearn.com


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

 

[Spring] Thymeleaf ๊ธฐ๋ณธ ๋ฌธ๋ฒ• ์ •๋ฆฌ

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

cl8d.tistory.com


 

| ์ž…๋ ฅ ํผ ์ฒ˜๋ฆฌ

 

[Spring] thymeleaf, bootstrap์„ ํ†ตํ•ด ๊ธฐ๋ณธ ์ƒํ’ˆ ์ €์žฅ ์›น ์‚ฌ์ดํŠธ ๋งŒ๋“ค๊ธฐ

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

cl8d.tistory.com

- ์ €๋ฒˆ ํฌ์ŠคํŒ…์—์„œ ์ง„ํ–‰ํ•˜์˜€๋˜ ์ƒํ’ˆ ๋“ฑ๋ก ํผ์„ ํƒ€์ž„๋ฆฌํ”„๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ž…๋ ฅ ํผ ๊ธฐ๋Šฅ์œผ๋กœ ๋ณ€๊ฒฝํ•ด๋ณด์ž.

 

th:object -> ์ปค๋งจ๋“œ ๊ฐ์ฒด ์ง€์ •

*{...} -> ์„ ํƒ ๋ณ€์ˆ˜ ์‹, th:object์—์„œ ์„ ํƒํ•œ ๊ฐ์ฒด์— ์ ‘๊ทผ

th:field -> HTML ํƒœ๊ทธ์˜ id, name, value ์†์„ฑ์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์คŒ

 

- ์šฐ์„ , th:object๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ์˜ค๋ธŒ์ ํŠธ ์ •๋ณด๋ฅผ ๋„˜๊ฒจ์ฃผ์ž.

 

[SItemController.java] - ์ˆ˜์ •

@GetMapping("/add")
public String addForm(Model model) {
    model.addAttribute("item", new SItem());
    return "basic/addForm";
}

- ๋ฐ์ดํ„ฐ๊ฐ€ ๋น„์–ด์žˆ๋Š” ๋นˆ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋„˜๊ฒจ์ค€๋‹ค.

 

[addForm.html] - ์ˆ˜์ •

<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">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">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">Quantity</label>
      <input type="text"
             th:field="*{quantity}"
             class="form-control text-center"
             placeholder="Please Enter an Item Quantity.">
    </div>
  </div>

- th:object="${item}"์œผ๋กœ form์—์„œ ์‚ฌ์šฉํ•  ๊ฐ์ฒด๋ฅผ ์ง€์ •ํ•ด์ค€๋‹ค. 

: ์ฆ‰, ๋ชจ๋ธ์—์„œ ๋„˜๊ธด ๋ฐ์ดํ„ฐ์˜ ํ‚ค๋ฅผ ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.

 

th:field="*{itemName}"

: โญ์‚ฌ์šฉํ•ด์ฃผ๋ฉด ์ž๋™์œผ๋กœ id, name, value ๊ฐ’์„ ๋„ฃ์–ด์ค€๋‹ค.

-> id="itemName" name="itemName" value=""

- id๋Š” ๊ณ ์œ ํ•œ id ๊ฐ’, name์€ ์ผ์ข…์˜ ๋ณ€์ˆ˜ ์ด๋ฆ„, value๋Š” ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๋Š” ๊ฐ’์„ ์˜๋ฏธํ•œ๋‹ค.

- id, name์€ ๊ธฐ๋ณธ์ ์œผ๋กœ th:field์— ์ž…๋ ฅํ•œ ๊ฐ’์„, value๋Š” ๋น„์›Œ์ฃผ๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

 

 

- ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ˆ˜์ • ํผ์—๋„ ์ ์šฉํ•ด์ฃผ์ž.

[editForm.html] - ์ˆ˜์ •

<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">Id</label>
      <input type="text"
             th:field="*{id}"
             class="form-control text-center"
             readonly>
    </div>
    <div class="col text-center">
      <label for="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">Price</label>
      <input type="text"
             th:field="*{price}"
             class="form-control text-center">
    </div>
    <div class="col text-center">
      <label for="quantity">Quantity</label>
      <input type="text"
             th:field="*{quantity}"
             class="form-control text-center">
    </div>
  </div>

- ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ th:field๋ฅผ ์ ์šฉํ•ด์ฃผ์—ˆ๋‹ค. ๊ธฐ์กด์— ์กด์žฌํ•˜๋˜ id, name, value ๊ฐ’์„ ์‹ ๊ฒฝ์จ์ฃผ์ง€ ์•Š์•„๋„ ๋˜์–ด์„œ ๋งค์šฐ ํŽธ๋ฆฌํ•ด์กŒ๋‹ค.

<input type="text" id="itemName" name="itemName" value="์ž…๋ ฅํ•œ๊ฐ’">

- ์†Œ์Šค๋ฅผ ๋ณด๋ฉด ์ด๋ ‡๊ฒŒ ์•Œ์•„์„œ ์ž˜ ๋งŒ๋“ค์–ด์ค€ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. (ํŠนํžˆ value ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ„ํŽธํ•ด์กŒ๋‹ค)


| ์ฒดํฌ ๋ฐ•์Šค

- ์ƒํ’ˆ ๋“ฑ๋ก ์‹œ ์š”๊ตฌ์‚ฌํ•ญ์„ ์กฐ๊ธˆ ๋” ์ถ”๊ฐ€ํ•ด๋ณด์ž.

- ํŒ๋งค ์—ฌ๋ถ€๋ฅผ ์ฒดํฌ ๋ฐ•์Šค๋กœ ์„ ํƒ, ๋“ฑ๋ก ์ง€์—ญ์„ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ฒดํฌ ๋ฐ•์Šค ๋‹ค์ค‘ ์„ ํƒ์œผ๋กœ. ์ƒํ’ˆ ์ข…๋ฅ˜๋ฅผ ๋ผ๋””์˜ค ๋ฒ„ํŠผ, ๋ฐฐ์†ก ๋ฐฉ์‹์„ ์…€๋ ‰ํŠธ ๋ฐ•์Šค๋กœ!

 

[SItemType.java]

@Getter
@AllArgsConstructor
public enum SItemType {
    BOOK("๋„์„œ"),
    FOOD("์‹ํ’ˆ"),
    ETC("๊ธฐํƒ€");

    private final String description;
}

 

[DeliveryCode.java]

@Data
@AllArgsConstructor
public class DeliveryCode {
    private String code;
    private String displayName;
}

- code๋Š” ์‹œ์Šคํ…œ ๋‚ด์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ’, displayName์€ ๊ณ ๊ฐ์—๊ฒŒ ๋ณด์—ฌ์ฃผ๋Š” ๊ฐ’.

 

[SItem.java] -  ์ˆ˜์ •

@Getter
@NoArgsConstructor
@Setter
public class SItem {
    private Long id;
    private String itemName;
    private Integer price;
    private Integer quantity;

    private Boolean open;
    private List<String> regions;
    private SItemType itemType;
    private String deliveryCode;

    public SItem(String itemName, Integer price, Integer quantity) {
        this.itemName = itemName;
        this.price = price;
        this.quantity = quantity;
    }

    public void changeId(Long id) {
        this.id = id;
    }

    public void changeItemInfo(String itemName, Integer price, Integer quantity) {
        this.itemName = itemName;
        this.price = price;
        this.quantity = quantity;
    }
}

- ์š”๊ตฌ์‚ฌํ•ญ์— ๋”ฐ๋ฅธ ํŒ๋งค ์—ฌ๋ถ€, ์ง€์—ญ, ์ƒํ’ˆ ์ข…๋ฅ˜, ๋ฐฐ์†ก ๋ฐฉ์‹ ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค.

 

[addForm.html] - ์ˆ˜์ •

<div class="row mt-2 fw-bold font-monospace">
  <div class="col">
    <div>Sales status</div>
    <div class="form-check">
      <input type="checkbox" id="open" name="open" class="form-check-input">
      <label for="open" class="form-check-label">Open</label>
    </div>
  </div>
</div>

- ๊ฐ„๋‹จํ•œ ์ฒดํฌ๋ฐ•์Šค๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์—ˆ๋‹ค.

- ์ด๋•Œ, ์ฒดํฌ๋ฐ•์Šค๋ฅผ ์„ ํƒํ•˜๋ฉด HTML form์—์„œ๋Š” open=on์ด๋ผ๋Š” ๊ฐ’์ด ๋„˜์–ด๊ฐ€๊ณ , ์Šคํ”„๋ง์€ ์ด๋ฅผ true๋กœ ๋ณ€ํ™˜ํ•ด์ค€๋‹ค.

- ๊ทธ๋ž˜์„œ, ์Šคํ”„๋ง ๋‹จ์œ„์—์„œ๋Š” item.open=true๋ผ๋Š” ๊ฐ’์ด ์ฐํžŒ๋‹ค.

- โญ๊ทธ๋Ÿฌ๋‚˜, ์„ ํƒํ•˜์ง€ ์•Š์œผ๋ฉด ์•„์˜ˆ item.open=null๋กœ ์ฐํ˜€์„œ ํ•„๋“œ ์ž์ฒด๊ฐ€ ์„œ๋ฒ„๋กœ ์ „์†ก๋˜์ง€ ์•Š๋Š”๋‹ค.

     - ์ด๋Ÿฌ๋ฉด, ์‚ฌ์šฉ์ž๊ฐ€ ์ฒดํฌ๋ฅผ '์˜๋„์ ์œผ๋กœ ํ•ด์ œ'ํ–ˆ์„ ๋•Œ๋„ ์˜ค์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋ฒ„ ๋‚ด๋ถ€์— ๊ฐ’ ์ €์žฅ์ด ๊ณค๋ž€ํ•ด์ง„๋‹ค.

 

- ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ, ํ•˜๋‚˜์˜ ํžˆ๋“  ํ•„๋“œ๋ฅผ ๋งŒ๋“ค์–ด๋‘์ž.

- ์ฒดํฌ๋ฐ•์Šค ์ด๋ฆ„์— _์„ ๋ถ™์—ฌ์„œ ์ „์†กํ•ด์ฃผ๋ฉด ์ฒดํฌ๋ฅผ ํ•ด์ œํ–ˆ๋‹ค๊ณ  ์ธ์‹ํ•œ๋‹ค. ํžˆ๋“  ํ•„๋“œ๋Š” ํ•ญ์ƒ ์„œ๋ฒ„๋กœ ์ „์†ก๋œ๋‹ค!

 

[addForm.html] - ์ˆ˜์ •

<div class="col">
  <div>Sales status</div>
  <div class="form-check">
    <input type="checkbox" id="open" name="open" class="form-check-input">
    <input type="hidden" name="_open" value="on">
    <label for="open" class="form-check-label">Open</label>
  </div>
</div>

- ์ด๋Ÿฌ๋ฉด, ์ฒดํฌ๋ฐ•์Šค๋ฅผ ์„ ํƒํ–ˆ์„ ๋•Œ๋Š” open=on&_open=on์ด ํ•จ๊ป˜ ์ „์†ก๋˜๊ณ , ํ•ด์ œํ–ˆ์„ ๋•Œ๋Š” _open=on๋งŒ ๊ฐ€๊ธฐ ๋•Œ๋ฌธ์— ์ฒดํฌ๋˜์ง€ ์•Š์•˜๋‹ค๊ณ  ์ธ์‹ํ•  ์ˆ˜ ์žˆ๋‹ค!

: ์Šคํ”„๋ง์—์„œ๋Š” item.open=true, item.open=false๋กœ ์„ฑ๊ณต์ ์œผ๋กœ ๊ตฌ๋ถ„ํ•œ๋‹ค!

 

 

- ๊ทธ๋Ÿฌ๋‚˜, ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์ถ”๊ฐ€ํ•˜๊ธฐ์—๋Š” ์—ญ์‹œ ๋ฒˆ๊ฑฐ๋กญ๋‹ค. ํƒ€์ž„๋ฆฌํ”„์—์„œ ์ œ๊ณตํ•˜๋Š๋‚˜ ํผ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์ž.

[addForm.html] - ์ˆ˜์ •

<div class="col">
  <div>Sales status</div>
  <div class="form-check">
    <input type="checkbox" th:field="*{open}" class="form-check-input">
    <label for="open" class="form-check-label">Open</label>
  </div>
</div>

- th:field="*{open}"์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด, ํƒ€์ž„๋ฆฌํ”„์—์„œ ์ž๋™์œผ๋กœ ์•„๋ž˜์™€ ๊ฐ™์€ ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค. 

<input type="checkbox" id="open1" class="form-check-input" name="open" value="true">

<input type="hidden" name="_open" value="on"/>

-> ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ตณ์ด hidden tag๋ฅผ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์•„๋„ ๋œ๋‹ค!

 

์ฐธ๊ณ ๋กœ, ์ฒดํฌ๋ฐ•์Šค๋Š” ๋‹ค์ค‘์œผ๋กœ ์„ ํƒ์ด ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— id๊ฐ€ open1์ด๋ผ๋Š” ๊ฐ’์œผ๋กœ ๋งŒ๋“ค์–ด์ง„๋‹ค (์ž๋™์œผ๋กœ 1์„ ์ถ”๊ฐ€ํ•ด์คŒ)

์ด์— ๋Œ€ํ•ด์„œ๋Š” ๋’ค์—์„œ ํ•œ ๋ฒˆ ๋” ๋‹ค๋ฃฐ ์˜ˆ์ •!

 

[item.html] - ์ˆ˜์ •

<div class="row mt-2 fw-bold font-monospace">
  <div class="col">
    <div>Sales status</div>
    <div class="form-check">
      <input type="checkbox" id="open" th:field="${item.open}"
             class="form-check-input" disabled>
      <label for="open" class="form-check-label">Open</label>
    </div>
  </div>

</div>

- ์—ฌ๊ธฐ์„œ๋Š” th:object ์„ ์–ธ์„ ์•ˆ ํ•ด์คฌ์œผ๋‹ˆ๊นŒ th.field="*{item.open}"์œผ๋กœ ์จ์•ผ ํ•œ๋‹ค!

- ๋˜ํ•œ, ์ฒดํฌ๊ฐ€ ๋˜์–ด ์žˆ์„ ๊ฒฝ์šฐ ๊ธฐ์กด์—๋Š” checked="checked" ๋ฅผ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ฒ˜๋ฆฌํ–ˆ์–ด์•ผ ํ–ˆ๋Š”๋ฐ, ์ด ์—ญ์‹œ ํƒ€์ž„๋ฆฌํ”„๊ฐ€ ์ž๋™์œผ๋กœ ํ•ด์ค€๋‹ค.

- ์•„๋ž˜๋Š” ํƒ€์ž„๋ฆฌํ”„๊ฐ€ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ด์ค€ ์†์„ฑ์ด๋‹ค.

<input type="checkbox" id="open" class="form-check-input" disabled name="open" value="true" checked="checked">

- ํƒ€์ž„๋ฆฌํ”„๋Š” th:field ๊ฐ’๊ณผ th:value ๊ฐ’์„ ๋น„๊ตํ•ด์„œ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

 

[editForm.html] - ์ˆ˜์ •

<div class="row mt-2 fw-bold font-monospace">
  <div class="col">
    <div>Sales status</div>
    <div class="form-check">
      <input type="checkbox" id="open" th:field="*{open}" class="form-check-input">
      <label for="open" class="form-check-label">Open</label>
    </div>
  </div>

</div>

 

[SItem.java] - ์ˆ˜์ •

public void changeItemInfo(String itemName, Integer price, Integer quantity,
        Boolean open, List<String> regions, SItemType itemType,
        String deliveryCode) {
    this.itemName = itemName;
    this.price = price;
    this.quantity = quantity;
    this.open = open;
    this.regions = regions;
    this.itemType = itemType;
    this.deliveryCode = deliveryCode;
}

 

[SItemRepository.java] - ์ˆ˜์ •

public void update(Long itemId, SItem updateInfo) {
    SItem findItem = store.get(itemId);
    findItem.changeItemInfo(updateInfo.getItemName(), updateInfo.getPrice(), updateInfo.getQuantity(),
    	updateInfo.getOpen(), updateInfo.getRegions(), updateInfo.getItemType(), updateInfo.getDeliveryCode());
}

 


| ์ฒดํฌ ๋ฐ•์Šค - ์—ฌ๋Ÿฌ ๊ฐœ 

- ์ฒดํฌ ๋ฐ•์Šค๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ์ฒดํฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด๋ณด์ž! (๋“ฑ๋ก ์ง€์—ญ)

 

[SItemController.java] - ์ถ”๊ฐ€

@ModelAttribute("regions")
public Map<String, String> regions() {
    Map<String, String> regions = new LinkedHashMap<>();
    regions.put("SEOUL", "์„œ์šธ");
    regions.put("BUSAN", "๋ถ€์‚ฐ");
    regions.put("JEJU", "์ œ์ฃผ");
    return regions;
}

- @ModelAttribute๋ฅผ ๋ฉ”์„œ๋“œ ๋‹จ์œ„์— ์“ด๋‹ค๊ณ ...?! ์‹ถ์„ ์ˆ˜๋„ ์žˆ๋‹ค.

- ๋“ฑ๋ก ํผ, ์ƒ์„ธ ํ™”๋ฉด, ์ˆ˜์ • ํผ์—์„œ๋„ ๋ชจ๋‘ ์ง€์—ญ์ด ์“ฐ์ธ ์ฒดํฌ ๋ฐ•์Šค๋ฅผ ๋ฐ˜๋ณตํ•ด์„œ ๋ณด์—ฌ์ค˜์•ผ ํ•˜๋Š”๋ฐ, ๊ฐ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ model.addAttribute๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ถ”๊ฐ€ํ•ด์ฃผ๊ธฐ์—๋Š” ๋„ˆ๋ฌด ๋ฒˆ๊ฑฐ๋กญ๋‹ค. 

- ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์—, ๋ฉ”์„œ๋“œ ๋‹จ์œ„์— ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ์— ์š”์ฒญ ์‹œ regions์—์„œ ๋ฐ˜ํ™˜๋œ ๊ฐ’์ด ์ž๋™์œผ๋กœ model์— ๋‹ด๊ธฐ๋„๋ก ํ•ด์ฃผ์ž!

 

[addForm.html] - ์ถ”๊ฐ€

<div class="row mt-2 fw-bold font-monospace">
  <div class="col">
    <div>Regions</div>
    <div th:each="region : ${regions}"
         class="form-check form-check-inline">
      <input type="checkbox" th:field="*{regions}"
             th:value="${region.key}" class="form-check-input">
      <label th:for="${#ids.prev('regions')}"
             th:text="${region.value}"
             class="form-check-label"
      >Seoul</label>
    </div>
  </div>
</div>

- th:each๋ฅผ ํ†ตํ•ด ๋ฐ˜๋ณต๋ฌธ์œผ๋กœ ์ฒดํฌ๋ฐ•์Šค๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์—ˆ๋‹ค. 

 

<input type="checkbox" th:field="*{regions}" th:value="${region.key}" class="form-check-input">

- regions๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, value ๊ฐ’์œผ๋กœ key๋ฅผ ๋„ฃ์—ˆ๋‹ค (์ฆ‰, ๊ฐ region ์ด๋ฆ„์˜ ์˜์–ด ์ด๋ฆ„)

- ๊ทธ๋ฆฌ๊ณ , ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์งˆ ๋•Œ๋Š” value ๊ฐ’, ์ฆ‰ region์˜ ํ•œ๊ธ€ ์ด๋ฆ„์ด ๋ณด์—ฌ์ง€๋„๋ก ํ•˜์˜€๋‹ค.

 

<label th:for="${#ids.prev('regions')}" th:text="${region.value}" class="form-check-label">

- โญ๋ณดํ†ต label์˜ for๋Š” input์—์„œ ์“ฐ์ธ id์™€ ๋™์ผํ•œ ๊ฐ’์„ ๋„ฃ๋Š”๋‹ค. (input๊ณผ label์„ ์—ฐ๊ด€์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ)

- ์ด๋•Œ, th:for์— ๋Œ€ํ•ด ids.prev๋ผ๋Š” ๊ฑธ ์„ค์ •ํ–ˆ๋Š”๋ฐ, ์ด๋Š” ๋ฐ˜๋ณต๋ฌธ์œผ๋กœ ์ฒดํฌ๋ฐ•์Šค ์ƒ์„ฑ ์‹œ id๊ฐ€ ๋ชจ๋‘ regions๋กœ ๋™์ผํ•ด์งˆ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—,

๊ณ ์œ ํ•œ id๋ฅผ ๊ฐ€์ง€๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ž„์˜๋กœ 1, 2, 3 ์ˆซ์ž๊ฐ€ ๋ถ™๋„๋ก ๋งŒ๋“ค์–ด์ฃผ์—ˆ๋‹ค. (name์˜ ๊ฒฝ์šฐ ๋™์ผํ•ด๋„ ๋ฌด๊ด€ํ•จ)

 

- ์‹ค์ œ๋กœ, ์ƒ์„ฑ๋œ ๊ฑธ ๋ณด๋ฉด

<input type="checkbox" value="SEOUL" class="form-check-input" id="regions1" name="regions"><input type="hidden" name="_regions" value="on"/>
                <label for="regions1"class="form-check-label">์„œ์šธ</label>

 

<input type="checkbox" value="BUSAN" class="form-check-input" id="regions2" name="regions"><input type="hidden" name="_regions" value="on"/>
                <label for="regions2" class="form-check-label">๋ถ€์‚ฐ</label>

- ์ด๋Ÿฐ ์‹์œผ๋กœ id ๊ฐ’์ด ์ฆ๊ฐ€๋˜๋ฉด์„œ ์ƒ๊ธฐ๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋‹ค!

 

- ์ฐธ๊ณ ๋กœ, ๊ตณ์ด ์ €๋ ‡๊ฒŒ ์“ฐ์ง€ ์•Š๊ณ  th:for="${regions}"๋ผ๊ณ  ํ•ด๋„ ์ž๋™์œผ๋กœ id๊ฐ€ ์ฆ๊ฐ€ํ•˜๋ฉด์„œ ์ƒ๊ธฐ๊ธฐ๋Š” ํ•œ๋‹ค... :D

 

 

๐Ÿšฉ prev ์™ธ์— ๋‹ค์–‘ํ•œ ์กฐ๊ฑด

- prev์˜ ๊ฒฝ์šฐ ์ด์ „ ์‹œํ€€์Šค์˜ ๊ฐ’์„ + 1

    - region1, region2, region3...

- next์˜ ๊ฒฝ์šฐ ๋‹ค์Œ ์‹œํ€€์Šค์˜ ๊ฐ’์„ + 1

    - region2, region3, region4...

- seq๋Š” ํ˜„์žฌ ์‹œํ€€์Šค ๊ฐ’์„ ๋ฐ˜ํ™˜ ํ›„ ์‹œํ€€์Šค ๊ฐ’์„ +1

    - region2, region4, region6...

 

๐Ÿšฉ ids์˜ ๊ฒฝ์šฐ ๋ณดํ†ต label ํƒœ๊ทธ์—์„œ ์‚ฌ์šฉํ•˜๋Š”๋ฐ, label์ด form์˜ ์•ž์— ์œ„์น˜ํ•œ๋‹ค๋ฉด next๋ฅผ, ์šฐ๋ฆฌ์˜ ์˜ˆ์ œ์ฒ˜๋Ÿผ form์˜ ๋’ค์— ์˜จ๋‹ค๋ฉด prev๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค๊ณ  ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, seq์˜ ๊ฒฝ์šฐ ์กฐ๊ธˆ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•œ๋‹ค.

- label์ด ๋จผ์ € ์˜ค๋Š” ๊ฒฝ์šฐ ํ˜„์žฌ ์‹œํ€€์Šค ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ ๋’ค +1์„ ์‹œํ–‰ํ•œ๋‹ค.

- label์ด ๋‚˜์ค‘์— ์˜ค๋Š” ๊ฒฝ์šฐ ํ˜„์žฌ ์‹œํ€€์Šค ๊ฐ’์—์„œ +1์„ ํ•œ ๋’ค ๋ฐ˜ํ™˜์„ ํ•ด์ค€๋‹ค.

 

 

โž• ์ด์™€ ๊ด€๋ จํ•ด์„œ ๋‹ค๋ฅธ ๋ถ„์ด ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด์ฃผ์…จ๋‹ค๊ณ  ํ•˜์…”์„œ, ๋Œ€์ถฉ ์˜ฎ๊ฒจ๋ณด์ž๋ฉด...

- th:each๋Š” iteration์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ๋ฐ˜๋ณต์€ ํ•˜์ง€๋งŒ, index๋ฅผ ์ œ๊ณตํ•ด์ฃผ์ง€๋Š” ์•Š๋Š”๋‹ค.

- ๊ทธ๋ž˜์„œ, ๊ฐ์ฒด ์ด์šฉ ์‹œ ๊ฐ€์ƒ์˜ index๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. (์ด๋ฆ„์€ ID Count) ์ด๋Š” 1๋ถ€ํ„ฐ ์‹œ์ž‘ํ•œ๋‹ค.

 

- ์ด๋•Œ, ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•˜์ง€ ์•Š์œผ๋ฉด index๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ , ์ด์šฉ์„ ํ•  ๋•Œ ์ƒ์„ฑํ•œ๋‹ค.

- ๋ฃจํ”„๋ฅผ ๋Œ ๋•Œ๋งˆ๋‹ค 1์”ฉ ์ฆ๊ฐ€ํ•œ๋‹ค.

 

- #ids.prev์˜ ๊ฒฝ์šฐ ์ด์ „์— ์ด์šฉํ•˜์˜€๋˜ ๊ฐ€์ƒ์˜ index๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๊ทธ ๊ฐ’์„ ๊ฐ€์ ธ์˜จ๋‹ค.

-- ๋งŒ์•ฝ label์ด ๋จผ์ € ์˜ค๋Š”๋ฐ ids.prev๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๊ฐ€์ƒ์˜ index๋ฅผ ์‚ฌ์šฉํ•œ ์ ์ด ์—†์œผ๋‹ˆ๊นŒ!

 

- #ids.next์˜ ๊ฒฝ์šฐ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค๋ฉด, ์ƒ์„ฑ๋œ ๊ฐ€์ƒ์˜ ์ธ๋ฑ์Šค ๊ฐ’์„ ๊ฐ€์ ธ์˜จ๋‹ค.

-- ์ด๋•Œ, ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์—๋Š” ์ธ๋ฑ์Šค๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์•˜์„ ํ…๋ฐ, #ids.next๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ์ฒด ์‚ฌ์šฉ์ด ์•ˆ ๋˜์–ด๋„ ๋ฏธ๋ฆฌ ์ด์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.

- #ids.seq์˜ ๊ฒฝ์šฐ, ๊ฐ€์ƒ์˜ index๋ฅผ ๋ฏธ๋ฆฌ ์ด์šฉํ•ด๋†“๊ณ (ids.next์ฒ˜๋Ÿผ) 1์„ ์ฆ๊ฐ€ํ•ด๋‘”๋‹ค(i++). ๊ทธ๋ž˜์„œ 2์”ฉ ์ฆ๊ฐ€ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋Š” ๊ฒƒ.

 

์ฐธ๊ณ ) https://inflearn.com/questions/273026

 

- ์•„๋ฌดํŠผ, ๋งŒ์•ฝ ์ฒดํฌ๋ฐ•์Šค๋ฅผ ์„ ํƒํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ •๋ณด๊ฐ€ ์ „์†ก๋œ๋‹ค.

regions=SEOUL&_regions=on&regions=BUSAN&_regions=on&_regions=on

ํžˆ๋“  ํƒœ๊ทธ์˜ ์ •๋ณด๊ฐ€ ์ „์†ก๋œ๋‹ค๋Š” ์ !

 

- ์•„๋ฌด๊ฒƒ๋„ ์„ ํƒ ์•ˆ ํ•˜๋ฉด ์ด๋ ‡๊ฒŒ ๋œฌ๋‹ค.

_regions=on&_regions=on&_regions=on

 

 

[item.html] - ์ถ”๊ฐ€

<div class="row mt-2 fw-bold font-monospace">
  <div class="col">
    <div>Regions</div>
    <div th:each="region : ${regions}"
         class="form-check form-check-inline">
      <input type="checkbox" th:field="${item.regions}"
             th:value="${region.key}" class="form-check-input" disabled>
      <label th:for="${#ids.prev('regions')}"
             th:text="${region.value}"
             class="form-check-label"
      >Seoul</label>
    </div>
  </div>
</div>

- ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์„ ํƒํ•œ ์ฒดํฌ๋ฐ•์Šค์— ๋Œ€ํ•ด์„œ๋Š” ์กฐํšŒ ์‹œ์— checked="checked" ์˜ต์…˜์ด ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜์–ด ์žˆ๋‹ค!

 

[editForm.html]

<div class="row mt-2 fw-bold font-monospace">
  <div class="col">
    <div>Regions</div>
    <div th:each="region : ${regions}"
         class="form-check form-check-inline">
      <input type="checkbox" th:field="*{regions}"
             th:value="${region.key}" class="form-check-input">
      <label th:for="${#ids.prev('regions')}"
             th:text="${region.value}"
             class="form-check-label"
      >Seoul</label>
    </div>
  </div>
</div>

 


| ๋ผ๋””์˜ค ๋ฒ„ํŠผ

- ์ด๋ฒˆ์—๋Š” ์ƒํ’ˆ ์ข…๋ฅ˜ ์ง€์ •์„ ์œ„ํ•œ ๋ผ๋””์˜ค ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•ด์ฃผ์ž.

[SItemController.java] - ์ถ”๊ฐ€

@ModelAttribute("itemTypes")
public SItemType[] itemTypes() {
    return SItemType.values();
}

- ์•„๊นŒ์™€ ๋™์ผํ•œ ์›๋ฆฌ๋กœ @ModelAttribute๋ฅผ ํ™œ์šฉํ•ด์„œ ๋ชจ๋ธ์— ๋„ฃ์–ด์ฃผ์ž.

- enum์˜ values๋ฅผ ํ™œ์šฉํ•˜๋ฉด enum์˜ ๋ชจ๋“  ์ •๋ณด๋ฅผ ๋ฐฐ์—ด๋กœ ๋ฐ˜ํ™˜๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

-> [BOOK, FOOD, ETC]

 

[addForm.html]

<div class="col">
  <div>Item Type</div>
  <div th:each="type: ${itemTypes}"
       class="form-check form-check-inline">
    <input type="radio" th:field="*{itemType}"
           th:value="${type.name()}"
           class="form-check-input">
    <label th:for="${#ids.prev('itemType')}"
           th:text="${type.description}"
           class="form-check-label">
      BOOK
    </label>
  </div>
</div>

- ๋ผ๋””์˜ค ๋ฒ„ํŠผ์€ ํ•œ ๋ฒˆ ์„ ํƒ๋˜๋ฉด ํ•ญ์ƒ ๋‹ค๋ฅธ ๊ฑธ ๋˜ ์„ ํƒํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํžˆ๋“  ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค!

- type.name์„ ํ•˜๋ฉด enum ํƒ€์ž…์˜ name ๋ถ€๋ถ„์„ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋œ๋‹ค.

ex) BOOK("๋„์„œ")๋ผ๊ณ  ํ–ˆ์„ ๋•Œ name์€ BOOK์ด ๋˜๋Š” ๊ฒƒ.

 

[item.html]

<div class="col">
  <div>Item Type</div>
  <div th:each="type: ${itemTypes}"
       class="form-check form-check-inline">
    <input type="radio" th:field="${item.itemType}"
           th:value="${type.name()}"
           class="form-check-input" disabled>
    <label th:for="${#ids.prev('itemType')}"
           th:text="${type.description}"
           class="form-check-label">
      BOOK
    </label>
  </div>
</div>

[editForm.html]

<div class="col">
  <div>Item Type</div>
  <div th:each="type: ${itemTypes}"
       class="form-check form-check-inline">
    <input type="radio" th:field="*{itemType}"
           th:value="${type.name()}"
           class="form-check-input">
    <label th:for="${#ids.prev('itemType')}"
           th:text="${type.description}"
           class="form-check-label">
      BOOK
    </label>
  </div>
</div>

- ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์„ ํƒ๋œ ๊ฒƒ์— ๋Œ€ํ•ด์„œ๋Š” ํƒ€์ž„๋ฆฌํ”„๊ฐ€ ์ž๋™์œผ๋กœ checked="checked" ์˜ต์…˜์„ ๋„ฃ์–ด์ค€๋‹ค.

 

- ์ถ”๊ฐ€์ ์œผ๋กœ, ํƒ€์ž„๋ฆฌํ”„์—์„œ๋Š” ENUM ํƒ€์ž…์— ์ง์ ‘ ์ ‘๊ทผ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

<div th:each="type : ${T(ํŒจํ‚ค์ง€๊ฒฝ๋กœ.SItemType).values()}">

- ์ด๋Ÿฐ ์‹์œผ๋กœ ์ง์ ‘ ์ ‘๊ทผ์€ ๋˜์ง€๋งŒ, ํŒจํ‚ค์ง€ ๊ฒฝ๋กœ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์—ฌ๊ธฐ๋„ ๋ฐ”๊ฟ”์ค˜์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์›ฌ๋งŒํ•˜๋ฉด ๋น„์ถ”์ฒœํ•œ๋‹ค.


| ์…€๋ ‰ํŠธ ๋ฐ•์Šค

- ์—ฌ๋Ÿฌ ์„ ํƒ์ง€ ์ค‘์— ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฐฐ์†ก ๋ฐฉ์‹์„ ์ด๊ฑธ๋กœ ๊ตฌํ˜„ํ•ด๋ณด์ž.

[SItemController.java] - ์ถ”๊ฐ€

@ModelAttribute("deliveryCodes")
public List<DeliveryCode> deliveryCodes() {
    return CreateDeliveryCode.getCodes();
}

@NoArgsConstructor
static class CreateDeliveryCode {
    private static final List<DeliveryCode> deliveryCodes = 
    	Arrays.asList(new DeliveryCode("FAST", "๋น ๋ฅธ ๋ฐฐ์†ก"),
            new DeliveryCode("NORMAL", "์ผ๋ฐ˜ ๋ฐฐ์†ก"),
            new DeliveryCode("SLOW", "๋Š๋ฆฐ ๋ฐฐ์†ก"));

    public static List<DeliveryCode> getCodes() {
    	return deliveryCodes;
    }
}

- ์ด๋ฒˆ์—๋Š” ์ž๋ฐ” ๊ฐ์ฒด ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•˜์˜€๋‹ค.

- ๋ฉ”์„œ๋“œ ๋‚ด์—์„œ ๋ฆฌ์ŠคํŠธ๋ฅผ ์„ ์–ธํ•˜๊ณ  add๋ฅผ ํ•ด์ค˜๋„ ๋ฌด๋ฐฉํ•˜๋‹ค.

 

๊ทธ๋Ÿฌ๋‚˜, deliveryCodes()๊ฐ€ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค ์‚ฌ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”๋ชจ๋ฆฌ ๋‚ญ๋น„๊ฐ€ ์ผ์–ด๋‚˜๊ธฐ ๋•Œ๋ฌธ์— ํด๋ž˜์Šค๋ฅผ ๋ณ„๋„๋กœ ์„ ์–ธํ•˜๊ณ  ๋ฏธ๋ฆฌ deliveryCodes์— ๋Œ€ํ•ด ์ดˆ๊ธฐํ™”๋ฅผ ์ง„ํ–‰ํ•ด๋‘” ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฆฌํ„ดํ•˜๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค. 

์ถ”๊ฐ€์ ์œผ๋กœ, static final๋กœ ํ•ด์„œ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ํ•˜์˜€๋‹ค.

 

[addForm.html] - ์ถ”๊ฐ€

<div class="col">
  <div>Delivery</div>
  <select th:field="*{deliveryCode}" class="form-select">
    <option value="">== SELECT DELIVERY TYPE ==</option>
    <option th:each="deliveryCode : ${deliveryCodes}"
            th:value="${deliveryCode.code}"
            th:text="${deliveryCode.displayName}">FAST</option>
  </select>
</div>

- th:each๋ฌธ์„ ํ†ตํ•ด value์— ๊ฐ๊ฐ์˜ code ๊ฐ’์ด ๋“ค์–ด๊ฐ€๋„๋ก ํ•˜๊ณ , ์‚ฌ์šฉ์ž์—๊ฒŒ๋Š” text ๊ฐ’์ด ๋ณด์—ฌ์ง€๋„๋ก ํ•˜์˜€๋‹ค.

- ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜์˜จ๋‹ค.

<select class="form-select" id="deliveryCode" name="deliveryCode">
                <option value="">== SELECT DELIVERY TYPE ==</option>
                <option value="FAST">๋น ๋ฅธ ๋ฐฐ์†ก</option>
                <option value="NORMAL">์ผ๋ฐ˜ ๋ฐฐ์†ก</option>
                <option value="SLOW">๋Š๋ฆฐ ๋ฐฐ์†ก</option>
</select>

- ์ฐธ๊ณ ๋กœ, ์…€๋ ‰ํŠธ ๋ฐ•์Šค์—์„œ๋Š” ์•„๋ฌด๊ฒƒ๋„ ์„ ํƒํ•˜์ง€ ์•Š์œผ๋ฉด ๋นˆ ๊ฐ’์ด ์ „๋‹ฌ๋œ๋‹ค.

 

[item.html]

<div class="col">
  <div>Delivery</div>
  <select th:field="${item.deliveryCode}" class="form-select" disabled>
    <option value="">== SELECT DELIVERY TYPE ==</option>
    <option th:each="deliveryCode : ${deliveryCodes}"
            th:value="${deliveryCode.code}"
            th:text="${deliveryCode.displayName}">FAST</option>
  </select>
</div>

[editForm.html]

<div class="col">
  <div>Delivery</div>
  <select th:field="*{deliveryCode}" class="form-select">
    <option value="">== SELECT DELIVERY TYPE ==</option>
    <option th:each="deliveryCode : ${deliveryCodes}"
            th:value="${deliveryCode.code}"
            th:text="${deliveryCode.displayName}">FAST</option>
  </select>
</div>

- ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์„ ํƒ๋œ ๊ฐ’์— ๋Œ€ํ•ด์„œ๋Š” ์ž๋™์œผ๋กœ checked="checked" ์˜ต์…˜์„ ๋„ฃ์–ด์ค€๋‹ค.


| ์‚ฌ์šฉํ•œ ์ตœ์ข… ์ฝ”๋“œ 

[Items]

[saveForm]

[Item]

[edit]

โœ” Code

๋”๋ณด๊ธฐ

[ํŒจํ‚ค์ง€ ๊ตฌ์กฐ]

 

- hello-form์€ ์ด์ „ ํ”„๋กœ์ ํŠธ์—์„œ ๋งŒ๋“  ๊ฑฐ์—ฌ์„œ ๋ฌด์‹œํ•ด๋„ ๋œ๋‹ค...!

 

[SItem.java]

@Getter
@NoArgsConstructor
@Setter
public class SItem {
    private Long id;
    private String itemName;
    private Integer price;
    private Integer quantity;

    private Boolean open;
    private List<String> regions;
    private SItemType itemType;
    private String deliveryCode;

    public SItem(String itemName, Integer price, Integer quantity) {
        this.itemName = itemName;
        this.price = price;
        this.quantity = quantity;
    }

    public void changeId(Long id) {
        this.id = id;
    }

    public void changeItemInfo(String itemName, Integer price, Integer quantity,
                               Boolean open, List<String> regions, SItemType itemType,
                               String deliveryCode) {
        this.itemName = itemName;
        this.price = price;
        this.quantity = quantity;
        this.open = open;
        this.regions = regions;
        this.itemType = itemType;
        this.deliveryCode = deliveryCode;
    }
}

 [DeliveryCode.java]

@Data
@AllArgsConstructor
public class DeliveryCode {
    private String code;
    private String displayName;
}

 [SItemType.java]

@Getter
@AllArgsConstructor
public enum SItemType {
    BOOK("๋„์„œ"),
    FOOD("์‹ํ’ˆ"),
    ETC("๊ธฐํƒ€");

    private final String description;
}

 [SItemRepository.java]

@Repository
public class SItemRepository {
    private static final Map<Long, SItem> store = new ConcurrentHashMap<>();
    private static AtomicLong sequence = new AtomicLong(0);

    public SItem save(SItem item) {
        item.changeId(sequence.incrementAndGet());
        store.put(item.getId(), item);
        return item;
    }

    public SItem findById(Long id) {
        return store.get(id);
    }

    public List<SItem> findAll() {
        return new ArrayList<>(store.values());
    }

    public void update(Long itemId, SItem updateInfo) {
        SItem findItem = store.get(itemId);
        findItem.changeItemInfo(updateInfo.getItemName(), updateInfo.getPrice(), updateInfo.getQuantity(),
                updateInfo.getOpen(), updateInfo.getRegions(), updateInfo.getItemType(), updateInfo.getDeliveryCode());
    }

    public void clearStore() {
        store.clear();
    }

}

 [SItemController.java]

@Controller
@RequestMapping("/basic/items")
@RequiredArgsConstructor
public class SItemController {
    private final SItemRepository repository;

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

    @GetMapping("/{itemId}")
    public String item(@PathVariable Long itemId, Model model) {
        SItem item = repository.findById(itemId);
        model.addAttribute("item", item);
        return "basic/item";
    }

    @GetMapping("/add")
    public String addForm(Model model) {
        model.addAttribute("item", new SItem());
        return "basic/addForm";
    }

    @PostMapping("/add")
    public String addItemV2 (SItem item, Model model, RedirectAttributes redirectAttributes) {
        SItem savedItem = repository.save(item);
        model.addAttribute("item", item);
        redirectAttributes.addAttribute("itemId", savedItem.getId());
        redirectAttributes.addAttribute("status", true);
        return "redirect:/basic/items/{itemId}";
    }

    @GetMapping("/{itemId}/edit")
    public String editForm(@PathVariable Long itemId, Model model) {
        SItem item = repository.findById(itemId);
        model.addAttribute("item", item);
        return "basic/editForm";
    }

    @PostMapping("/{itemId}/edit")
    public String edit(@PathVariable Long itemId, SItem item, Model model) {
        repository.update(itemId, item);
        model.addAttribute("item", item);
        return "redirect:/basic/items/{itemId}";
    }

    @ModelAttribute("regions")
    public Map<String, String> regions() {
        Map<String, String> regions = new LinkedHashMap<>();
        regions.put("SEOUL", "์„œ์šธ");
        regions.put("BUSAN", "๋ถ€์‚ฐ");
        regions.put("JEJU", "์ œ์ฃผ");
        return regions;
    }

    @ModelAttribute("itemTypes")
    public SItemType[] itemTypes() {
        return SItemType.values();
    }

    @ModelAttribute("deliveryCodes")
    public List<DeliveryCode> deliveryCodes() {
        return CreateDeliveryCode.getCodes();
    }

    @NoArgsConstructor
    static class CreateDeliveryCode {
        private static final List<DeliveryCode> deliveryCodes
                = Arrays.asList(new DeliveryCode("FAST", "๋น ๋ฅธ ๋ฐฐ์†ก"),
                new DeliveryCode("NORMAL", "์ผ๋ฐ˜ ๋ฐฐ์†ก"),
                new DeliveryCode("SLOW", "๋Š๋ฆฐ ๋ฐฐ์†ก"));


        public static List<DeliveryCode> getCodes() {
            return deliveryCodes;
        }
    }

    // ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ (์˜์กด๊ด€๊ณ„ ์ฃผ์ž… ์ดํ›„ ์‹คํ–‰)
    @PostConstruct
    public void init() {
        repository.save(new SItem("itemA", 10000, 10));
        repository.save(new SItem("itemB", 20000, 20));
    }
}

 

 -----------------

 

[addform.html]

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <link href="../css/bootstrap.min.css"
        th:href="@{/css/bootstrap.min.css}"
        rel="stylesheet">
  <title>Item Save Form</title>
  <style>

  </style>
</head>
<body>
<div class="container">
  <div class="py-5 text-center">
    <h2 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">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">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">Quantity</label>
        <input type="text"
               th:field="*{quantity}"
               class="form-control text-center"
               placeholder="Please Enter an Item Quantity.">
      </div>
    </div>

    <div class="row mt-2 fw-bold font-monospace">
      <div class="col">
        <div>Sales status</div>
        <div class="form-check">
          <input type="checkbox" th:field="*{open}" class="form-check-input">
          <label for="open" class="form-check-label">Open</label>
        </div>
      </div>

      <div class="col">
        <div>Delivery</div>
        <select th:field="*{deliveryCode}" class="form-select">
          <option value="">== SELECT DELIVERY TYPE ==</option>
          <option th:each="deliveryCode : ${deliveryCodes}"
                  th:value="${deliveryCode.code}"
                  th:text="${deliveryCode.displayName}">FAST</option>
        </select>
      </div>

    </div>

    <div class="row mt-2 fw-bold font-monospace">
      <div class="col">
        <div>Regions</div>
        <div th:each="region : ${regions}"
             class="form-check form-check-inline">
          <input type="checkbox" th:field="*{regions}"
                 th:value="${region.key}" class="form-check-input">
          <label th:for="${#ids.prev('regions')}"
                 th:text="${region.value}"
                 class="form-check-label"
          >Seoul</label>
        </div>
      </div>

      <div class="col">
        <div>Item Type</div>
        <div th:each="type: ${itemTypes}"
             class="form-check form-check-inline">
          <input type="radio" th:field="*{itemType}"
                 th:value="${type.name()}"
                 class="form-check-input">
          <label th:for="${#ids.prev('itemType')}"
                 th:text="${type.description}"
                 class="form-check-label">
            BOOK
          </label>
        </div>
      </div>


    </div>

    <hr class="my-4">


    <div class="row">
      <div class="col">
        <button class="w-100 btn btn-warning btn-lg"
                type="submit">
          โญ•
        </button>
      </div>
      <div class="col">
        <button class="w-100 btn btn-danger btn-lg"
                onclick="location.href='items.html'"
                th:onclick="|location.href='@{/basic/items}'|"
                type="button">
          โŒ
        </button>
      </div>
    </div>
  </form>
</div>
</body>
</html>

 [editForm.html]

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <link href="../css/bootstrap.min.css"
        th:href="@{/css/bootstrap.min.css}"
        rel="stylesheet">
  <title>Item Edit Form</title>
  <style>

  </style>
</head>
<body>
<div class="container">
  <div class="py-5 text-center">
    <h2 class="font-monospace fw-bold">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">Id</label>
        <input type="text"
               th:field="*{id}"
               class="form-control text-center"
               readonly>
      </div>
      <div class="col text-center">
        <label for="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">Price</label>
        <input type="text"
               th:field="*{price}"
               class="form-control text-center">
      </div>
      <div class="col text-center">
        <label for="quantity">Quantity</label>
        <input type="text"
               th:field="*{quantity}"
               class="form-control text-center">
      </div>
    </div>

    <div class="row mt-2 fw-bold font-monospace">
      <div class="col">
        <div>Sales status</div>
        <div class="form-check">
          <input type="checkbox" id="open" th:field="*{open}" class="form-check-input">
          <label for="open" class="form-check-label">Open</label>
        </div>
      </div>

      <div class="col">
        <div>Delivery</div>
        <select th:field="*{deliveryCode}" class="form-select">
          <option value="">== SELECT DELIVERY TYPE ==</option>
          <option th:each="deliveryCode : ${deliveryCodes}"
                  th:value="${deliveryCode.code}"
                  th:text="${deliveryCode.displayName}">FAST</option>
        </select>
      </div>
    </div>

    <div class="row mt-2 fw-bold font-monospace">
      <div class="col">
        <div>Regions</div>
        <div th:each="region : ${regions}"
             class="form-check form-check-inline">
          <input type="checkbox" th:field="*{regions}"
                 th:value="${region.key}" class="form-check-input">
          <label th:for="${#ids.prev('regions')}"
                 th:text="${region.value}"
                 class="form-check-label"
          >Seoul</label>
        </div>
      </div>

      <div class="col">
        <div>Item Type</div>
        <div th:each="type: ${itemTypes}"
             class="form-check form-check-inline">
          <input type="radio" th:field="*{itemType}"
                 th:value="${type.name()}"
                 class="form-check-input">
          <label th:for="${#ids.prev('itemType')}"
                 th:text="${type.description}"
                 class="form-check-label">
            BOOK
          </label>
        </div>
      </div>
    </div>


    <hr class="my-4">

    <div class="row">
      <div class="col">
        <button class="w-100 btn btn-warning btn-lg"
                type="submit">
          โญ•
        </button>
      </div>
      <div class="col">
        <button class="w-100 btn btn-danger btn-lg"
                onclick="location.href='items.html'"
                th:onclick="|location.href='@{/basic/items/{itemId}(itemId=${item.id})}'|"
                type="button">
          โŒ
        </button>
      </div>
    </div>
  </form>
</div>
</body>
</html>

 [item.html]

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <link href="../css/bootstrap.min.css"
        th:href="@{/css/bootstrap.min.css}"
        rel="stylesheet">
  <title>item Detail</title>
</head>
<body>
<div class="container">
  <div class="py-5 text-center">
    <h2 class="font-monospace fw-bold">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">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">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">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">Quantity</label>
      <input type="text" id="quantity" name="quantity"
             class="form-control text-center"
             value="10" th:value="${item.quantity}" readonly>
    </div>
  </div>


  <div class="row mt-2 fw-bold font-monospace">
    <div class="col">
      <div>Sales status</div>
      <div class="form-check">
        <input type="checkbox" id="open" th:field="${item.open}"
               class="form-check-input" disabled>
        <label for="open" class="form-check-label">Open</label>
      </div>
    </div>

    <div class="col">
      <div>Delivery</div>
      <select th:field="${item.deliveryCode}" class="form-select" disabled>
        <option value="">== SELECT DELIVERY TYPE ==</option>
        <option th:each="deliveryCode : ${deliveryCodes}"
                th:value="${deliveryCode.code}"
                th:text="${deliveryCode.displayName}">FAST</option>
      </select>
    </div>

  </div>

  <div class="row mt-2 fw-bold font-monospace">
    <div class="col">
      <div>Regions</div>
      <div th:each="region : ${regions}"
           class="form-check form-check-inline">
        <input type="checkbox" th:field="${item.regions}"
               th:value="${region.key}" class="form-check-input" disabled>
        <label th:for="${#ids.prev('regions')}"
               th:text="${region.value}"
               class="form-check-label"
        >Seoul</label>
      </div>
    </div>


    <div class="col">
      <div>Item Type</div>
      <div th:each="type: ${itemTypes}"
           class="form-check form-check-inline">
        <input type="radio" th:field="${item.itemType}"
               th:value="${type.name()}"
               class="form-check-input" disabled>
        <label th:for="${#ids.prev('itemType')}"
               th:text="${type.description}"
               class="form-check-label">
          BOOK
        </label>
      </div>
    </div>
  </div>

  <hr class="my-4">

  <div class="row">
    <div class="col">
      <button class="w-100 btn btn-warning btn-lg"
              onclick="location.href='editForm.html'"
              th:onclick="|location.href='@{/basic/items/{itemId}/edit(itemId=${item.id})}'|"
              type="button">
        โœ๐Ÿป
      </button>
    </div>
    <div class="col">
      <button class="w-100 btn btn-info btn-lg"
              onclick="location.href='items.html'"
              th:onclick="|location.href='@{/basic/items}'|"
              type="button">
        ๐Ÿ“‘
      </button>
    </div>
  </div>
</div>

</body>
</html>

 [items.html]

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <link href="../css/bootstrap.min.css"
        th:href="@{/css/bootstrap.min.css}"
        rel="stylesheet">
  <title>item List</title>
</head>
<body>
<div class="container" style="max-width: 600px">
  <div class="py-5 text-center">
    <h2 class="font-monospace fw-bold">Item List</h2>
    <button class="btn btn-warning float-end text-white"
            onclick="location.href='addForm.html'"
            th:onclick="|location.href='@{/basic/items/add}'|"
            type="button">
      โž•
    </button>
  </div>
  <div class="mt-4 font-monospace">
    <table class="table table-hover table-bordered">
      <thead>
      <tr class="text-center">
        <th>ID</th>
        <th>์ƒํ’ˆ๋ช…</th>
        <th>๊ฐ€๊ฒฉ</th>
        <th>์ˆ˜๋Ÿ‰</th>
      </tr>
      </thead>
      <tbody class="text-center">
      <tr th:each="item : ${items}">
        <td><a href="item.html"
               th:href="@{/basic/items/{itemId}(itemId=${item.id})}"
               th:text="${item.id}">Id</a></td>
        <td><a href="item.html"
               th:href="@{|/basic/items/${item.id}|}"
               th:text="${item.itemName}">ItemName</a></td>
        <td th:text="${item.price}">Price</td>
        <td th:text="${item.quantity}">Quantity</td>
      </tr>
      </tbody>
    </table>
  </div>

</div>

</body>
</html>

 

- ํƒ€์ž„๋ฆฌํ”„์— ๋Œ€ํ•ด ์ œ๋Œ€๋กœ ๊ณต๋ถ€ํ–ˆ๋˜ ์‹œ๊ฐ„์ด์—ˆ๋‹ค...! ๋‹ค์Œ ํฌ์ŠคํŒ…๋ถ€ํ„ฐ๋Š” ๋ฉ”์‹œ์ง€, ๊ตญ์ œํ™” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž.

Comments