JiwonDev

Thymeleaf#2 Spring๊ณผ HTML Form ์ฒ˜๋ฆฌ

by JiwonDev

ํƒ€์ž„๋ฆฌํ”„๋Š” ์Šคํ”„๋ง์ด ์—†์–ด๋„ ๋™์ž‘ํ•œ๋‹ค. ๋‹ค๋งŒ ์Šคํ”„๋ง๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ธฐ ์ •๋ง ํŽธํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ ธ์žˆ๋‹ค.

๋ฉ”์‹œ์ง€, ๊ตญ์ œํ™”, ๊ฒ€์ฆ์€ ๋‚ด์šฉ์ด ๋งŽ์•„์„œ ๋‹ค์Œ ๊ธ€์— ์•Œ์•„๋ณด๋„๋ก ํ•˜์ž.

 


# ์Šคํ”„๋ง์—์„œ ํƒ€์ž„๋ฆฌํ”„ ์‚ฌ์šฉํ•˜๊ธฐ

์›๋ž˜๋Š” @Bean์œผ๋กœ ํƒ€์ž„๋ฆฌํ”„ ์—”์ง„๊ณผ ํƒ€์ž„๋ฆฌํ”„ ๋ทฐ๋ฆฌ์กธ๋ฒ„๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด์•ผ ํ–ˆ์œผ๋‚˜, ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” ํ•œ์ค„๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.

implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

์Šคํ”„๋ง ๋ถ€ํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ด๋ ‡๊ฒŒ ์ˆ˜๋™์œผ๋กœ [์—”์ง„๊ณผ ๋ทฐ๋ฆฌ์กธ๋ฒ„]๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด์•ผํ•œ๋‹ค.

 


# HTML Form ๋งŒ๋“ค๊ธฐ

@GetMapping("/add")
public String addForm(Model model) {
    model.addAttribute("item", new Item());
    return "form/addForm";
}
<form action="item.html" method="post" th:action th:object="${item}">
  <div>
    <label for="itemName">์ƒํ’ˆ๋ช…</label>
    <input class="form-control" placeholder="์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”" th:field="*{itemName}" 
     type="text">
  </div>
  <div>
    <label for="price">๊ฐ€๊ฒฉ</label>
    <input class="form-control" placeholder="๊ฐ€๊ฒฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”" th:field="*{price}"
     type="text">
  </div>
  <div>
    <label for="quantity">์ˆ˜๋Ÿ‰</label>
    <input class="form-control"  placeholder="์ˆ˜๋Ÿ‰์„ ์ž…๋ ฅํ•˜์„ธ์š”" th:field="*{quantity}"
     type="text">
  </div>

๋ Œ๋”๋ง ๋œ ๋ชจ์Šต. ๋‹ค๋งŒ id ๊ฐ™์€ ๊ฒฝ์šฐ intelliJ์—์„œ ํƒ€์ž„๋ฆฌํ”„ ์ธ์‹์„ ๋ชปํ•ด์„œ, ๊ทธ๋ƒฅ ์ ์–ด์ฃผ๊ธฐ๋„ ํ•œ๋‹ค.

 

 


# ๋ณต์žกํ•œ Form ๋งŒ๋“ค๊ธฐ

๋ณต์žกํ•œ Form์„ ๋งŒ๋“ค์–ด๋ณด๋ฉฐ ํƒ€์ž„๋ฆฌํ”„๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์–ด๋–ป๊ฒŒ HTML์„ ์ฒ˜๋ฆฌํ•˜๋Š”์ง€ ์•Œ์•„๋ณด์ž.

์ด๋ ‡๊ฒŒ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์˜ต์…˜์„ ์ถ”๊ฐ€ํ•œ Form์„ ํƒ€์ž„๋ฆฌํ”„๋กœ ๋งŒ๋“ค์–ด๋ณด์ž.

์˜ˆ์ œ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋„๋ฉ”์ธ ๊ฐ์ฒด์™€ Model ์ฝ”๋“œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ

๋”๋ณด๊ธฐ

๋„๋ฉ”์ธ ๊ฐ์ฒด

@Data
public class Item {
  private Long id;
  private String itemName;
  private Integer price;
  private Integer quantity;

