DevLog ๐Ÿ˜ถ

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

Back-end/Spring

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

dolmeng2 2022. 8. 18. 12:10

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

 

์Šคํ”„๋ง MVC 1ํŽธ - ๋ฐฑ์—”๋“œ ์›น ๊ฐœ๋ฐœ ํ•ต์‹ฌ ๊ธฐ์ˆ  - ์ธํ”„๋Ÿฐ | ๊ฐ•์˜

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

www.inflearn.com


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

 

[Spring] HTTP ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ, ์‘๋‹ต ๋ฐ์ดํ„ฐ ์ƒ์„ฑ, HttpMessageConverter์™€ ArgumentResolver

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

cl8d.tistory.com

- ์ง€๋‚œ ํฌ์ŠคํŒ…์—์„œ๋Š” HTTP ์š”์ฒญ/์‘๋‹ต ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ArgumentResolver, ๊ทธ๋ฆฌ๊ณ  HttpMessageConverter์˜ ๋™์ž‘ ๋ฐฉ์‹์„ ์•Œ์•„๋ณด์•˜๋‹ค.

- ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋ณธ๊ฒฉ์ ์œผ๋กœ ์ž‘์€ ์›น ์‚ฌ์ดํŠธ๋ฅผ ๋งŒ๋“ค๋ฉด์„œ ์ง€๊ธˆ๊นŒ์ง€ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์‘์šฉํ•ด๋ณด์ž.

- ์Šคํ”„๋ง MVC 1์˜ ๋งˆ์ง€๋ง‰ ํฌ์ŠคํŒ…์ด ๋  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค :D

 

 

| ๊ฐ„๋‹จํ•œ ์›น ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ

- dependency๋กœ Spring Web, Thymeleaf, Lombok์„ ์ถ”๊ฐ€ํ•ด์ฃผ์ž.

- ๊ฐ„๋‹จํ•œ ์ƒํ’ˆ ์ฃผ๋ฌธ ์›น ์‚ฌ์ดํŠธ๋ฅผ ๋งŒ๋“ค ์˜ˆ์ •์ด๋‹ค.

 

[SItem.java]

- ๋‚˜์˜ ๊ฒฝ์šฐ ์ง€๊ธˆ๊นŒ์ง€ ํฌ์ŠคํŒ… ํ–ˆ๋˜ ๋‚ด์šฉ์˜ ํ”„๋กœ์ ํŠธ๊ฐ€ ๋‹ค ํ•˜๋‚˜์˜ ํ”„๋กœ์ ํŠธ์—ฌ์„œ ๋„๋ฉ”์ธ ์ด๋ฆ„์„ ์•ˆ ๊ฒน์น˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ์ด๋ ‡๊ฒŒ ์„ค์ •ํ•˜์˜€๋‹ค.

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


    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;
    }
}

- setter๋ฅผ ์•ˆ ์—ด๊ธฐ ์œ„ํ•ด์„œ ์ตœ๋Œ€ํ•œ ๋ฉ”์„œ๋“œ ๋‹จ์œ„๋กœ ์—ด์—ˆ๋‹ค.

 

[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());
    }

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

}

- ๋™์‹œ์„ฑ ์ด์Šˆ๋กœ ์ธํ•ด์„œ concurrentHashMap๊ณผ AtomicLong์„ ์‚ฌ์šฉํ•˜์˜€๋‹ค. (์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋ฌด๋ฐฉํ•˜๋‹ค)

 

[SItemRepositoryTest.java]

class SItemRepositoryTest {
    SItemRepository repository = new SItemRepository();

    @AfterEach
    void afterEach() {
        repository.clearStore();
    }

    @Test
    public void save() throws Exception {
        // given
        SItem item = new SItem("itemA", 10000, 10);

        // when
        SItem savedItem = repository.save(item);

        // then
        SItem findItem = repository.findById(savedItem.getId());
        assertThat(findItem).isEqualTo(savedItem);
    }

