스프링부트 너 뭐 돼?🤷‍♀️(4) - 상품 관리 페이지

joyfulwave·2022년 12월 5일
0

피할 수 없다면 즐기자! 스프링부트 너.. 뭐 돼?




📚 상품 관리 페이지를 만들어보자

📌 index.html - 메인 페이지

<!DOCTYPE html>
<!-- 
	html 파일에 타임리프를 사용하기 위해선 아래와 같은 선언이 필요하다.
	<html xmlns:th="http://www.thymeleaf.org">
-->
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<ul>
		<li>상품관리
			<ul>
				<li>
					<a href="/basic/items">상품관리</a>
				</li>
			</ul>
		</li>
	</ul>
</body>
</html>

📌 items.html - 상품 리스트 페이지

<!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">
</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>
		<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>
							<!-- /basic/items/아이템의ID -->
							<a href="item.html" 
								th:href="@{/basic/items/{itemId}(itemId=${item.id})}" 
								th:text="${item.id}">1</a>						
						</td>
						<td>
							<!-- /basic/items/아이템의ID -->
							<a href="item.html" 
								th:href="@{|/basic/items/${item.id}|}" 
								th:text="${item.itemName}">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>

📌 item.html - 상품 상세 페이지

<!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>
		
		<!-- 추가 -->
		<h2 th:if="${param.status}" th:text="'저장완료!'"></h2>
		<div>
			<label for="itemId">상품 ID</label> 
			<input type="text" id="itemId" name="itemId" class="form-control" th:value="${item.id}" value="1" 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>판매 여부</div>
			<div>
				<div class="form-check">
					<input type="checkbox" id="open" th:field="${item.open}" 
					class="form-check-input" disabled/>
					<label for="open" class="form-check-label">판매 오픈</label>
			</div>
		</div>
		
		<!-- multi checkbox -->
		<div>
			<div>등록 지역</div>
			<div th:each="region : ${regions}" class="form-check form-check-inline">
				<input type="checkbox" th:field="${item.regions}" th:value="${region.key}" class="form-check-input" disabled />
				<label th:for="${#ids.prev('regions')}" class="form-check-label" th:text="${region.value}">서울</label>
			</div>
		</div>
		
		<!-- radio button -->
			<div>
				<div>상품 종류</div>
				<div th:each="type : ${itemType}" class="form-check form-check-inline">
					<input type="radio" th:field="${item.itemType}" th:value="${type.name()}" class="form-check-input" disabled/>
					<label class="form-check-label" th:for="${#ids.prev('itemType')}" th:text="${type.description}"></label>
				</div>
			</div>
			
		<!-- select box -->
		<div>
			<div>배송방식</div>
			<select class="form-select" th:field="${item.deliveryCode}" disabled>
				<option value="">=== 배송 방식 선택 ===</option>
				<option th:each="deliveryCode : ${deliveryCodes}"
					th:value="${deliveryCode.code}" th:text="${deliveryCode.displayName}">					
				</option>
			</select>
		</div>
			
		<hr class="my-4">	
		<div class="row">
			<div class="col">
				<!-- /basic/items/아이템Id/edit -->
				<button class="w-100 btn btn-primary 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-secondary btn-lg" 
					onclick="location.href='items.html'" 
					th:onclick="|location.href='@{/basic/items}'|"
					type="button">
						목록으로
				</button>
			</div>
		</div>
	</div>
	<!-- /container -->
	<script th:inline="javascript">
		if([[${param.status}]]){
			alert("저장완료!");
		}
	</script>
</body>
</html>

📌 addForm.html - 상품 등록 페이지

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

.field-error {
	border-color: #dc3545;
	color: #dc3545;
}

</style>
</head>
<body>
	<div class="container">
		<div class="py-5 text-center">
			<h2>상품 등록 폼</h2>
		</div>
		<h4 class="mb-3">상품 입력</h4>
		<!-- 같은 url에 전송하고 방법만 달라지는 경우는 url을 생략해도 된다. -->
		<form action="item.html" th:object="${item}" th:action method="post">
			<div>
				<!-- 
					- 타임리프는 스프링의 BindingResult를 활용해서 편리한 오류 표현기능 제공
					- field : BindingResult가 제공하는 오류에 접근 할 수 있다.
					- th:errorclass : th:field에서 지정한 필드에 오류가 있으면 class를 추가
					- th:errors : 해당 필드에 오류가 있는 경우에 태그를 출력한다. th:if의 편의 기능
				 -->
				<label for="itemName">상품명</label> 