  private Boolean open; //ํŒ๋งค ์—ฌ๋ถ€ (true,false)
  private List<String> regions; //๋“ฑ๋ก ์ง€์—ญ (List)
  private ItemType itemType; //์ƒํ’ˆ ์ข…๋ฅ˜ (Enum)
  private String deliveryCode; //๋ฐฐ์†ก ๋ฐฉ์‹ (String)

  public Item() { }
  
  public Item(String itemName, Integer price, Integer quantity) {
    this.itemName = itemName;
    this.price = price;
    this.quantity = quantity;
  }
}
@Data
@AllArgsConstructor
public class DeliveryCode {
  /* FAST: "๋น ๋ฅธ ๋ฐฐ์†ก"
     NORMAL: "์ผ๋ฐ˜ ๋ฐฐ์†ก"
     SLOW: "๋Š๋ฆฐ ๋ฐฐ์†ก"   */
    private String code;
    private String displayName;

}
public enum ItemType {
  BOOK("๋„์„œ"), FOOD("์Œ์‹"), ETC("๊ธฐํƒ€");

  private final String description;

  ItemType(String description) {
    this.description = description;
  }

  public String getDescription() {
    return description;
  }
}

 

Model

@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 ItemType[] itemTypes() {
  return ItemType.values();
}

@ModelAttribute("deliveryCodes")
public List<DeliveryCode> deliveryCodes() {
  List<DeliveryCode> deliveryCodes = new ArrayList<>();
  deliveryCodes.add(new DeliveryCode("FAST", "๋น ๋ฅธ ๋ฐฐ์†ก"));
  deliveryCodes.add(new DeliveryCode("NORMAL", "์ผ๋ฐ˜ ๋ฐฐ์†ก"));
  deliveryCodes.add(new DeliveryCode("SLOW", "๋Š๋ฆฐ ๋ฐฐ์†ก"));
  return deliveryCodes;
}

 

@ ๋‹จ์ผ ์ฒดํฌ ๋ฐ•์Šค - ๊ธฐ์กด HTML

์•„๋ž˜์™€ ๊ฐ™์ด ๋งŒ๋“ค์—ˆ์„ ๋•Œ, open = on ์ „์†ก๋˜๋ฉฐ ์ด๋Š” ์Šคํ”„๋ง ํƒ€์ž… ์ปจ๋ฒ„ํ„ฐ์—์„œ true, false๋กœ ๋ฐ”๊ฟ”์ค€๋‹ค.

  • ํ•˜์ง€๋งŒ ์‹ค์ œ ๋™์ž‘ํ•ด๋ณด๋ฉด true๋Š” ์ž˜ ๋‚˜์™€๋„ false๋Š” ๋‚˜์˜ค์ง€ ์•Š๋Š”๋‹ค. null์ด ๋ฐ˜ํ™˜๋œ๋‹ค.

HTML์—์„œ ์•„์˜ˆ ๊ฐ’ ์ž์ฒด๋ฅผ ์ „์†กํ•˜์ง€ ์•Š๋Š”๋‹ค. open์ด๋ผ๋Š” ๊ฐ’ ์ž์ฒด๊ฐ€ ์—†์–ด์ง„๋‹ค

  <!-- single checkbox -->
  <div>ํŒ๋งค ์—ฌ๋ถ€</div>
  <div>
    <div class="form-check">
      <input type="checkbox" id="open" name="open" class="form-check-input">
      <label for="open" class="form-check-label">ํŒ๋งค ์˜คํ”ˆ</label>
    </div>
  </div>

 

  • ๊ทธ๋ž˜์„œ ๊ทธ๋ƒฅ HTML์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ์•„๋ž˜์™€ ๊ฐ™์ด ํžˆ๋“  ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ผผ์ˆ˜๋ฅผ ์ด์šฉํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด open ๊ณผ _open์„ ํ•จ๊ป˜ ์ „์†กํ•˜๊ณ , ์„œ๋ฒ„์—์„œ ์ฒดํฌํ•˜์—ฌ null์ด ๋ฐ˜ํ™˜๋˜์–ด ์ฒดํฌ๋ฐ•์Šค ๊ฐ’์ด ์ˆ˜์ •๋˜์ง€์•Š๋Š” ์˜ค๋ฅ˜๋ฅผ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค.
<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">ํŒ๋งค ์˜คํ”ˆ</label>

 

 

@ ๋‹จ์ผ ์ฒดํฌ ๋ฐ•์Šค - ํƒ€์ž„๋ฆฌํ”„ th:filed