    @Test
    public void findAll() throws Exception {
        // given
        SItem item1 = new SItem("itemA", 10000, 10);
        SItem item2 = new SItem("itemB", 20000, 20);

        repository.save(item1);
        repository.save(item2);

        // when
        List<SItem> findItems = repository.findAll();

        // then
        assertThat(findItems.size()).isEqualTo(2);
        assertThat(findItems).contains(item1, item2);
    }

    @Test
    public void updateItem() throws Exception {
        // given
        SItem item1 = new SItem("itemA", 10000, 10);
        SItem savedItem = repository.save(item1);
        Long itemId = savedItem.getId();

        // when
        SItem updateItem = new SItem("itemB", 20000, 20);
        repository.update(itemId, updateItem);
        SItem findItem = repository.findById(itemId);

        // then
        assertThat(findItem.getItemName()).isEqualTo(updateItem.getItemName());
        assertThat(findItem.getPrice()).isEqualTo(updateItem.getPrice());
        assertThat(findItem.getQuantity()).isEqualTo(updateItem.getQuantity());

    }
}

- ๊ฐ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ๊ฐ„๋‹จํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์ด๋‹ค. ๋ชจ๋‘ ์ž˜ ๋Œ์•„๊ฐ€๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

- ์ด์ œ, html์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ„๋‹จํ•œ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์„ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์•„๋‘์ž.

 

Download

Download Bootstrap to get the compiled CSS and JavaScript, source code, or include it with your favorite package managers like npm, RubyGems, and more.

getbootstrap.com

- ์••์ถ• ํ•ด์ œ ํ›„ bootstrap.min.css๋ฅผ ๋ณต์‚ฌํ•ด์„œ resources/static/css ๋ฐ‘์— ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ๋‹ค.

 

- ์ดํ›„ ํŒŒ์ผ๋“ค์€ resources/static/basic ํด๋” ๋ฐ‘์—๋‹ค ์ถ”๊ฐ€ํ•˜์˜€๋‹ค.

[items.html]

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link 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'"
            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>
                    <td><a href="item.html">1</a></td>
                    <td><a href="item.html">ํ…Œ์ŠคํŠธ ์ƒํ’ˆ1</a></td>
                    <td>10000</td>
                    <td>10</td>
                </tr>
                <tr>
                    <td><a href="item.html">2</a></td>
                    <td><a href="item.html">ํ…Œ์ŠคํŠธ ์ƒํ’ˆ2</a></td>
                    <td>20000</td>
                    <td>20</td>
                </tr>
            </tbody>
            </table>
        </div>
    </div>
</body>
</html>

- ๋Œ€์ถฉ ์ด๋Ÿฐ ๋Š๋‚Œ์œผ๋กœ ์•„์ดํ…œ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค.

 

[item.html]

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link 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>

  <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" 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" 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" 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" readonly>
    </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'" type="button">
        โœ๐Ÿป
      </button>
    </div>
    <div class="col">
      <button class="w-100 btn btn-info btn-lg"
              onclick="location.href='items.html'" type="button">
        ๐Ÿ“‘
      </button>
    </div>
  </div>
</div>

</body>
</html>

- ๊ฐ item์— ๋Œ€ํ•œ ์„ธ๋ถ€ ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํ™”๋ฉด์ด๋‹ค. 

- ์•„๋ž˜ ๋…ธ๋ž€์ƒ‰ ๋ฒ„ํŠผ์€ ์ˆ˜์ •, ํ•˜๋Š˜์ƒ‰ ๋ฒ„ํŠผ์€ ๋ชฉ๋ก์œผ๋กœ ๋Œ์•„๊ฐ€๋Š” ๋ฒ„ํŠผ์ด๋‹ค.

 

[addForm.html]

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link 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" method="post">
    <div class="row fw-bold font-monospace">
      <div class="col text-center">
        <label for="itemName">Name</label>
        <input type="text" id="itemName" name="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" id="price" name="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" id="quantity" name="quantity"
               class="form-control text-center"
               placeholder="Please Enter an Item Quantity.">
      </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'" type="button">
          โŒ
        </button>
      </div>
    </div>
  </form>
</div>
</body>
</html>

