
๋ง๋๋ ๊ฒ ์ค์ ์ ์ผ ์ด๋ ค์ด ํธ
package com.shop.dto;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class CartItemDto {
// ์ ํ ์์ธ ํ์ด์ง์์ ์ฅ๋ฐ๊ตฌ๋์ ๋ด์ ์ํ Id ์ ์๋์ ์ ๋ฌ ๋ฐ์
@NotNull(message = "์ํ ์์ด๋๋ ํ์ ์
๋ ฅ ๊ฐ ์
๋๋ค.")
private Long itemId;
@Min(value = 1, message = "์ต์ 1๊ฐ ์ด์ ๋ด์์ฃผ์ธ์.")
private int count;
}
...
// ๋ฉค๋ฒ๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์์ ์ฅ๋ฐ๊ตฌ๋๋ฅผ ์์ฑํ๋ static ๋ฉ์๋
// static ๐ ํผ์ ๋๊ฒ ๋ค
public static Cart createCart(Member member){
// ํ์ ํ ๋ช
๋น 1๊ฐ์ ์ฅ๋ฐ๊ตฌ๋๋ฅผ ๊ฐ๊ธฐ ๋๋ฌธ์
// ์ฒ์ ์ฅ๋ฐ๊ตฌ๋์ ์ํ์ ๋ด์ ๋๋ ํด๋น ํ์์ ์ฅ๋ฐ๊ตฌ๋๋ฅผ ์์ฑ
Cart cart = new Cart();
cart.setMember(member);
return cart;
}
}
...
// ์ฅ๋ฐ๊ตฌ๋์ ๋ด์ CartItem ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๋ฉ์๋
public static CartItem createCartItem(Cart cart, Item item, int count){
CartItem cartItem = new CartItem();
cartItem.setCart(cart);
cartItem.setItem(item);
cartItem.setCount(count);
return cartItem;
}
//์ฅ๋ฐ๊ตฌ๋์ ๋ด๊ฒจ ์๋ ์ํ์ ๋ ์ฅ๋ฐ๊ตฌ๋๋ก ๋ด์์ ๊ฒฝ์ฐ ์๋ ์ฆ๊ฐ
public void addCount(int count) {
this.count += count;
}
}
package com.shop.repository;
import com.shop.entity.Cart;
import com.shop.entity.CartItem;
import org.springframework.data.jpa.repository.JpaRepository;
// ์ฟผ๋ฆฌ๋ฌธ ๋ ๋ฆฌ๋ JpaRepository
public interface CartRepository extends JpaRepository<Cart, Long> {
//ํ์ฌ ๋ก๊ทธ์ธํ ์ ์ ์ ์ฅ๋ฐ๊ตฌ๋(Cart)๋ฅผ ์ฐพ๊ธฐ ์ํ ์ฟผ๋ฆฌ ๋ฉ์๋
Cart findByMemberId(Long memberId);
}
package com.shop.repository;
import com.shop.entity.CartItem;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CartItemRepository extends JpaRepository<CartItem, Long> {
// ์ํ์ด ์ฅ๋ฐ๊ตฌ๋์ ๋ค์ด์๋์ง ์กฐํํ๋ ์ฟผ๋ฆฌ ๋ฉ์๋
CartItem findByCartIdAndItemId(Long cartId, Long itemId);
}
package com.shop.service;
import com.shop.dto.CartItemDto;
import com.shop.entity.Cart;
import com.shop.entity.CartItem;
import com.shop.entity.Item;
import com.shop.entity.Member;
import com.shop.repository.CartItemRepository;
import com.shop.repository.CartRepository;
import com.shop.repository.ItemRepository;
import com.shop.repository.MemberRepository;
import jakarta.persistence.EntityExistsException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
@RequiredArgsConstructor
public class CartService {
private final ItemRepository itemRepository;
private final MemberRepository memberRepository;
private final CartRepository cartRepository;
private final CartItemRepository cartItemRepository;
public Long addCart(CartItemDto cartItemDto, String email){
Item item = itemRepository.findById(cartItemDto.getItemId())
.orElseThrow(EntityExistsException::new);
Member member = memberRepository.findByEmail(email);
Cart cart = cartRepository.findByMemberId(member.getId());
// ์ฅ๋ฐ๊ตฌ๋๊ฐ ์์ผ๋ฉด ์์ฑ
if (cart == null){
cart = Cart.createCart(member);
cartRepository.save(cart);
}
CartItem savedCartItem = cartItemRepository.
findByCartIdAndItemId(cart.getId(), item.getId());
// ํด๋น ์ํ์ด ์ฅ๋ฐ๊ตฌ๋์ ์ด๋ฏธ ์กด์ฌ ํ๋ฉด ์๋ ์ฆ๊ฐ ๐ ๋ณ๊ฒฝ ๊ฐ์ง
if (savedCartItem != null){
savedCartItem.addCount(cartItemDto.getCount());
return savedCartItem.getId();
}
// ์ฅ๋ฐ๊ตฌ๋ ์์ ํด๋น ์ํ์ด ์กด์ฌ ํ์ง ์์ผ๋ฉด ์์ฑ ํ ์ถ๊ฐ
else {
CartItem cartItem = CartItem.createCartItem(cart, item, cartItemDto.getCount());
cartItemRepository.save(cartItem);
return cartItem.getId();
}
}
}
package com.shop.controller;
import com.shop.dto.CartItemDto;
import com.shop.service.CartService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import java.security.Principal;
import java.util.List;
@Controller
@RequiredArgsConstructor
public class CartController {
private final CartService cartService;
@PostMapping(value = "/cart")
public @ResponseBody
ResponseEntity order(@RequestBody @Valid CartItemDto cartItemDto,
BindingResult bindingResult, Principal principal){
if (bindingResult.hasErrors()){
StringBuilder sb = new StringBuilder();
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
for (FieldError fieldError : fieldErrors){
sb.append(fieldError.getDefaultMessage());
}
return new ResponseEntity<String>(sb.toString(), HttpStatus.BAD_REQUEST);
}
String email = principal.getName();
Long cartItemId;
try {
// ์ ํ ์์ธ ํ์ด์ง์์ ๋์ด์จ CartItemDto ๊ฐ์ฒด์ email
cartItemId = cartService.addCart(cartItemDto, email);
}catch (Exception e){
return new ResponseEntity<String>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<Long>(cartItemId, HttpStatus.OK);
}
}
package com.shop.service;
import com.shop.constant.ItemSellStatus;
import com.shop.dto.CartItemDto;
import com.shop.entity.CartItem;
import com.shop.entity.Item;
import com.shop.entity.Member;
import com.shop.repository.CartItemRepository;
import com.shop.repository.ItemRepository;
import com.shop.repository.MemberRepository;
import jakarta.persistence.EntityNotFoundException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.transaction.annotation.Transactional;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@Transactional
@TestPropertySource(locations = "classpath:application-test.properties")
class CartServiceTest {
@Autowired
ItemRepository itemRepository;
@Autowired
MemberRepository memberRepository;
@Autowired
CartService cartService;
@Autowired
CartItemRepository cartItemRepository;
public Item saveItem(){
Item item = new Item();
item.setItemNm("ํ
์คํธ ์ํ");
item.setPrice(10000);
item.setItemDetail("ํ
์คํธ ์ํ ์์ธ ์ค๋ช
");
item.setItemSellStatus(ItemSellStatus.SELL);
item.setStockNumber(100);
return itemRepository.save(item);
}
public Member saveMember(){
Member member = new Member();
member.setEmail("test@test.com");
return memberRepository.save(member);
}
@Test
@DisplayName("์ฅ๋ฐ๊ตฌ๋ ๋ด๊ธฐ ํ
์คํธ")
public void addCart(){
Item item = saveItem();
Member member = saveMember();
CartItemDto cartItemDto = new CartItemDto();
cartItemDto.setCount(5);
cartItemDto.setItemId(item.getId());
Long cartItemId = cartService.addCart(cartItemDto, member.getEmail());
CartItem cartItem = cartItemRepository.findById(cartItemId)
.orElseThrow(EntityNotFoundException::new);
assertEquals(item.getId(), cartItem.getItem().getId());
assertEquals(cartItemDto.getCount(), cartItem.getCount());
System.out.println("item.getId() : " + item.getId());
System.out.println("cartItem.getItem().getId() : " + cartItem.getItem().getId());
System.out.println("--------------------------------------------------");
System.out.println("cartItemDto.getCount() : " + cartItemDto.getCount());
System.out.println("cartItem.getCount() : " + cartItem.getCount());
}
}

function addCart(){
// Ajax ํต์ ํ ๋, csrf ํ ํฐ ๊ฐ์ ์กฐํํด์ ์ง์ ๋ณด๋ด์ผํจ
var token = $("meta[name = '_csrf']").attr("content");
var header = $("meta[name = '_csrf_header']").attr("content");
var url = "/cart";
var paramData = {
itemId : $("#itemId").val(),
count : $("#count").val()
};
var param = JSON.stringfy(paramData);
$.ajax({
url : url,
type : "POST",
contentType : "application/json",
data : param,
beforeSend : function(xhr){
// ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ธฐ ์ ์ ํค๋์ csrf ๊ฐ์ ์ค์
xhr.setRequestHeader(header, token);
},
dataType : "json",
cache : false,
success : function(result, status){
alert("์ํ์ ์ฅ๋ฐ๊ตฌ๋์ ๋ด์์ต๋๋ค.");
location.href = '/';
},
error : function(jqXHR, status, error){
if(jqXHR.status == '401'){
alert('๋ก๊ทธ์ธ ํ ์ด์ฉํด์ฃผ์ธ์');
location.href = '/members/login';
}
else{
alert(jqXHR.responseText);
}
}
});
}




๐ 10๊ฐ ๋ ๋ด์๋ณด์



๐ ์์ง ๋ก์ง์ ์ง์ง ์์์ 500๊ฐ๊ฐ ๋ด๊ฒจ๋ฒ๋ฆฐ๋ค

๐ Cart DB ์๋ ๋ด๊ฒจ๋ฒ๋ฆผ..


๐ ์ฌ๊ณ ๋ ๊ทธ๋๋ก

- ํ์๊ฐ์ ํ๋ฉด
- ๋ก๊ทธ์ธ ํ๋ฉด
- ??? ๋ ํ์ํฉ๋๋ค ๋ฑ ์ด์๊ฒ ๊พธ๋ฉฐ๋ณด์
package com.shop.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class CartDetailDto {
private Long cartItemId; // ์ฅ๋ฐ๊ตฌ๋ ์ํ ์์ด๋
private String itemNm; // ์ํ๋ช
private int price; // ๊ฐ๊ฒฉ
private int count; // ์๋
private String imgUrl; // ์ํ์ด๋ฏธ์ง ๊ฒฝ๋ก
// CartItem ๊ฐ์ฒด์ ๋น์ท ๐ List์ ๋ด๊ธด๋ค
public CartDetailDto(Long cartItemId, String itemNm, int price, int count, String imgUrl) {
this.cartItemId = cartItemId;
this.itemNm = itemNm;
this.price = price;
this.count = count;
this.imgUrl = imgUrl;
}
}
package com.shop.repository;
import com.shop.dto.CartDetailDto;
import com.shop.entity.CartItem;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface CartItemRepository extends JpaRepository<CartItem, Long> {
// ์ํ์ด ์ฅ๋ฐ๊ตฌ๋์ ๋ค์ด์๋์ง ์กฐํํ๋ ์ฟผ๋ฆฌ ๋ฉ์๋
CartItem findByCartIdAndItemId(Long cartId, Long itemId);
// select ๋ถ๋ถ์ new ํค์๋์ ํด๋น DTO์ ํจํค์ง, ํด๋์ค๋ช
์ ์ง์
// ๐ Dto๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ ๋ฐ๋ก ๋ฃ๋ ํ์
@Query("select new com.shop.dto.CartDetailDto(ci.id, i.itemNm, i.price, ci.count, im.imgUrl)"
+ "from CartItem ci, ItemImg im "
+ "join ci.item i " // CartItem๊ณผ ItemImg ์กฐ์ธ ๐ itemImg๋ฅผ ๊ฐ์ง๊ณ ์ค๋ ค๊ณ
+ "where ci.cart.id = :cartId " // ์กฐ๊ฑด1 : CartItem์ CartId์ ์ธ๋ถ ๋งค๊ฐ๋ณ์ CartId๊ฐ ๊ฐ์์ง ๋น๊ต
+ "and im.item.id = ci.item.id " // ์กฐ๊ฑด2 : CartItem์ itemId์ ItemImg์ itemId๊ฐ ๊ฐ์์ง ๋น๊ต
+ "and im.repImgYn = 'Y' " // ์กฐ๊ฑด3 : ItemImg ๋ํ์ด๋ฏธ์ง
+ "order by ci.regTime desc") // ์ฅ๋ฐ๊ตฌ๋ ๋ฑ๋ก์๊ฐ ๋ด๋ฆผ์ฐจ์ ๊ธฐ์ค์ผ๋ก ๋ํ์ด๋ฏธ์ง ๋ฑ๋ก
// ์ต์ ํ๋ฅผ ์ํด ํ ๋ฒ์ ๋ฐํ ํ๋๋ก List<CartDetailDto> ํ์
์ง์
List<CartDetailDto> findCartDetailDtoList(Long cartId);
}
...
@Transactional(readOnly = true)
// ํ์ฌ ๋ก๊ทธ์ธํ ์ ์ ์ ์ฅ๋ฐ๊ตฌ๋์ ์กด์ฌํ๋ ์ํ ์กฐํ
public List<CartDetailDto> getCartList(String email){
List<CartDetailDto> cartDetailDtoList = new ArrayList<>();
// ๋ก๊ทธ์ธํ ์ ์ ์ ๋ณด
Member member = memberRepository.findByEmail(email);
// ์ ์ ์ ์ฅ๋ฐ๊ตฌ๋ ์กฐํ
Cart cart = cartRepository.findByMemberId(member.getId());
// ์ฅ๋ฐ๊ตฌ๋๊ฐ ๋น์ด์์ผ๋ฉด ์๋ ์ฑ๋ก cartDetailDtoList ๋ฐํ
if (cart == null){
return cartDetailDtoList;
}
// ๋น์ด์์ง ์์ผ๋ฉด ์ฅ๋ฐ๊ตฌ๋์ ๋ด๊ธด ์ํ๋ค์ cartDetailDtoList์ ์ถ๊ฐ ํ ๋ฐํ
cartDetailDtoList = cartItemRepository.findCartDetailDtoList(cart.getId());
return cartDetailDtoList;
}
}
...
@GetMapping(value = "/cart")
public String orderHist(Principal principal, Model model){
List<CartDetailDto> cartDetailDtoList = cartService.getCartList(principal.getName());
model.addAttribute("cartItems", cartDetailDtoList);
return "/cart/cartList";
}
}

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/layout1}">
<head>
<meta name="_csrf" th:content="${_csrf.token}"/>
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
</head>
<th:block layout:fragment="script">
<script th:inline="javascript">
// ์ฒดํฌ ๋ ๋ ๋ง๋ค ์ด ๊ธ์ก์ ๊ตฌํ๋ ํจ์ ํธ์ถ
$(document).ready(function(){
$("input[name=cartChkBox]").change( function(){
getOrderTotalPrice();
});
});
// ์ฃผ๋ฌธํ ์ํ์ ์ฒดํฌ ํ๊ฑฐ๋ ํด์ ํ ๊ฒฝ์ฐ ์ด ์ฃผ๋ฌธ ๊ธ์ก์ ๊ตฌํ๋ ํจ์
function getOrderTotalPrice(){
var orderTotalPrice = 0;
// name ์์ฑ ๊ฐ์ด cartChkBox ์ด๊ณ ์ฒดํฌ๋ input ํ๊ทธ์ ๋ํด ๊ฐ๊ฐ ํจ์ ์ํ
$("input[name=cartChkBox]:checked").each(function() {
var cartItemId = $(this).val();
//id ๊ฐ price~ ์ธ ํ๊ทธ๋ฅผ ์ง์
var price = $("#price_" + cartItemId).attr("data-price");
//id ๊ฐ count~ ์ธ ํ๊ทธ๋ฅผ ์ง์
var count = $("#count_" + cartItemId).val();
orderTotalPrice += price * count;
});
$("#orderTotalPrice").html(orderTotalPrice+'์');
}
// ์ํ ์๋ ๋ณ๊ฒฝ
// ์๋ ๋ถ๋ถ์ ์๋์ ๋ณ๊ฒฝ ํ๋ฉด changeCount() ์คํ
function changeCount(obj){
var count = obj.value; // ํ์ฌ ์๋
var cartItemId = obj.id.split('_')[1]; // count_ + id ์ด๋ฏ๋ก id๋ฅผ ์ถ์ถ
// id = price{CartItemId} ์๋ฆฌ๋จผํธ data ์์ key = price ์ธ value ์ถ์ถ
var price = $("#price_" + cartItemId).data("price");
var totalPrice = count * price;
$("#totalPrice_" + cartItemId).html(totalPrice+"์");
// ๋ณ๊ฒฝ๋ ์๋์ ์ ์ฉํ๊ธฐ ์ํด ์ด ์ฃผ๋ฌธ ๊ธ์ก์ ์์ ํ๋ getOrderTotalPrice() ์คํ
getOrderTotalPrice();
// DB ๋ณ๊ฒฝ์ ์ํ updateCartItemCount ํจ์ ํธ์ถ
updateCartItemCount(cartItemId, count);
}
// ์ ์ฒด ์ ํ ๋๋ ์ ์ฒด ํด์
function checkAll(){
// checkall ํ๊ทธ์ ํ๋กํผํฐ ๊ฐ์ด checked ๋ผ๋ฉด cartChkBox ํ๊ทธ๋ฅผ ๋ชจ๋ checked ์ํ๋ก ๋ณ๊ฒฝ
// .prop() : JavaScript์ ํ๋กํผํฐ๋ฅผ ์ทจ๊ธ (true,false)
// .attr() ์ HTML์ ์์ฑ์ ์ทจ๊ธ
if($("#checkall").prop("checked")){
$("input[name=cartChkBox]").prop("checked",true);
}
// checked ๊ฐ ์๋๋ผ๋ฉด ๋ชจ๋ checked ์ํ๋ฅผ ํด์
else{
$("input[name=cartChkBox]").prop("checked",false);
}
// ๋ง์ง๋ง์ ์ด ์ฃผ๋ฌธ ๊ธ์ก์ ์์ ํ๋ getOrderTotalPrice() ํจ์ ํธ์ถ
getOrderTotalPrice();
}
// ์ํ ์๋ ๋ณ๊ฒฝ DB ์ ์ฉ์ ์ํด "/cartItem/{cartItemId}?count=value" ์์ฒญ
function updateCartItemCount(cartItemId, count){
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
// ์ํ Id ๊ฐ์ PathVariable, ์๋(count)์ QueryString
var url = "/cartItem/" + cartItemId+"?count=" + count;
$.ajax({
url : url,
// ์ผ ๋ถ๋ถ๋ง ์์ ํ๊ธฐ ์ํด PATCH ์์ฒญ
type : "PATCH",
beforeSend : function(xhr){
/* ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ธฐ ์ ์ ํค๋์ csrf๊ฐ์ ์ค์ */
xhr.setRequestHeader(header, token);
},
dataType : "json",
cache : false,
success : function(result, status){
console.log("cartItem count update success");
},
error : function(jqXHR, status, error){
if(jqXHR.status == '401'){
alert('๋ก๊ทธ์ธ ํ ์ด์ฉํด์ฃผ์ธ์');
location.href='/members/login';
} else{
alert(jqXHR.responseText);
}
}
});
}
// ์ฅ๋ฐ๊ตฌ๋ ์ญ์ ์์ฒญ
function deleteCartItem(obj){
var cartItemId = obj.dataset.id;
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
var url = "/cartItem/" + cartItemId;
$.ajax({
url : url,
type : "DELETE",
beforeSend : function(xhr){
/* ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ธฐ ์ ์ ํค๋์ csrf๊ฐ์ ์ค์ */
xhr.setRequestHeader(header, token);
},
dataType : "json",
cache : false,
success : function(result, status){
location.href='/cart';
},
error : function(jqXHR, status, error){
if(jqXHR.status == '401'){
alert('๋ก๊ทธ์ธ ํ ์ด์ฉํด์ฃผ์ธ์');
location.href='/members/login';
} else{
alert(jqXHR.responseText);
}
}
});
}
// ์ฅ๋ฐ๊ตฌ๋ ์ํ(๋ค) ์ฃผ๋ฌธ
function orders(){
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
var url = "/cart/orders";
var dataList = new Array();
var paramData = new Object();
$("input[name=cartChkBox]:checked").each(function() {
var cartItemId = $(this).val();
var data = new Object();
data["cartItemId"] = cartItemId;
dataList.push(data);
});
paramData['cartOrderDtoList'] = dataList;
var param = JSON.stringify(paramData);
$.ajax({
url : url,
type : "POST",
contentType : "application/json",
data : param,
beforeSend : function(xhr){
/* ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ธฐ ์ ์ ํค๋์ csrf๊ฐ์ ์ค์ */
xhr.setRequestHeader(header, token);
},
dataType : "json",
cache : false,
success : function(result, status){
alert("์ฃผ๋ฌธ์ด ์๋ฃ ๋์์ต๋๋ค.");
location.href='/orders';
},
error : function(jqXHR, status, error){
if(jqXHR.status == '401'){
alert('๋ก๊ทธ์ธ ํ ์ด์ฉํด์ฃผ์ธ์');
location.href='/members/login';
} else{
alert(jqXHR.responseText);
}
}
});
}
</script>
</th:block>
<th:block layout:fragment="css">
<style>
.content-mg{
margin-left:25%;
margin-right:25%;
margin-top:2%;
margin-bottom:100px;
}
.repImgDiv{
margin-right:15px;
margin-left:15px;
height:auto;
}
.repImg{
height:100px;
width:100px;
}
.fs18{
font-size:18px
}
.fs24{
font-size:24px
}
</style>
</th:block>
<div layout:fragment="content" class="content-mg">
<h2 class="mb-4">์ฅ๋ฐ๊ตฌ๋ ๋ชฉ๋ก</h2>
<div>
<table class="table">
<colgroup>
<col width="15%"/>
<col width="70%"/>
<col width="15%"/>
</colgroup>
<thead>
<tr class="text-center">
<td>
<input type="checkbox" id="checkall" onclick="checkAll()"> ์ ์ฒด ์ ํ
</td>
<td>์ํ ์ ๋ณด</td>
<td>์ํ ๊ธ์ก</td>
</tr>
</thead>
<tbody>
<tr th:each="cartItem : ${cartItems}">
<td class="text-center align-middle">
<input type="checkbox" name="cartChkBox" th:value="${cartItem.cartItemId}">
</td>
<td class="d-flex">
<div class="repImgDiv align-self-center">
<img th:src="${cartItem.imgUrl}" class="rounded repImg" th:alt="${cartItem.itemNm}">
</div>
<div class="align-self-center">
<span th:text="${cartItem.itemNm}" class="fs24 font-weight-bold"></span>
<div class="fs18 font-weight-light">
<span class="input-group mt-2">
<!--// id = price{CartItemId} ์๋ฆฌ๋จผํธ์ data ์๋ฃ๊ตฌ์กฐ ์์ฑ ํ key = price ์ ๋ํ value ์ง์ -->
<span th:id="'price_' + ${cartItem.cartItemId}"
th:data-price="${cartItem.price}"
th:text="${cartItem.price} + '์'" class="align-self-center mr-2">
</span>
<input type="number" name="count" th:id="'count_' + ${cartItem.cartItemId}"
th:value="${cartItem.count}" min="1" onchange="changeCount(this)"
class="form-control mr-2">
<button type="button" class="close" aria-label="Close">
<span aria-hidden="true" th:data-id="${cartItem.cartItemId}"
onclick="deleteCartItem(this)">×</span> <!-- ×๋ X ๋ชจ์์ -->
</button>
</span>
</div>
</div>
</td>
<td class="text-center align-middle">
<span th:id="'totalPrice_' + ${cartItem.cartItemId}"
name="totalPrice" th:text="${cartItem.price * cartItem.count} + '์'">
</span>
</td>
</tr>
</tbody>
</table>
<h2 class="text-center">
์ด ์ฃผ๋ฌธ ๊ธ์ก : <span id="orderTotalPrice" class="text-danger">0์</span>
</h2>
<div class="text-center mt-3">
<button type="button" class="btn btn-success btn-lg" onclick="orders()">์ฃผ๋ฌธ ํ๊ธฐ</button>
</div>
</div>
</div>
</html>

๐ DB์ ๊ฐ์ด 511๊ฐ๊ฐ ๋ด๊ฒจ์๋ค

๐ ์๋ ๋ณ๊ฒฝ์ด๋ ์ฃผ๋ฌธํ๊ธฐ ๋ก์ง์ ์ถ๊ฐํ๋ฉด ๋

...
// ์๋ ๋ณ๊ฒฝ์ ํ๋ฉด ๋ณ๊ฒฝ ๊ฐ์ง๋ก ์
๋ฐ์ดํธ ๋๋ ๋ฉ์๋
public void updateCount(int count){
this.count = count;
}
}
...
@Transactional(readOnly = true)
public boolean validateCartItem(Long cartItemId, String email){
Member curMember = memberRepository.findByEmail(email);
CartItem cartItem = cartItemRepository.findById(cartItemId)
.orElseThrow(EntityExistsException::new);
Member savedMember = cartItem.getCart().getMember();
// ํ์ฌ ๋ก๊ทธ์ธํ ์ ์ ์ ํด๋น ์ฅ๋ฐ๊ตฌ๋ ์ํ์ ์ ์ฅํ ์ ์ ๊ฐ ๊ฐ์์ง ๊ฒ์ฆ
if(!StringUtils.equals(curMember.getEmail(), savedMember.getEmail())){
return false;
}
return true;
}
// ๋ณ๊ฒฝ ๊ฐ์ง๋ฅผ ํตํ update
public void updateCartItemCount(Long cartItemId, int count){
CartItem cartItem = cartItemRepository.findById(cartItemId)
.orElseThrow(EntityExistsException::new);
cartItem.updateCount(count);
}
}
...
@PatchMapping(value = "/cartItem/{cartItemId}")
// ์์ ์กด์ฌํ๋ updateCartItemCount() ํจ์ ํธ์ถ ์ ์ ์ก ๋๋ PATCH(์์ ) ์์ฒญ์ ๋ํ Controller
public @ResponseBody ResponseEntity updateCartItem(@PathVariable("cartItemId") Long cartItemId,
int count, Principal principal){
System.out.println(cartItemId);
// ํ์ฌ ๋ก๊ทธ์ธํ ์ ์ ์ ํด๋น ์ฅ๋ฐ๊ตฌ๋ ์ํ์ ์ ์ฅํ ์ ์ ๊ฐ ๊ฐ์์ง ๊ฒ์ฆ
if (count <= 0){
return new ResponseEntity<String>("์ต์ 1๊ฐ ์ด์ ๋ด์์ฃผ์ธ์.", HttpStatus.BAD_REQUEST);
}
// ํ์ฌ ๋ก๊ทธ์ธํ ์ ์ ์ ํด๋น ์ฅ๋ฐ๊ตฌ๋ ์ํ์ ์ ์ฅํ ์ ์ ๊ฐ ๊ฐ์์ง ๊ฒ์ฆ
else if (!cartService.validateCartItem(cartItemId, principal.getName())){
return new ResponseEntity<String>("์์ ๊ถํ์ด ์์ต๋๋ค.", HttpStatus.FORBIDDEN);
}
// ๋ฐ๊พผ count ๊ทธ๋๋ก ์ฌ๋ฆผ
cartService.updateCartItemCount(cartItemId, count);
return new ResponseEntity<Long>(cartItemId, HttpStatus.OK);
}
}
// ์ํ ์๋ ๋ณ๊ฒฝ DB ์ ์ฉ์ ์ํด "/cartItem/{cartItemId}?count=value" ์์ฒญ
function updateCartItemCount(cartItemId, count){
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
// ์ํ Id ๊ฐ์ PathVariable, ์๋(count)์ QueryString
var url = "/cartItem/" + cartItemId+"?count=" + count;
$.ajax({
url : url,
// ์ผ ๋ถ๋ถ๋ง ์์ ํ๊ธฐ ์ํด PATCH ์์ฒญ
type : "PATCH",
beforeSend : function(xhr){
/* ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ธฐ ์ ์ ํค๋์ csrf๊ฐ์ ์ค์ */
xhr.setRequestHeader(header, token);
},
dataType : "json",
cache : false,
success : function(result, status){
console.log("cartItem count update success");
},
error : function(jqXHR, status, error){
if(jqXHR.status == '401'){
alert('๋ก๊ทธ์ธ ํ ์ด์ฉํด์ฃผ์ธ์');
location.href='/members/login';
} else{
alert(jqXHR.responseText);
}
}
});
}