๊ทธ๋ƒฅ th:filed ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด id, name, value์™€ ํ•จ๊ป˜ ์•Œ์•„์„œ ํžˆ๋“ ํ•„๋“œ๊นŒ์ง€ ๋งŒ๋“ค์–ด์ค€๋‹ค. ๋„ˆ๋ฌด๋‚˜ ํŽธํ•˜๋‹ค.

  • ๋˜ํ•œ th:filed๋Š” open ๊ฐ’์˜ true, false์— ๋”ฐ๋ผ checked="checked" ์†์„ฑ์„ ์•Œ์•„์„œ ์ถ”๊ฐ€/์‚ญ์ œํ•ด์ค€๋‹ค.
<!-- single checkbox -->
<div>ํŒ๋งค ์—ฌ๋ถ€</div>
<div>
  <div class="form-check">
    <input class="form-check-input" id="open" th:field="*{open}" type="checkbox">
    <label class="form-check-label" for="open">ํŒ๋งค ์˜คํ”ˆ</label>
  </div>
</div>

์ฒดํฌ๋ฐ•์Šค ์‚ฌ์šฉ์‹œ th:filed๋Š” ํžˆ๋“  ํ•„๋“œ๋ฅผ ๋งŒ๋“ค์–ด์ค€๋‹ค.

 


@ ๋ฉ€ํ‹ฐ ์ฒดํฌ ๋ฐ•์Šค - ํƒ€์ž„๋ฆฌํ”„ th:each

