#9 [스프링 스터디] 쇼핑몰 만들기 프로젝트 - 장바구니 기능 구현

myeonji·2022년 1월 30일
4

쇼핑몰에서 구매자 기능에 해당하는 장바구니를 구현해보겠습니다!

1. Cart Entity, CartItem Entity를 생성합니다.

  • 1:N 이기 때문에 CartItem에서 @ManyToOne 연관관계를 설정합니다. 양방향으로 설정할 것이기 때문에 Cart에는 @OneToMany 입니다.
  • Cart에 User과 @OneToOne 연관관계가 설정되어 있습니다. 쇼핑몰에는 구매자 한 명에게는 장바구니 하나가 있기 때문입니다.

< Cart >

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Entity
public class Cart {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @OneToOne(fetch = FetchType.EAGER)
    @JoinColumn(name="user_id")
    private User user; // 구매자

    private int count; // 카트에 담긴 총 상품 개수

    @OneToMany(mappedBy = "cart")
    private List<CartItem> cartItems = new ArrayList<>();

    @DateTimeFormat(pattern = "yyyy-mm-dd")
    private LocalDate createDate; // 날짜

    @PrePersist
    public void createDate(){
        this.createDate = LocalDate.now();
    }

    public static Cart createCart(User user) {
        Cart cart = new Cart();
        cart.setCount(0);
        cart.setUser(user);
        return cart;
    }

}

< CartItem >

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Entity
public class CartItem {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name="cart_id")
    private Cart cart;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name="item_id")
    private Item item;

    private int count; // 상품 개수

    public static CartItem createCartItem(Cart cart, Item item, int amount) {
        CartItem cartItem = new CartItem();
        cartItem.setCart(cart);
        cartItem.setItem(item);
        cartItem.setCount(amount);
        return cartItem;
    }

    // 이미 담겨있는 물건 또 담을 경우 수량 증가
    public void addCount(int count) {
        this.count += count;
    }

}

2. 상품 상세 페이지 html에서 장바구니 버튼을 만듭니다.

  • 장바구니 버튼을 누르면 수량과 item 정보를 Controller에게 넘깁니다.
  • form 태그에서 역할이 ROLE_USER 일 경우에만 가능하도록 렌더링 하였습니다.
  • input 태그로 구매자로부터 수량을 입력받습니다.
<form class="d-flex" sec:authorize="hasRole('ROLE_USER')" th:action="@{/user/cart/{id}/{itemId}(id=${user.id}, itemId=${item.id})}" method="post">
                    <div class="d-flex">
                    <input class="form-control text-center me-3" id="amount" name="amount" type="num" value="1"
                               style="max-width: 3rem"/>
                        <button class="btn btn-outline-dark flex-shrink-0" type="submit">
                            <i class="bi-cart-fill me-1"></i>
                            장바구니
                        </button>
                    </div>
                </form>

3. 장바구니 Controller에서 데이터를 받아 처리합니다. (POST)

  • 매개변수 amount는 html에서 사용자에게 입력받은 상품 수량 입니다.
  • Service에서 로직을 수행하기 위해 user, item, amount를 넘겨줍니다!

< Controller >

// 장바구니에 물건 넣기
    @PostMapping("/user/cart/{id}/{itemId}")
    public String addCartItem(@PathVariable("id") Integer id, @PathVariable("itemId") Integer itemId, int amount) {

        User user = userPageService.findUser(id);
        Item item = itemService.itemView(itemId);

        cartService.addCart(user, item, amount);

        return "redirect:/item/view/{itemId}";
    }

4. cartService에서 addCart 메소드로 로직을 수행합니다.

  • 매개변수로 받은 상품이 장바구니에 이미 존재하는지 여부를 확인합니다.
  • 존재하지 않는다면 CartItem 객체를 새로 만들어서 저장하고, 존재한다면 수량만 amount만큼 증가시켜주면 될 것입니다!

< CartService >

// 장바구니 담기
@Transactional
public void addCart(User user, Item newItem, int amount) {

    // 유저 id로 해당 유저의 장바구니 찾기
    Cart cart = cartRepository.findByUserId(user.getId());

    // 장바구니가 존재하지 않는다면
    if (cart == null) {
        cart = Cart.createCart(user);
        cartRepository.save(cart);
    }

    Item item = itemRepository.findItemById(newItem.getId());
    CartItem cartItem = cartItemRepository.findByCartIdAndItemId(cart.getId(), item.getId());

    // 상품이 장바구니에 존재하지 않는다면 카트상품 생성 후 추가
    if (cartItem == null) {
        cartItem = CartItem.createCartItem(cart, item, amount);
        cartItemRepository.save(cartItem);
    }

    // 상품이 장바구니에 이미 존재한다면 수량만 증가
    else {
        CartItem update = cartItem;
        update.setCart(cartItem.getCart());
        update.setItem(cartItem.getItem());
        update.addCount(amount);
        update.setCount(update.getCount());
        cartItemRepository.save(update);
    }

    // 카트 상품 총 개수 증가
    cart.setCount(cart.getCount()+amount);

}