๐ 100๊ฐ๋ก ๋ณ๊ฒฝํ๊ณ ์ํฐ

๐ ์๋์ผ๋ก DB์ ๋ฐ์ ๋๋ค.

๐ 1๊ฐ ์๋๋ก ๋ด๋ ค๊ฐ์ง ์์ผ๋ฉฐ 0์ ๊ฐ์ ๋ก ์ ๋ ฅ์ ์๋ฌ๋ฉ์ธ์ง๊ฐ ์ ์ถ๋ ฅ ๋๋ค

๐ DB์๋ 1๊ฐ๋ก ์ ์ฅ์ด ๋๋ค

...
// ์ฅ๋ฐ๊ตฌ๋ ์ํ ๋ฒํธ๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์์ ์ญ์
public void deleteCartItem(Long cartItemId){
CartItem cartItem = cartItemRepository.findById(cartItemId)
.orElseThrow(EntityExistsException::new);
cartItemRepository.delete(cartItem);
}
}
...
// ์ญ์ ์์ฒญ์ด๋ฏ๋ก DeleteMapping ์ด๋
ธํ
์ด์
์ง์
@DeleteMapping(value = "/cartItem/{cartItemId}")
public @ResponseBody ResponseEntity deleteCartItem(@PathVariable("cartItemId") Long cartItemId,
Principal principal){
// ์ํ ์ญ์ ์์ฒญ์ ํ๋ ์ ์ ์ ํด๋น ์ฅ๋ฐ๊ตฌ๋ ์ํ์ ์ ์ ๊ฐ ์ผ์นํ๋์ง ๊ฒ์ฆ
if (!cartService.validateCartItem(cartItemId, principal.getName())){
return new ResponseEntity<String>("์ญ์ ๊ถํ์ด ์์ต๋๋ค.", HttpStatus.FORBIDDEN);
}
cartService.deleteCartItem(cartItemId);
return new ResponseEntity<Long>(cartItemId, HttpStatus.OK);
}
}
// ์ฅ๋ฐ๊ตฌ๋ ์ญ์ ์์ฒญ
function deleteCartItem(obj){
var cartItemId = obj.dataset.id;
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
var url = "/cartItem/" + cartItemId;
$.ajax({
url : url,
type : "DELETE",
beforeSend : function(xhr){
/* ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ธฐ ์ ์ ํค๋์ csrf๊ฐ์ ์ค์ */
xhr.setRequestHeader(header, token);
},
dataType : "json",
cache : false,
success : function(result, status){
location.href='/cart';
},
error : function(jqXHR, status, error){
if(jqXHR.status == '401'){
alert('๋ก๊ทธ์ธ ํ ์ด์ฉํด์ฃผ์ธ์');
location.href='/members/login';
} else{
alert(jqXHR.responseText);
}
}
});
}



