이 내용은 스프링 부트 쇼핑몰 프로젝트 with JPA 책을 학습한 내용입니다.
- 장바구니 조회 페이지에 전달할 Dto 클래스
- CartItem 객체와 비슷함. 따라서 List 에 담겨서 전달됨
| @Getter |
| @Setter |
| public class CartDetailDto { |
| |
| private Long cartItemId; |
| |
| private String itemNm; |
| |
| private int price; |
| |
| private int count; |
| |
| private String imgUrl; |
| |
| 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; |
| } |
| } |
- 장바구니 페이지에 전달할 CartDetailDto 리스트를 쿼리 하나로 조회하는 JPQL 문
- 지연 로딩이기 때문에 최적화를 위해서 아래 코드와 같이 DTO의 생성자를 이용하여 조회 결과를 DTO 객체로 변환 후 반환할 수 있음
| @Query("select new kr.spring.cart.dto.CartDetailDto(ci.id, i.itemNm, i.price, ci.count, im.imgUrl) " + |
| "from CartItem ci, ItemImg im " + |
| "join ci.item i " + |
| "where ci.cart.id = :cartId " + |
| "and im.item.id = ci.item.id " + |
| "and im.repimgYn = 'Y' " + |
| "order by ci.regTime desc") |
| List<CartDetailDto> findCartDetailDtoList(@Param("cartId") Long cartId); |
- 현재 로그인한 유저의 장바구니안에 존재하는 상품(들) 조회 로직
- 로그인 유저 정보 -> 유저의 cart 조회 -> cart 를 참조하는 CartItem 조회 (cartDetailDto 로 변환)
| |
| @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()); |
| if(cart == null) { |
| return cartDetailDtoList; |
| } |
| cartDetailDtoList = cartItemRepository.findCartDetailDtoList(cart.getId()); |
| |
| return cartDetailDtoList; |
| } |
| @GetMapping("/cart") |
| public String orderHist(Principal principal, Model model) { |
| List<CartDetailDto> cartDetailList = cartService.getCartList(principal.getName()); |
| |
| model.addAttribute("cartItems", cartDetailList); |
| |
| return "cart/cartList"; |
| } |
- 주문할 상품을 체크하거나 해제할 경우 총 주문 금액을 구하는 함수 및 호출
| <!-- cartList.html --> |
| $(document).ready(function(){ |
| $("input[name=cartChkBox]").change( function(){ |
| getOrderTotalPrice(); |
| }); |
| }); |
| |
| function getOrderTotalPrice(){ |
| var orderTotalPrice = 0; |
| $("input[name=cartChkBox]:checked").each(function() { |
| var cartItemId = $(this).val(); |
| var price = $("#price_" + cartItemId).attr("data-price"); |
| var count = $("#count_" + cartItemId).val(); |
| orderTotalPrice += price*count; |
| }); |
| |
| $("#orderTotalPrice").html(orderTotalPrice+'원'); |
| } |
- checkall 태그의 프로퍼티 값이 checked 라면 cartChkBox 태그를 모두 checked 상태로 변경
- checked 가 아니라면 모두 checked 상태를 해제
- 마지막에 총 주문 금액을 수정하는 getOrderTotalPrice() 함수 호출
| function checkAll(){ |
| if($("#checkall").prop("checked")){ |
| $("input[name=cartChkBox]").prop("checked",true); |
| }else{ |
| $("input[name=cartChkBox]").prop("checked",false); |
| } |
| getOrderTotalPrice(); |
| } |
- 수량 부분의 수량을 변경하면 changeCount() 함수 호출
- 변경된 수량을 적용하기 위해 총 주문 금액을 수정하는 getOrderTotalPrice() 함수 호출
| <input type="number" name="count" th:id="'count_' + ${cartItem.cartItemId}" |
| th:value="${cartItem.count}" min="1" |
| onchange="changeCount(this)" class="form-control mr-2" > |
| function changeCount(obj){ |
| var count = obj.value; |
| var cartItemId = obj.id.split('_')[1]; |
| var price = $("#price_" + cartItemId).data("price"); |
| var totalPrice = count*price; |
| $("#totalPrice_" + cartItemId).html(totalPrice+"원"); |
| getOrderTotalPrice(); |
| updateCartItemCount(cartItemId, count); |
| } |
| |
| <span th:id="'price_' + ${cartItem.cartItemId}" |
| th:data-price="${cartItem.price}" |
| th:text="${cartItem.price} + '원'" class="align-self-center mr-2"> |
| </span> |
- 장바구니에 들어있는 상품의 수량을 변경하면 CartItem 의 count 값도 수정되야 하므로 마지막에 updateCartItemCount() 함수 호출
| function updateCartItemCount(cartItemId, count){ |
| var token = $("meta[name='_csrf']").attr("content"); |
| var header = $("meta[name='_csrf_header']").attr("content"); |
| |
| |
| var url = "/cartItem/" + cartItemId+"?count=" + count; |
| |
| $.ajax({ |
| url : url, |
| type : "PATCH", |
| beforeSend : function(xhr){ |
| |
| 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.responseJSON.message); |
| } |
| |
| } |
| }); |
| } |
| |
| public void updateCount(int count) { |
| this.count = count; |
| } |
- updateCount() 메소드를 호출하는 로직을 추가
- 현재 로그인한 유저와 해당 장바구니 상품의 저장한 유저가 같은지 검증
| |
| @Transactional(readOnly = true) |
| public boolean validateCartItem(Long cartItemId, String email) { |
| Member curMember = memberRepository.findByEmail(email); |
| CartItem cartItem = cartItemRepository.findById(cartItemId).orElseThrow(EntityNotFoundException::new); |
| |
| Member savedMmeber = cartItem.getCart().getMember(); |
| |
| if(!StringUtils.equals(curMember.getEmail(), savedMmeber.getEmail())) { |
| return false; |
| } |
| |
| return true; |
| } |
- 위에 존재하는 updateCartItemCount() 함수 호출 시 전송되는 PATCH 요청에 대한 Controller
- cartService.updateCartItemCount() 메소드 호출
- 상품의 개수를 0으로 지정할 시 에러 메시지 반환
| |
| @PatchMapping("/cartItem/{cartItemId}") |
| public @ResponseBody ResponseEntity updateCartItem(@PathVariable("cartItemId") Long cartItemId, int count, Principal principal) { |
| |
| if(count <= 0) { |
| return new ResponseEntity<String>("최소 1개 이상 담아주세요.", HttpStatus.BAD_REQUEST); |
| } else if(!cartService.validateCartItem(cartItemId, principal.getName())) { |
| return new ResponseEntity<String>("수정 권한이 없습니다.", HttpStatus.FORBIDDEN); |
| } |
| |
| cartService.updateCartItemCount(cartItemId, count); |
| return new ResponseEntity<Long>(cartItemId, HttpStatus.OK); |
| } |