<!-- 				th:field를 사용하면 id 와 name, value을 생략할 수 있다. 그러나 label로 묶여있는 경우에는 id속성을 남겨놔야한다. -->
<!-- 				<input type="text" id="itemName" name="itemName" th:field="${item.itemName}" class="form-control" placeholder="이름을 입력하세요"> -->
				<input type="text" id="itemName" th:field="*{itemName}" th:errorclass="field-error" class="form-control" placeholder="이름을 입력하세요">
				<div class="field-error" th:errors="*{itemName}">상품명 오류</div>
			</div>
			<div>
				<label for="price">가격</label> 
				<input type="text" id="price" th:field="*{price}" th:errorclass="field-error" class="form-control" placeholder="가격을 입력하세요">
				<div class="field-error" th:errors="*{price}">상품 가격 오류</div>
			</div>
			<div>
				<label for="quantity">수량</label> 
				<input type="text" id="quantity" th:field="*{quantity}" th:errorclass="field-error" class="form-control" placeholder="수량을 입력하세요">
				<div class="field-error" th:errors="*{quantity}">상품 수량 오류</div>
			</div>
			
			<hr class="my-4">
			
			<!-- checkbox, thymeleaf -->
          	<!--

              [체크박스]
              - 체크박스 체크여부 
                  open=on&_open=on
                  -> open에 값이 있는 것을 확인하고 사용
                  -> true 결과값을 출력
                  -> _open을 무시

                  _open=on
                  -> 체크박스를 체크하지 않으면 Spring이 _open만 있는 것을 확인하고 open의 값이 체크되지 않았다고 인식.
                  -> 이 경우 서버에서 null이 아니라 false인 것을 확인 할 수 있다.

              -->
			<div>판매 여부</div>
			<div>
				<div class="form-check">
					<input type="checkbox" id="open" th:field="*{open}" class="form-check-input"/>
					<label for="open" class="form-check-label">판매 오픈</label>
				</div>
			</div>
			
			<!-- multi checkbox -->
			<div>
				<div>등록 지역</div>
				<div th:each="region : ${regions}" class="form-check form-check-inline">
					<input type="checkbox" th:field="*{regions}" th:value="${region.key}" class="form-check-input" />
					<label th:for="${#ids.prev('regions')}" class="form-check-label" th:text="${region.value}">서울</label>
				</div>
			</div>
			
			<!-- radio button -->
			<div>
				<div>상품 종류</div>
				<div th:each="type : ${itemType}" class="form-check form-check-inline">
					<input type="radio" th:field="*{itemType}" th:value="${type.name()}" class="form-check-input" />
					<label class="form-check-label" th:for="${#ids.prev('itemType')}" th:text="${type.description}"></label>
				</div>
			</div>
			
			<!-- select box -->
			<div>
				<div>배송방식</div>
				<select class="form-select" th:field="*{deliveryCode}">
					<option value="">=== 배송 방식 선택 ===</option>
					<option th:each="deliveryCode : ${deliveryCodes}"
						th:value="${deliveryCode.code}" th:text="${deliveryCode.displayName}">					
					</option>
				</select>
			</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'" 
					th:onclick="|location.href='@{/basic/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">
