🧺 장바구니 기능 구현
주문 기능까지 구현했기 때문에 어느정도 쇼핑몰에 관한 핵심적인 기능들은 갖추었다. 이제는 부가적인 기능들을 다룰 생각이다. 온라인 쇼핑몰의 "장바구니" 기능이다. 상품 상세 페이지에서 장바구니에 담을 수량을 선택하고 장바구니 담기 버튼을 클릭할 때 상품이 장바구니에 담기는 기능을 먼저 구현할 것이다.
장바구니같은 경우, "장바구니 자체"인 Cart
와 "장바구니에 담은 상품" CartItem
을 따로 생각해야했다.
지금까지의 기능들을 구현할 때와 마찬가지로 레이어드에 따른 기능 구현은 비슷하다. 다만, CartItem
Entity에서 상품을 장바구니에 담을 때 기존의 수량에서 현재 담을 수량을 더해주는 로직과 같은 디테일한 로직을 구현하는 것이 다른 부분이었다.
상품 상세 페이지에서 장바구니에 담을 상품의 아이디와 수량을 전달 받을 CartItemDto
클래스를 생성한다.
CartItemDto.java
package com.shop.dto;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
@Getter @Setter
public class CartItemDto {
@NotNull(message = "상품 아이디는 필수 입력 값입니다.")
private Long itemId;
@Min(value = 1, message = "최소 1개 이상 담아주세요")
private int count;
}
회원 한 명당 1개의 장바구니를 가진다. 따라서 처음 장바구니에 상품을 담을 때는 해당 회원의 장바구니를 생성해주어야 한다. Member Entity
를 파라미터로 받아서 장바구니 Entity를 생성하게끔 해준다.
Cart.java
package com.shop.entity;
// ..import 생략
@Entity
@Table(name = "cart")
@Getter
@Setter
@ToString
public class Cart extends BaseEntity {
// ..코드 생략
public static Cart createCart(Member member) {
Cart cart = new Cart();
cart.setMember(member);
return cart;
}
}
장바구니에 담을 상품 Entity를 생성하는 메소드와 장바구니에 담을 수량을 증가시켜주는 메소드를 CartItem 클래스에 추가해주어야 한다.
CartItem.java
package com.shop.entity;
// ..기존 임포트 생략
@Entity
@Getter
@Setter
@Table(name="cart_item")
public class CartItem extends BaseEntity {
// ..코드 생략
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;
}
}
현재 로그인한 회원의 Cart Entity
를 찾기 위해 CartRepository
에 쿼리 메소드를 추가한다.
CartRepository.java
package com.shop.repository;
import com.shop.entity.Cart;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CartRepository extends JpaRepository<Cart, Long> {
Cart findByMemberId(Long memberId);
}
또한 장바구니에 들어갈 상품을 저장하거나 조회하기 위해서 com.shop.repository 패키지 아래에 CartItemRepository
인터페이스를 생성한다.
CartItemRepository.java
package com.shop.repository;
import com.shop.entity.CartItem;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CartItemRepository extends JpaRepository<CartItem, Long> {
CartItem findByCartIdAndItemId(Long cartId, Long itemId);
본격적으로 장바구니에 상품을 담는 로직을 만들기 위해 CartService
클래스를 생성한다.
CartService.java
package com.shop.service;
// ..import 생략
@Service
@RequiredArgsConstructor
@Transactional
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();
}
}
CartService
의 코드를 살펴보면 장바구니에 담을 상품 엔티티를 조회하고, 현재 로그인한 회원 엔티티와 장바구니 엔티티를 조회한다. 또한 if (cart == null)
즉, 상품을 처음으로 장바구니에 담을 경우 해당 회원의 장바구니 엔티티를 생성한다.
CartItem savedCartItem =
cartItemRepository.findByCartIdAndItemId(cart.getId(), item.getId());
: 현재 상품이 장바구니에 이미 들어가 있는지 조회한다.
if(savedCartItem != null)
{
savedCartItem.addCount(cartItemDto.getCount());
return savedCartItem.getId();
}
: 장바구니에 이미 있는 상품인 경우, 기존 수량에서 현재 장바구니에 담을 수량만큼 더해준다.
장바구니와 관련된 요청들을 처리하기 위해 CartController
클래스를 생성한다.
CartController.java
package com.shop.controller;
// ..import 생략
@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()) {
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);
}
}
principal.getName();
으로 현재 로그인한 회원의 이메일 정보를 변수에 저장한다.try { cartItemId = cartService.addCart(cartItemDto, email);}
문으로 화면으로부터 넘어온 장바구니에 담을 상품 정보와 현재 로그인한 회원의 이메일 정보를 이용하여 장바구니에 상품을 담는 로직을 호출한다.