- ์•„์ดํ…œ ์ €์žฅ form์ด๋‹ค. ์ƒํ’ˆ ์ด๋ฆ„, ๊ฐ€๊ฒฉ, ์ˆ˜๋Ÿ‰์„ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค.

 

[editForm.html]

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link 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" method="post">
    <div class="row fw-bold font-monospace">
      <div class="col text-center">
        <label for="id">Id</label>
        <input type="text" id="id" name="id"
               class="form-control text-center"
               value="1" 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">
      </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">
      </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">
      </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'" type="button">
          โŒ
        </button>
      </div>
    </div>
  </form>
</div>
</body>
</html>

- ์ƒํ’ˆ ์ˆ˜์ •ํผ๋„ ๋™์ผํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ์—ˆ๋‹ค.

 


| ์ปจํŠธ๋กค๋Ÿฌ์™€ ๋ทฐ ํ…œํ”Œ๋ฆฟ ๋งŒ๋“ค๊ธฐ 

- ๋™์ ์œผ๋กœ ๊ฐ’์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„œ. html ๊ฒฝ๋กœ๋ฅผ resources/templates/basic ๋ฐ‘์œผ๋กœ ์˜ฎ๊ฒจ์ฃผ์—ˆ๋‹ค.

[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";
    }

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

- DB๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋ฒ„ ์žฌ์‹œ์ž‘๋งˆ๋‹ค ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์—†์œผ๋‹ˆ๊นŒ @PostConstruct๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค.

- ์ „์ฒด ์ƒํ’ˆ ๋ชฉ๋ก์„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋Š” ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ๋งŒ ์ƒ์„ฑํ•˜์˜€๋‹ค.

 

- ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  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>

โœ” th:href

- ๊ธฐ์กด์˜ href ๊ฐ’์„ th:href ๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค. ๊ฐ’์ด ์—†๋‹ค๋ฉด ์ƒˆ๋กœ ์ƒ์„ฑํ•œ๋‹ค.

- HTML์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ๋•Œ๋Š” href ์†์„ฑ์ด, ๋ทฐ ํ…œํ”Œ๋ฆฟ์„ ๊ฑฐ์น˜๋ฉด th:href ๊ฐ’์ด ๋™์ ์œผ๋กœ ๋Œ€์ฒด๋œ๋‹ค.

- ๋Œ€๋ถ€๋ถ„์˜ HTML ์†์„ฑ์€ ํƒ€์ž„๋ฆฌํ”„๋ฅผ ํ†ตํ•ด th:๋กœ ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

- HTML์„ ํŒŒ์ผ๋กœ ์—ด์—ˆ์„ ๋•Œ๋Š” th:๊ฐ€ ์žˆ์–ด๋„ ์›น ๋ธŒ๋ผ์šฐ์ €๋Š” ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์—, HTML ํŒŒ์ผ ๋ณด๊ธฐ + ํ…œํ”Œ๋ฆฟ ๊ธฐ๋Šฅ์„ ํ•จ๊ป˜ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

โœ” URL ๋งํฌ ํ‘œํ˜„ @{...}

- URL ๋งํฌ๋ฅผ @{...}๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ์„œ๋ธ”๋ฆฟ ์ปจํ…์ŠคํŠธ๋ฅผ ์ž๋™์œผ๋กœ ํฌํ•จ์‹œํ‚จ๋‹ค.

(์˜›๋‚ ์— ๊ฒฝ๋กœ ์ด๋ฆ„์„ ์ง€์ •ํ–ˆ์–ด์•ผ ํ–ˆ๋Š”๋ฐ, ์ง€๊ธˆ์€ ์ž˜ ์•ˆ ์จ์„œ ํฌ๊ฒŒ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์•„๋„ ๋œ๋‹ค.)

 

th:href="@{/basic/items/{itemId}(itemId=${item.id})}"

- itemId๊ฐ€ item.id์˜ ๊ฐ’์œผ๋กœ ์น˜ํ™˜์ด ๋˜๊ณ , ๊ฒฐ๊ณผ์ ์œผ๋กœ ๊ฒฝ๋กœ ๋ณ€์ˆ˜์˜ {itemId}์— ๋“ค์–ด๊ฐ„๋‹ค.

 

