Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
323 additions
and
0 deletions.
There are no files selected for viewing
97 changes: 97 additions & 0 deletions
97
validation/src/main/java/hello/itemservice/web/validation/ValidationItemControllerV2.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package hello.itemservice.web.validation; | ||
|
||
import hello.itemservice.domain.item.Item; | ||
import hello.itemservice.domain.item.ItemRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.stereotype.Controller; | ||
import org.springframework.ui.Model; | ||
import org.springframework.util.StringUtils; | ||
import org.springframework.web.bind.annotation.*; | ||
import org.springframework.web.servlet.mvc.support.RedirectAttributes; | ||
|
||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
@Slf4j | ||
@Controller | ||
@RequestMapping("/validation/v2/items") | ||
@RequiredArgsConstructor | ||
public class ValidationItemControllerV2 { | ||
|
||
private final ItemRepository itemRepository; | ||
|
||
@GetMapping | ||
public String items(Model model) { | ||
List<Item> items = itemRepository.findAll(); | ||
model.addAttribute("items", items); | ||
return "validation/v2/items"; | ||
} | ||
|
||
@GetMapping("/{itemId}") | ||
public String item(@PathVariable long itemId, Model model) { | ||
Item item = itemRepository.findById(itemId); | ||
model.addAttribute("item", item); | ||
return "validation/v2/item"; | ||
} | ||
|
||
@GetMapping("/add") | ||
public String addForm(Model model) { | ||
model.addAttribute("item", new Item()); | ||
return "validation/v2/addForm"; | ||
} | ||
|
||
@PostMapping("/add") | ||
public String addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes, Model model) { | ||
|
||
//검증 오류 결과를 보관 | ||
Map<String, String> errors = new HashMap<>(); | ||
|
||
//검증 로직 | ||
if (!StringUtils.hasText(item.getItemName())) { | ||
errors.put("itemName", "상품 이름은 필수입니다."); | ||
} | ||
if (item.getPrice() == null || item.getPrice() < 1000 || item.getPrice() > 1000000) { | ||
errors.put("price", "가격은 1,000 ~ 1,000,000 까지 허용합니다."); | ||
} | ||
if (item.getQuantity() == null || item.getQuantity() >= 9999) { | ||
errors.put("quantity", "수량은 최대 9,999 까지 허용합니다."); | ||
} | ||
|
||
//특정 필드가 아닌 복합 룰 검증 | ||
if (item.getPrice() != null && item.getQuantity() != null) { | ||
int resultPrice = item.getPrice() * item.getQuantity(); | ||
if (resultPrice < 10000) { | ||
errors.put("globalError", "가격 * 수량의 합은 10,000원 이상이어야 합니다. 현재 값 = " + resultPrice); | ||
} | ||
} | ||
|
||
//검증에 실패하면 다시 입력 폼으로 | ||
if (!errors.isEmpty()) { | ||
log.info("errors = {} ", errors); | ||
model.addAttribute("errors", errors); | ||
return "validation/v2/addForm"; | ||
} | ||
//성공 로직 | ||
Item savedItem = itemRepository.save(item); | ||
redirectAttributes.addAttribute("itemId", savedItem.getId()); | ||
redirectAttributes.addAttribute("status", true); | ||
return "redirect:/validation/v2/items/{itemId}"; | ||
} | ||
|
||
@GetMapping("/{itemId}/edit") | ||
public String editForm(@PathVariable Long itemId, Model model) { | ||
Item item = itemRepository.findById(itemId); | ||
model.addAttribute("item", item); | ||
return "validation/v2/editForm"; | ||
} | ||
|
||
@PostMapping("/{itemId}/edit") | ||
public String edit(@PathVariable Long itemId, @ModelAttribute Item item) { | ||
itemRepository.update(itemId, item); | ||
return "redirect:/validation/v2/items/{itemId}"; | ||
} | ||
|
||
} | ||
|
60 changes: 60 additions & 0 deletions
60
validation/src/main/resources/templates/validation/v2/addForm.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<!DOCTYPE HTML> | ||
<html xmlns:th="http://www.thymeleaf.org"> | ||
<head> | ||
<meta charset="utf-8"> | ||
<link th:href="@{/css/bootstrap.min.css}" | ||
href="../css/bootstrap.min.css" rel="stylesheet"> | ||
<style> | ||
.container { | ||
max-width: 560px; | ||
} | ||
.field-error { | ||
border-color: #dc3545; | ||
color: #dc3545; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<div class="container"> | ||
<div class="py-5 text-center"> | ||
<h2 th:text="#{page.addItem}">상품 등록</h2> | ||
</div> | ||
<form action="item.html" th:action th:object="${item}" method="post"> | ||
<div th:if="${errors?.containsKey('globalError')}"> | ||
<p class="field-error" th:text="${errors['globalError']}">전체 오류 | ||
메시지</p> | ||
</div> | ||
<div> | ||
<label for="itemName" th:text="#{label.item.itemName}">상품명</label> | ||
<input type="text" id="itemName" th:field="*{itemName}" | ||
th:class="${errors?.containsKey('itemName')} ? 'form-control field-error' : 'form-control'" class="form-control" placeholder="이름을 입력하세요"> | ||
<div class="field-error" th:if="${errors?.containsKey('itemName')}" th:text="${errors['itemName']}">상품명 오류</div> | ||
</div> | ||
<div> | ||
<label for="price" th:text="#{label.item.price}">가격</label> | ||
<input type="text" id="price" th:field="*{price}" | ||
th:class="${errors?.containsKey('price')} ? 'form-control field-error' : 'form-control'" class="form-control" placeholder="가격을 입력하세요"> | ||
<div class="field-error" th:if="${errors?.containsKey('price')}" th:text="${errors['price']}">가격 오류</div> | ||
</div> | ||
<div> | ||
<label for="quantity" th:text="#{label.item.quantity}">수량</label> | ||
<input type="text" id="quantity" th:field="*{quantity}" | ||
th:class="${errors?.containsKey('quantity')} ? 'form-control field-error' : 'form-control'" class="form-control" placeholder="수량을 입력하세요"> | ||
<div class="field-error" th:if="${errors?.containsKey('quantity')}" th:text="${errors['quantity']}">수량 오류</div> | ||
</div> | ||
<hr class="my-4"> | ||
<div class="row"> | ||
<div class="col"> | ||
<button class="w-100 btn btn-primary btn-lg" type="submit" th:text="#{button.save}">저장</button> | ||
</div> | ||
<div class="col"> | ||
<button class="w-100 btn btn-secondary btn-lg" | ||
onclick="location.href='items.html'" | ||
th:onclick="|location.href='@{/validation/v2/items}'|" | ||
type="button" th:text="#{button.cancel}">취소</button> | ||
</div> | ||
</div> | ||
</form> | ||
</div> <!-- /container --> | ||
</body> | ||
</html> |
57 changes: 57 additions & 0 deletions
57
validation/src/main/resources/templates/validation/v2/editForm.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
<!DOCTYPE HTML> | ||
<html xmlns:th="http://www.thymeleaf.org"> | ||
<head> | ||
<meta charset="utf-8"> | ||
<link th:href="@{/css/bootstrap.min.css}" | ||
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 th:text="#{page.updateItem}">상품 수정</h2> | ||
</div> | ||
|
||
<form action="item.html" th:action th:object="${item}" method="post"> | ||
<div> | ||
<label for="id" th:text="#{label.item.id}">상품 ID</label> | ||
<input type="text" id="id" th:field="*{id}" class="form-control" readonly> | ||
</div> | ||
<div> | ||
<label for="itemName" th:text="#{label.item.itemName}">상품명</label> | ||
<input type="text" id="itemName" th:field="*{itemName}" class="form-control"> | ||
</div> | ||
<div> | ||
<label for="price" th:text="#{label.item.price}">가격</label> | ||
<input type="text" id="price" th:field="*{price}" class="form-control"> | ||
</div> | ||
<div> | ||
<label for="quantity" th:text="#{label.item.quantity}">수량</label> | ||
<input type="text" id="quantity" th:field="*{quantity}" class="form-control"> | ||
</div> | ||
|
||
<hr class="my-4"> | ||
|
||
<div class="row"> | ||
<div class="col"> | ||
<button class="w-100 btn btn-primary btn-lg" type="submit" th:text="#{button.save}">저장</button> | ||
</div> | ||
<div class="col"> | ||
<button class="w-100 btn btn-secondary btn-lg" | ||
onclick="location.href='item.html'" | ||
th:onclick="|location.href='@{/validation/v2/items/{itemId}(itemId=${item.id})}'|" | ||
type="button" th:text="#{button.cancel}">취소</button> | ||
</div> | ||
</div> | ||
|
||
</form> | ||
|
||
</div> <!-- /container --> | ||
</body> | ||
</html> |
60 changes: 60 additions & 0 deletions
60
validation/src/main/resources/templates/validation/v2/item.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<!DOCTYPE HTML> | ||
<html xmlns:th="http://www.thymeleaf.org"> | ||
<head> | ||
<meta charset="utf-8"> | ||
<link th:href="@{/css/bootstrap.min.css}" | ||
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 th:text="#{page.item}">상품 상세</h2> | ||
</div> | ||
|
||
<!-- 추가 --> | ||
<h2 th:if="${param.status}" th:text="'저장 완료'"></h2> | ||
|
||
<div> | ||
<label for="itemId" th:text="#{label.item.id}">상품 ID</label> | ||
<input type="text" id="itemId" name="itemId" class="form-control" value="1" th:value="${item.id}" readonly> | ||
</div> | ||
<div> | ||
<label for="itemName" th:text="#{label.item.itemName}">상품명</label> | ||
<input type="text" id="itemName" name="itemName" class="form-control" value="상품A" th:value="${item.itemName}" readonly> | ||
</div> | ||
<div> | ||
<label for="price" th:text="#{label.item.price}">가격</label> | ||
<input type="text" id="price" name="price" class="form-control" value="10000" th:value="${item.price}" readonly> | ||
</div> | ||
<div> | ||
<label for="quantity" th:text="#{label.item.quantity}">수량</label> | ||
<input type="text" id="quantity" name="quantity" class="form-control" value="10" th:value="${item.quantity}" 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'" | ||
th:onclick="|location.href='@{/validation/v2/items/{itemId}/edit(itemId=${item.id})}'|" | ||
type="button" th:text="#{page.updateItem}">상품 수정</button> | ||
</div> | ||
<div class="col"> | ||
<button class="w-100 btn btn-secondary btn-lg" | ||
onclick="location.href='items.html'" | ||
th:onclick="|location.href='@{/validation/v2/items}'|" | ||
type="button" th:text="#{button.cancel}">목록으로</button> | ||
</div> | ||
</div> | ||
|
||
</div> <!-- /container --> | ||
</body> | ||
</html> |
49 changes: 49 additions & 0 deletions
49
validation/src/main/resources/templates/validation/v2/items.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<!DOCTYPE HTML> | ||
<html xmlns:th="http://www.thymeleaf.org"> | ||
<head> | ||
<meta charset="utf-8"> | ||
<link th:href="@{/css/bootstrap.min.css}" | ||
href="../css/bootstrap.min.css" rel="stylesheet"> | ||
</head> | ||
<body> | ||
|
||
<div class="container" style="max-width: 600px"> | ||
<div class="py-5 text-center"> | ||
<h2 th:text="#{page.items}">상품 목록</h2> | ||
</div> | ||
|
||
<div class="row"> | ||
<div class="col"> | ||
<button class="btn btn-primary float-end" | ||
onclick="location.href='addForm.html'" | ||
th:onclick="|location.href='@{/validation/v2/items/add}'|" | ||
type="button" th:text="#{page.addItem}">상품 등록</button> | ||
</div> | ||
</div> | ||
|
||
<hr class="my-4"> | ||
<div> | ||
<table class="table"> | ||
<thead> | ||
<tr> | ||
<th th:text="#{label.item.id}">ID</th> | ||
<th th:text="#{label.item.itemName}">상품명</th> | ||
<th th:text="#{label.item.price}">가격</th> | ||
<th th:text="#{label.item.quantity}">수량</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr th:each="item : ${items}"> | ||
<td><a href="item.html" th:href="@{/validation/v2/items/{itemId}(itemId=${item.id})}" th:text="${item.id}">회원id</a></td> | ||
<td><a href="item.html" th:href="@{|/validation/v2/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> |