일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- API
- 멋쟁이사자처럼
- 파이썬 크롤링
- 깃허브
- 멋사 10기
- 멋사
- 멋쟁이 사자처럼
- 멋쟁이사자처럼 서류
- discord
- 멋쟁이사자처럼대학
- 디스코드봇
- django
- 크롤링
- 멋쟁이사자처럼11기
- 기사 제목 크롤링
- 멋사11기
- ㅏㄴ
- 코딩동아리
- 멋사 서류
- 백엔드
- 멋사 합격
- 파이썬
- IT동아리
- 멋사 서류평가
- 멋사12
- 멋사 면접
- 멋사10기
- 알림봇
- 웹동아리
- 멋쟁이사자처럼10기
- Today
- Total
ACHO.pk devlog
[Springboot] 장바구니 기능 본문
1. 장바구니 담기
상품 상세 페이지에서 장바구니에 담을 상품의 아이디와 수량을 전달 받을 클래스를 생성한다.
장바구니에 담을 상품의 최소 수량은 1개 이상으로 제한한다.
dto > CartItemDto
package com.shop.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개의 장바구니를 갖으므로 처음 장바구니에 상품을 담을 때는 해당 회원의 장바구니를 생성해줘야한다.
Cart 클래스에 회원 엔티티를 파라미터로 받아서 장바구니 엔티티를 생성하는 로직을 추가한다.
entity > Cart
package com.shop.shop.entity;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.*;
@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;
}
}
장바구니에 담을 상품 엔티티를 생성하는 메소드와 장바구니에 담을 수량을 증가시켜 주는 메소드를 CartItem클래스에 추가한다.
entity > CartItem
package com.shop.shop.entity;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
@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;
}
}
▹장바구니에 기존에 담겨 있는 상품인데, 해당 상품을 추가로 장바구니에 담을 때 기존 수량에 현재 담을 수량을 더 해줄 때 사용할 메소드이다.
public void addCount(int count){
현재 로그인한 회원의 Cart 엔티티를 찾기 위해서 CartRepository에 쿼리 메소드를 추가한다.
repository > CartRepository
package com.shop.shop.repository;
import com.shop.shop.entity.Cart;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CartRepository extends JpaRepository<Cart, Long> {
Cart findByMemberId(Long memberId);
}
장바구니에 들어갈 상품을 저장하거나 조회하기 위해서 CartItemRepository 인터페이스를 생성한다.
repository > CartItemRepository
package com.shop.shop.repository;
import com.shop.shop.entity.CartItem;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CartItemRepository extends JpaRepository<CartItem, Long> {
CartItem findByCartIdAndItemId(Long cartId, Long itemId);
}
▹카트 아이디와 상품 아이디를 이용해서 상품이 장바구니에 들어있는지 조회한다.
CartItem findByCartIdAndItemId(Long cartId, Long itemId);
장바구니에 상품을 담는 로직을 작성해보자.
service > CartService
package com.shop.shop.service;
import com.shop.shop.dto.CartItemDto;
import com.shop.shop.entity.Cart;
import com.shop.shop.entity.CartItem;
import com.shop.shop.entity.Item;
import com.shop.shop.entity.Member;
import com.shop.shop.repository.CartItemRepository;
import com.shop.shop.repository.CartRepository;
import com.shop.shop.repository.ItemRepository;
import com.shop.shop.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityNotFoundException;
import java.util.ArrayList;
import java.util.List;
import org.thymeleaf.util.StringUtils;
@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();
}
}
}
▹장바구니에 담을 상품 엔티티를 조회한다.
Item item = itemRepository.findById(cartItemDto.getItemId())
▹현재 로그인한 회원 엔티티를 조회한다.
Member member = memberRepository.findByEmail(email);
▹현재 로그인한 회원의 장바구니 엔티티를 조회한다.
Cart cart = cartRepository.findByMemberId(member.getId());
▹상품을 처음으로 장바구니에 담을 경우 해당 회원의 장바구니 엔티티를 생성한다.
if(cart == null){
▹현재 상품이 장바구니에 이미 들어가 있는지 조회한다.
CartItem savedCartItem = cartItemRepository.findByCartIdAndItemId(cart.getId(), item.getId());
▹장바구니에 이미 있던 상품일 경우 기존 수량에 현재 장바구니에 담을 수량 만큼을 더해준다.
savedCartItem.addCount(cartItemDto.getCount());
▹장바구니 엔티티, 상품 엔티티, 장바구니에 담을 수량을 이용해서 CartItem 엔티티를 생성한다.
CartItem cartItem = CartItem.createCartItem(cart, item, cartItemDto.getCount());
▹장바구니에 들어갈 상품을 저장한다.
cartItemRepository.save(cartItem);
장바구니와 관련된 요청을 처리하기 위해서 CartController 클래스를 생성한다.
controller > CartController
package com.shop.shop.controller;
import com.shop.shop.dto.CartItemDto;
import com.shop.shop.service.CartService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.validation.Valid;
import java.security.Principal;
import java.util.List;
@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);
}
}
▹장바구니에 담을 상품 정보를 받는 cartItemDto 객체에 데이터 바인딩 시 에러가 있는지 검사한다.
if(bindingResult.hasErrors()){
▹현재 로그인한 회원의 이메일 정보를 변수에 저장한다.
String email = principal.getName();
▹화면으로부터 넘어온 장바구니에 담을 상품 정보와 현재 로그인한 회원의 이메일 정보를 이용하여 장바구니에 상품을 담는 로직을 호출한다.
cartItemId = cartService.addCart(cartItemDto, email);
▹결과값으로 생성된 장바구니 상품 아이디와 요청이 성공하였다는 HTTP 응답 상태 코드를 반환한다.
return new ResponseEntity<Long>(cartItemId, HttpStatus.OK);
1-1. 테스트 코드
상품을 장바구니에 담는 로직을 테스트해보자
service > CartServiceTest
package com.shop.shop.service;
import com.shop.shop.constant.ItemSellStatus;
import com.shop.shop.dto.CartItemDto;
import com.shop.shop.entity.CartItem;
import com.shop.shop.entity.Item;
import com.shop.shop.entity.Member;
import com.shop.shop.repository.CartItemRepository;
import com.shop.shop.repository.ItemRepository;
import com.shop.shop.repository.MemberRepository;
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.assertEquals;
@SpringBootTest
@Transactional
@TestPropertySource(locations="classpath:application-test.properties")
class CartServiceTest {
@Autowired
ItemRepository itemRepository;
@Autowired
MemberRepository memberRepository;
@Autowired
CartService cartService;
@Autowired
CartItemRepository cartItemRepository;
public Item saveItem(){
Item item = new Item();
item.setItemNm("테스트 상품");
item.setPrice(10000);
item.setItemDetail("테스트 상품 상세 설명");
item.setItemSellStatus(ItemSellStatus.SELL);
item.setStockNumber(100);
return itemRepository.save(item);
}
public Member saveMember(){
Member member = new Member();
member.setEmail("test@test.com");
return memberRepository.save(member);
}
@Test
@DisplayName("장바구니 담기 테스트")
public void addCart(){
Item item = saveItem();
Member member = saveMember();
CartItemDto cartItemDto = new CartItemDto();
cartItemDto.setCount(5);
cartItemDto.setItemId(item.getId());
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());
}
}
▹테스트를 위해서 장바구니에 담을 상품과 회원 정보를 저장하는 메소드를 생성한다.
public Item saveItem(){
public Member saveMember(){
▹장바구니에 담을 상품과 수량을 cartItemDto 객체에 세팅한다.
cartItemDto.setCount(5);
cartItemDto.setItemId(item.getId());
▹상품을 장바구니에 담는 로직 호출 결과 생성된 장바구니 상품 아이디를 cartItemId 변수에 저장한다.
Long cartItemId = cartService.addCart(cartItemDto, member.getEmail());
▹장바구니 상품 아이디를 이용하여 생성된 장바구니 상품 정보를 조회한다.
CartItem cartItem = cartItemRepository.findById(cartItemId)
▹상품 아이디와 장바구니에 저장된 상품 아이디가 같다면 테스트가 통과한다.
assertEquals(item.getId(), cartItem.getItem().getId());
▹장바구니에 담았던 수량과 실제로 장바구니에 저장된 수량이 같다면 테스트가 통과한다.
assertEquals(cartItemDto.getCount(), cartItem.getCount());
테스트 실행 결과 장바구니에 상품을 담는 로직이 정상적으로 동작함을 볼 수 있다.
상품 상세 페이지에서 구현한 장바구니 담기 로직을 호출하는 코드를 스크립트 영역에 추가해보자.
아래 링크에서 코드를 가져오자.
https://github.com/roadbook2/shop/blob/master/src/main/resources/templates/item/itemDtl.html
<장바구니 담기> 버튼을 클릭하면 상품을 장바구니에 담았다는 메시지가 나타나고, <확인>을 누르면 메인 페이지로 이동한다.
2. 장바구니 조회하기
먼저 장바구니 조회 페이지에 전달할 DTO 클래스를 생성하자. JPQL로 쿼리 작성시 생성자를 이용해서 DTO로 바로 반환하는 방법을 알아보자.
dto > CartDetailDto
package com.shop.shop.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class CartDetailDto {
private Long cartItemId; //장바구니 상품 아이디
private String itemNm; //상품명
private int price; //상품 금액
private int count; //수량
private String imgUrl; //상품 이미지 경로
public CartDetailDto(Long cartItemId, String itemNm, int price, int count, String imgUrl){
this.cartItemId = cartItemId;
this.itemNm = itemNm;
this.price = price;
this.count = count;
this.imgUrl = imgUrl;
}
}
▹장바구니 페이지에 전달한 데이터를 생성자의 파라미터로 만들어준다.
public CartDetailDto(Long cartItemId, String itemNm, int price, int count, String imgUrl){
장바구니 페이지에 전달할 CartDetailDto 리스트를 쿼리 하나로 조회하는 JPQL 문을 작성한다.
연관 관계 매핑을 지연 로딩으로 설정할 경우 엔티티에 매핑된 다른 엔티티를 조회할 때 추가적으로 쿼리문이 실행된다.
repository > CartItemRepository
package com.shop.shop.repository;
import com.shop.shop.dto.CartDetailDto;
import com.shop.shop.entity.CartItem;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface CartItemRepository extends JpaRepository<CartItem, Long> {
CartItem findByCartIdAndItemId(Long cartId, Long itemId);
@Query("select new com.shop.shop.dto.CartDetailDto(ci.id, i.itemNm, i.price, ci.count, im.imgUrl) " +
"from CartItem ci, ItemImg im " +
"join ci.item i " +
"where ci.cart.id = :cartId " +
"and im.item.id = ci.item.id " +
"and im.repimgYn = 'Y' " +
"order by ci.regTime desc"
)
List<CartDetailDto> findCartDetailDtoList(Long cartId);
}
▹CartDetailDto의 생성자를 이용하여 DTO를 반환할 때는
new com.shop.shop.dto.CartDetailDto(ci.id, i.itemNm, i.price, ci.count, im.imgUrl) "
처럼 new 키워드와 해당 DTO의 패키지, 클래스명을 적어준다. 또한 생성자의 파라미터 순서는 DTO 클래스에 명시한 순으로 넣어줘야 한다.
@Query("select new com.shop.shop.dto.CartDetailDto(ci.id, i.itemNm, i.price, ci.count, im.imgUrl) " +
▹장바구니에 담겨있는 상품의 대표 이미지만 가지고 오도록 조건문을 작성한다.
"and im.item.id = ci.item.id " +
"and im.repimgYn = 'Y' " +
현재 로그인한 회원의 정보를 이용하여 장바구니에 들어있는 상품을 조회하는 로직을 작성해보자.
service > CartService
package com.shop.shop.service;
import com.shop.shop.dto.CartDetailDto;
import com.shop.shop.dto.CartItemDto;
import com.shop.shop.entity.Cart;
import com.shop.shop.entity.CartItem;
import com.shop.shop.entity.Item;
import com.shop.shop.entity.Member;
import com.shop.shop.repository.CartItemRepository;
import com.shop.shop.repository.CartRepository;
import com.shop.shop.repository.ItemRepository;
import com.shop.shop.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityNotFoundException;
import java.util.ArrayList;
import java.util.List;
import org.thymeleaf.util.StringUtils;
@Service
@RequiredArgsConstructor
@Transactional
public class CartService {
...생략...
@Transactional(readOnly = true)
public List<CartDetailDto> getCartList(String email){
List<CartDetailDto> cartDetailDtoList = new ArrayList<>();
Member member = memberRepository.findByEmail(email);
Cart cart = cartRepository.findByMemberId(member.getId());
if(cart == null){
return cartDetailDtoList;
}
cartDetailDtoList = cartItemRepository.findCartDetailDtoList(cart.getId());
return cartDetailDtoList;
}
}
▹현재 로그인한 회원의 장바구니 엔티티를 조회한다.
Cart cart = cartRepository.findByMemberId(member.getId());
▹장바구니에 상품을 한 번도 안 담았을 경우 장바구니 엔티티가 없으므로 빈 리스트를 반환한다.
if(cart == null){
▹장바구니에 담겨 있는 상품 정보를 조회한다.
cartDetailDtoList = cartItemRepository.findCartDetailDtoList(cart.getId());
장바구니 페이지로 이동할 수 있도록 컨트롤러에 추가하자.
controller > CartController
package com.shop.shop.controller;
import com.shop.shop.dto.CartDetailDto;
import com.shop.shop.dto.CartItemDto;
import com.shop.shop.service.CartService;
import lombok.RequiredArgsConstructor;
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.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.validation.Valid;
import java.security.Principal;
import java.util.List;
@Controller
@RequiredArgsConstructor
public class CartController {
...생략....
@GetMapping(value = "/cart")
public String orderHist(Principal principal, Model model){
List<CartDetailDto> cartDetailList = cartService.getCartList(principal.getName());
model.addAttribute("cartItems", cartDetailList);
return "cart/cartList";
}
}
▹현재 로그인한 사용자의 이메일 정보를 이용하여 장바구니에 담겨있는 상품 정보를 조회한다.
List<CartDetailDto> cartDetailList = cartService.getCartList(principal.getName());
▹조회한 장바구니 상품 정보를 뷰로 전달한다.
model.addAttribute("cartItems", cartDetailList);
조회한 장바구니 상품 정보를 이용하여 장바구니 목록을 보여주는 페이지를 구현해보자.
아래 링크에서 코드를 가져오면 된다.
https://github.com/roadbook2/shop/blob/master/src/main/resources/templates/cart/cartList.html
장바구니에서 상품의 수량을 변경할 경우 실시간으로 해당 회원의 장바구니 상품의 수량도 변경하도록 로직을 추가한다.
entity > CartItem
package com.shop.shop.entity;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
@Entity
@Getter
@Setter
@Table(name="cart_item")
public class CartItem extends BaseEntity{
...생략...
public void updateCount(int count){
this.count = count;
}
}
CartService 클래스에 장바구니 상품의 수량을 업데이트하는 로직을 추가한다. 현재 로그인한 회원과 해당 장바구니 상품을 저장한 회원이 같은지 검사하는 로직도 작성한다.
service > CartService
package com.shop.shop.service;
import com.shop.shop.dto.CartDetailDto;
import com.shop.shop.dto.CartItemDto;
import com.shop.shop.entity.Cart;
import com.shop.shop.entity.CartItem;
import com.shop.shop.entity.Item;
import com.shop.shop.entity.Member;
import com.shop.shop.repository.CartItemRepository;
import com.shop.shop.repository.CartRepository;
import com.shop.shop.repository.ItemRepository;
import com.shop.shop.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityNotFoundException;
import java.util.ArrayList;
import java.util.List;
import org.thymeleaf.util.StringUtils;
@Service
@RequiredArgsConstructor
@Transactional
public class CartService {
...생략...
@Transactional(readOnly = true)
public boolean validateCartItem(Long cartItemId, String email){
Member curMember = memberRepository.findByEmail(email);
CartItem cartItem = cartItemRepository.findById(cartItemId)
.orElseThrow(EntityNotFoundException::new);
Member savedMember = cartItem.getCart().getMember();
if(!StringUtils.equals(curMember.getEmail(), savedMember.getEmail())){
return false;
}
return true;
}
public void updateCartItemCount(Long cartItemId, int count){
CartItem cartItem = cartItemRepository.findById(cartItemId)
.orElseThrow(EntityNotFoundException::new);
cartItem.updateCount(count);
}
}
▹현재 로그인한 회원을 조회한다.
Member curMember = memberRepository.findByEmail(email);
▹장바구니 상품을 저장한 회원을 조회한다.
Member savedMember = cartItem.getCart().getMember();
▹현재 로그인한 회원과 장바구니 상품을 저장한 회원이 다를 경우 false, 같으면 true를 반환한다.
if(!StringUtils.equals(curMember.getEmail(), savedMember.getEmail())){
return true;
▹장바구니 상품의 수량을 업데이트하는 메소드이다.
public void updateCartItemCount(Long cartItemId, int count){
장바구니 상품의 수량을 업데이트하는 요청을 처리할 수 있도록 로직을 추가한다.
controller > CartController
package com.shop.shop.controller;
import com.shop.shop.dto.CartDetailDto;
import com.shop.shop.dto.CartItemDto;
import com.shop.shop.service.CartService;
import lombok.RequiredArgsConstructor;
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;
@Controller
@RequiredArgsConstructor
public class CartController {
...생략...
@PatchMapping(value = "/cartItem/{cartItemId}")
public @ResponseBody ResponseEntity updateCartItem(@PathVariable("cartItemId") Long cartItemId, int count, Principal principal){
if(count <= 0){
return new ResponseEntity<String>("최소 1개 이상 담아주세요", HttpStatus.BAD_REQUEST);
} else if(!cartService.validateCartItem(cartItemId, principal.getName())){
return new ResponseEntity<String>("수정 권한이 없습니다.", HttpStatus.FORBIDDEN);
}
cartService.updateCartItemCount(cartItemId, count);
return new ResponseEntity<Long>(cartItemId, HttpStatus.OK);
}
}
▹HTTP 메소드에서 PATCH는 요청된 자원의 일부를 업데이트할 때 PATCH를 사용한다. 장바구니 상품의 수량만 업데이트하기 때문에 @PatchMapping을 사용한다.
@PatchMapping(value = "/cartItem/{cartItemId}")
▹장바구니에 담겨있는 상품의 개수를 0개 이하로 업데이트 요청을 할 때 에러 메시지를 담아서 반환한다.
if(count <= 0){
▹수정 권한을 체크한다.
} else if(!cartService.validateCartItem(cartItemId, principal.getName())){
▹장바구니 상품의 개수를 업데이트한다.
cartService.updateCartItemCount(cartItemId, count);
상품 정보에 있는 x 버튼을 클릭할 때 장바구니에 넣어 놓은 상품을 삭제해보자.
장바구니 상품 번호를 파라미터로 받아서 삭제하는 로직을 추가한다.
servicer > CartService
package com.shop.shop.service;
import com.shop.shop.dto.CartDetailDto;
import com.shop.shop.dto.CartItemDto;
import com.shop.shop.entity.Cart;
import com.shop.shop.entity.CartItem;
import com.shop.shop.entity.Item;
import com.shop.shop.entity.Member;
import com.shop.shop.repository.CartItemRepository;
import com.shop.shop.repository.CartRepository;
import com.shop.shop.repository.ItemRepository;
import com.shop.shop.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityNotFoundException;
import java.util.ArrayList;
import java.util.List;
import org.thymeleaf.util.StringUtils;
@Service
@RequiredArgsConstructor
@Transactional
public class CartService {
...생략...
public void deleteCartItem(Long cartItemId) {
CartItem cartItem = cartItemRepository.findById(cartItemId)
.orElseThrow(EntityNotFoundException::new);
cartItemRepository.delete(cartItem);
}
}
장바구니 상품을 삭제하는 요청을 처리할 수 있도록 로직을 추가한다.
controller > CartController
package com.shop.shop.controller;
import com.shop.shop.dto.CartDetailDto;
import com.shop.shop.dto.CartItemDto;
import com.shop.shop.service.CartService;
import lombok.RequiredArgsConstructor;
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;
@Controller
@RequiredArgsConstructor
public class CartController {
...생략...
@DeleteMapping(value = "/cartItem/{cartItemId}")
public @ResponseBody ResponseEntity deleteCartItem(@PathVariable("cartItemId") Long cartItemId, Principal principal){
if(!cartService.validateCartItem(cartItemId, principal.getName())){
return new ResponseEntity<String>("수정 권한이 없습니다.", HttpStatus.FORBIDDEN);
}
cartService.deleteCartItem(cartItemId);
return new ResponseEntity<Long>(cartItemId, HttpStatus.OK);
}
}
▹HTTP 메소드에서 DELETE의 경우 요청된 자원을 삭제할 때 사용한다. 장바구니 상품을 삭제하기 때문에 @DeleteMapping을 사용한다.
@DeleteMapping(value = "/cartItem/{cartItemId}")
▹수정 권한을 체크한다.
if(!cartService.validateCartItem(cartItemId, principal.getName())){
▹해당 장바구니 상품을 삭제한다.
cartService.deleteCartItem(cartItemId);
3. 장바구니 상품 주문하기
장바구니 페이지에서 주문할 상품 데이터를 전달할 DTO를 생성하자.
dto > CartOrderDto
package com.shop.shop.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class CartOrderDto {
private Long cartItemId;
private List<CartOrderDto> cartOrderDtoList;
}
▹장바구니에서 여러 개의 상품을 주문하므로 CartOrderDto 클래스가 자기 자신을 List로 가지고 있도록 만들자
private List<CartOrderDto> cartOrderDtoList;
장바구니에서 주문할 상품 데이터를 전달받아서 주문을 생성하는 로직을 만들어보자
service > OrderService
package com.shop.shop.service;
import com.shop.shop.dto.OrderItemDto;
import com.shop.shop.entity.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.shop.shop.dto.OrderDto;
import com.shop.shop.dto.OrderHistDto;
import com.shop.shop.repository.ItemImgRepository;
import com.shop.shop.repository.ItemRepository;
import com.shop.shop.repository.MemberRepository;
import com.shop.shop.repository.OrderRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.thymeleaf.util.StringUtils;
import javax.persistence.EntityNotFoundException;
import java.util.ArrayList;
import java.util.List;
@Service
@Transactional
@RequiredArgsConstructor
public class OrderService {
...생략....
public Long orders(List<OrderDto> orderDtoList, String email){
Member member = memberRepository.findByEmail(email);
List<OrderItem> orderItemList = new ArrayList<>();
for (OrderDto orderDto : orderDtoList) {
Item item = itemRepository.findById(orderDto.getItemId())
.orElseThrow(EntityNotFoundException::new);
OrderItem orderItem = OrderItem.createOrderItem(item, orderDto.getCount());
orderItemList.add(orderItem);
}
Order order = Order.createOrder(member, orderItemList);
orderRepository.save(order);
return order.getId();
}
}
▹주문할 상품 리스트를 만들어 준다.
for (OrderDto orderDto : orderDtoList) {
▹현재 로그인한 회원과 주문 상품 목록을 이용하여 주문 엔티티를 만든다.
Order order = Order.createOrder(member, orderItemList);
▹주문 데이터를 저장한다.
orderRepository.save(order);
주문 로직으로 전달할 orderDto 리스트 생성 및 주문 로직 호출, 주문한 상품은 장바구니에서 제거하는 로직을 구현한다.
service > CartService
package com.shop.shop.service;
import com.shop.shop.dto.CartDetailDto;
import com.shop.shop.dto.CartItemDto;
import com.shop.shop.dto.CartOrderDto;
import com.shop.shop.dto.OrderDto;
import com.shop.shop.entity.Cart;
import com.shop.shop.entity.CartItem;
import com.shop.shop.entity.Item;
import com.shop.shop.entity.Member;
import com.shop.shop.repository.CartItemRepository;
import com.shop.shop.repository.CartRepository;
import com.shop.shop.repository.ItemRepository;
import com.shop.shop.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityNotFoundException;
import java.util.ArrayList;
import java.util.List;
import org.thymeleaf.util.StringUtils;
@Service
@RequiredArgsConstructor
@Transactional
public class CartService {
...생략...
public Long orderCartItem(List<CartOrderDto> cartOrderDtoList, String email){
List<OrderDto> orderDtoList = new ArrayList<>();
for (CartOrderDto cartOrderDto : cartOrderDtoList) {
CartItem cartItem = cartItemRepository
.findById(cartOrderDto.getCartItemId())
.orElseThrow(EntityNotFoundException::new);
OrderDto orderDto = new OrderDto();
orderDto.setItemId(cartItem.getItem().getId());
orderDto.setCount(cartItem.getCount());
orderDtoList.add(orderDto);
}
Long orderId = orderService.orders(orderDtoList, email);
for (CartOrderDto cartOrderDto : cartOrderDtoList) {
CartItem cartItem = cartItemRepository
.findById(cartOrderDto.getCartItemId())
.orElseThrow(EntityNotFoundException::new);
cartItemRepository.delete(cartItem);
}
return orderId;
}
}
▹장바구니 페이지에서 전달받은 주문 상품 번호를 이용하여 주문 로직으로 전달할 orderDto 객체를 만든다.
for (CartOrderDto cartOrderDto : cartOrderDtoList) {
▹장바구니에 담은 상품을 주문하도록 주문 로직을 호출한다.
Long orderId = orderService.orders(orderDtoList, email);
▹주문한 상품들을 장바구니에서 제거한다.
for (CartOrderDto cartOrderDto : cartOrderDtoList) {
장바구니 상품의 수량을 업데이트하는 요청을 처리할 수 있도록 로직을 추가한다.
controller > CartController
package com.shop.shop.controller;
import com.shop.shop.dto.CartDetailDto;
import com.shop.shop.dto.CartItemDto;
import com.shop.shop.dto.CartOrderDto;
import com.shop.shop.service.CartService;
import lombok.RequiredArgsConstructor;
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;
@Controller
@RequiredArgsConstructor
public class CartController {
...생략...
@PostMapping(value = "/cart/orders")
public @ResponseBody ResponseEntity orderCartItem(@RequestBody CartOrderDto cartOrderDto, Principal principal){
List<CartOrderDto> cartOrderDtoList = cartOrderDto.getCartOrderDtoList();
if(cartOrderDtoList == null || cartOrderDtoList.size() == 0){
return new ResponseEntity<String>("주문할 상품을 선택해주세요", HttpStatus.FORBIDDEN);
}
for (CartOrderDto cartOrder : cartOrderDtoList) {
if(!cartService.validateCartItem(cartOrder.getCartItemId(), principal.getName())){
return new ResponseEntity<String>("주문 권한이 없습니다.", HttpStatus.FORBIDDEN);
}
}
Long orderId = cartService.orderCartItem(cartOrderDtoList, principal.getName());
return new ResponseEntity<Long>(orderId, HttpStatus.OK);
}
}
▹주문할 상품을 선택하지 않았는지 체크한다.
if(cartOrderDtoList == null || cartOrderDtoList.size() == 0){
▹주문 권한을 체크한다.
for (CartOrderDto cartOrder : cartOrderDtoList) {
▹주문 로직 호출 결과 생성된 주문 번호를 반환받는다.
Long orderId = cartService.orderCartItem(cartOrderDtoList, principal.getName());
▹생성된 주문 번호와 요청이 성공했다는 HTTP 응답 상태 코드를 반환한다.
return new ResponseEntity<Long>(orderId, HttpStatus.OK);
이렇게 하면
요청이 성공했을 때 주문이 완료되었다는 메시지가 나타난다. <확인> 버튼을 클릭하면 구매 이력 페이지로 이동하며 장바구니에 담았던 상품들이 정상적으로 주문이 됐다는 것을 확인할 수 있다.
'프레임워크 > Springboot' 카테고리의 다른 글
[Springboot] 주문 이력 조회 및 주문 취소하기 (0) | 2023.03.21 |
---|---|
[Springboot] 상품 주문 기능 구현하기 (0) | 2023.03.20 |
[Springboot] 등록 상품 메인 화면에서 보기 / 상품 상세 페이지 (0) | 2023.03.15 |
[Springboot] 상품 관리하기 (2) | 2023.03.14 |
[Springboot] 상품 수정하기 (0) | 2023.03.13 |