[Spring Boot] 24. 장바구니

shr·2022년 3월 11일
0

Spring

목록 보기
23/23
post-thumbnail

장바구니


  1. 설계

    EntityDAODTOControllerServicemapperView
    CartItemCartDaoCartDto
    CartOrderDto
    CartDeleteDto
    CartController
    OrderController
    CartService
    OrderService
    cartMapperproduct/read.html
    cart/list.html
    • 장바구니 읽기 (/cart/list)
    • 장바구니에 담기 (/cart/add)
    • 장바구니 물품 개수 증가 (/cart/increse)
    • 장바구니 물품 개수 감소 (/cart/decrese)
    • 장바구니에서 물품 삭제 (/cart/delete)
    • 장바구니에서 주문 (/order/multiple)

  1. 엔티티 설정
  • src/main/java - com.example.demo.entity - CartItem
    @Data
    @AllArgsConstructor
    public class CartItem {
        private String username;
        private Integer pno;
        private Integer count;
    }

  1. DAO 설정
  • src/main/java - com.example.demo.dao - CartDao

    @Mapper
    public interface CartDao {
        // 장바구니에 저장
        void save(CartItem cartItem);
    
        // 해당 사용자의 장바구니 찾기
        List<CartItem> findByUsername(String username);
    
        // 장바구니 내의 상품 찾기
        CartItem findByUsernameAndPno(String username, Integer pno);
    
        // 장바구니 상품 개수 증가
        void increase(CartItem cartItem);
    
        // 장바구니 상품 개수 감소
        void decrease(CartItem cartItem);
    
        // 장바구니에서 삭제
        void delete(List<Integer> pnos, String username);
    
    }

  1. DTO 설정
  • src/main/java - com.example.demo.dto - CartDto

    @Data
    @AllArgsConstructor
    public class CartDto {
        private Integer pno;
        private String vendor;
        private String name;
        private String imagename;
        private Integer price;
        private Integer count;
    |

    장바구니에서 출력할 정보만 담은 DTO.

  • src/main/java - com.example.demo.dto - CartOrderDto

    @Data
    public class CartOrderDto {
        @Data
        public static class Param {
            private Integer pno;
            private Integer count;
        }
    
        private List<Param> list;
    }

    주문할 때 필요한 정보만 담은 DTO.

  • src/main/java - com.example.demo.dto - CartDeleteDto

    @Data
    public class CartDeleteDto {
        private List<Integer> pnos;
    }

    삭제할 때 필요한 정보만 담은 DTO.


  1. 컨트롤러 설정
  • src/main/java - com.example.demo.controller.mvc - CartController

    
    @Secured("ROLE_USER")
    @AllArgsConstructor
    @Controller
    public class CartController {
        private CartService service;
    
        // 장바구니 보기
        @GetMapping("/cart/list")
        public ModelAndView read(Principal principal) {
            List<CartDto> list = service.read(principal.getName());
            return new ModelAndView("cart/list").addObject("list", list);
        }
    
        // 장바구니 담기 - 이미 담긴 상품일 경우 개수를 증가시킨다.
        @PostMapping("/cart/add")
        public String add(Integer pno, Integer count, Principal principal) {
            Boolean result = service.add(pno, count, principal.getName());
            if (result == false)
                return "redirect:/product/read?pno=" + pno;
            return "redirect:/cart/list";
        }
    
        // 장바구니 물품 개수 증가
        @PatchMapping("/cart/increase/{pno}")
        public ResponseEntity<Integer> increase(@PathVariable Integer pno, Principal principal) {
            Integer count = service.increase(pno, principal.getName());
            if (count <= 0)
                return ResponseEntity.status(HttpStatus.CONFLICT).body(count);
            return ResponseEntity.ok(count);
        }
    
        // 장바구니 물품 개수 감소
        @PatchMapping("/cart/decrease/{pno}")
        public ResponseEntity<Integer> decrease(@PathVariable Integer pno, Principal principal) {
            Integer count = service.decrease(pno, principal.getName());
            if (count <= 0)
                return ResponseEntity.status(HttpStatus.CONFLICT).body(count);
            return ResponseEntity.ok(count);
        }
    
        // 장바구니에서 삭제 
        @PostMapping("/cart/delete")
        public String delete(CartDeleteDto dto, Principal principal) {
            service.delete(dto, principal.getName());
            return "redirect:/cart/list";
        }
    }
  • src/main/java - com.example.controller.mvc - OrderController

    @Secured("ROLE_USER")
    @AllArgsConstructor
    @Controller
    public class OrderController {
        private OrderService service;
    
            // 여러 개 주문 시의 주문 정보 (장바구니)
        @PostMapping("/order/multiple")
        public ModelAndView order(CartOrderDto cartOrderDto, Principal principal, HttpSession session) {
            OrderDto.View dto = service.orderList(cartOrderDto.getList(), principal.getName());
            session.setAttribute("dto", dto);
            return new ModelAndView("order/view").addObject("order", dto);
        }
    }

  1. 서비스 설정
  • src/main/java - com.example.demo.service - CartService

    @RequiredArgsConstructor
    @Service
    public class CartService {
        private final CartDao cartDao;
        private final ProductDao productDao;
    
        @Value("${product.image.path}")
        private String imagePath;
    
        // 장바구니 읽기
        public List<CartDto> read(String loginId) {
            List<CartItem> cartItems = cartDao.findByUsername(loginId);
            List<CartDto> cartDtos = new ArrayList<>();
            for (CartItem cartItem : cartItems) {
                Product p = productDao.findById(cartItem.getPno());
                CartDto cartDto = new CartDto(p.getPno(), p.getVendor(), p.getName(), imagePath+p.getImagename(), p.getPrice(), cartItem.getCount());
                cartDtos.add(cartDto);
            }
            return cartDtos;
        }
    
        // 장바구니 상품 추가
        public Boolean add(Integer pno, Integer count, String loginId) {
            CartItem cartItem = cartDao.findByUsernameAndPno(loginId, pno);
            if (cartItem != null) {
                Product product = productDao.findById(pno);
                if ((cartItem.getCount() + count) > product.getStock())
                    return false;
                cartDao.increase(new CartItem(loginId, pno, count));
            } else {
                cartDao.save(new CartItem(loginId, pno, count));
            }
            return true;
        }
    
        // 장바구니 상품 개수 증가
        public Integer increase(Integer pno, String loginId) {
            Product product = productDao.findById(pno);
            CartItem cartItem = cartDao.findByUsernameAndPno(loginId, pno);
            if ((cartItem.getCount()+1) >= product.getStock()) {
                return -1;
            }
            cartDao.increase(new CartItem(loginId, pno, 1));
            return cartItem.getCount() + 1;
        }
    
        // 장바구니 상품 개수 감소
        public Integer decrease(Integer pno, String loginId) {
            Product product = productDao.findById(pno);
            CartItem cartItem = cartDao.findByUsernameAndPno(loginId, pno);
            if (cartItem.getCount() <= 1)
                return -1;
            cartDao.decrease(new CartItem(loginId, pno, 1));
            return cartItem.getCount() - 1;
        }
    
        // 장바구니에서 상품 삭제
        public void delete(CartDeleteDto dto, String loginId) {
            cartDao.delete(dto.getPnos(), loginId);
        }
    }
  • src/main/java - com.example.demo.service - OrderService

    @RequiredArgsConstructor
    @Service
    public class OrderService {
    
        private final OrderDao orderDao;
        private final OrderItemDao orderItemDao;
        private final AddressDao addressDao;
        private final ProductDao productDao;
    
        @Value("${product.image.path}")
        private String imagePath;
    
            public OrderDto.View orderList(List<Param> list, String loginId) {
            List<OrderItem> orderItems = new ArrayList<>();
            Integer totalPrice = 0;
            for (Param param : list) {
                Product product = productDao.findById(param.getPno());
                OrderItem orderItem = new OrderItem(null, param.getPno(), product.getVendor(), product.getName(), imagePath+product.getImagename(), product.getPrice(), param.getCount(), product.getPrice() * param.getCount());
                totalPrice = totalPrice + (product.getPrice() * param.getCount());
                orderItems.add(orderItem);
            }
            return new OrderDto.View(totalPrice, orderItems);
        }
    
    }

  1. Mapper 설정
  • src/main/resources - mapper - cartMapper.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.example.demo.dao.CartDao">
        <select id="findByUsername" resultType="cartitem">
            select * from cart_item where username=#{username}
        </select>
    
        <select id="findByUsernameAndPno" resultType="cartitem">
            select * from cart_item where username=#{username} and pno=#{pno} and rownum=1
        </select>
    
        <update id="update">
            update cart_item set count=#{count} where username=#{username}
        </update>
    
        <insert id="save">
            insert into cart_item values(#{username}, #{pno}, #{count})
        </insert>
    
        <update id="increase">
            update cart_item set count=count+#{count} where username=#{username} and pno=#{pno}
        </update>
    
        <update id="decrease">
            update cart_item set count=count-#{count} where username=#{username} and pno=#{pno}
        </update>
    
        <delete id="delete">
            delete from cart_item where username=#{username} and pno in
            <foreach collection='pnos' item='pno' open='(' close=')' separator=','>
                #{pno}
            </foreach>
        </delete>
    </mapper>

  1. 화면 설정
  • src/main/resources - templates - cart - list.html

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
    <title>Insert title here</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="/css/main.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
    <script src="//cdn.jsdelivr.net/npm/sweetalert2@11"></script>
    <script>
        // 화면이 로딩되면 전체 장바구니 가격을 계산해서 #total_price에 출력
        function getTotalPrice() {
            let totalPrice = 0;
            // $.each(배열, function(idx, 원소))
            // jQuery 객체를 대상으로는 $().each(function(idx, 원소))
            $('.item_price').each(function(idx, item) {
                totalPrice += parseInt($(item).text());
            });
            return totalPrice;
        }
    
        // + 플러스 버튼 처리
        function plus() {
            const $pno = $(this).attr('data-pno');
            $.ajax({
                url: "/cart/increase/" + $pno,
                method: "patch"
            }).done((count) => {
                $(this).prev().text(count);
                const $price = $(this).parent().prev().text();
                $(this).parent().parent().parent().parent().next().find('.item_price').text(count * $price);
                $('#total_price').text(getTotalPrice());
            }).fail(() => alert("더 이상 구입할 수 없습니다. T-T"));
        }
    
        // - 마이너스 버튼 처리
        function minus() {
            const $pno = $(this).attr('data-pno');
            let count = $(this).next().text();
            if (count < 1)
                return;
            $.ajax({
                url: "/cart/decrease/" + $pno,
                method: "patch"
            }).done((count) => {
                $(this).prev().text(count);
                const $price = $(this).parent().prev().text();
                $(this).parent().parent().parent().parent().next().find('.item_price').text(count * $price);
                $('#total_price').text(getTotalPrice());
            })
        }
    
        $(document).ready(function() {
            $('#total_price').text(getTotalPrice());
    
            $('.plus').click(plus);
            $('.minus').click(minus);
    
            // 상품 삭제
            // 여러 개의 값을 넘기는 방법 1. 서버에서 List로 받는다. 
            $('#delete').click(function() {
                const $form = $('<form>').attr('method', 'post').attr('action', '/cart/delete').appendTo($('body'));
                $('.pno').each(function(idx, checkbox) {
                    if ($(this).prop('checked') == true) {
                        $('<input>').attr('name', 'pnos').attr('type', 'text').val($(this).attr('data-pno')).appendTo($form);
                    }
                })
                if ($form.serialize() != "")
                    $form.submit();
            })
    
            // 상품 결제
            // 여러 개의 값을 넘기는 방법 2. 서버가 여러 개의 값을 가지고 DTO를 만들고, DTO의 리스트를 갖는 DTO를 또 만든다.
            $('#order').click(function() {
                const result = confirm("상품을 주문하시겠습니까?");
                if (result == true) {
                    let idx = 0;
                    const $form = $('<form>').attr('method', 'post').attr('action', '/order/multiple').appendTo($('body'));
                    $('.pno').each(function(idx, checkbox) {
                        const pno = parseInt($(checkbox).attr('data-pno'));
                        const count = parseInt($(checkbox).parent().next().next().find('.count').text());
                        $('<input>').attr('type', 'hidden').attr('name', 'list[' + idx + '].pno').val(pno).appendTo($form);
                        $('<input>').attr('type', 'hidden').attr('name', 'list[' + idx + '].count').val(count).appendTo($form);
                        idx++;
                    })
                    $form.submit();
                }
            })
        })
    </script>
    </head>
    <body>
    <div id="page">
        <header th:replace="/fragments/header.html">
        </header>
        <nav th:replace="/fragments/nav.html">
        </nav>
        <div id="main">
            <aside th:replace="/fragments/aside.html">
            </aside>
            <section>
                <table style="width:100%;" id="cart_table">
                    <tr th:each="item:${list}">
                        <td style="width:5%;">
                            <input type="checkbox" th:attr="data-pno=${item.pno}" class="pno">
                        </td>
                        <td style="width:10%;">
                            <img th:src="${item.imagename}" width="75px;" >
                        </td>
                        <td style="width:60%;">
                            <div class="upper">
                                <span th:text="${item.vendor}">LG</span>
                                <span th:text="${item.name}">OLED TV</span>
                            </div>
                            <div class="lower" style="overflow:hidden;">
                                <div class="left" style="float:left;">
                                    <span th:text="${item.price}">9500</span><div class="btn-group">
                                        <button type="button" class="btn btn-primary minus" th:attr="data-pno=${item.pno}">-</button>
                                        <button type="button" class="btn btn-primary count" th:text="${item.count}"></button>
                                        <button type="button" class="btn btn-primary plus" th:attr="data-pno=${item.pno}">+</button>
                                    </div>
                                </div>
                                <div class="right" style="float:right;">
                                    <button type="button" class="btn btn-primary delete" th:attr="data-pno=${item.pno}">삭제</button>
                                </div>
                            </div>
                        </td>
                        <td style="width:25%; text-align: center;">
                            <span th:text="${item.count * item.price}" class="item_price"></span></td>
                    </tr>
                </table>
                <div class="well well-sm">총액:<span id="total_price"></span></div>
                <button id="order">결제</button>
                <button id="delete">선택 상품 삭제</button>
            </section>
        </div>
        <footer th:replace="/fragments/footer.html">
        </footer>
    </div>
    </body>
    </html>
  • src/main/java - templates - product - read.html

    <script>
    // 장바구니 버튼 처리
        function cart() {
            const $form = $('<form>').attr('method', 'post').attr('action', '/cart/add').appendTo($('body'));
            $('<input>').attr('type', 'hidden').attr('name', 'pno').val($('#pno').text()).appendTo($form);
            $('<input>').attr('type', 'hidden').attr('name', 'count').val($('#count_of_product').text()).appendTo($form);
            $form.submit();		
        }
    
        $(document).ready(function() {
                // 장바구니 버튼
            $('#cart').click(cart);
        })
    </script>

profile
못하다 보면 잘하게 되는 거야 ・ᴗ・̥̥̥

0개의 댓글