- ์ถ”๊ฐ€์ ์œผ๋กœ, ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

th:href="@{/basic/items/{itemId}(itemId=${item.id}, query='test')}"

--> /basic/items/1?query=test

 

- ๋ฆฌํ„ฐ๋Ÿด ๋Œ€์ฒด ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

th:href="@{|/basic/items/${item.id}|}"

 

 

โœ” ์†์„ฑ ๋ณ€๊ฒฝ th:onclick

- ์—ฌ๊ธฐ์„œ, ๋ฆฌํ„ฐ๋Ÿด ๋Œ€์ฒด ๋ฌธ๋ฒ•์ด ์‚ฌ์šฉ๋˜์—ˆ๋‹ค.

th:onclick="|location.href='@{/basic/items/add}'|"

|...| : ํƒ€์ž„๋ฆฌํ”„์—์„œ ๊ธฐ์กด์—๋Š” ๋ฌธ์ž, ํ‘œํ˜„์‹์ด ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์–ด์„œ +๋ฅผ ํ†ตํ•ด ๋”ํ•ด์„œ ์‚ฌ์šฉํ–ˆ์–ด์•ผ ํ–ˆ๋Š”๋ฐ, ||๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ทธ๋Ÿด ํ•„์š”๊ฐ€ ์—†๋‹ค.

 

ex) th:text="'hi' + ${item.name} + '!'" -> th:text="|hi ${item.name}!|"

- ์šฐ๋ฆฌ๋Š” ๊ธฐ์กด์ด๋ผ๋ฉด ๊ฒฝ๋กœ์— ๋Œ€ํ•ด์„œ / ๋ผ๋Š” ๋ฌธ์ž๋ฅผ ๋”ํ•ด์คฌ์–ด์•ผ ํ–ˆ๋Š”๋ฐ, ๋ฆฌํ„ฐ๋Ÿด ๋Œ€์ฒด ๋ฌธ๋ฒ•์œผ๋กœ ํŽธํ•˜๊ฒŒ ์ง€์ •ํ•ด์ฃผ์—ˆ๋‹ค.

 

 

โœ” ๋ฐ˜๋ณต ์ถœ๋ ฅ th:each

th:each="item : ${items}

- 'items'๋ผ๋Š” ์ปฌ๋ ‰์…˜์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํ•˜๋‚˜์”ฉ ๊บผ๋‚ด์„œ item์— ๋‹ด์•„์ค€๋‹ค. item.name ์ด๋Ÿฐ ์‹์œผ๋กœ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

 

 

โœ” ๋ณ€์ˆ˜ ํ‘œํ˜„์‹ ${...}, ๋‚ด์šฉ ๋ณ€๊ฒฝ th:text

th:text="${item.itemName}">ItemName</a></td>

- ๋ชจ๋ธ์— ํฌํ•จ๋œ ๊ฐ’์ด๋‚˜, ํƒ€์ž„๋ฆฌํ”„ ๋ณ€์ˆ˜๋กœ ์„ ์–ธํ•œ ๊ฐ’์„ ${...}์„ ํ†ตํ•ด ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.

- ๋‚ด์šฉ์˜ ๊ฐ’์„ th:text๋ฅผ ํ†ตํ•ด ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

 

 

- ์ž, ์ด์ œ ์ด์–ด์„œ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„๋„ ๋งŒ๋“ค์–ด๋ณด์ž.

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

@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() {
    return "basic/addForm";
}

- ์ƒํ’ˆ ์ƒ์„ธ์™€ ์ƒํ’ˆ ๋“ฑ๋ก์— ๊ด€ํ•œ ํผ์„ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค.

 