<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:object="${item}" th:action method="post">
			<div>
				<label for="id">상품 ID</label> 
				<input type="text" id="id" th:field="*{id}" class="form-control" value="1" th:value="${item.id}" readonly>
			</div>
			<div>
				<label for="itemName">상품명</label> 
				<input type="text" id="itemName" th:field="*{itemName}" class="form-control" value="상품A" th:value="${item.itemName}">
			</div>
			<div>
				<label for="price">가격</label> 
				<input type="text" id="price" th:field="*{price}" class="form-control" value="10000" th:value="${item.price}">
			</div>
			<div>
				<label for="quantity">수량</label> 
				<input type="text" id="quantity" th:field="*{quantity}" class="form-control" value="10" th:value="${item.quantity}">
			</div>
			
			<hr class="my-4">
			
			<div>판매 여부</div>
			<div>
				<div class="form-check">
					<input type="checkbox" id="open" th:field="*{open}" class="form-check-input"/>
					<label for="open" class="form-check-label">판매 오픈</label>
				</div>
			</div>
			
			<!-- multi checkbox -->
			<div>
				<div>등록 지역</div>
				<div th:each="region : ${regions}" class="form-check form-check-inline">
					<input type="checkbox" th:field="*{regions}" th:value="${region.key}" class="form-check-input" />
					<label th:for="${#ids.prev('regions')}" class="form-check-label" th:text="${region.value}">서울</label>
				</div>
			</div>
			
			<!-- radio button -->
			<div>
				<div>상품 종류</div>
				<div th:each="type : ${itemType}" class="form-check form-check-inline">
					<input type="radio" th:field="*{itemType}" th:value="${type.name()}" class="form-check-input" />
					<label class="form-check-label" th:for="${#ids.prev('itemType')}" th:text="${type.description}"></label>
				</div>
			</div>
			
			<!-- select box -->
			<div>
				<div>배송방식</div>
				<select class="form-select" th:field="*{deliveryCode}">
					<option value="">=== 배송 방식 선택 ===</option>
					<option th:each="deliveryCode : ${deliveryCodes}"
						th:value="${deliveryCode.code}" th:text="${deliveryCode.displayName}">					
					</option>
				</select>
			</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>

📌 itemController.java

package com.koreait.item.domain.controller;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.koreait.item.domain.item.DeliveryCode;
import com.koreait.item.domain.item.Item;
import com.koreait.item.domain.item.ItemRepository;
import com.koreait.item.domain.item.ItemType;

import lombok.RequiredArgsConstructor;

@Controller
@RequestMapping("/basic/items")
@RequiredArgsConstructor
// @RequiredArgsConstructor : 
// final 이 붙은 멤버변수만 사용해서 생성자를 자동으로 만들어준다.
public class ItemController {
	private final ItemRepository itemRepository;
	
//	@Autowired
	// 생성자가 1개만 있으면 @Autowired 생략가능
//	public ItemController(ItemRepository itemRepository) {
//		this.itemRepository = itemRepository;
//	}
	
	// @ModelAttribute : 
	// 		Controller를 호출할 때(어떤 메서드가 호출이 되던간에) 
	//		model에 자동으로 해당 내용이 담기는게 보장된다.
	@ModelAttribute("regions")
	public Map<String, String> regions(){
		// LinkedHashMap : 순서가 보장되는 hashmap
		Map<String, String> regions = new LinkedHashMap<String, String>();
		regions.put("SEOUL", "서울");
		regions.put("BUSAN", "부산");
		regions.put("JEJU", "제주");
//		model.addAttribute("regions", regions);
		
		return regions;
	}
	
	@ModelAttribute("itemType")
	public ItemType[] itemTypes() {
		// enum에 있는 값을 배열로 넘겨준다.
		return ItemType.values();
	}
	
	@ModelAttribute("deliveryCodes")
	public List<DeliveryCode> deliveryCodes(){
		List<DeliveryCode> deliveryCodes = new ArrayList<DeliveryCode>();
		deliveryCodes.add(new DeliveryCode("FAST", "빠른배송"));
		deliveryCodes.add(new DeliveryCode("NORMAL", "일반배송"));
		deliveryCodes.add(new DeliveryCode("SLOW", "느린배송"));
		
		return deliveryCodes;
	}
	
	@GetMapping
	public String items(Model model) {
		List<Item> items = itemRepository.findAll();
		model.addAttribute("items", items);
		return "basic/items";
	}
	
	// /basic/items/아이템Id
	@GetMapping("/{itemId}")
	public String item(@PathVariable long itemId, Model model) {
		Item item = itemRepository.findById(itemId);
		model.addAttribute(item);
		return "basic/item";
	}
	
