📝상품 상세 페이지
- 상품 상세 페이지는 Bootstrap의 쇼핑몰 무료 템플릿을 참고하였습니다.
ItemController
@GetMapping("items/{itemId}")
public String itemView(@PathVariable(name = "itemId") Long itemId, Model model) {
Item item = itemService.findItem(itemId);
List<ItemImage> itemImageList = itemImageService.findItemImageDetail(itemId, "N");
List<ItemImageDto> itemImageDtoList = itemImageList.stream()
.map(ItemImageDto::new)
.collect(Collectors.toList());
ItemForm itemform = new ItemForm(
item.getId(),
item.getName(),
item.getPrice(),
item.getStockQuantity(),
item.getDescription(),
itemImageDtoList
);
model.addAttribute("item", itemform);
return "item/itemView";
}
- 메인 페이지의 View Option을 클릭하면 상세화면으로 이동합니다.
- 메인 페이지에서 넘어오는 itemId를 이용하여 상품 정보와 상품 이미지를 데이터베이스에서 가져옵니다.
itemImageService.findItemImageDetail(itemId, "N");
: 삭제 처리된 이미지는 화면에 반환하면 안되기 때문에 삭제 처리되지 않은 이미지들만 가져오도록 하였습니다.
- 엔티티를 View에 반환하지 않기 위해 DTO로 변환해주고 View에 전달하였습니다.
@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();
}
}
상품 상세 페이지
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="" />
<meta name="author" content="" />
<title>Roominis</title>
<link rel="icon" type="image/x-icon" href="assets/favicon.ico" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css" rel="stylesheet" />
<link href="/css/styles.css" rel="stylesheet" />
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
</head>
<script th:inline="javascript">
<!-- 처음 웹페이지 로딩시 호출 -->
$(document).ready(function(){
$("#description").val().replace()
calculateTotalPrice();
<!-- count 값이 변경될때마다 호출 -->
$("#count").change(function(){
calculateTotalPrice();
});
});
<!-- 총 상품 금액 계산 -->
function calculateTotalPrice(){
var quantity = $("#stockQuantity").val()*1;
var count = $("#count").val();
var price = $("#price").val();
<!-- 재고 부족 -->
if (quantity < count) {
alert("샹품 재고가 부족합니다. 재고:" + quantity + "개")
return;
}
var totalPrice = price*count;
$("#totalPrice").html(totalPrice + '원');
}
</script>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark" style="background-color: #e3f2fd;">
<div class="container px-4 px-lg-5">
<a class="navbar-brand" th:href="@{/}">Home</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0 ms-lg-4">
</ul>
<form class="d-flex">
<button class="btn btn-light btn-outline-secondary" type="submit">
<i class="bi-cart-fill me-1"></i>
Cart
<span class="badge bg-dark text-white ms-1 rounded-pill">0</span>
</button>
</form>
</div>
</div>
</nav>
<section class="py-5">
<div class="container px-4 px-lg-5 my-5" th:object="${item}">
<div class="row gx-4 gx-lg-5 align-items-center" >
<div class="col-md-6"><img class="card-img-top mb-5 mb-md-0" th:src="|/images/${item.getItemImageListDto().get(0).getStoreName()}|" alt="..." /></div>
<div class="col-md-6">
<h1 class="display-5 fw-bolder" th:text="${item.getName()}">Shop item template</h1>
<div class="fs-5 mb-5">
<input type="hidden" th:value="${item.price}" id="price" name="price">
<span class="text-decoration-none" th:text="${item.getPrice()}"></span>원
</div>
<hr class="my-4">
<div class="input-group fs-5 mb-5">
<div class="input-group-prepend">
<input type="hidden" th:value="${item.stockQuantity}" id="stockQuantity" name="stockQuantity">
<span class="input-group-text">주문 수량</span>
</div>
<input class="form-control text-center me-3" id="count" name="count" type="number" value="1" style=" max-width: 5rem" />
</div>
<div class="container bg-light">
<h6>총 상품 금액</h6>
<h4 name="totalPrice" id="totalPrice" class="font-weight-bold" ></h4>
</div>
<br>
<div class="d-flex">
<form th:action="@{/logout}" class="d-flex" method="post">
<button class="btn btn-outline-dark"
type="submit">
바로 구매하기
</button>
</form>
 
<button class="btn btn-outline-dark flex-shrink-0" type="button">
<i class="bi-cart-fill me-1"></i>
장바구니 담기
</button>
</div>
</div>
</div>
</div>
</section>
<section class="py-5 bg-light">
<div class="container">
<p class="lead" id="description" style="text-align: center" th:text="${item.getDescription()}">Lorem ipsum dolor sit amet consectetur adipisicing?</p>
</div>
<hr class="my-4">
<div class="container px-4 px-lg-5 mt-5" >
<div th:each="itemImage : ${item.getItemImageListDto()}" class="text-center">
<img class="card-img-top rounded mb-5 mb-md-0" style="padding-bottom: 50px; width: 550px; height:700px" th:src="|/images/${itemImage.getStoreName()}|" >
</div>
</div>
</section>
<footer class="py-5 bg-dark">
<div class="container"><p class="m-0 text-center text-white">Copyright © Roominis</p></div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="js/scripts.js"></script>
</body>
</html>
- 컨트롤러에서 넘어온 상품 정보와 이미지를 이용해 화면에 표시되도록 하였습니다.
- JQuery를 이용하여 주문 수량이 변경(.change())되면 총 상품 금액도 그에 맞게 계산되도록 구현하였습니다.
- 주문 수량이 상품 재고를 초과하면 경고 메세지를 출력하도록 하였습니다.
결과 화면
📝참고
https://startbootstrap.com/template/shop-item