개발/Project(Spring-쇼핑몰)

Project (7-1) 장바구니 담기

잇(IT) 2023. 8. 16. 13:01
728x90

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

 

- 상품 상세 페이지에서 장바구니에 담을 상품의 아이디와 수량을 전달 받을 CartItemDto 클래스를 생성한다. (장바구니에 담을 상품의 최소 수량은 1개 이상으로 제한한다.)

- CartItemDto.java

@Getter
@Setter
public class CartItemDto {

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

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

- 회원 한 명당 1개의 장바구니를 갖어야 한다. 처음 장바구니에 상품을 담을 때는 해당 회원의 장바구니를 최초로 생성해줘야 한다.

- Cart 클래스에 회원 엔티티를 파라미터로 받아서 장바구니 엔티티를 생성하는 로직을 작성한다.

- Cart.java

...

public static Cart createCart(Member member) {
        Cart cart = new Cart();
        cart.setMember(member);
        return cart;
    }

- cart 객체를 생성하고 파라미터로 넘어온 member를 cart 필드의 member로 지정하게 되면서 회원이 입력된 장바구니 객체를 생성하게 된다.


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

- CartItem.java

...

//    장바구니에 담을 상품 엔티티를 생성하는 메소드와 장바구니에 담을 수량을 증가시켜 주는 메소드를 작성
    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;
    }

- createCartItem 메서드의 파라미터로 cart, item, count를 넘김으로서 특정 cart에 담을 item과 해당 item의 갯수를 입력하고 cartItem을 반환한다.

- addCount 메서드는 최초 cart에 item을 담는 것이 아닌 기존에 있던 item을 추가할 때 사용할 메서드다.


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

- CartRepository.java

public interface CartRepository extends JpaRepository<Cart, Long> {

//    현재 로그인한 회원의 Cart 엔티티를 찾기 위해서 CartRepository에 쿼리 메소드를 추가한다.
    Cart findByMemberId(Long memberId);
}

- 파라미터로 넘어온 memberId를 기반으로 cart 엔티티의 데이터들을 조회한다.


- 장바구니에 들어갈 상품을 저장하거나 조회하기 위해서 CartItemRepository 인터페이스를 생성한다.

- CartItemRepository.java

//장바구니에 들어갈 상품을 저장하거나 조회하기 위해서 CartItemRepository 인터페이스를 생성한다.
public interface CartItemRepository extends JpaRepository<CartItem, Long> {

    CartItem findByCartIdAndItemId(Long cartId, Long itemId);
//    카트 아이디와 상품 아이디를 이용해서 상품이 장바구니에 들어있는지 조회한다.
}

- JPA의 메서드명을 통한 쿼리로 데이터를 조회하고 있다.


- 상품 상세 페이지에서 장바구니에 담기 버튼이 눌리게 되면 itemId, count 값이 /cart url로 body에 담겨 post 방식으로 전달된다.

- cartItemDto는 JSON으로 넘어온 데이터를 자신의 필드에 담아 저장한다.

- 전달받은 데이터를 기반으로 controller에서 Service. addCart 메서드를 호출한다.


- 장바구니에 상품을 담는 로직을 작성하기 위한 CartService 클래스를 생성한다.

- CartService.java

//장바구니에 상품을 담는 로직을 작성하기 위한 CartService 클래스
@Service
@RequiredArgsConstructor
@Transactional
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(EntityNotFoundException::new);
//        1. 장바구니에 담을 상품 엔티티를 조회한다.
        Member member = memberRepository.findByEmail(email);
//        2. 현재 로그인한 회원 엔티티를 조회한다

        Cart cart = cartRepository.findByMemberId(member.getId());
//        3. 현재 로그인한 회원의 장바구니 엔티티를 조회한다.
        if (cart == null) {
            cart = Cart.createCart(member);
            cartRepository.save(cart);
//            4. 상품을 처음으로 장바구니에 담을 경우 해당 회원의 장바구니 엔티티를 생성한다.
        }

        CartItem savedCartItem = cartItemRepository.findByCartIdAndItemId(cart.getId(), item.getId());