@ModelAttribute("myregions") // ํ•ด๋‹น ํด๋ž˜์Šค์˜ ๋ชจ๋“  Controller์— myregions ๋ชจ๋ธ ์ถ”๊ฐ€
public Map<String, String> regions() {
  Map<String, String> regions = new LinkedHashMap<>();
  regions.put("SEOUL", "์„œ์šธ");
  regions.put("BUSAN", "๋ถ€์‚ฐ");
  regions.put("JEJU", "์ œ์ฃผ");
  return regions;
}

 

  • th:each๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ฐ๊ฐ์˜ <input>๊ณผ <label> ์„ ๋ฐ˜๋ณตํ•ด์„œ ๋งŒ๋“ ๋‹ค.
  • ๋ฐ˜๋ณต๋ฌธ์—์„œ ์‚ฌ์šฉ๋˜๋Š” id๋Š” th:filed์—์„œ [ region1, region2, region3... ]์œผ๋กœ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•œ๋‹ค.
    ๋‹ค๋งŒ ์ด ๊ฒฝ์šฐ <label>์—์„œ <input>๊ณผ ๋™์ผํ•œ id๋ฅผ ์ ์–ด์ฃผ๊ธฐ๊ฐ€ ์–ด๋ ต๋‹ค.
  • ๊ทธ๋ž˜์„œ ํƒ€์ž„๋ฆฌํ”„ ์œ ํ‹ธ์—์„œ ${#ids} ๋ผ๋Š” ๊ฒƒ์„ ์ œ๊ณตํ•œ๋‹ค. 
    ids.prev{'regions'}๋Š” ์ด์ „์— ์ž๋™ ์ƒ์„ฑ๋œ *{regions} id ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค.
<!-- multi checkbox -->
<form action="item.html" method="post" th:action th:object="${item}">
  <div>๋“ฑ๋ก ์ง€์—ญ</div>
      <div class="form-check form-check-inline" th:each="rg : ${myregions}">
        <input class="form-check-input" th:field="*{regions}" th:value="${rg.key}"
             type="checkbox">      <!-- th:filed="${item.regions}" -->

        <label class="form-check-label"
             th:for="${#ids.prev('regions')}" th:text="${rg.value}">์„œ์šธ</label>
      </div>
  </div>
</form>

์ด ์ฝ”๋“œ๊ฐ€ rg : {$myregions}fh ๊ฐ’์ด ๋ฐ”๋€Œ๋ฉฐ 3๋ฒˆ ๋ฐ˜๋ณต๋œ๋‹ค.
๋ฐ˜๋ณต๋ฌธ์œผ๋กœ ์ฒดํฌ๋ฐ•์Šค 3๊ฐœ๊ฐ€ ๋งŒ๋“ค์–ด์ง„ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

์ด HTML Form ์•„๋ž˜์™€ ๊ฐ™์€ ํŒŒ๋ผ๋ฉ”ํƒ€๋ฅผ ๋งŒ๋“ ๋‹ค. ์ด๋Š” ์Šคํ”„๋ง ํƒ€์ž…์ปจ๋ฒ„ํ„ฐ์—์„œ ์ ์ ˆํ•˜๊ฒŒ ๋ฐ”๊ฟ”์ค€๋‹ค.

  • ์•ž์—์„œ ์„ค๋ช…ํ•œ ๊ฒƒ ์ฒ˜๋Ÿผ, null์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด _regions๋ผ๋Š” ํžˆ๋“ ํ•„๋“œ๋ฅผ ์•Œ์•„์„œ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

๋กœ๊ทธ = ์Šคํ”„๋ง ํƒ€์ž…์ปจ๋ฒ„ํ„ฐ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ’.

 


# ๋ผ๋””์˜ค ๋ฒ„ํŠผ - ํƒ€์ž„๋ฆฌํ”„

๋ผ๋””์˜ค ๋ฒ„ํŠผ๋„ ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด๋„ ์ƒ๊ด€์—†์ง€๋งŒ, Enum์„ ์ด์šฉํ•ด์„œ ๋งŒ๋“ค์–ด๋ณด์ž.

  • Enum.name()์€ ํ•ด๋‹น ์ด๋„˜ ๊ฐ์ฒด์˜ toString() ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. (BOOK, FOOD, ETC)
  • ์ฒดํฌ๋ฐ•์Šค์˜ ๊ฒฝ์šฐ null์ด ๋ฐ˜ํ™˜๋˜์–ด๋„ ์ƒ๊ด€์—†์–ด์„œ ๋”ฐ๋กœ ํžˆ๋“ ํ•„๋“œ๋ฅผ ๋งŒ๋“ค์ง€๋Š” ์•Š๋Š”๋‹ค.
public enum ItemType {
  BOOK("๋„์„œ"), FOOD("์Œ์‹"), ETC("๊ธฐํƒ€");

  private final String description;

  ItemType(String description) {
    this.description = description;
  }

  public String getDescription() {
    return description;
  }
}
@ModelAttribute("itemTypes")
public ItemType[] itemTypes() {
  return ItemType.values();
}
<!-- radio button -->
<form action="item.html" method="post" th:action th:object="${item}">
<div>
  <div>์ƒํ’ˆ ์ข…๋ฅ˜</div>
  <div class="form-check form-check-inline" th:each="type : ${itemTypes}">
    <input class="form-check-input" th:field="*{itemType}" th:value="${type.name()}"
           type="radio">       <!-- th:filed = "${item.itemType}" -->
    <label class="form-check-label" th:for="${#ids.prev('itemType')}"
           th:text="${type.description}">
    </label>
  </div>
</div>
</form>

 

์ฐธ๊ณ ๋กœ ์Šคํ”„๋ง-ํƒ€์ž„๋ฆฌํ”„ ์—ฐ๋™๊ธฐ๋Šฅ์œผ๋กœ Enum์„ Model์„ ๊ฑฐ์น˜์ง€ ์•Š๊ณ  ์Šคํ”„๋ง ๋„๋ฉ”์ธ ๊ฐ์ฒด์— ์ง์ ‘ ์ ‘๊ทผํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‹ค๋งŒ ํŒจํ‚ค์ง€ ๊ฒฝ๋กœ๋ฅผ ๋‹ค ์ž…๋ ฅํ•ด์ค˜์•ผํ•ด์„œ ๊ถŒ์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์•„๋‹ˆ๋‹ค.

  • ${ T(ํŒจํ‚ค์ง€ ๊ฒฝ๋กœ).values() } ๋กœ ENUM์˜ ๋ชจ๋“  ์ •๋ณด๋ฅผ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ์Šคํ”„๋งEL ๋ฌธ๋ฒ•์ด๋ผ๊ณ  ํ•œ๋‹ค.
  • ${ @mybean.doSomthing() } ์œผ๋กœ ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋ก๋œ ์Šคํ”„๋ง ๋นˆ์„ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
<!-- model์— ๋‹ด์•„์„œ ์ „์ฒด Enum์„ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ• -->
<div th:each="type : ${itemTypes}">

<!-- ์Šคํ”„๋ง ๊ฐ์ฒด๋ฅผ ์ฝ์–ด ์ „์ฒด Enum์„ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ๋ฒ• -->
<div th:each="type : ${T(hello.itemservice.domain.item.ItemType).values()}">

 


# Select ๋ฐ•์Šค - ํƒ€์ž„๋ฆฌํ”„

์œ„์—์„œ ํ–ˆ๋˜ ๊ฒƒ๊ณผ ํŠน๋ณ„ํ•˜๊ฒŒ ๋‹ค๋ฅธ๊ฑด ์—†๋‹ค. ๊ทธ๋ƒฅ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

@Data
@AllArgsConstructor
public class DeliveryCode {
  /* FAST: "๋น ๋ฅธ ๋ฐฐ์†ก"
     NORMAL: "์ผ๋ฐ˜ ๋ฐฐ์†ก"
     SLOW: "๋Š๋ฆฐ ๋ฐฐ์†ก"   */
    private String code;
    private String displayName;

}
  @ModelAttribute("deliveryCodes")
  public List<DeliveryCode> deliveryCodes() {
    List<DeliveryCode> deliveryCodes = new ArrayList<>();
    deliveryCodes.add(new DeliveryCode("FAST", "๋น ๋ฅธ ๋ฐฐ์†ก"));
    deliveryCodes.add(new DeliveryCode("NORMAL", "์ผ๋ฐ˜ ๋ฐฐ์†ก"));
    deliveryCodes.add(new DeliveryCode("SLOW", "๋Š๋ฆฐ ๋ฐฐ์†ก"));
    return deliveryCodes;
  }
<!-- SELECT -->
<div>
  <div>๋ฐฐ์†ก ๋ฐฉ์‹</div>
  <select class="form-select" th:field="*{deliveryCode}">
    <option value="">==๋ฐฐ์†ก ๋ฐฉ์‹ ์„ ํƒ==</option>
    <option th:each="deliveryCode : ${deliveryCodes}" th:text="${deliveryCode.displayName}"
            th:value="${deliveryCode.code}">
    </option>
  </select>
</div>

 

 

# ์Šคํ”„๋ง์—์„œ ์˜ค๋ฅ˜, ์˜ˆ์™ธ์ฒ˜๋ฆฌ(BindingResult)

https://jiwondev.tistory.com/169#head5

 

Spring Vaildation#1 ๊ฒ€์ฆ

๊ฐ์ฒด๋ฅผ ๋งคํ•‘ํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ์˜ ์ค‘์š”ํ•œ ์—ญํ• ์ค‘ ํ•˜๋‚˜๋Š” HTTP ์š”์ฒญ์ด ์ •์ƒ์ธ์ง€ ๊ฒ€์ฆํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์–ด์ฉŒ๋ฉด ์ •์ƒ ๋กœ์ง๋ณด๋‹ค ์ด๋Ÿฌํ•œ ์˜ˆ์™ธ, ๊ฒ€์ฆ ๋กœ์ง์„ ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์ด ํ›จ์”ฌ ๋” ์–ด๋ ค์šธ ์ˆ˜ ์žˆ๋‹ค. ํ•ด๋‹น๊ธ€์€ Bea

jiwondev.tistory.com

 

์Šคํ”„๋ง์˜ bindingErrors๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํƒ€์ž„๋ฆฌํ”„์—์„œ๋Š” ๊ฐ„๊ฒฐํ•œ ๋ฌธ๋ฒ•์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#validation-and-error-messages

<form action="item.html" method="post" th:action th:object="${item}">
    <div th:if="${#fields.hasGlobalErrors()}">
      <p class="field-error" th:each="err : ${#fields.globalErrors()}" th:text="${err}">๊ธ€๋กœ๋ฒŒ ์˜ค๋ฅ˜
        ๋ฉ”์‹œ์ง€</p>
    </div>

    <div>
      <label for="itemName" th:text="#{label.item.itemName}">์ƒํ’ˆ๋ช…</label>
      <input class="form-control" id="itemName" placeholder="์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”"
             th:errorclass="field-error" th:field="*{itemName}" type="text">
             
      <div class="field-error" th:errors="*{itemName}">
        ์ƒํ’ˆ๋ช… ์˜ค๋ฅ˜
      </div>
    </div>
    ...
</form>

 

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

JiwonDev

JiwonDev

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