๐ X ๋ฅผ ๋๋ฅด๋ฉด ์ญ์ ๊ฐ ๋๋ค.

package com.shop.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
// ์ฅ๋ฐ๊ตฌ๋ ํ์ด์ง์์ ์ฃผ๋ฌธํ ์ํ์ ๋ฐ์ดํฐ๋ฅผ ์ํ Dto
public class CartOrderDto {
private Long cartItemId;
// List์ ์๊ธฐ ์์ ์ ๋ด๋๋ค.
private List<CartOrderDto> cartOrderDtoList;
}
...
// ์ฅ๋ฐ๊ตฌ๋ ํ์ด์ง์์ ์ ๋ฌ ๋ฐ์ ๊ตฌ๋งค ์ํ์ผ๋ก ์ฃผ๋ฌธ์ ์์ฑํ๋ ๋ฉ์๋
public Long orders(List<OrderDto> orderDtoList, String email){
// ๋ก๊ทธ์ธํ ์ ์ ์กฐํ
Member member = memberRepository.findByEmail(email);
List<OrderItem> orderItemList = new ArrayList<>();
// orderDto ๊ฐ์ฒด๋ฅผ ์ด์ฉํ์ฌ itme ๊ฐ์ฒด์ count ๊ฐ์ ์ป์ด OrderItem ๊ฐ์ฒด ์์ฑ
for (OrderDto orderDto : orderDtoList){
Item item = itemRepository.findById(orderDto.getItemId())
.orElseThrow(EntityNotFoundException::new);
OrderItem orderItem = OrderItem.createOrderItem(item, orderDto.getCount());
orderItemList.add(orderItem);
}
// Order Entity ํด๋์ค์ ์กด์ฌํ๋ createOrder ๋ฉ์๋๋ก Order ์์ฑ ๋ฐ ์ ์ฅ
Order order = Order.createOrder(member, orderItemList);
orderRepository.save(order);
return order.getId();
}
}
...
private final OrderService orderService;
...
public Long orderCartItem(List<CartOrderDto> cartOrderDtoList, String email){
List<OrderDto> orderDtoList = new ArrayList<>();
// CartOrderDto ๊ฐ์ฒด๋ฅผ ์ด์ฉํด cartItem ๊ฐ์ฒด ์กฐํ
// cartItem ๊ฐ์ฒด์์ itemId ์ count ๊ฐ์ ์ด์ฉํด orderDto ๊ฐ์ฒด ์์ฑ
for (CartOrderDto cartOrderDto : cartOrderDtoList){
CartItem cartItem = cartItemRepository.findById(cartOrderDto.getCartItemId())
.orElseThrow(EntityExistsException::new);
OrderDto orderDto = new OrderDto();
orderDto.setItemId(cartItem.getItem().getId());
orderDto.setCount(cartItem.getCount());
orderDtoList.add(orderDto);
}
Long orderId = orderService.orders(orderDtoList, email);
// ์ฃผ๋ฌธํ ์ฅ๋ฐ๊ตฌ๋ ์ํ์ ์ ๊ฑฐ
for (CartOrderDto cartOrderDto : cartOrderDtoList){
CartItem cartItem = cartItemRepository.findById(cartOrderDto.getCartItemId())
.orElseThrow(EntityExistsException::new);
cartItemRepository.delete(cartItem);
}
return orderId;
}
}
...
@PostMapping(value = "/cart/orders")
public @ResponseBody ResponseEntity orderCartItem(@RequestBody CartOrderDto cartOrderDto,
Principal principal){
System.out.println(cartOrderDto.getCartItemId());
List<CartOrderDto> cartOrderDtoList = cartOrderDto.getCartOrderDtoList();
if(cartOrderDtoList == null || cartOrderDtoList.size() == 0){
return new ResponseEntity<String>("์ฃผ๋ฌธํ ์ํ์ ์ ํํด์ฃผ์ธ์.", HttpStatus.FORBIDDEN);
}
// ์ฅ๋ฐ๊ตฌ๋ ์ฃผ๋ฌธ ์ํ ํ๋์ฉ ๊ฒ์ฆ
for (CartOrderDto cartOrder : cartOrderDtoList){
if (!cartService.validateCartItem(cartOrder.getCartItemId(), principal.getName())){
return new ResponseEntity<String>("์ฃผ๋ฌธ ๊ถํ์ด ์์ต๋๋ค.", HttpStatus.FORBIDDEN);
}
}
Long orderId = cartService.orderCartItem(cartOrderDtoList, principal.getName());
return new ResponseEntity<Long>(orderId, HttpStatus.OK);
}
}
// ์ฅ๋ฐ๊ตฌ๋ ์ํ(๋ค) ์ฃผ๋ฌธ
function orders(){
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
var url = "/cart/orders";
var dataList = new Array();
var paramData = new Object();
$("input[name=cartChkBox]:checked").each(function() {
var cartItemId = $(this).val();
var data = new Object();
data["cartItemId"] = cartItemId;
dataList.push(data);
});
paramData['cartOrderDtoList'] = dataList;
var param = JSON.stringify(paramData);
$.ajax({
url : url,
type : "POST",
contentType : "application/json",
data : param,
beforeSend : function(xhr){
/* ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ธฐ ์ ์ ํค๋์ csrf๊ฐ์ ์ค์ */
xhr.setRequestHeader(header, token);
},
dataType : "json",
cache : false,
success : function(result, status){
alert("์ฃผ๋ฌธ์ด ์๋ฃ ๋์์ต๋๋ค.");
location.href='/orders';
},
error : function(jqXHR, status, error){
if(jqXHR.status == '401'){
alert('๋ก๊ทธ์ธ ํ ์ด์ฉํด์ฃผ์ธ์');
location.href='/members/login';
} else{
alert(jqXHR.responseText);
}
}
});
}
</script>
</th:block>