[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>

  <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>

  <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>

 

โœ” ์†์„ฑ ๋ณ€๊ฒฝ th:value

value="10" th:value="${item.quantity}"

- th:value๋ฅผ ํ†ตํ•ด item์— ์žˆ๋Š” quantity ์ •๋ณด๋ฅผ value์— ๋‹ด์•„์ค€๋‹ค. (๋ณ€์ˆ˜ ํ‘œํ˜„์‹ ์‚ฌ์šฉ)

 

[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 method="post">
    <div class="row fw-bold font-monospace">
      <div class="col text-center">
        <label for="itemName">Name</label>
        <input type="text" id="itemName" name="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" id="price" name="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" id="quantity" name="quantity"
               class="form-control text-center"
               placeholder="Please Enter an Item Quantity.">
      </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>

 

โœ” ์†์„ฑ ๋ณ€๊ฒฝ th:action

- HTML form์—์„œ action์— ๊ฐ’์ด ์—†์œผ๋ฉด ํ˜„์žฌ URL์— ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•ด์ค€๋‹ค.

    - ์ฆ‰, ์—ฌ๊ธฐ์„œ ํ˜„์žฌ url์ด /basic/item/add์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด URL๋กœ POST ๋ฐฉ์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•˜๊ฒŒ ๋œ๋‹ค.

- ์šฐ๋ฆฌ๋Š” ์ƒํ’ˆ ๋“ฑ๋ก ํผ์€ /basic/item/add์—์„œ GET์œผ๋กœ, ์‹ค์ œ ๋“ฑ๋ก์€ /basic/item/add์— POST ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์˜€๋‹ค.

- ๊ฐ™์€ URL์ด์ง€๋งŒ HTTP ๋ฉ”์„œ๋“œ๋งŒ ๋‹ค๋ฅด๊ฒŒ ํ•œ ๊ฒƒ์ด๋‹ค. (ํ•˜๋‚˜์˜ ํผ์—์„œ ๋“ฑ๋ก ์ฒ˜๋ฆฌ๊นŒ์ง€ ์ง„ํ–‰)

 


| ์ƒํ’ˆ ๋“ฑ๋ก/์ˆ˜์ • ์ง„ํ–‰ํ•˜๊ธฐ 

- ์ƒํ’ˆ ๋“ฑ๋ก์€ ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ ํ˜•์‹์œผ๋กœ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ํ•œ๋‹ค.

- content-type: application/x-www-form-urlencoded

 

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

@PostMapping("/add")
public String addItemV1 (@RequestParam String itemName,
                         @RequestParam Integer price,
                         @RequestParam Integer quantity,
                         Model model) {
    SItem item = new SItem(itemName, price, quantity);
    repository.save(item);
    model.addAttribute("item", item);
    return "basic/item";
}

์ด ๋ฐฉ๋ฒ•์€ ์ž˜ ๋™์ž‘ํ•˜์ง€๋งŒ... ์ด๋ ‡๊ฒŒ ๋ชจ๋“  ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ @RequestParam์„ ํ†ตํ•ด ๋ฐ›๋Š” ๊ฑด ๋ถˆํŽธํ•˜๋‹ค.

 

@PostMapping("/add")
public String addItemV2 (@ModelAttribute SItem item, Model model) {
    repository.save(item);
    model.addAttribute("item", item);
    return "basic/item";
}

๊ฐ„๋‹จํ•˜๊ฒŒ ์ค„์ธ ๋ฒ„์ „์ด๋‹ค.

@ModelAttribute์˜ ๊ฒฝ์šฐ SItem ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐ ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๊ฐ’์„ ํ”„๋กœํผํ‹ฐ ์ ‘๊ทผ๋ฒ•(setter ์ด์šฉ)์œผ๋กœ ์ž…๋ ฅํ•ด์ค€๋‹ค.

์ด ๊ฒฝ์šฐ์—๋Š” SItem์— @Setter๊ฐ€ ์žˆ์–ด์•ผ ํ•œ๋‹ค!! (ํ˜น์€ setXXX๋ผ๋Š” ์ด๋ฆ„์„ ๊ฐ€์ง„ ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์ž)

 

โญ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ์ƒ์„ฑ์ž๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ ์ƒ์„ฑ์ž๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ์„, ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๋งŒ ์žˆ์œผ๋ฉด setter๋ฅผ ํ†ตํ•ด ๋ฐ”์ธ๋”ฉ์„ ์‹œ๋„ํ•œ๋‹ค.

 

๋˜ํ•œ, model์—๋‹ค๊ฐ€ @ModelAttribute๊ฐ€ ์ง€์ •ํ•œ ๊ฐ์ฒด๋ฅผ ๋„ฃ์–ด์ฃผ๋Š” ์—ญํ• ๋„ ํ•œ๋‹ค!

model์— ๋„ฃ์„ ๋•Œ ์ด๋ฆ„์€ @ModelAttribute์— ์ง€์ •ํ•œ name(value) ์†์„ฑ์„ ์‚ฌ์šฉํ•œ๋‹ค.

 

@PostMapping("/add")
public String addItemV3 (SItem item) {
    repository.save(sItem);
    return "basic/item";
}

- ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ ์ค„์ธ ๋ฒ„์ „์ด๋‹ค.

- @ModelAttribute๋ฅผ ์ƒ๋žตํ•˜๊ณ , model.addAttribute() ๋ถ€๋ถ„์„ ์ œ๊ฑฐํ•˜์˜€๋‹ค.

- ์ƒ๋žตํ–ˆ์–ด๋„ SItem ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ์š”์ฒญ๋œ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๊ฐ’(itemName, price, quantity)๋ฅผ setXXX ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์ฃผ์ž…ํ•œ๋‹ค.

 

- โญ ๋ชจ๋ธ์— ์ €์žฅ๋  ๋•Œ 'ํด๋ž˜์Šค๋ช…'์„ ์‚ฌ์šฉํ•œ๋‹ค. ์ด๋•Œ, ํด๋ž˜์Šค์˜ ์ฒซ๊ธ€์ž๋งŒ ์†Œ๋ฌธ์ž๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.

- ๊ทธ๋ž˜์„œ, ์œ„ ์ฝ”๋“œ์˜ ๊ฒฝ์šฐ model.addAttribute('sItem', item')์ด ๋œ๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

- ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์—, ์ด ์ฝ”๋“œ๊ฐ€ ๋™์ž‘ํ•˜๋ ค๋ฉด html์—์„œ item ๋ถ€๋ถ„์„ sItem์œผ๋กœ ๋ฐ”๊พธ์–ด ์ค˜์•ผ ํ•œ๋‹ค.

 

- ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ˆ˜์ •๋„ ๋ฐ”๊พธ์–ด์ฃผ์ž.

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

@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}"
}

- ์ˆ˜์ • ํผ๊ณผ ์‹ค์ œ ์ˆ˜์ •์„ ์ฒ˜๋ฆฌํ•˜๋Š” edit ๊ธฐ๋Šฅ์ด๋‹ค.

- ์ˆ˜์ • ํ›„์—๋Š” ์ƒํ’ˆ ์ƒ์„ธ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜์˜€๋‹ค.

 

[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 method="post">
    <div class="row fw-bold font-monospace">
      <div class="col text-center">
        <label for="id">Id</label>
        <input type="text" id="id" name="id"
               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}">
      </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}">
      </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}">
      </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>