	// /basic/items/아이템Id
	@GetMapping("/add")
	public String addItem(Model model) {
		model.addAttribute("item", new Item());
		
		
		
		return "basic/addForm";
	}
	
	
//	@PostMapping("/add")
	public String save(@RequestParam String itemName,
			@RequestParam int price,
			@RequestParam Integer quantity,
			Model model) {
		Item item = new Item();
		item.setItemName(itemName);
		item.setPrice(quantity);
		item.setQuantity(quantity);
		itemRepository.save(item);
		
		model.addAttribute("item", item);
		return "basic/item";
	}
	
//	@PostMapping("/add")
	public String saveV2(@ModelAttribute("item") Item item) {
		//@ModelAttribute가 해주는 역할
//		Item item = new Item();
//		item.setItemName(itemName);
//		item.setPrice(quantity);
//		item.setQuantity(quantity);
		itemRepository.save(item);
		
//		model.addAttribute("item", item);
		return "basic/item";
	}
	
	/*
	 *  @ModelAttribute 에서 name 생략
	 *   -> 생략시 model에 저장되는 name은 클래스명 첫 글자만 소문자로 등록
	 *   	Item -> item
	 */
	
//	@PostMapping("/add")
	public String saveV3(@ModelAttribute Item item) {
		itemRepository.save(item);
		return "basic/item";
	}
	
//	@PostMapping("/add")
	public String saveV4(Item item) {
		itemRepository.save(item);
		return "basic/item";
	}
	
//	@PostMapping("/add")
	public String saveV5(Item item) {
		itemRepository.save(item);
		return "redirect:/basic/items/"+ item.getId();
	}
	
	/*
	 * redirect:/basic/items/{itemId}
	 * 	-> @PathVariable		: {itemId}
	 * 	-> 나머지는 파라미터로 처리 	: ?status=true
	 * 
	 * */
	
//	@PostMapping("/add")
	   public String saveV6(Item item, RedirectAttributes redirectAttributes) {
		  
		  System.out.println("item.open = " + item.getOpen());
		  System.out.println("item.regions = " + item.getRegions());
		  System.out.println("item.itemType = " + item.getItemType());
		  System.out.println("item.deliveryCode = " + item.getDeliveryCode());
		  
	      Item saveItem = itemRepository.save(item);
	      redirectAttributes.addAttribute("itemId", saveItem.getId());
	      redirectAttributes.addAttribute("status", true);
	      
	      return "redirect:/basic/items/{itemId}";
	   }
	
	   
	   
