Spring MVC - ์น ํ์ด์ง ๋ง๋ค๊ธฐ with Thymeleaf
by rlaehddnd0422์ด๋ฒ ํฌ์คํ ์์๋ ์ด์ ํฌ์คํ ์์ ๊ณต๋ถํ MVC์ ๊ธฐ๋ณธ ๊ธฐ๋ฅ๊ณผ ๋ทฐ ํ ํ๋ฆฟ Thymeleaf์ ์ด์ฉํด ๊ฐ๋จํ๊ฒ ์ํ์ ๊ด๋ฆฌํ ์ ์๋ ์๋น์ค๋ฅผ ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค.
์๊ตฌ์ฌํญ
์ํ ๋๋ฉ์ธ ๋ชจ๋ธ
- ์ํ ID
- ์ํ๋ช
- ๊ฐ๊ฒฉ
์ํ ๊ด๋ฆฌ ๊ธฐ๋ฅ
- ์ํ ๋ชฉ๋ก
- ์ํ ์์ธ
- ์ํ ๋ฑ๋ก
- ์ํ ์์
์ํ ๋๋ฉ์ธ ๊ฐ๋ฐ
Item - ์ํ ๊ฐ์ฒด
@Getter @Setter
public class Item {
private Long id;
private String itemName;
private Integer price;
private Integer quantity;
public Item(){}
public Item(String itemName, Integer price, Integer quantity) {
this.itemName = itemName;
this.price = price;
this.quantity = quantity;
}
}
ItemRepository - ์ํ ์ ์ฅ์
package hello.itemservice.domain.item;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Repository
public class ItemRepository {
private static final Map<Long, Item> store = new HashMap<>();
private static long sequence = 0L;
public Item save(Item item)
{
item.setId(++sequence);
store.put(item.getId(),item);
return item;
}
public Item findById(Long id)
{
return store.get(id);
}
public List<Item> findAll()
{
return new ArrayList<>(store.values());
}
public void update(Long itemId, Item updateParam)
{
Item findItem = findById(itemId);
findItem.setItemName(updateParam.getItemName());
findItem.setPrice(updateParam.getPrice());
findItem.setQuantity(updateParam.getQuantity());
}
public void clearStore()
{
sequence = 0L;
store.clear();
}
}
์ํ ์๋น์ค HTML
๋ถํธ์คํธ๋ฉ
HTML์ ํธ๋ฆฌํ๊ฒ ๊ฐ๋ฐํ๊ธฐ ์ํด ๋ถํธ์คํธ๋ฉ์ ์ฌ์ฉํ์ต๋๋ค.
๋ถํธ์คํธ๋ฉ์ด๋ ?
์น์ฌ์ดํธ๋ฅผ ์ฝ๊ฒ ๋ง๋ค ์ ์๊ฒ ๋์์ฃผ๋ HTML, CSS, JS ํ๋ ์์ํฌ๋ก, ํ๋์ CSS๋ก ํด๋ํฐ, ํ๋ธ๋ฆฟ, ๋ฐ์คํฌํ๊น์ง ๋ค์ํ ๊ธฐ๊ธฐ์์ ์๋ํฉ๋๋ค. ๋ค์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ฌ ์ฌ์ฉ์๊ฐ ์ฝ๊ฒ ์น์ฌ์ดํธ๋ฅผ ์ ์, ์ ์ง, ๋ณด์ํ ์ ์๋๋ก ๋์์ค๋๋ค.
- ๋ถํธ์คํธ๋ฉ ๊ณต์ ์ฌ์ดํธ์์ ๋ถํธ์คํธ๋ฉ์ ๋ค์ด๋ฐ์ ์์ถ์ ์ถ๊ณ bootstrap.min.css ๋ฅผ ๋ณต์ฌํด์ ๋ค์ ํด๋์ ์ถ๊ฐํฉ๋๋ค.
- ์ฐธ๊ณ ๋ก /resources/static์ ๋ฃ์ด๋์๊ธฐ ๋๋ฌธ์ ์คํ๋ง ๋ถํธ๊ฐ ์ ์ ๋ฆฌ์์ค๋ฅผ ์ ๊ณตํฉ๋๋ค.
์ด๋ ๊ฒ ์ ์ ๋ฆฌ์์ค๊ฐ ๊ณต๊ฐ๋๋ /resources/static ํด๋์ HTML์ ๋ฃ์ด๋๋ฉด, ์ค์ ์๋น์ค์์๋ ๊ณต๊ฐ๋ฉ๋๋ค. ์๋น์ค๋ฅผ ์ด์ํ๋ค๋ฉด ์ง๊ธ์ฒ๋ผ ๊ณต๊ฐํ ํ์์๋ HTML์ staticํด๋์ ๋๋ ๊ฒ์ ์ฃผ์ํฉ์๋ค.
์๋น์ค HTML - static
์ํ ๋ชฉ๋ก - items.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link href="../css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container" style="max-width: 600px">
<div class="py-5 text-center">
<h2>์ํ ๋ชฉ๋ก</h2> </div>
<div class="row">
<div class="col">
<button class="btn btn-primary float-end" onclick="location.href='addForm.html'" type="button">์ํ
๋ฑ๋ก</button> </div>
</div>
<hr class="my-4">
<div>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>์ํ๋ช
</th> <th>๊ฐ๊ฒฉ</th> <th>์๋</th>
</tr>
</thead>
<tbody>
<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> <!-- /container -->
</body>
</html>
์ํ ์์ธ - item.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link href="../css/bootstrap.min.css" rel="stylesheet">
<style>
.container {
max-width: 560px;
} </style>
</head>
<body>
<div class="container">
<div class="py-5 text-center">
<h2>์ํ ์์ธ</h2> </div>
<div>
<label for="itemId">์ํ ID</label>
<input type="text" id="itemId" name="itemId" class="form-control"
value="1" readonly>
</div> <div>
<label for="itemName">์ํ๋ช
</label>
<input type="text" id="itemName" name="itemName" class="form-control"
value="์ํA" readonly> </div>
<div>
<label for="price">๊ฐ๊ฒฉ</label>
<input type="text" id="price" name="price" class="form-control"
value="10000" readonly>
</div> <div>
<label for="quantity">์๋</label>
<input type="text" id="quantity" name="quantity" class="form-control"
value="10" readonly>
</div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-primary btn-lg" onclick="location.href='editForm.html'" type="button">์ํ ์์ </button>
</div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='items.html'" type="button">๋ชฉ๋ก์ผ๋ก</button> </div>
</div>
</div> <!-- /container -->
</body>
</html>
์ํ ๋ฑ๋ก - addForm.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link href="../css/bootstrap.min.css" rel="stylesheet">
<style>
.container {
max-width: 560px;
} </style>
</head>
<body>
<div class="container">
<div class="py-5 text-center">
<h2>์ํ ๋ฑ๋ก ํผ</h2>
</div>
<h4 class="mb-3">์ํ ์
๋ ฅ</h4>
<form action="item.html" method="post">
<div>
<label for="itemName">์ํ๋ช
</label>
<input type="text" id="itemName" name="itemName" class="form-control" placeholder="์ด๋ฆ์ ์
๋ ฅํ์ธ์"> </div>
<div>
<label for="price">๊ฐ๊ฒฉ</label>
<input type="text" id="price" name="price" class="form-control" placeholder="๊ฐ๊ฒฉ์ ์
๋ ฅํ์ธ์">
</div> <div>
<label for="quantity">์๋</label>
<input type="text" id="quantity" name="quantity" class="form-control" placeholder="์๋์ ์
๋ ฅํ์ธ์"> </div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-primary btn-lg" type="submit">์ํ ๋ฑ๋ก</button> </div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='items.html'" type="button">์ทจ์</button> </div>
</div>
</form>
</div> <!-- /container -->
</body>
</html>
์ํ ํธ์ง - editForm.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link href="../css/bootstrap.min.css" rel="stylesheet">
<style>
.container {
max-width: 560px;
}
</style>
</head>
<body>
<div class="container">
<div class="py-5 text-center"> <h2>์ํ ์์ ํผ</h2>
</div>
<form action="item.html" method="post">
<div>
<label for="id">์ํ ID</label>
<input type="text" id="id" name="id" class="form-control" value="1" readonly>
</div>
<div>
<label for="itemName">์ํ๋ช
</label>
<input type="text" id="itemName" name="itemName" class="form-control" value="์ํA">
</div>
<div>
<label for="price">๊ฐ๊ฒฉ</label>
<input type="text" id="price" name="price" class="form-control" value="10000">
</div>
<div>
<label for="quantity">์๋</label>
<input type="text" id="quantity" name="quantity" class="form-control" value="10">
</div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-primary btn-lg" type="submit">์ ์ฅ
</button>
</div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='item.html'" type="button">์ทจ์</button> </div>
</div>
</form>
</div> <!-- /container -->
</body>
</html>
์ํ ์ ์ฒด ๋ชฉ๋ก ์กฐํ - ํ์๋ฆฌํ
์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ์ปจํธ๋กค๋ฌ์ ๋ทฐ ํ ํ๋ฆฟ์ ์ด์ฉํด ์๋น์ค๋ฅผ ๊ฐ๋ฐํด๋ณด๊ฒ ์ต๋๋ค.
์ฐธ๊ณ ๋ก ๋ทฐ ํ ํ๋ฆฟ์ ๋ถํธ์คํธ๋ฉ์ ์ด์ฉํ HTML ํผ์ ํ์๋ฆฌํ๋ฅผ ์ถ๊ฐํด ์ฌ์ฉํ ์์ ์ ๋๋ค.
์ฐ์ ์ปจํธ๋กค๋ฌ๋ถํฐ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
BasicItemController.java
@Slf4j
@Controller
@RequestMapping("/basic/items")
@RequiredArgsConstructor
public class BasicItemController {
private final ItemRepository itemRepository;
@GetMapping
public String items(Model model)
{
List<Item> items = itemRepository.findAll();
model.addAttribute("items",items);
return "basic/items";
}
/*
ํ
์คํธ์ฉ ๋ฐ์ดํฐ ์ถ๊ฐ
*/
@PostConstruct
public void init()
{
itemRepository.save(new Item("ItemA",10000,10));
itemRepository.save(new Item("ItemB",20000,20));
}
์ปจํธ๋กค๋ฌ ๋ก์ง
@GetMapping
public String items(Model model)
{
List<Item> items = itemRepository.findAll();
model.addAttribute("items",items);
return "basic/items";
}
localhost:8080/basic/items URL ํธ์ถ
โถ๏ธ itemRepository์์ ๋ชจ๋ ์ํ์ ์กฐํํ ๋ค์์ List์ ๋ด์ ํ ๋ชจ๋ธ์ ์ถ๊ฐํ๊ณ ๋ ๋ค
โถ๏ธ ๋ทฐ ํ ํ๋ฆฟ("/basic/items")์ ํธ์ถ
- @RequiredArgsConstructor : final์ด ๋ถ์ ๋ฉค๋ฒ๋ณ์๋ง ์ฌ์ฉํด์ ์์ฑ์๋ฅผ ์๋์ผ๋ก ๋ง๋ค์ด์ค๋๋ค.
- ์ค์ง์ ์ผ๋ก ์๋ ์ฝ๋๋ฅผ ์์ฑํ ๊ฒ๊ณผ ๊ฐ์ ํจ๊ณผ๋ฅผ ๋ณด์ ๋๋ค.
public BasicItemController(ItemRepository itemRepository) {
this.itemRepository = itemRepository;
์ด๋ ๊ฒ ์์ฑ์๊ฐ ๋ฑ 1๊ฐ๋ง ์์ผ๋ฉด ์คํ๋ง์ ํด๋น ์์ฑ์์ @Autowired๋ก ์์กด๊ด๊ณ๋ฅผ ์๋์ผ๋ก ์ฃผ์ ํด์ค๋๋ค.
๋ฐ๋ผ์ final ํค์๋๋ฅผ ๋นผ๋ฉด ์๋ฉ๋๋ค. ๊ทธ๋ฌ๋ฉด ItemRepository ์์กด๊ด๊ณ ์ฃผ์ ์ด ์๋จ.
ํ ์คํธ์ฉ ๋ฐ์ดํฐ ์ถ๊ฐ
ํ ์คํธ์ฉ ๋ฐ์ดํฐ๊ฐ ์์ผ๋ฉด ํ์ ๋ชฉ๋ก ๊ธฐ๋ฅ์ด ์ ์ ๋์ํ๋์ง ํ์ธํ๊ธฐ ์ด๋ ต์ต๋๋ค.
@PostConstruct : ํด๋น ๋น์ ์์กด๊ด๊ณ๊ฐ ๋ชจ๋ ์ฃผ์ ๋๊ณ ๋๋ฉด ์ด๊ธฐํ ์ฉ๋๋ก ํธ์ถ
์ฌ๊ธฐ์๋ ๊ฐ๋จํ๊ฒ ํ ์คํธ์ฉ ๋ฐ์ดํฐ๋ฅผ ๋ฃ๊ธฐ ์ํด ์ฌ์ฉํ์ต๋๋ค.
๋ทฐ ํ ํ๋ฆฟ - /basic/items
์ด์ resources/static/html ์ ์ ์ฅํด๋ ์ ์ HTML์ resources/templates/basic์ items.html์ ๋ฃ๊ณ thymeleaf ๋ทฐ ํ ํ๋ฆฟ์ ์ ์ฉ์์ผ๋ณด๊ฒ ์ต๋๋ค.
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link href="/Users/kdo6301/Desktop/item-service/src/main/resources/static/css/bootstrap.min.css"
th:href="@{/css/bootstrap.min.css}"
rel="stylesheet">
</head>
<body>
<div class="container" style="max-width: 600px">
<div class="py-5 text-center">
<h2>์ํ ๋ชฉ๋ก</h2>
</div>
<div class="row">
<div class="col">
<button class="btn btn-primary float-end"
onclick="location.href='addForm.html'"
th:onclick="|location.href='@{/basic/items/add}'|"
type="button">์ํ ๋ฑ๋ก
</button>
</div>
</div>
<div class="row">
<div class="col">
<button class="btn btn-primary float-start"
onclick="location.href='items.html'"
th:onclick="|location.href='@{/basic/items/clear}'|"
type="button">์ํ ์ด๊ธฐํ
</button>
</div>
</div>
<hr class="my-4">
<div>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>์ํ๋ช
</th>
<th>๊ฐ๊ฒฉ</th>
<th>์๋</th>
</tr>
</thead>
<tbody>
<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}">์ํ๋ช
</a>
</td>
<td th:text="${item.price}">10000</td>
<td th:text="${item.quantity}">10</td>
</tr>
</tbody>
</table>
</div>
</div> <!-- /container -->
</body>
</html>
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link href="/Users/kdo6301/Desktop/item-service/src/main/resources/static/css/bootstrap.min.css"
th:href="@{/css/bootstrap.min.css}"
rel="stylesheet">
</head>
Thymeleaf ์ฌ์ฉ ์ ์ธ
<html xmlns:th="http://www.thymeleaf.org">
์์ฑ ๋ณ๊ฒฝ - th:href
<link href="/Users/kdo6301/Desktop/item-service/src/main/resources/static/css/bootstrap.min.css"
th:href="@{/css/bootstrap.min.css}"
rel="stylesheet">
href="value"๋ฅผ th:href="value"๋ก ๋ณ๊ฒฝ
- ํ์๋ฆฌํ ๋ทฐํ ํ๋ฆฟ์ ๊ฑฐ์น๊ฒ ๋๋ฉด ์๋ ๊ฐ์ th:xxx ๊ฐ์ผ๋ก ๋ณ๊ฒฝํฉ๋๋ค. ๋ง์ฝ ๊ฐ์ด ์๋ค๋ฉด ์๋ก ์์ฑ.
- HTML์ ๊ทธ๋๋ก ๋ณผ ๋์๋ href ์์ฑ์ด ์ฌ์ฉ๋๊ณ ๋ทฐ ํ ํ๋ฆฟ์ ๊ฑฐ์น๊ฒ ๋๋ฉด th:href์ ๊ฐ์ด href ๊ฐ์ ๋์ฒดํ๋ฉด์ ๋์ ์ผ๋ก ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
- ๋๋ถ๋ถ์ HTML ์์ฑ์ th:xxx ๋ก ๋ณ๊ฒฝ ๊ฐ๋ฅ
ํ์๋ฆฌํ ํต์ฌ
- th:xxx๊ฐ ๋ถ์ ๋ถ๋ถ์ ์๋ฒ์ฌ์ด๋ ๋ ๋๋ง ๋ฐ ๊ธฐ์กด ๊ฒ ๋์ฒด.
- th๊ฐ ์์ผ๋ฉด ๊ธฐ์กด html์ ์์ฑ์ ๊ทธ๋๋ก ์ฌ์ฉ
- HTML์ ํ์ผ๋ก ์ง์ ์ด์์ ๋์๋ th:xxx๊ฐ ์์ด๋ ์น ๋ธ๋ผ์ฐ์ ๋ th: ์์ฑ์ ์์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ ๋ฌด์ํฉ๋๋ค.
- ๋ฐ๋ผ์ HTML์ ํ์ผ ๋ณด๊ธฐ๋ฅผ ์ ์งํ๋ฉด์ ํ ํ๋ฆฟ ๊ธฐ๋ฅ๋ ํ ์ ์์ต๋๋ค.
์์ฑ ๋ณ๊ฒฝ
1. ์ํ๋ฑ๋ก - th:onclick
<div class="row">
<div class="col">
<button class="btn btn-primary float-end"
onclick="location.href='addForm.html'"
th:onclick="|location.href='@{/basic/items/add}'|"
type="button">์ํ ๋ฑ๋ก
</button>
</div>
</div>
- onclick="location.href='addForm.html'" : static HTML์์ ์ํ๋ฑ๋ก ๋ฒํผ์ ํด๋ฆญํ์ ๋ ๋์
- th:onclick="|location.href='@{/basic/items/add}'|" : template HTML์์ ์ํ๋ฑ๋ก ๋ฒํผ์ ํด๋ฆญํ์ ๋ ๋์( ์ํ๋ฑ๋ก ๋ทฐ ํ ํ๋ฆฟ์ธ /basic/items/add ๋ก ์ด๋)
์ฌ๊ธฐ์ ์ฌ์ฉํ ๋ฆฌํฐ๋ด ๋์ฒด ๋ฌธ๋ฒ
- |~~~~| : ํ์๋ฆฌํ์์๋ ๋ฌธ์์ ํํ์ ๋ฑ์ ๋ถ๋ฆฌ๋์ด ์๊ธฐ ๋๋ฌธ์ ๋ํด์ ์ฌ์ฉํด์ผ ํฉ๋๋ค
- ์ด๋ฅผํ
๋ฉด ์ด๋ ๊ฒ
- <span th:text="'Welcome to our application, ' + ${user.name} + '!'"
- ํ์ง๋ง ๋ฆฌํฐ๋ด ๋์ฒด ๋ฌธ๋ฒ |~~~~|์ ์ฌ์ฉํ๋ฉด ๋ํ๊ธฐ ์์ด ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- <span th:text="|Welcome to our application, ${user.name}!|"
์ฌ๊ธฐ์ ์ฌ์ฉํ URL ๋งํฌ ํํ์
- @{~~~} : ํ์๋ฆฌํ๋ URL ๋งํฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ @{~~~~}๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ด๊ฑธ URL ๋งํฌ ํํ์์ด๋ผ๊ณ ํฉ๋๋ค.
2. ์ํ ์ด๊ธฐํ - th:onclick
<div class="row">
<div class="col">
<button class="btn btn-primary float-start"
onclick="location.href='items.html'"
th:onclick="|location.href='@{/basic/items/clear}'|"
type="button">์ํ ์ด๊ธฐํ
</button>
</div>
</div>
์ํ์ด๊ธฐํ ๋ฒํผ ํด๋ฆญ์ /basic/items/clear URL ํธ์ถ
โถ๏ธ @GetMapping("/basic/items/clear") ๋์
@GetMapping("/clear")
public String clearList()
{
itemRepository.clearStore();
return "basic/items";
}
itemRepository ์ด๊ธฐํ ํ ํ basic/items ๋ทฐ ํ ํ๋ฆฟ ํธ์ถ
3. ์ํ ๋ชฉ๋ก ์กฐํ - th:each(๋ฐ๋ณต ์ถ๋ ฅ)
<tbody>
<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}">์ํ๋ช
</a>
</td>
<td th:text="${item.price}">10000</td>
<td th:text="${item.quantity}">10</td>
</tr>
</tbody>
์ฌ๊ธฐ์ ์ฌ์ฉํ ๋ฐ๋ณต ์ถ๋ ฅ ๋ฌธ๋ฒ th:each
- <th th:each="item : ${items}">
- ๋ฐ๋ณต์ th:each๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋ชจ๋ธ์ ํฌํจ๋ items ์ปฌ๋ ์ ๋ฐ์ดํฐ๊ฐ item ๋ณ์์ ํ๋์ฉ ํฌํจ๋๊ณ , ๋ฐ๋ณต๋ฌธ ์์์ item ๋ณ์๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- ์ปฌ๋ ์ ์ ์ ๋งํผ <tr>..</tr>๊ฐ ํ์ ํ๊ทธ๋ฅผ ํฌํจํด์ ์์ฑ๋ฉ๋๋ค.
์ฌ๊ธฐ์ ์ฌ์ฉํ ๋งํฌ ํํ์ @{.../{value}(value=${param.value})
- th:href="@{/basic/items/{itemId}(itemId=${item.id})}"
- URL ๋งํฌ ํํ์์ ๊ฒฝ๋ก๋ณ์๋ฅผ ์ฌ์ฉํ์ฌ ์ง์ ํ ์ ์์ต๋๋ค.
- ๊ฒฝ๋ก๋ณ์ {itemId} ๋ฟ๋ง ์๋๋ผ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๋ ์์ฑํฉ๋๋ค.
- ํ์ ID๋ ์ํ๋ช ์ ํด๋ฆญํ๋ฉด /basic/items/{ItemId} ๋งํฌ๋ก ์ด๋ํฉ๋๋ค - ์ํ ์์ธ ํผ
- ex) th:href="@{/basic/items/{itemId}(itemId=${item.id}, query = 'test')}"
- ์์ฑ ๋งํฌ : http://localhost:8080/basic/items/1?query=test
์ฌ๊ธฐ์ ์ฌ์ฉํ ๋ด์ฉ ๋ณ๊ฒฝ ๋ฌธ๋ฒ th:text
- <td th:text="${item.price}">10000</td>
- ๋ด์ฉ์ ๊ฐ(10000)์ th:text์ ๊ฐ์ผ๋ก ๋ณ๊ฒฝํฉ๋๋ค.
์ฌ๊ธฐ์ ์ฌ์ฉํ ๋ณ์ ํํ์ ${..}
- <td th:text="${item.price}">10000</td>
- ๋ชจ๋ธ์ ํฌํจ๋ ๊ฐ์ด๋, ํ์๋ฆฌํ ๋ณ์๋ก ์ ์ธํ ๊ฐ์ ์กฐํํ ์ ์์ต๋๋ค.
- ํ๋กํผํฐ ์ ๊ทผ๋ฒ ์ฌ์ฉ (item.getPrice())
โ๏ธํ์๋ฆฌํ๋ ์์ HTML ํ์ผ์ ์น ๋ธ๋ผ์ฐ์ ์์ ์ด์ด๋ ๋ด์ฉ์ ํ์ธํ ์ ์๊ณ , ์๋ฒ๋ฅผ ํตํด ๋ทฐ ํ ํ๋ฆฟ์ ๊ฑฐ์ณ ๋์ ์ผ๋ก ๋ณ๊ฒฝ๋ ๊ฒฐ๊ณผ๋ ํ์ธํ ์ ์์ต๋๋ค. ์ด๋ ๊ฒ ์์ HTML ์ ๊ทธ๋๋ก ์ ์งํ๋ฉด์ ๋ทฐํ ํ๋ฆฟ๋ ์ฌ์ฉํ ์ ์๋ ํ์๋ฆฌํ์ ํน์ง์ ๋ค์ธ๋ด ํ ํ๋ฆฟ ์ด๋ผ๊ณ ํฉ๋๋ค.
์ํ ์์ธ ์กฐํ - ํ์๋ฆฌํ
BasicItemController์ ์ปจํธ๋กค๋ฌ ์ถ๊ฐ
@Slf4j
@Controller
@RequestMapping("/basic/items")
@RequiredArgsConstructor
public class BasicItemController {
private final ItemRepository itemRepository;
@GetMapping("/{itemId}")
public String item(@PathVariable Long itemId, Model model)
{
Item item = itemRepository.findById(itemId);
model.addAttribute("item",item);
return "basic/item";
}
...
}
์ปจํธ๋กค๋ฌ ๋ก์ง
โถ๏ธ /basic/items/{itemId} URL ํธ์ถ
โถ๏ธ @PathVariable ๊ฒฝ๋ก๋ณ์๋ก ๋์ด์จitemid๋ฅผ ํตํด Item์ ์ฐพ์ model์ item์ ๋ด์ต๋๋ค.
โถ๏ธ View ํ ํ๋ฆฟ basic/item ํธ์ถ
๋ทฐ ํ ํ๋ฆฟ
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link href="/Users/kdo6301/Desktop/item-service/src/main/resources/static/css/bootstrap.min.css"
th:href="@{/css/bootstrap.min.css}"
rel="stylesheet">
<style>
.container {
max-width: 560px;
} </style>
</head>
<body>
<div class="container">
<div class="py-5 text-center">
<h2>์ํ ์์ธ</h2>
</div>
<!--์ถ๊ฐ-->
<h2 th:if="${param.status}" th:text="'์ ์ฅ ์๋ฃ'"></h2>
<div>
<label for="itemId">์ํ ID</label>
<input type="text" id="itemId" name="itemId" class="form-control"
value="1"
th:value="${item.id}"
readonly>
</div>
<div>
<label for="itemName">์ํ๋ช
</label>
<input type="text" id="itemName" name="itemName" class="form-control"
th:value="${item.itemName}"
value="์ํA" readonly>
</div>
<div>
<label for="price">๊ฐ๊ฒฉ</label>
<input type="text" id="price" name="price" class="form-control"
th:value="${item.price}"
value="10000" readonly>
</div>
<div>
<label for="quantity">์๋</label>
<input type="text" id="quantity" name="quantity" class="form-control"
th:value="${item.quantity}"
value="10" readonly>
</div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-primary btn-lg"
th:onclick="|location.href='@{/basic/items/{itemId}/edit
(itemId=${item.getId()})}'|"
onclick="location.href='editForm.html'" type="button">์ํ ์์ </button>
</div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
th:onclick="|location.href='@{/basic/items}'|"
onclick="location.href='items.html'" type="button">๋ชฉ๋ก์ผ๋ก</button> </div>
</div>
</div> <!-- /container -->
</body>
</html>
์ฌ๊ธฐ์ ์ฌ์ฉํ ์์ฑ ๋ณ๊ฒฝ ๋ฌธ๋ฒ th:value
- th:value="${item.id}"
- ๋ชจ๋ธ์ ์๋ item์ ๋ณด๋ฅผ ํ๋ํ๊ณ ํ๋กํผํฐ ์ ๊ทผ๋ฒ์ผ๋ก ์ถ๋ ฅํฉ๋๋ค.
- value ์์ฑ์ th:value ์์ฑ์ผ๋ก ๋ณ๊ฒฝํฉ๋๋ค.
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-primary btn-lg"
th:onclick="|location.href='@{/basic/items/{itemId}/edit
(itemId=${item.getId()})}'|"
onclick="location.href='editForm.html'" type="button">์ํ ์์ </button>
</div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
th:onclick="|location.href='@{/basic/items}'|"
onclick="location.href='items.html'" type="button">๋ชฉ๋ก์ผ๋ก</button> </div>
</div>
- th:onclick = "|location.href='@{/basic/items/{itemId}/edit (itemId = ${item.id})}'|"
- ์ํ ์์ ๋ฒํผ ํด๋ฆญ ์ /basic/items/{itemid}/edit์ผ๋ก ์ด๋ํฉ๋๋ค.
- th:onclick="|location.href='@{/basic/items}'|"
- ๋ชฉ๋ก์ผ๋ก ๋ฒํผ ํด๋ฆญ์ /basic/items๋ก ์ด๋ํฉ๋๋ค.
์ํ ๋ฑ๋ก - ํ์๋ฆฌํ
BasicItemController์ ์ปจํธ๋กค๋ฌ ์ถ๊ฐ
@Slf4j
@Controller
@RequestMapping("/basic/items")
@RequiredArgsConstructor
public class BasicItemController {
@GetMapping("/add")
public String addForm()
{
return "basic/addForm";
}
// @PostMapping("/add")
// public String addItemV1(@RequestParam String itemName,
// @RequestParam int price,
// @RequestParam Integer quantity,
// Model model)
// {
// Item item = new Item();
// item.setItemName(itemName);
// item.setPrice(price);
// item.setQuantity(quantity);
//
// itemRepository.save(item);
//
// model.addAttribute("item",item);
//
// return "basic/item";
// }
//
// @PostMapping("/add")
// public String addItemV2(@ModelAttribute("item") Item item, Model model)
// {
// log.info("item = {}",item);
// log.info("itemName = {} , itemPrice = {} , itemQuantity = {} ",
// item.getItemName(),item.getPrice(),item.getQuantity());
// itemRepository.save(item);
//
// return "basic/item";
// }
/*
@ModelAttribute์ ์ด๋ฆ์ ์๋ตํ๋ฉด ๋ชจ๋ธ์ ์ ์ฅ๋ ๋ ํด๋์ค๋ช
์ ์๋ฌธ์๋ก ๋ณ๊ฒฝํด์ ์ฌ์ฉํฉ๋๋ค.
*/
// @PostMapping("/add")
// public String addItemV3(@ModelAttribute Item item, Model model)
// {
// log.info("item = {}",item);
// log.info("itemName = {} , itemPrice = {} , itemQuantity = {} ",
// item.getItemName(),item.getPrice(),item.getQuantity());
//
// itemRepository.save(item);
// return "basic/item";
// }
// /*
// @ModelAttribute ์์ฒด๋ ์๋ต ๊ฐ๋ฅ.
// ๊ฐ์ฒด์ ๊ฒฝ์ฐ ์๋์ผ๋ก ModelAttribute ์ ์ฉ
// ๋จ์ ํ์
์ ๊ฒฝ์ฐ @RequestParam ์ ์ฉ
// */
// @PostMapping("/add")
// public String addItemV4(Item item)
// {
// itemRepository.save(item);
// return "redirect:/basic/items/" + item.getId();
// }
@PostMapping("/add")
public String addItemV6(Item item, RedirectAttributes redirectAttributes)
{
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId",savedItem.getId());
redirectAttributes.addAttribute("status",true);
return "redirect:/basic/items/{itemId}";
}
}
์ํ ๋ฑ๋ก ํผ์ @GetMapping, @PostMapping ๋ ๊ฐ๋ก ๋ฐ์ต๋๋ค.
@GetMapping์ /basic/items/add URL๋ก ์ด๋ ์ addForm ๋ทฐ ํ ํ๋ฆฟ์ ํธ์ถํ๊ธฐ ์ํด ์ฌ์ฉํ๊ณ ,
@PostMapping์ /basic/items/add URL( ๋ทฐํ ํ๋ฆฟ - /basic/addForm )์์ HTML ํผ์ ์ด์ฉํด ์ํ ์ ๋ณด ์ ๋ ฅ ํ ๋ฑ๋กํ ๋์ ์ฌ์ฉํ๊ธฐ ์ํด ์ฌ์ฉ
๋ทฐ ํ ํ๋ฆฟ
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link href="/Users/kdo6301/Desktop/item-service/src/main/resources/static/css/bootstrap.min.css"
th:href="@{/css/bootstrap.min.css}"
rel="stylesheet">
<style>
.container {
max-width: 560px;
} </style>
</head>
<body>
<div class="container">
<div class="py-5 text-center">
<h2>์ํ ๋ฑ๋ก ํผ</h2>
</div>
<h4 class="mb-3">์ํ ์
๋ ฅ</h4>
<form action="item.html" th:action method="post">
<div>
<label for="itemName">์ํ๋ช
</label>
<input type="text" id="itemName" name="itemName" class="form-control" placeholder="์ด๋ฆ์ ์
๋ ฅํ์ธ์"> </div>
<div>
<label for="price">๊ฐ๊ฒฉ</label>
<input type="text" id="price" name="price" class="form-control" placeholder="๊ฐ๊ฒฉ์ ์
๋ ฅํ์ธ์">
</div>
<div>
<label for="quantity">์๋</label>
<input type="text" id="quantity" name="quantity" class="form-control" placeholder="์๋์ ์
๋ ฅํ์ธ์">
</div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-primary btn-lg" type="submit">์ํ
๋ฑ๋ก</button> </div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
th:onclick="|location.href='@{/basic/items}'|"
onclick="location.href='items.html'" type="button">์ทจ์</button> </div>
</div>
</form>
</div> <!-- /container -->
</body>
</html>
์ฌ๊ธฐ์ ์ฌ์ฉํ ์์ฑ ๋ณ๊ฒฝ th:action
- HTML form์์ action์ ๊ฐ์ด ์์ผ๋ฉด ํ์ฌ URL์ ๋ฐ์ดํฐ๋ฅผ ์ ์กํฉ๋๋ค
- ์ํ ๋ฑ๋ก ํผ์ URL๊ณผ ์ค์ ์ํ ๋ฑ๋ก์ ์ฒ๋ฆฌํ๋ URL์ ๋๊ฐ์ด ๋ง์ถ๊ณ HTTP๋ฉ์๋๋ก ๋ ๊ธฐ๋ฅ์ ๊ตฌ๋ถํ์ต๋๋ค.
- ์ํ ๋ฑ๋ก ํผ : GET - /basic/items/add
- ์ํ ๋ฑ๋ก ์ฒ๋ฆฌ : POST - /basic/items/add
- ์ด๋ ๊ฒ ํ๋ฉด ํ๋์ URL๋ก ๋ฑ๋ก ํผ๊ณผ, ๋ฑ๋ก ์ฒ๋ฆฌ๋ฅผ ๊น๋ํ๊ฒ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
์ทจ์ ๋ฒํผ ํด๋ฆญ ์ /basic/items ๋ก ์ด๋
BasicItemController์์ ์ํ ๋ฑ๋ก์ ์ฒ๋ฆฌํ๋ ์ปจํธ๋กค๋ฌ๋ฅผ ์์๋ด ์๋ค.
v1 - @RequestParam์ผ๋ก ์ฒ๋ฆฌ
@PostMapping("/add")
public String addItemV1(@RequestParam String itemName,
@RequestParam int price,
@RequestParam Integer quantity,
Model model)
{
Item item = new Item();
item.setItemName(itemName);
item.setPrice(price);
item.setQuantity(quantity);
itemRepository.save(item);
model.addAttribute("item",item);
return "basic/item";
}
@RequestParam์ผ๋ก addForm์์ post๋ก ๋ฐ์ itemName, price, quantity๋ฅผ ํตํด item๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ณ repository์ ๋ด๊ณ , ๋ชจ๋ธ์ ๋ด์ ๋ค basic/item ๋ทฐํ ํ๋ฆฟ์ ํธ์ถํฉ๋๋ค.
์ค์ : ์ฌ๊ธฐ์๋ ์ํ ์์ธ์์ ์ฌ์ฉํ item.html ๋ทฐํ ํ๋ฆฟ์ ๊ทธ๋๋ก ์ฌํ์ฉํฉ๋๋ค.
v2 - @ModelAttribute ์ฌ์ฉ
@PostMapping("/add")
public String addItemV2(@ModelAttribute("item") Item item, Model model)
{
log.info("item = {}",item);
log.info("itemName = {} , itemPrice = {} , itemQuantity = {} ",
item.getItemName(),item.getPrice(),item.getQuantity());
itemRepository.save(item);
model.addAttribute("item",item); // ์๋ต ๊ฐ๋ฅ
return "basic/item";
}
@ModelAttribute๋ฅผ ์ฌ์ฉํ๋ฉด @RequestParam์ผ๋ก ๋ณ์ ํ๋ํ๋๋ฅผ ๋ฐ์์ Item์ ์์ฑํ๋ ๊ณผ์ ์ ์๋ตํ ์ ์์ต๋๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก model์ ๊ฐ์ฒด๋ฅผ ๋ด์ ๋ทฐํ ํ๋ฆฟ ํธ์ถํ์ต๋๋ค.
์ฐธ๊ณ ๋ก
- model.addAttribute("item",item) ์๋ต์ด ๊ฐ๋ฅํฉ๋๋ค. @ModelAttribute๋ Model์ ModelAttribute๋ก ์ง์ ํ ๊ฐ์ฒด๋ฅผ ์๋์ผ๋ก ๋ฃ์ด์ฃผ๋ ๊ธฐ๋ฅ์ด ์์ต๋๋ค.
- @ModelAttribute("item") โถ๏ธ @ModelAttribute ๋ก ๋ณ๊ฒฝ๋ ๊ฐ๋ฅํฉ๋๋ค.
- @ModelAttribute ์์ ๋ฅผ ์๋ต๋ ๊ฐ๋ฅํ์ง๋ง ๊ถ์ฅํ์ง ์์ - ๋ช ์์ ์ด์ง ์์
- ๋ชจ๋ธ์ ๋ฐ์ดํฐ๋ฅผ ๋ด์ ๋๋ ์ด๋ฆ์ด ํ์ํ๋ฐ ์ด ์ด๋ฆ๊ณผ @ModelAttribute ์ ์ง์ ํ Item item ์ด ๊ฐ๊ธฐ ๋๋ฌธ์ ์ด๋ฐ ๊ฒฝ์ฐ์๋ ์๋ต์ด ๊ฐ๋ฅํฉ๋๋ค.
์ํ ์์ - ํ์๋ฆฌํ
์ํ ์์ ํผ ์ปจํธ๋กค๋ฌ
BasicItemController ์ ์ถ๊ฐ
@GetMapping("/{itemId}/edit")
public String editForm(@PathVariable Long itemId, Model model)
{
Item item = itemRepository.findById(itemId);
model.addAttribute("item",item);
return "basic/editForm";
}
@PostMapping("/{itemId}/edit")
public String edit(@PathVariable Long itemId, @ModelAttribute Item item)
{
itemRepository.update(itemId, item);
return "redirect:/basic/items/{itemId}";
}
1. editForm
/basic/items/{itemId}/edit URL ํธ์ถ ์ ๊ฒฝ๋ก๋ณ์๋ฅผ ํตํด itemId๋ฅผ ์ฐพ๊ณ ๋ชจ๋ธ์ ๋ด์ ํ
basic/editForm ๋ทฐ ํ ํ๋ฆฟ์ ํธ์ถํฉ๋๋ค.
2. edit
- /basic/items/{itemId}/edit URL (๋ทฐ ํ ํ๋ฆฟ์ basic/editForm) ์์ Post๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ผ ๋ ๋์ํฉ๋๋ค.
- ํผ์ ํตํด ์ ๋ ฅํ ์ ๋ณด๋ฅผ Item์ ๋ด๊ณ ๋ง์ฐฌ๊ฐ์ง๋ก ๊ฒฝ๋ก๋ณ์ itemId๋ฅผ ํตํด repository์ ํด๋น id์ item์ ์ ๋ณด๋ฅผ ๋ชจ๋ธ์ ๋ด๊ธด Item์ ์ ๋ณด๋ก ์ ๋ฐ์ดํธํฉ๋๋ค.
- ๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง์ ๋ทฐ ํ ํ๋ฆฟ์ ํธ์ถํ๋ ๋์ ์ ์ํ ์์ธํ๋ฉด์ผ๋ก ์ด๋ํ๋๋ก ๋ฆฌ๋ค์ด๋ ํธ๋ฅผ ํธ์ถํ์ต๋๋ค.
- HTML ์ ์ก์ PUT, PATCH๋ฅผ ์ง์ํ์ง ์๊ณ , POST, GET๋ง ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ ์์ ์์ ์์ POST ๋ฐฉ์์ ์ฌ์ฉํ์ต๋๋ค.
์คํ๋ง์ redirect:/...์ผ๋ก ํธ๋ฆฌํ๊ฒ ๋ฆฌ๋ค์ด๋ ํธ ์ง์ํฉ๋๋ค.
- ์ปจํธ๋กค๋ฌ์ ๋งคํ๋ @PathVariable์ ๊ฐ์ redirect์๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๋ฆฌ๋ค์ด๋ ์ ์ฐธ๊ณ :
basic/editForm ๋ทฐ ํ ํ๋ฆฟ
<!DOCTYPE HTML>
<html 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"
>
<style>
.container {
max-width: 560px;
}
</style>
</head>
<body>
<div class="container">
<div class="py-5 text-center"> <h2>์ํ ์์ ํผ</h2>
</div>
<form action="item.html" th:action method="post">
<div>
<label for="id">์ํ ID</label>
<input type="text" id="id" name="id" class="form-control"
value="1"
th:value="${item.id}"
readonly>
</div>
<div>
<label for="itemName">์ํ๋ช
</label>
<input type="text" id="itemName" name="itemName" class="form-control"
value="์ํA"
th:value="${item.itemName}"
>
</div>
<div>
<label for="price">๊ฐ๊ฒฉ</label>
<input type="text" id="price" name="price" class="form-control"
value="10000"
th:value="${item.price}">
</div>
<div>
<label for="quantity">์๋</label>
<input type="text" id="quantity" name="quantity" class="form-control"
value="10"
th:value="${item.quantity}">
</div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-primary btn-lg" type="submit">์ ์ฅ
</button>
</div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='item.html'"
th:onclick="|location.href='@{/basic/items/{itemId}
(itemId=${item.id})}'|"
type="button">์ทจ์</button> </div>
</div>
</form>
</div> <!-- /container -->
</body>
</html>
๋ฑ๋ก ํผ๊ณผ ์ ์ฌํด์ ํน๋ณํ ์ง๊ณ ๋์ด๊ฐ ๋ถ๋ถ์ด ์์ต๋๋ค.
PRG (Post/Redirect/Get)
์ฌ์ค ์ง๊ธ๊น์ง ์งํํ ์ํ ๋ฑ๋ก ์ฒ๋ฆฌ ์ปจํธ๋กค๋ฌ๋ ์ฌ๊ฐํ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. ์ํ ๋ฑ๋ก์ ์๋ฃํ๊ณ ์น ๋ธ๋ผ์ฐ์ ์ ์๋ก๊ณ ์นจ ๋ฒํผ์ ๋๋ฌ๋ณดใ ๋ณด๋ฉด ์ํ์ด ๊ณ์ํด์ ์ค๋ณต ๋ฑ๋ก๋๋๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
์น ๋ธ๋ผ์ฐ์ ์ ์๋ก๊ณ ์นจ์ ๋ง์ง๋ง์ ์๋ฒ์ ์ ์กํ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ์ ์กํฉ๋๋ค.
์ํ ๋ฑ๋ก ํผ์์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ ค๊ฐ๊ณ ์ ์ฅ์ ์ ํํ๋ฉด POST /add + ์ํ ๋ฐ์ดํฐ๋ฅผ ์๋ฒ๋ก ์ ์กํฉ๋๋ค.
์ด ์ํ์์ ์๋ก๊ณ ์นจ์ ๋ ์ ํํ๋ฉด ๋ง์ง๋ง์ ์ ์กํ POST /add + ์ํ ๋ฐ์ดํฐ๋ฅด ๋ค์ ์ ์กํ๊ฒ ๋ฉ๋๋ค.
๊ทธ๋์ ๋ด์ฉ์ ๊ฐ๊ณ ID๋ง ๋ค๋ฅธ ์ํ ๋ฐ์ดํฐ๊ฐ ๊ณ์ ์์ด๊ฒ ๋ฉ๋๋ค.
์ด ๋ฌธ์ ๋ฅผ ์ด๋ป๊ฒ ํด๊ฒฐํ ์ ์์๊น์?
์ํ ์ ์ฅ ํ์ ๋ทฐ ํ ํ๋ฆฟ์ผ๋ก ์ด๋ํ๋ ๊ฒ์ด ์๋๋ผ, ์ํ ์์ธํ๋ฉด์ผ๋ก ๋ฆฌ๋ค์ด๋ ํธ๋ฅผ ํธ์ถํด์ฃผ๋ฉด ๋ฉ๋๋ค.
@PostMapping("/add")
public String addItemV4(Item item)
{
itemRepository.save(item);
return "redirect:/basic/items/" + item.getId();
}
์ด๋ฐ ๋ฌธ์ ํด๊ฒฐ ๋ฐฉ์์ PRG( POST -> Redirect -> GET) ๋ฐฉ์ ์ด๋ผ๊ณ ํฉ๋๋ค.
ํ์ง๋ง ์ฌ๊ธฐ์ item.getId()๋ฅผ ํตํด URL์ ๋ณ์๋ฅผ ๋ํด์ ์ฌ์ฉํ๋ ๊ฒ์ URL ์ธ์ฝ๋ฉ์ด ์๋๊ธฐ ๋๋ฌธ์ ์ํํฉ๋๋ค.
๋ค์์ ์ค๋ช ํ๋ RedirectAttributes๋ฅผ ์ฌ์ฉํฉ์๋ค.
RedirectAttributes
์ํ ์ ์ฅ ํ ์ํ ์์ธํ๋ฉด์ผ๋ก ๋ฆฌ๋ค์ด๋ ํธํ ๊ฒ ๊น์ง๋ ์ข์์ต๋๋ค. ํ์ง๋ง ํด๋ผ์ด์ธํธ ์ ์ฅ์์ ์ ์ฅ์ด ์ ๋ ๊ฒ์ธ์ง ์ ๋ ๊ฒ์ธ์ง ํ์ ์ด ๋ค์ง ์์ ์ ์๊ธฐ ๋๋ฌธ์ ์ ์ฅ์ด ์ ๋์์ผ๋ฉด ์ํ ์์ธ ํ๋ฉด์ "์ ์ฅ๋์์ต๋๋ค" ๋ผ๋ ๋ฉ์์ง๋ฅผ ๋ณด์ฌ์ฃผ๋ ์๊ตฌ์ฌํญ๊น์ง ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค.
BasicItemController ์ ์ถ๊ฐ
@PostMapping("/add")
public String addItemV6(Item item, RedirectAttributes redirectAttributes)
{
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId",savedItem.getId());
redirectAttributes.addAttribute("status",true);
return "redirect:/basic/items/{itemId}";
}
๋ฆฌ๋ค์ด๋ ํธ ํ ๊ฒฝ์ฐ ๊ฐ๋จํ status=true๋ฅผ ์ถ๊ฐํด๋ด ์๋ค. ๊ทธ๋ฆฌ๊ณ ๋ทฐ ํ ํ๋ฆฟ์์ ์ด ๊ฐ์ด ์์ผ๋ฉด "์ ์ฅ๋์์ต๋๋ค." ๋ผ๋ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํด๋ณด๊ฒ ์ต๋๋ค. ์คํํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋ฆฌ๋ค์ด๋ ํธ ๊ฒฐ๊ณผ๊ฐ ๋์ต๋๋ค.
localhost:8080/basic/items/3?status=true
RedirectAttributes๋ฅผ ์ฌ์ฉํ๋ฉด
URL ์ธ์ฝ๋ฉ๋ ํด์ฃผ๊ณ ,
pathVariable๊ณผ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๊น์ง ์ฒ๋ฆฌํด์ค๋๋ค.
- redirect:/basic/items/{itemid}
- {itemId} ๋ redirectAttribute์ ์ถ๊ฐํ itemId, ๋๋จธ์ง๋ ์ฟผ๋ฆฌํ๋ผ๋ฏธํฐ๋ก ์ฒ๋ฆฌ ?status=true
/basic/items/{itemId}๋ก ๋ฆฌ๋ค์ด๋ ํธ์ /basic/items/{itemId} URL ํธ์ถํ๋ฉด
@GetMapping("/{itemId}")
public String item(@PathVariable Long itemId, Model model)
{
Item item = itemRepository.findById(itemId);
model.addAttribute("item",item);
return "basic/item";
}
์์ด๋๋ฅผ ์ฐพ๊ณ ๋ชจ๋ธ์ ๋ด์ ํ basic/item ๋ทฐํ ํ๋ฆฟ์ ํธ์ถํฉ๋๋ค.
basic/item ๋ทฐํ ํ๋ฆฟ์ ์ํ์์ธ ์๋์ ๋ฉ์์ง๋ฅผ ์ถ๊ฐํ๊ฒ ์ต๋๋ค.
<!--์ถ๊ฐ-->
<h2 th:if="${param.status}" th:text="'์ ์ฅ ์๋ฃ'"></h2>
- th:if๋ ํด๋น ์กฐ๊ฑด์ด ์ฐธ์ด๋ฉด ์คํํ๋ ํ์๋ฆฌํ ์กฐ๊ฑด๋ฌธ์ ๋๋ค.
- ${param.status}๋ ํ์๋ฆฌํ์์ ์ฟผ๋ฆฌํ๋ผ๋ฏธํฐ๋ฅผ ํธ๋ฆฌํ๊ฒ ์กฐํํ ์ ์๋ ๊ธฐ๋ฅ์ผ๋ก ์๋๋ ์ปจํธ๋กค๋ฌ์์ ๋ชจ๋ธ์ ์ง์ ๋ด๊ณ ๊ฐ์ ๊บผ๋ด์ผ ํ์ง๋ง ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๋ ์์ฃผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ํ์๋ฆฌํ์์ ์ง์ ์ง์ํฉ๋๋ค.
'๐ Backend > Thymeleaf Template' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Thymeleaf - ์คํ๋ง ํตํฉ ๊ธฐ๋ฅ (0) | 2023.03.19 |
---|---|
Thymeleaf ๊ธฐ๋ฅ - ํ ํ๋ฆฟ ์กฐ๊ฐ, ํ ํ๋ฆฟ ๋ ์ด์์ (0) | 2023.03.17 |
Thymeleaf ๊ธฐ๋ฅ - ๊ธฐ๋ณธ (0) | 2023.03.17 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
Study Repository
rlaehddnd0422