//        5. 현재 상품이 장바구니에 이미 들어가 있는지 조회한다.

        if (savedCartItem != null) {
            savedCartItem.addCount(cartItemDto.getCount());
//            6. 장바구니에 이미 있던 상품일 경우 기존 수량에 현재 장바구니에 담을 수량 만큼 더해준다.
            return savedCartItem.getId();
        } else {
            CartItem cartItem = CartItem.createCartItem(cart, item, cartItemDto.getCount());
//            7. 장바구니 엔티티, 상품 엔티티, 장바구니에 담을 수량을 이용하여 CartItem 엔티티를 생성한다.
            cartItemRepository.save(cartItem);
//            8. 장바구니에 들어갈 상품을 저장한다.
            return cartItem.getId();
        }
    }
}
public Long addCart(CartItemDto cartItemDto, String email) {
        Item item = itemRepository.findById(cartItemDto.getItemId()).orElseThrow(EntityNotFoundException::new);
//        1. 장바구니에 담을 상품 엔티티를 조회한다.
        Member member = memberRepository.findByEmail(email);
//        2. 현재 로그인한 회원 엔티티를 조회한다

- addCart메서드는 클라이언트로부터 받은 cartItemDto로부터 해당 상품과 갯수를 받아온다. 또 Principal에 담긴 정보를 토대로 email정보를 받아온다.

Cart cart = cartRepository.findByMemberId(member.getId());
//        3. 현재 로그인한 회원의 장바구니 엔티티를 조회한다.
        if (cart == null) {
            cart = Cart.createCart(member);
            cartRepository.save(cart);
//            4. 상품을 처음으로 장바구니에 담을 경우 해당 회원의 장바구니 엔티티를 생성한다.
        }

- cart엔티티를 memberId로부터 조회해서 해당 카트가 존재하는지 확인한다. 한 번도 장바구니에 상품을 담은적이 없으면 cart를 생성하고 cart 엔티티를 저장한다.

CartItem savedCartItem = cartItemRepository.findByCartIdAndItemId(cart.getId(), item.getId());

- cartId와 itemId를 조회하여 장바구니에 해당 상품이 있는지 조회해본다.

if (savedCartItem != null) {
            savedCartItem.addCount(cartItemDto.getCount());
//            6. 장바구니에 이미 있던 상품일 경우 기존 수량에 현재 장바구니에 담을 수량 만큼 더해준다.
            return savedCartItem.getId();
        } else {
            CartItem cartItem = CartItem.createCartItem(cart, item, cartItemDto.getCount());
//            7. 장바구니 엔티티, 상품 엔티티, 장바구니에 담을 수량을 이용하여 CartItem 엔티티를 생성한다.
            cartItemRepository.save(cartItem);
//            8. 장바구니에 들어갈 상품을 저장한다.
            return cartItem.getId();
        }
    }

- 만약 기존에 존재하는 상품일 경우, addCount메서드를 통해 추가로 갯수를 늘린다.

- 기존에 존재하는 상품이 아닐경우, createCartItem 메서드를 통해 새롭게 장바구니에 담길 상품 및 수량을 생성해준 다음, 해당 엔티티를 DB에 저장한다.

- 모든 반환값으로는 cartItem의 Id 값을 반환 받는다.


- 장바구니와 관련된 요청들을 처리하기 위해 CartController를 생성해준다.

- CartController.java

@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()) {
//            1. 장바구니에 담을 상품 정보를 받는 cartItemDto 객체에 데이터 바인딩 시 에러가 있는지 검사한다.
            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();
//        2. 현재 로그인한 회원의 이메일 정보를 변수에 저장한다.
        Long cartItemId;

        try {
            cartItemId = cartService.addCart(cartItemDto, email);
//            3. 화면으로부터 넘어온 장바구니에 담을 상품 정보와 현재 로그인한 회원의 이메일 정보를 이용하여
//            장바구니에 상품을 담는 로직을 호출한다.
        } catch (Exception e) {
            return new ResponseEntity<String>(e.getMessage(),
                    HttpStatus.BAD_REQUEST);
        }

        return new ResponseEntity<Long>(cartItemId, HttpStatus.OK);
//        4. 결과값으로 생성된 장바구니 상품 아이디와 요청이 성공하였다는 HTTP 응답 상태 코드를 반환한다.
    }

- 추후에 View로 작성될 코드의 Javascript 부분에 의해 Controller의 /cart로의 POST 요청이 호출된다.

- bindingResult를 통해 바인딩 시 에러가 있는 지 검사한다.

try {
            cartItemId = cartService.addCart(cartItemDto, email);

- 이상이 없다면 addCart 메서드를 호출하여 장바구니에 상품을 추가해준다. (addCart 메서드에 대해서는 위에서 설명하였다.)

return new ResponseEntity<Long>(cartItemId, HttpStatus.OK);

- 반환값으로 ResponseEntity의 객체를 반환하고 해당 body에는 cartItemId를, HTTP 응답코드는 OK를 담아서 응답한다.


- ItemDtl.html

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

                }
            });
        }

- ItemDtl.html의 페이지에서 입력된 데이터들이 Javascript에 의해 Post방식으로 /cart Url에 요청을 보내게 되면 위에 Controller에서 /cart로 Post 요청을 받아 아래 코드들을 수행하게 된다.

728x90