코드 그라데이션
통합을 위한 작업 - 프로젝트 구조 본문
재구조화 실시
클래스
Item.java
@Data
@NoArgsConstructor
public class Item {
private Long id;
private String itemName;
private Integer price;
private Integer quantity;
public Item(String itemName, Integer price, Integer quantity) {
this.itemName = itemName;
this.price = price;
this.quantity = quantity;
}
}
ItemRepository.java
@Repository // 인터페이스가 아니므로 붙인 듯. 안에 @Component 있음
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 item) {
Item findItem = findById(itemId);
findItem.setItemName(item.getItemName());
findItem.setPrice(item.getPrice());
findItem.setQuantity(item.getQuantity());
}
public void clearStore() { // 테스트용 메서드
store.clear();
}
}
FormItemController.java
@Controller
@RequestMapping("/form/items")
@RequiredArgsConstructor
public class FormItemController {
private final ItemRepository itemRepository;
@GetMapping
public String items(Model model) {
// 상품 목록을 조회
List<Item> items = itemRepository.findAll();
// 조회한 상품 목록을 모델에 추가
model.addAttribute("items", items);
// "form/items" 뷰 템플릿을 호출하여 상품 목록을 화면에 표시
return "form/items";
}
@GetMapping("/{itemId}")
public String item(@PathVariable long itemId, Model model) {
// 경로 변수로부터 받은 itemId를 사용하여 상품을 조회.
Item item = itemRepository.findById(itemId);
// 조회한 상품을 모델에 추가. 이후 뷰 템플릿에서 사용할 수 있다.
model.addAttribute("item", item);
// "form/item" 뷰 템플릿을 호출하여 상품 상세 정보를 화면에 표시.
return "form/item";
}
@GetMapping("/add")
public String addForm() {
return "form/addForm";
}
/**
* 상품을 추가하는 메서드.
*
* @param item 추가할 상품 정보를 나타내는 객체
* @param redirectAttributes Spring MVC 리다이렉트 시 데이터를 전달하기 위한 객체
* @return 상품 추가 후 해당 상품 상세 정보 페이지로 리다이렉트
*/
@PostMapping("/add")
public String addItem(Item item, RedirectAttributes redirectAttributes) {
// 상품 정보를 데이터베이스에 저장하고 저장된 상품 정보를 반환.
Item savedItem = itemRepository.save(item);
// 리다이렉트 시 URL에 파라미터를 추가하기 위해 RedirectAttributes를 사용.
// "itemId" 파라미터에 저장된 상품의 ID를 추가.
redirectAttributes.addAttribute("itemId", savedItem.getId());
// "status" 파라미터를 추가하고 값을 true로 설정. (예: 성공적으로 상품을 추가한 상태를 나타냄)
redirectAttributes.addAttribute("status", true);
// 상품 추가 후 해당 상품의 상세 정보 페이지로 리다이렉트.
return "redirect:/form/items/{itemId}";
}
/**
* 지정된 ID를 가진 아이템의 수정 폼을 표시한다.
*
* @param itemId 수정할 아이템의 ID
* @param model 뷰에 데이터를 담기 위한 모델
* @return 수정 폼 뷰의 이름
*/
@GetMapping("/{itemId}/edit")
public String editForm(@PathVariable Long itemId, Model model) {
// 아이템 레포지토리에서 아이템을 ID를 사용하여 검색.
Item item = itemRepository.findById(itemId);
// 검색한 아이템을 모델에 추가하여 뷰에서 표시할 수 있도록 함.
model.addAttribute("item", item);
// 수정 폼 뷰의 이름을 반환.
return "form/editForm";
}
/**
* 상품을 수정하는 메서드.
*
* @param itemId 수정할 상품의 ID
* @param item 수정된 상품 정보를 나타내는 객체
* @return 상품 수정 후 해당 상품 상세 정보 페이지로 리다이렉트
*/
@PostMapping("/{itemId}/edit")
public String edit(@PathVariable Long itemId, @ModelAttribute Item item) {
// 상품 수정 메서드를 호출하여 상품 정보를 업데이트 (itemRepository.update 메서드 사용)
itemRepository.update(itemId, item);
// 수정된 상품 정보를 포함하는 해당 상품의 상세 정보 페이지로 리다이렉트.
return "redirect:/form/items/{itemId}";
}
}
TestDataInit - 테스트용 데이터 클래스 분리
@Component
@RequiredArgsConstructor
public class TestDataInit {
private final ItemRepository itemRepository;
/**
* 테스트용 데이터 추가
*/
@PostConstruct
public void init() {
itemRepository.save(new Item("itemA", 10000, 10));
itemRepository.save(new Item("itemB", 20000, 20));
}
}
타임리프
addForm.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<!-- 부트스트랩 CSS 파일을 불러옵니다. -->
<link th:href="@{/css/bootstrap.min.css}" href="../css/bootstrap.min.css" rel="stylesheet">
<style>
/* 스타일 정의: .container 클래스에 최대 너비를 설정합니다. */
.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="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">
<!-- 취소 버튼: 버튼 클릭 시 'items.html' 페이지로 이동합니다. -->
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='items.html'"
th:onclick="|location.href='@{/form/items}'|"
type="button">취소</button>
</div>
</div>
</form>
</div> <!-- /container -->
</body>
</html>
editForm.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<!-- 부트스트랩 CSS 파일을 불러옵니다. -->
<link th:href="@{/css/bootstrap.min.css}" href="../css/bootstrap.min.css" rel="stylesheet">
<style>
/* 스타일 정의: .container 클래스에 최대 너비를 설정합니다. */
.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>
<!-- 상품 ID 입력 필드와 레이블: 수정 불가능한 필드로 item.id 값을 출력합니다. -->
<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='@{/form/items/{itemId}(itemId=${item.id})}'|"
type="button">취소</button>
</div>
</div>
</form>
</div> <!-- /container -->
</body>
</html>
item.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<!-- 부트스트랩 CSS 파일을 불러옵니다. -->
<link th:href="@{/css/bootstrap.min.css}" href="../css/bootstrap.min.css" rel="stylesheet">
<style>
/* 스타일 정의: .container 클래스에 최대 너비를 설정합니다. */
.container {
max-width: 560px;
}
</style>
</head>
<body>
<div class="container">
<div class="py-5 text-center">
<!-- 페이지 제목을 출력합니다. -->
<h2>상품 상세</h2>
</div>
<!-- 추가: 'status' 파라미터가 있으면 '저장 완료' 메시지를 표시합니다. -->
<h2 th:if="${param.status}" th:text="'저장 완료'"></h2>
<div>
<!-- 상품 ID 출력 필드와 레이블: 수정 불가능한 필드로 item.id 값을 출력합니다. -->
<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" value="상품A" th:value="${item.itemName}" readonly>
</div>
<div>
<!-- 가격 출력 필드와 레이블: 수정 불가능한 필드로 현재 가격을 출력합니다. -->
<label for="price">가격</label>
<input type="text" id="price" name="price" class="form-control" value="10000" th:value="${item.price}" readonly>
</div>
<div>
<!-- 수량 출력 필드와 레이블: 수정 불가능한 필드로 현재 수량을 출력합니다. -->
<label for="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='@{/form/items/{itemId}/edit(itemId=${item.id})}'|"
type="button">상품 수정</button>
</div>
<div class="col">
<!-- 목록으로 버튼: 목록 페이지로 이동합니다. -->
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='items.html'"
th:onclick="|location.href='@{/form/items}'|"
type="button">목록으로</button>
</div>
</div>
</div> <!-- /container -->
</body>
</html>
items.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<!-- 부트스트랩 CSS 파일을 불러옵니다. -->
<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>상품 목록</h2>
</div>
<div class="row">
<div class="col">
<!-- 상품 등록 버튼: 상품 등록 폼으로 이동합니다. -->
<button class="btn btn-primary float-end"
onclick="location.href='addForm.html'"
th:onclick="|location.href='@{/form/items/add}'|"
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}">
<!-- 상품 ID를 표시하고 해당 상품 상세 페이지로 링크합니다. -->
<td><a href="item.html" th:href="@{/form/items/{itemId}(itemId=${item.id})}" th:text="${item.id}">회원id</a></td>
<!-- 상품명을 표시하고 해당 상품 상세 페이지로 링크합니다. -->
<td><a href="item.html" th:href="@{|/form/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>
728x90
'Spring > Thymeleaf' 카테고리의 다른 글
요구사항 (또) 추가 (0) | 2023.11.30 |
---|---|
Thymeleaf 통합 (1) 입력 폼 처리 (1) | 2023.11.29 |
타임리프 스프링 통합 (1) | 2023.11.26 |
Thymeleaf (15) 템플릿 레이아웃 02 (0) | 2023.11.24 |
Thymeleaf (14) 템플릿 레이아웃 01 (0) | 2023.11.21 |
Comments