	   /*
	    * BindingResult : 
	    *  - Item 객체에 값이 잘 담기지 않을 때 BindingResult 객체에 값이 담긴다.
	    *  - 스프링이 제공하는 검증 오류를 보관하는 객체, 검증 오류가 발생하면 여기에 보관
	    *  - 주의) BindingResult는 검증할 대상 바로 다음에 와야한다. 순서가 중요하다.
	    *  - BindingResult는 Model에 자동으로 포함된다. 
	    */
	   
//	@PostMapping("/add")
	   public String saveV7(Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
		
		  // FieldError : field단위의 error를 처리하는 spring에서 제공해주는 객체
		  // new FieldError(객체, 에러 발생시 에러메시지를 가져갈 대상, 뱉어낼 에러 메시지)
		
		  // StringUtils.hasText(item.getItemName()
		  //  - 값이 있을 경우에는 true, 공백이나 null 들어올 경우에는 false를 반환하게 된다.
		  if(!StringUtils.hasText(item.getItemName())) {
			  bindingResult.addError(new FieldError("item", "itemName", "상품이름은 필수입니다!"));
		  }
		  
		  /*
		   * price
		   *  - 조건 : null, 1000원 미만 x, 1000000 초과 x
		   * 
		   * quantity
		   *  - 조건 : null, 최대 9999 까지만 허용
		   */
		  
		  if(item.getPrice() == null) {
			  bindingResult.addError(new FieldError("item", "price", "상품 금액은 필수입니다!"));
		  }else if(item.getPrice() < 1000 || item.getPrice() > 1000000) {
			  bindingResult.addError(new FieldError("item", "price", "상품 금액은 1000원 이상 100000 미만입니다."));
		  }
		  
		  if(item.getQuantity() == null) {
			  bindingResult.addError(new FieldError("item", "quantity", "상품의 수량은 필수입니다!"));
		  }else if(item.getQuantity() > 10000) {
			  bindingResult.addError(new FieldError("item", "quantity", "상품의 수량은 9999개 이하 입니다."));
		  }
		   
		  // 검증에 실패하면 다시 입력 폼으로
		  if(bindingResult.hasErrors()) {
//			  System.out.println("errors = " + bindingResult);
			  return "basic/addForm";
		  }
		  
	      Item saveItem = itemRepository.save(item);
	      redirectAttributes.addAttribute("itemId", saveItem.getId());
	      redirectAttributes.addAttribute("status", true);
	      
	      return "redirect:/basic/items/{itemId}";
	   }
	
//	@PostMapping("/add")
	   public String saveV8(Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
		
		/*
		 * FieldError param
		 *  - objectName	: 오류가 발생한 객체 이름 
		 *  - field			: 오류 필드
		 *  - rejectedValue : 사용자가 입력한 값(거절된 값)
		 *  - bindingFailure: 타입오류와 같은 바인딩 실패인지를 구분
		 *  - codes			: 메시지 코드
		 *  - arguments		: 메시지에서 사용하는 인자
		 *  - defaultMessage: 기본 오류 메시지		
		 */
	
		  if(!StringUtils.hasText(item.getItemName())) {
			  bindingResult.addError(new FieldError("item", "itemName", item.getItemName(), false, null, null, "상품 이름은 필수입니다."));
		  }		
		  
		  if(item.getPrice() == null || item.getPrice() < 1000 || item.getPrice() > 1000000) {
			  bindingResult.addError(new FieldError("item", "price", item.getPrice(), false, null, null, "상품 금액은 1000원 이상 100000 미만입니다."));
		  }
		  
		  if(item.getQuantity() == null || item.getQuantity() > 10000) {
			  bindingResult.addError(new FieldError("item", "quantity", item.getQuantity(), false, null, null, "상품의 수량은 9999개 이하 입니다."));
		  }
		  
		  if(bindingResult.hasErrors()) {
			  return "basic/addForm";
		  }
		  
	      Item saveItem = itemRepository.save(item);
	      redirectAttributes.addAttribute("itemId", saveItem.getId());
	      redirectAttributes.addAttribute("status", true);
	      
	      return "redirect:/basic/items/{itemId}";
	   }
	
	@PostMapping("/add")
	   public String saveV9(Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
			
		  if(!StringUtils.hasText(item.getItemName())) {
			  bindingResult.addError(new FieldError("item", "itemName", item.getItemName(), false, 
					  new String[] {"required.item.itemName", "required.default"}, null, null));
		  }		
		  
		  if(item.getPrice() == null || item.getPrice() < 1000 || item.getPrice() > 1000000) {
			  bindingResult.addError(new FieldError("item", "price", item.getPrice(), false, 
					  new String[] {"range.item.price"}, new Object[] {1000, 10000}, null));
		  }
		  
		  if(item.getQuantity() == null || item.getQuantity() > 10000) {
			  bindingResult.addError(new FieldError("item", "quantity", item.getQuantity(), false,
					  new String[] {"max.item.quantity"}, new Object[] {9999}, null));
		  }
		  
		  if(bindingResult.hasErrors()) {
			  return "basic/addForm";
		  }
		  
	      Item saveItem = itemRepository.save(item);
	      redirectAttributes.addAttribute("itemId", saveItem.getId());
	      redirectAttributes.addAttribute("status", true);
	      
	      return "redirect:/basic/items/{itemId}";
	   }
	
	
	@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 update(@PathVariable Long itemId, @ModelAttribute Item item) {
		itemRepository.update(itemId, item);
		// 상세페이지
		return "redirect:/basic/items/{itemId}";
	}
	
	
	
	// 테스트용 데이터 추가
	@PostConstruct
	public void init() {
//		System.out.println("초기화 메서드");
		itemRepository.save(new Item("testA", 10000, 10));
		itemRepository.save(new Item("testB", 20000, 20));
	}
	
