
장바구니 부분을 구현하는데 장바구니 아이템과 장바구니를 따로 나누었다.
@OneToMany(mappedBy = "basket")
private List<BasketItem> basketItems = new ArrayList<>();
@OneToMany, mappedBy: 단방향의 1:N 관계를 표시한다. basketItems는 해당 장바구니에 연결된 BasketItem 엔티티들을 나타낸다. mappedBy 속성은 관계의 주인을 지정하는데, 여기서는 BasketItem 엔티티의 basket 필드가 관계의 주인이다.
package com.shopingmall.seungjae.domain;
import jakarta.persistence.*;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Entity @Data
public class Basket {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long basketId;
@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "memberId")
private Member member; //사용자 id와 1대1
@OneToMany(mappedBy = "basket")//1대다 관계 여러개의 basketItem 가짐
private List<BasketItem> basketItems = new ArrayList<>();
public static Basket createBasket(Member member) { // 장바구니 생성 장바구니를 생성하는 정적 메서드.
//Member를 인자로 받아 새로운 Basket 엔티티를 생성하고 member를 설정하여 반환.
Basket basket = new Basket();
basket.setMember(member);
return basket;
}
@Override
public String toString() {
return "Basket{" +
"basketId=" + basketId +
'}';
}
}
package com.shopingmall.seungjae.domain;
import jakarta.persistence.*;
import lombok.Data;
@Data @Entity
public class BasketItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long basketItemId; // 고유 id
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name="basketId")
private Basket basket; // 장바구니 고유 id
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name="itemId")
private Item item; // item id
public static BasketItem createBasketItem(Basket basket, Item item) {
BasketItem basketItem = new BasketItem();
basketItem.setBasket(basket);
basketItem.setItem(item);
return basketItem;
}
@Override
public String toString() {
return "BasketItem{" +
"basketItemId=" + basketItemId +
// 필요한 필드만 출력
'}';
}
}
@Transactional DB에 저장하기 때문에 필수이다.
장바구니에 아이템을 추가하는 메서드 addBasket와 장바구니에서 아이템을 삭제하는 메서드 removeBasket이 있다.
addBasket은 member의 id를 통해 basket을 찾고 newItem을 createBasketItem을 통해 basketItem으로 만들고 이를 basketItemRepository에 저장한다.
이렇게 되면 basketItem은 basket의 id와 newItem의 id를 저장하고 있기 때문에 member의 id에 따라 newItem 리스트를 불러올 수 있다.
removeBasket은 member를 통해 basket을 찾고 basket에서 deleteItem과 같은 basketItem을 찾아 이것이 null이 아니라면 삭제한다.
package com.shopingmall.seungjae.service;
import com.shopingmall.seungjae.domain.Basket;
import com.shopingmall.seungjae.domain.BasketItem;
import com.shopingmall.seungjae.domain.Item;
import com.shopingmall.seungjae.domain.Member;
import com.shopingmall.seungjae.repository.Basket.BasketItemRepository;
import com.shopingmall.seungjae.repository.Basket.BasketRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service @Transactional
@RequiredArgsConstructor
public class BasketService {
private final BasketRepository basketRepository;
private final BasketItemRepository basketItemRepository;
public void addBasket(Member member, Item newItem) {
Basket basket = basketRepository.findByMemberId(member.getId()); //넣을 장바구니
BasketItem basketItem = BasketItem.createBasketItem(basket, newItem);
//TODO 장바구니에 이미 있는 상품이면 추가 불가.
basketItemRepository.save(basketItem);
}
public void removeBasket(Member member, Item deleteItem) {
Basket basket = basketRepository.findByMemberId(member.getId());
// 해당 상품을 장바구니에서 찾아서 삭제
BasketItem basketItem = basket.getBasketItems()
.stream()
.filter(item -> item.getItem().equals(deleteItem))
.findFirst()
.orElse(null);
if (basketItem != null) {
basketItemRepository.delete(basketItem);
}
}
}
총 가격을 지역변수를 선언하고 for문을 통해서 구해준다.
총 개수는 size메서드를 통해 구한다.
package com.shopingmall.seungjae.controller.Basket;
import com.shopingmall.seungjae.controller.Member.SessionConst;
import com.shopingmall.seungjae.domain.Basket;
import com.shopingmall.seungjae.domain.BasketItem;
import com.shopingmall.seungjae.domain.Item;
import com.shopingmall.seungjae.domain.Member;
import com.shopingmall.seungjae.repository.Basket.BasketRepository;
import com.shopingmall.seungjae.repository.Item.ItemRepository;
import com.shopingmall.seungjae.service.BasketService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Controller
@RequestMapping("/basket")
@RequiredArgsConstructor
public class ShoppingMallBasketController {
private final BasketService basketService;
private final ItemRepository itemRepository;
private final BasketRepository basketRepository;
// 내 장바구니 페이지 return
@GetMapping()
public String BasketPage(@SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember, Model model) {
Basket basket = basketRepository.findByMemberId(loginMember.getId());
List<BasketItem> basketItems = basket.getBasketItems();
model.addAttribute("basketItems", basketItems);
model.addAttribute("totalAmount", basketItems.size());
int totalPrice = 0; //총 가격 계산
for (BasketItem basketItem : basketItems) {
totalPrice += basketItem.getItem().getPrice();
}
model.addAttribute("totalPrice",totalPrice);
return "basket/basketPage";
}
// 물품 추가하기
@PostMapping("/{itemId}")
public String AddBasket(@PathVariable Long itemId, @SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember) {
Item item = itemRepository.findById(itemId);
basketService.addBasket(loginMember, item); //Service 참고
return "redirect:/basket";
}
//물품 삭제하기
@GetMapping("/{itemId}")
public String deleteItemAtBasket(@PathVariable Long itemId, @SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember) {
Item item = itemRepository.findById(itemId);
basketService.removeBasket(loginMember, item); //Service 참고
return "redirect:/basket";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>장바구니</title>
</head>
<body>
<h1>장바구니</h1>
<table>
<thead>
<tr>
<th>상품명</th>
<th>가격</th>
</tr>
</thead>
<tbody>
<tr th:each="items : ${basketItems}">
<td th:text="${items.item.itemName}"></td>
<td th:text="${items.item.price}"></td>
<td><button type="button" th:onclick="|location.href = '@{/basket/{itemId}(itemId=${items.item.itemId})}'|">
장바구니에서 상품 삭제하기
</button></td>
</tr>
</tbody>
</table>
<h3>총 합계: <span th:text="${totalPrice}"></span>원</h3>
<h3>총 수량: <span th:text="${totalAmount}"></span>개</h3>
<a href="/checkout">결제하기</a>
<div class="form-group">
<button type="button" onclick="location.href='/';">홈으로 돌아가기</button>
</div>
</body>
</html>
Basket은 memebr_id와 같다.
MemberService에서 join 메서드 내에 아래의 코드를 넣어 사용자가 회원가입할 때 장바구니 역시 자동 생성되도록 해주었다.
Basket basket = Basket.createBasket(member);
basketRepository.save(basket);
제품을 장바구니에 추가하면 아래와 같이 추가 된다. 장바구니에서 상품 삭제하기 를 누르면 상품이 삭제된다.
총 합계는 지역변수를 통해서 for문으로 상품의 가격을 다 더해주었다.
총 수량은 list의 size메서드를 통해 구해서 model.addAttribute 해주었다.
아래는 basket_item 테이블이다. item_id를 외래키로 가지고 있고, basket_id를 가지고 있어 사용자가 누군지를 이 id를 통해 확인한다.

다음번엔 우리 프로젝트의 ERD를 다시 한번 고민해보고, 작성한다. 프로젝트를 어디까지 진행할지를 정해야 할 것 같다.
SQLD자격증 시험이 9월 9일이라 8월 25일까지는 이 중고거래 쇼핑몰 프로젝트를 어느정도 마무리하려고 한다