등록된 상품 정보, 이미지를 수정하는 기능을 구현하도록 하겠습니다.
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class ItemForm {
private Long itemId;
@NotEmpty(message = "상품 이름은 필수입니다.")
private String name; //상품명
@NotNull(message = "상품 가격은 필수입니다.")
private int price; //상품 가격
@NotNull(message = "상품 재고 수량은 필수입니다.")
private int stockQuantity; //재고 수량
private String description; //상품 설명
//상품 수정, 장바구니에 사용
private List<ItemImageDto> itemImageListDto = new ArrayList<>();
public ItemServiceDTO toServiceDTO() {
return ItemServiceDTO.builder()
.id(itemId)
.name(name)
.price(price)
.stockQuantity(stockQuantity)
.description(description)
.build();
}
@GetMapping("/items/{itemId}/edit")
public String updateItemForm(@PathVariable(name = "itemId") Long itemId, Model model) {
Item findItem = itemService.findItem(itemId);
List<ItemImage> itemImageList = itemImageService.findItemImageDetail(itemId, "N");
//엔티티 -> DTO로 변환
List<ItemImageDto> itemImageListDto = itemImageList.stream()
.map(ItemImageDto::new)
.collect(Collectors.toList());
ItemForm itemForm = new ItemForm(
findItem.getId(),
findItem.getName(),
findItem.getPrice(),
findItem.getStockQuantity(),
findItem.getDescription(),
itemImageListDto
);
model.addAttribute("itemForm", itemForm);
return "item/updateItemForm";
}
//ItemService
@Transactional(readOnly=true)
public Item findItem(Long ItemId) {
return itemRepository.findById(ItemId).orElse(null);
}
//== select * from Item where Item_Id = ItemId;
//ItemImageService
//삭제 여부를 판단하여 상품 이미지 정보를 조회한다
@Transactional(readOnly = true)
public List<ItemImage> findItemImageDetail(Long itemId, String YN) {
return itemImageRepository.findByItemIdAndDeleteYN(itemId, YN);
}
@PostMapping("/items/{itemId}/edit")
public String updateItem(@ModelAttribute ItemForm itemForm, Model model,
@RequestPart(name = "itemImages") List<MultipartFile> multipartFiles) throws IOException {
List<ItemImage> findItemImages = itemImageService.findItemImageDetail(itemForm.getItemId(), "N");
//상품 이미지를 등록안하면
if (findItemImages.isEmpty() && multipartFiles.get(0).isEmpty()) {
model.addAttribute("errorMessage", "상품 사진을 등록해주세요!");
return "item/itemForm";
}
//상품 정보 수정
itemService.updateItem(itemForm.toServiceDTO(), multipartFiles);
return "redirect:/userHome";
}
//ItemService
//상품 정보 업데이트 (Dirty Checking, 변경감지)
public void updateItem(ItemServiceDTO itemServiceDTO, List<MultipartFile> multipartFileList) throws IOException {
Item findItem = itemRepository.findById(itemServiceDTO.getId()).orElse(null); //DB에서 찾아옴 -> 영속 상태
findItem.updateItem(itemServiceDTO.getName(), itemServiceDTO.getDescription(), itemServiceDTO.getPrice(), itemServiceDTO.getStockQuantity());
log.info("=====findItem={}", findItem.getName());
//상품 이미지를 수정(삭제, 추가) 하지 않으면 실행 x
if(!multipartFileList.get(0).isEmpty()) {
itemImageService.addItemImage(multipartFileList, findItem);
}
}
//상품 이미지 추가
public void addItemImage(List<MultipartFile> multipartFiles, Item item) throws IOException {
List<ItemImage> itemImages = fileHandler.storeImages(multipartFiles);
for (ItemImage itemImage : itemImages) {
item.addItemImage(itemImageRepository.save(itemImage));
}
}
/**
* 아이템 이미지 삭제 처리
*/
@PostMapping("item/delete")
public String deleteItemImage(@RequestParam("itemImageId") Long itemImageId, @RequestParam("itemId") Long itemId) {
itemImageService.delete(itemImageId);
return "redirect:/items/ " + itemId + "/edit";
}
//ItemImageSerivce
//데이터베이스를 삭제하지 않고 flag를 이용하여 처리
public void delete(Long itemImageId) {
ItemImage itemImage = itemImageRepository.findById(itemImageId).get();
itemImage.deleteSet("Y");
}
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class ItemServiceTest {
@Autowired
ItemRepository itemRepository;
@Autowired
ItemService itemService;
private Item createItem() {
return Item.createItem("후드 집업", "데상트 후드 집업", 70000, 300);
}
@Test
@DisplayName("상품 수정 테스트")
public void updateItem() {
//given
Item item = this.createItem();
itemRepository.save(item);
//when
Item findItem = itemRepository.findById(item.getId()).orElse(null);
findItem.updateItem("후드 집업", "지프 후드 집업", 55000, 300);
//then
Assert.assertEquals(item.getName(), "후드 집업");
Assert.assertEquals(item.getPrice(), 55000);
}
}
<head>
<meta charset="utf-8">
<th:block layout:fragment="script">
<script th:inline="javascript">
var error = [[${errorMessage}]];
if(error != null){
alert(error);
}
</script>
</th:block>
</head>
<body>
<section>
<div layout:fragment="content" class="container">
<div class="py-5 text-center">
<h2>상품 수정</h2>
</div>
<form th:action th:object="${itemForm}" method="post" enctype="multipart/form-data">
<div th:if="${#fields.hasGlobalErrors()}">
<p class="field-error" th:each="err : ${#fields.globalErrors()}"
th:text="${err}">전체 오류 메시지</p>
</div>
<input type="hidden" th:field="*{itemId}">
<div>
<label for="name">상품명</label>
<input type="text" id="name" th:field="*{name}" class="form-control" placeholder="상품명을 입력해주세요"
th:errorclass="field-error">
<div class="field-error" th:errors="*{name}" />
</div>
<br>
<div>
<label for="price">가격</label>
<input type="number" id="price" th:field="*{price}" class="form-control" placeholder="가격을 입력해주세요"
th:errorclass="field-error">
<div class="field-error" th:errors="*{price}" />
</div>
<br>
<div>
<label for="stockQuantity">재고 수량</label>
<input type="number" id="stockQuantity" th:field="*{stockQuantity}" class="form-control" placeholder="재고 수량을 입력해주세요"
th:errorclass="field-error">
<div class="field-error" th:errors="*{stockQuantity}" />
</div>
<br>
<div>
<label for="description">상품 상세 설명</label>
<textarea id="description" th:field="*{description}" class="form-control" aria-label="With textarea"></textarea>
</div>
<br>
<br>
<div>
<label>상품 이미지</label>
<div class="form-group" th:each="itemImage : ${itemForm.itemImageListDto}">
<div class="custom-file img-div">
<span th:text="${itemImage.originalName}">파일이름1.png</span>
<span>
<button th:fileId="${itemImage.id}" th:onclick="itemImgageDelete(this.getAttribute('fileId'))"
type="button" class="btn btn-outline-danger">삭제</button>
</span>
</div>
</div>
</div>
<div class="mb-3">
<label for="formFileMultiple" class="form-label">파일업로드</label>
<input class="form-control" type="file" id="formFileMultiple" name="itemImages" multiple>
</div>
<br>
<br>
<div style="text-align:center">
<button class="w-50 btn btn-primary btn-lg" th:align="center" type="submit">
상품 수정</button>
</div>
<br>
<br>
</form>
</div> <!-- /container -->
</section>
</body>
</html>
<script>
function itemImgageDelete(fileId){
if (confirm("정말로 삭제하시겠습니까?")) {
//배열생성
const form = document.createElement('form');
form.setAttribute('method', 'post');
form.setAttribute('action', '/item/delete');
//파일 id
var input1 = document.createElement('input');
input1.setAttribute("type", "hidden");
input1.setAttribute("name", "itemImageId");
input1.setAttribute("value", fileId);
//게시판 id
const selectedElements = document.querySelector("#itemId")
var input2 = document.createElement('input');
input2.setAttribute("type", "hidden");
input2.setAttribute("name", "itemId");
input2.setAttribute("value", selectedElements.value);
form.appendChild(input1);
form.appendChild(input2);
document.body.appendChild(form);
form.submit();
}
}
</script>