- ์ •๋ง ์ž˜ ๋™์ž‘ํ•˜๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋‹ค!

 


| RPG, Redirect

- ํ˜„์žฌ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ์‚ฌ์ดํŠธ๋Š” ์ƒํ’ˆ ๋“ฑ๋ก ํ›„ ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•˜๋ฉด ์ƒํ’ˆ์ด ๊ณ„์† ์ค‘๋ณตํ•ด์„œ ์Œ“์ธ๋‹ค.

- ์ƒˆ๋กœ๊ณ ์นจ : ๋งˆ์ง€๋ง‰์œผ๋กœ ์„œ๋ฒ„์— ์ „์†กํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ์ „์†ก

- ์ƒํ’ˆ ๋“ฑ๋ก ํผ์˜ POST /add๊ฐ€ ๊ณ„์† ์žฌ์ „์†ก๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋™์ผํ•œ ์ƒํ’ˆ์ด ๊ณ„์† ์ถ”๊ฐ€๋˜๋Š” ๊ฒƒ.

-> ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ, ์ƒํ’ˆ ์ €์žฅ ํ›„์— ๋ทฐ ํ…œํ”Œ๋ฆฟ์ด ์•„๋‹Œ ์ƒํ’ˆ ์ƒ์„ธ ํ™”๋ฉด์œผ๋กœ redirect๋ฅผ ์‹œ์ผœ์ฃผ์ž.