	// 종료 메서드
	@PreDestroy
	public void destroy() {
		System.out.println("종료 메서드");
	}	
}

📌 item.java - item dto

package com.koreait.item.domain.item;

import java.util.List;

import lombok.Getter;
import lombok.Setter;

@Getter @Setter
public class Item {
	
	private Long id;
	private String itemName;
	private Integer price;
	private Integer quantity;
	
	private Boolean open;			// 판매 여부
	private List<String> regions;	// 등록 지역
	private ItemType itemType;		// 상품 종류
	private String deliveryCode; 	// 배송 방식
	
	public Item() {
		
	}

	public Item(String itemName, Integer price, Integer quantity) {
		super();
		this.itemName = itemName;
		this.price = price;
		this.quantity = quantity;
	}
	
	
	
}

📌 DeliveryCode.java

package com.koreait.item.domain.item;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class DeliveryCode {

	private String code;
	private String displayName;
	
	
}

📌 itemType.java

상품 타입을 정리한 java 파일로 enum으로 작성하였다.

package com.koreait.item.domain.item;

public enum ItemType {

	BOOK("도서"), FOOD("식품"), ETC("기타");
	
	final private String description;
	
	private ItemType(String description) {
		this.description = description;
	}
	
	public String getDescription() {
		return description;
	}	
}

📌 itemRepository.java

메소드 정의 파일

package com.koreait.item.domain.item;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Repository;

@Repository
public class ItemRepository {

	// static 사용
	private static final Map<Long, Item> store = new HashMap<Long, Item>();
	private static long sequence = 0L;
	
	// 저장
	public Item save(Item item) {
		item.setId(++sequence);
		store.put(item.getId(), item);
		return item;
	}
	
	// id로 찾기
	public Item findById(Long Id) {
		return store.get(Id);
	}
	
	// 전체 찾기
	public List<Item> findAll(){
		return new ArrayList<Item>(store.values());
	}
	
	// 수정
	public void update(Long itemId, Item updateParam) {
		// item을 먼저 찾는다
		Item findItem = findById(itemId);
		findItem.setItemName(updateParam.getItemName());
		findItem.setPrice(updateParam.getPrice());
		findItem.setQuantity(updateParam.getQuantity());
		
		findItem.setOpen(updateParam.getOpen()); // 판매 여부
		findItem.setRegions(updateParam.getRegions()); // 배송 지역
		findItem.setItemType(updateParam.getItemType()); // 상품 품목
		findItem.setDeliveryCode(updateParam.getDeliveryCode()); // 배송 방법
	}
	
}



📚 사용 어노테이션 정리

⚫ @postConstruct

  • 객체의 초기화 부분
  • 객체가 생성된 후 별도의 초기화 작업을 위해 실행하는 메서드를 선언해요.
  • WAS가 띄어질 때 실행돼요.

⚫ @PreDestroy

  • 마지막 소멸 단계

⚫ 경로 이동

  • context path를 추가해서 이동
  • 프로젝트이름/index.html
    프로젝트이름/user/userMain.jsp
  • /

⚫ @PathVariable

@GetMapping("/member/{name}")
public String memberFind(@PathVariable("name") String name){

}

@GetMapping("/member/{name}")
public String memberFind(@PathVariable String name){

}

// 여러개
@GetMapping("/member/{id}/{name}")
public String memberFind(@PathVariable("id") String id, @PathVariable("name") String name){

}

@GetMapping("/member/{id}/{name}")
public String memberFind(String id, String name){

}

⚫ @ModelAttribute

  • 요청 파라미터 처리
    - Item 객체를 생성하고, 요청 파라미터의 값을 프로퍼티 접근법. (set..)으로 입력해줘요.
    - model.addAttribute("item", item); 의 역할도 대신해줘요.
    - @ModelAttribute("이름 설정")

⚫ RedirectAttributes

  • redirect 여러 속성



무사히 적응할 그 날을 기대 ✔️




출처
https://media.giphy.com/media/kyUIknbbDNvID5XzU4/giphy.gif
https://media.giphy.com/media/A6aHBCFqlE0Rq/giphy.gif

0개의 댓글