이렇게 장바구니에 물건이 담기는 것을 확인할 수 있습니다!!

장바구니 물건 추가 기능 외에도 장바구니 페이지 (GET), 물건 삭제 기능도 있습니다.
상품 CRUD 기능과 유사하기 때문에 이를 응용하여 쉽게 구현할 수 있습니다!!

< 장바구니 페이지 접속 >

  • 장바구니 페이지 (userCart.html) 에 해당 구매자의 Cart에 들어있는 cartItem들을 model 로 넘겨주었습니다.
// 장바구니 페이지 접속
@GetMapping("/user/cart/{id}")
public String userCartPage(@PathVariable("id") Integer id, Model model, @AuthenticationPrincipal PrincipalDetails principalDetails) {
    // 로그인이 되어있는 유저의 id와 장바구니에 접속하는 id가 같아야 함
    if (principalDetails.getUser().getId() == id) {

        User user = userPageService.findUser(id);
        // 로그인 되어 있는 유저에 해당하는 장바구니 가져오기
        Cart userCart = user.getCart();

        // 장바구니에 들어있는 아이템 모두 가져오기
        List<CartItem> cartItemList = cartService.allUserCartView(userCart);

        // 장바구니에 들어있는 상품들의 총 가격
        int totalPrice = 0;
        for (CartItem cartitem : cartItemList) {
            totalPrice += cartitem.getCount() * cartitem.getItem().getPrice();
        }

        model.addAttribute("totalPrice", totalPrice);
        model.addAttribute("totalCount", userCart.getCount());
        model.addAttribute("cartItems", cartItemList);
        model.addAttribute("user", userPageService.findUser(id));

        return "/user/userCart";
    }
    // 로그인 id와 장바구니 접속 id가 같지 않는 경우
    else {
        return "redirect:/main";
    }
}

< 장바구니에서 상품 삭제 >

  • 삭제할 상품의 id를 매개변수로 받습니다.
  • CartItem에서 삭제시킨 후, Cart의 총 개수를 감소시킵니다.
// 장바구니에서 물건 삭제
// 삭제하고 남은 상품의 총 개수
@GetMapping("/user/cart/{id}/{cartItemId}/delete")
public String deleteCartItem(@PathVariable("id") Integer id, @PathVariable("cartItemId") Integer itemId, Model model, @AuthenticationPrincipal PrincipalDetails principalDetails) {
    // 로그인 유저 id와 장바구니 유저의 id가 같아야 함
    if (principalDetails.getUser().getId() == id) {
        // itemId로 장바구니 상품 찾기
        CartItem cartItem = cartService.findCartItemById(itemId);

        // 장바구니 물건 삭제
        cartService.cartItemDelete(itemId);

        // 해당 유저의 카트 찾기
        Cart userCart = cartService.findUserCart(id);

        // 해당 유저의 장바구니 상품들
        List<CartItem> cartItemList = cartService.allUserCartView(userCart);

        // 총 가격 += 수량 * 가격
        int totalPrice = 0;
        for (CartItem cartitem : cartItemList) {
            totalPrice += cartitem.getCount() * cartitem.getItem().getPrice();
        }

        // 총 개수 += 수량
        //int totalCount = 0;
        //for (CartItem cartitem : cartItemList) {
        //    totalCount += cartitem.getCount();
        //}
        userCart.setCount(userCart.getCount()-cartItem.getCount());

        model.addAttribute("totalPrice", totalPrice);
        model.addAttribute("totalCount", userCart.getCount());
        model.addAttribute("cartItems", cartItemList);
        model.addAttribute("user", userPageService.findUser(id));

        return "/user/userCart";
    }
    // 로그인 id와 장바구니 삭제하려는 유저의 id가 같지 않는 경우
    else {
        return "redirect:/main";
    }
}

이렇게 하면 구매자의 장바구니 기능 설계 끝입니다~!

다음은 주문을 다뤄보겠습니다!

주문은 장바구니 상품 한 번에 전체 주문 / 상품 개별로 주문 으로 나누어 구현해보겠습니다.
화이띵 !!

profile
DBA, 경제 그리고 고냥이

0개의 댓글