--> ์ด๋Ÿฌ๋ฉด ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•ด๋„ ์ƒํ’ˆ ์ƒ์„ธ ํ™”๋ฉด์— ๊ณ„์† ๋จธ๋ฌผ๊ฒŒ ๋œ๋‹ค.

 

[SItemController.java] - ์ˆ˜์ •

@PostMapping("/add")
public String addItemV2 (SItem item, Model model) {
    repository.save(item);
    model.addAttribute("item", item);
    return "redirect:/basic/items/" + item.getId();
}

- ์ด๋Ÿฐ ๋ฌธ์ œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด RPG - POST/REDIRECT/GET ๋ฐฉ์‹์ด๋‹ค.

- ๊ทธ๋Ÿฌ๋‚˜, ์œ„์ฒ˜๋Ÿผ redirect๋ฌธ์„ ์ž‘์„ฑํ•˜๋Š” ๊ฑด URL ์ธ์ฝ”๋”ฉ์ด ์•ˆ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ข‹์ง€ ์•Š๋‹ค. ๋‹ค์Œ์˜ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์ž.

 

[SItemController.java] - ์ˆ˜์ •

@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};
}

- ์ƒํ’ˆ ์ €์žฅ ํ›„ ์ž˜ ์ €์žฅ๋˜์—ˆ๋‹ค๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•ด์ฃผ๊ธฐ ์œ„ํ•ด์„œ status๋ผ๋Š” ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ๋‹ค.

- ๋˜ํ•œ +๋ฅผ ํ†ตํ•œ redirect๋ฌธ ๋Œ€์‹ , addAttribute๋กœ ์ถ”๊ฐ€ํ•ด์ค€ itemId๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค. ( {itemId} )

- redirectAttributes๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฒฝ๋กœ ๋ณ€์ˆ˜ + ๋‚˜๋จธ์ง€๋Š” ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ฒ˜๋ฆฌํ•ด์ค€๋‹ค.

์œ„ URL) basic/items/3?status=true

 

[item.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">Item Detail</h2>
  </div>
  <h2 class="font-monospace fw-bold text-center text-danger"
      th:if="${param.status}" th:text="'Save Success!'"></h2>

  ...

</body>
</html>

 

โœ” th:if

- ํƒœ๊ทธ์— ๋Œ€ํ•œ ์กฐ๊ฑด์„ ๊ฑธ ์ˆ˜ ์žˆ๋‹ค.

- ${param.status} -> ํƒ€์ž„๋ฆฌํ”„์—์„œ ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค! param.~์œผ๋กœ ์กฐํšŒํ•˜๋ฉด ๋œ๋‹ค.

- ์˜ˆ์˜์ง€๋Š” ์•Š์ง€๋งŒ ์ž˜ ๋œฌ๋‹ค.

- ์ด๋ ‡๊ฒŒ ์Šคํ”„๋ง MVC1 ๊ฐ•์˜๋ฅผ ์™„๊ฐ•ํ•˜์˜€๋‹ค. ์ฝ”๋“œ ๋ ˆ๋ฒจ์—์„œ ๊นŒ๋ณด๋ฉด์„œ ๊ณต๋ถ€ํ–ˆ๋˜ ๊ฒŒ ๋ณต์Šตํ•  ๋•Œ ์ œ์ผ ์ข‹์•˜๋˜ ๊ฒƒ ๊ฐ™๋‹ค.

- ๋นจ๋ฆฌ ์Šคํ”„๋ง MVC2๋„ ๋ณต์Šตํ•ด์•ผ๊ฒ ๋‹ค... ์•„์ž์•„์ž...!

Comments