15. 장바구니(작성중)

진크·2022년 3월 13일
0
post-thumbnail

장바구니 담기

상품 상세 페이지에서 장바구니에 담을 수량을 선택하고 장바구니 담기 버튼을 클릭할 때 상품이 장바구니에 담기는 기능을 먼저 구현하겠습니다.

CartItemDto 생성

package me.jincrates.gobook.web.dto;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

@Getter
@NoArgsConstructor
public class CartItemDto {

    @NotNull(message = "상품 아이디는 필수 입력 값입니다.")
    private Long itemId;

    @Min(value = 1, message = "최소 1개 이상 담아주세요.")
    private int count;

    @Builder
    public CartItemDto(Long itemId, int count) {
        this.itemId = itemId;
        this.count = count;
    }
}

Cart

회원 한 명당 1개의 장바구니를 갖으므로 Cart 클래스에 회원 엔티티를 파라미터로 받아서 장바구니 엔티티를 생성하는 로직을 추가합니다.

package me.jincrates.gobook.domain.carts;

///...
@NoArgsConstructor
@Table(name = "cart")
@Entity
public class Cart extends BaseEntity {

    ///...

    public static Cart createCart(Member member) {
        Cart cart = Cart.builder()
                .member(member)
                .build();
        return cart;
    }
}

CartItem

장바구니에 담을 상품 엔티티를 생성하는 메소드와 장바구니에 담을 수량을 증가시켜 주는 메소드를 CartItem 클래스에 추가합니다.

package me.jincrates.gobook.domain.carts;

//...

@NoArgsConstructor
@Getter
@Table(name = "cart_item")
@Entity
public class CartItem extends BaseEntity {

    //...

    public static CartItem createCartItem(Cart cart, Item item, int count) {
        CartItem cartItem = CartItem.builder()
                .cart(cart)
                .item(item)
                .count(count)
                .build();

        return cartItem;
    }

    public void addCount(int count) {
        this.count += count;
    }
}

CartRepository

현재 로그인한 회원의 Cart 엔티티를 찾기 위해서 쿼리 메소드를 추가합니다.

package me.jincrates.gobook.domain.carts;

import org.springframework.data.jpa.repository.JpaRepository;

public interface CartRepository extends JpaRepository<Cart, Long> {

    Cart findByMemberId(Long memberId);

}

CartItemRepository

package me.jincrates.gobook.domain.carts;

import me.jincrates.gobook.web.dto.CartDetailDto;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface CartItemRepository extends JpaRepository<CartItem, Long> {

    CartItem findByCartIdAndItemId(Long cartId, Long itemId);
}

CartService

장바구니에 상품을 담는 로직을 작성합니다.

package me.jincrates.gobook.service;

import lombok.RequiredArgsConstructor;
import me.jincrates.gobook.domain.carts.Cart;
import me.jincrates.gobook.domain.carts.CartItem;
import me.jincrates.gobook.domain.carts.CartItemRepository;
import me.jincrates.gobook.domain.carts.CartRepository;
import me.jincrates.gobook.domain.items.Item;
import me.jincrates.gobook.domain.items.ItemRepository;
import me.jincrates.gobook.domain.members.Member;
import me.jincrates.gobook.domain.members.MemberRepository;
import me.jincrates.gobook.domain.orders.Order;
import me.jincrates.gobook.web.dto.CartDetailDto;
import me.jincrates.gobook.web.dto.CartItemDto;
import me.jincrates.gobook.web.dto.CartOrderDto;
import me.jincrates.gobook.web.dto.OrderDto;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.util.StringUtils;

import javax.persistence.EntityNotFoundException;
import java.util.ArrayList;
import java.util.List;

@Transactional
@RequiredArgsConstructor
@Service
public class CartService {

    private final ItemRepository itemRepository;
    private final MemberRepository memberRepository;
    private final CartRepository cartRepository;
    private final CartItemRepository cartItemRepository;
    private final OrderService orderService;

    public Long addCart(CartItemDto cartItemDto, String email) {

        Item item = itemRepository.findById(cartItemDto.getItemId())
                .orElseThrow(EntityNotFoundException::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();
        }
    }
}

CartController

package me.jincrates.gobook.web;

import lombok.RequiredArgsConstructor;
import me.jincrates.gobook.service.CartService;
import me.jincrates.gobook.web.dto.CartDetailDto;
import me.jincrates.gobook.web.dto.CartItemDto;
import me.jincrates.gobook.web.dto.CartOrderDto;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.security.Principal;
import java.util.List;

@RequiredArgsConstructor
@Controller
public class CartController {

    private final CartService cartService;

    @PostMapping(value = "/cart")
    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 {
            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 me.jincrates.gobook.service;

import me.jincrates.gobook.domain.carts.CartItem;
import me.jincrates.gobook.domain.carts.CartItemRepository;
import me.jincrates.gobook.domain.items.Item;
import me.jincrates.gobook.domain.items.ItemRepository;
import me.jincrates.gobook.domain.items.ItemSellStatus;
import me.jincrates.gobook.domain.members.Member;
import me.jincrates.gobook.domain.members.MemberRepository;
import me.jincrates.gobook.web.dto.CartItemDto;
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 javax.persistence.EntityNotFoundException;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
@Transactional
@TestPropertySource(properties = {"spring.config.location=classpath:application-test.yml"})
public class CartServiceTest {

    @Autowired
    ItemRepository itemRepository;

    @Autowired
    MemberRepository memberRepository;

    @Autowired
    CartService cartService;

    @Autowired
    CartItemRepository cartItemRepository;

    public Item saveItem() {
        Item item = Item.builder()
                .itemNm("테스트 상품")
                .price(10000)
                .itemDetail("테스트 상품 설명")
                .itemSellStatus(ItemSellStatus.SELL)
                .stockNumber(100)
                .build();
        return itemRepository.save(item);
    }

    public Member saveMember() {
        Member member = Member.builder()
                .email("test@email.com")
                .build();
        return memberRepository.save(member);
    }

    @Test
    @DisplayName("장바구니 담기 테스트")
    public void addCart() {
        Item item = saveItem();
        Member member = saveMember();

        CartItemDto cartItemDto = CartItemDto.builder()
                .itemId(item.getId())
                .count(5)
                .build();

        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());
    }
}

장바구니 담기 호출 구현하기 - itemDetail.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layouts/default}">

<!-- 사용자 스크립트 추가 -->
<th:block layout:fragment="script">
    <script th:inline="javascript">

        <!-- 코드 생략 -->

        function addCart() {
            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.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 = '/';
                },
                error: function(jqXHR, status, error) {

                    if (jqXHR.status == '401') {
                        alert('로그인 후 이용해주세요.');
                        location.href = '/members/login';
                    } else {
                        alert(jqXHR.responseText);
                    }
                }
            });
        }
    </script>
</th:block>

<!-- 코드 생략 -->
<button type="button" class="btn btn-light border btn-outline-dark btn-lg" onclick="addCart()">장바구니 담기</button>
<!-- 코드 생략 -->

</html>

장바구니 조회하기

장바구니 상품 주문하기

profile
철학있는 개발자 - 내가 무지하다는 것을 인정할 때 비로소 배움이 시작된다.

0개의 댓글