๐ ํ๊บผ๋ฒ์ ์ฃผ๋ฌธ ๋จ (์ฃผ๋ฌธ์)

๐ ์ฃผ๋ฌธ๋ ์ํ๋ค


๐ ์ฅ๋ฐ๊ตฌ๋์์ ์ฌ๋ผ์ง

๐ ํ๋๋ง ์ฃผ๋ฌธ


cartList.html ๐ CartContoller ๐ CartService ๐ OrderService ๐ CartService ๐ CartController ๐ carList.html
๋ณต์กํ๋ค..๊ณ์ธต์ ์ด๋ํ ๋๋ Dto ๊ฐ์ฒด๋ก ๋ฐ์ดํฐ๋ฅผ ๋๊ธฐ๊ณ , ํด๋น ๊ณ์ธต์์ Dto ๊ฐ์ฒด๋ฅผ ์ด์ฉํ์ฌ Entity ๊ฐ์ฒด๋ฅผ ์กฐํ, ํด๋น Entity ๊ฐ์ฒด๋ฅผ ์ด์ฉํ์ฌ ์์ ์ ๋ก์ง์ ์ํ
Controller
CartOrderDto์์CartOrderList๋ฅผ ๊ฐ์ ธ์จ ๋คcartService.orderCartItem()๋ฉ์๋ ์ํCartService
cartOrderDto์ ์กด์ฌํ๋cartItemId๋ฅผ ์ด์ฉํ์ฌCartItem๊ฐ์ฒด ์กฐํcartItem์ ์กด์ฌํ๋ItemId์count๊ฐ์ ์ป์ ๋ค์ ์ด๋ฅผ ์ด์ฉํ์ฌOrderDto๊ฐ์ฒด ์์ฑOrderDtoList๋ฅผorderService.orders()๋ฉ์๋ ํ๋ผ๋ฏธํฐ๋ก ๋๊ฒจ ํธ์ถOrderService
OrderDto์ ์กด์ฌํ๋ItemId์count๊ฐ์ ์ป์ ๋ค์ ์ด๋ฅผ ์ด์ฉํ์ฌOrderItem๊ฐ์ฒด ์์ฑOrder.createOrder()๋ฉ์๋ ํธ์ถํ์ฌOrder๊ฐ์ฒด ์์ฑํsave. ์ด ๋, ํ๋ผ๋ฏธํฐ๋member,orderItemList
์ฌ๊ณ ๋ณด๋ค ์ฅ๋ฐ๊ตฌ๋์ ๋ด๋ ์๋์ด ๋ง์ ๊ฒฝ์ฐ ๊ทธ๋๋ก DB์ ๋ด๊ธฐ๋ ํ์ ์์
- ์ฃผ๋ฌธ ์ทจ์ํ๊ณ ๋ค์ ๋ด๊ธฐ
- ์ฃผ๋ฌธ ์ทจ์ํ๋ฉด ํ๋งค์๊ฐ ์ทจ์ ํ์ธํ๊ธฐ
- ๊ฒฐ์ ์ ๋ณด?