1) 프로젝트 진행도
- 가게 CRUD 구현
가게 생성가게 수정가게 단건 조회(메뉴 같이 조회)가게 이름 검색(이름으로 검색히 연관된 가게 다 조회)가게 페업
- 메뉴 CRUD 구현
메뉴 생성(1일차)메뉴 삭제메뉴 수정(2일차)
- 장바구니 (쿠키 활용)
장바구니 메뉴 추가장바구니 메뉴 주문 처리(3일차)
- 장바구니 메뉴 추가
public List<BasketItemDto> addBasket(OrderRequestDto requestDto, Cookie basketCookie, HttpServletResponse response) {
List<BasketItemDto> basket = parseBasketFromCookie(basketCookie);
//메뉴 가져오기
Menu menu = menuRepository.findByMenuOrElseThrow(requestDto.getMenuId());
//한 가게의 메뉴만 허용
if(!basket.isEmpty() && !basket.get(0).getStoreId().equals(menu.getStore().getId())) {
// 다른 가게 메뉴가 담긴 경우 초기화
basket.clear();
}
//장바구니에 메뉴 넣기
BasketItemDto basketItem = new BasketItemDto(
menu.getId(),
menu.getStore().getId(),
requestDto.getCount()
);
basket.add(basketItem);
//쿠키에 저장
saveBasketToCookie(basket, response);
return basket;
}
- 장바구니에 메뉴를 추가하는 서비스 로직
- 맨 처음 JSON 형태로 데이터를 받기 위해 파싱을 진행.
- 다른 가게의 메뉴를 가져오면 basket이 초기화 되도록 조건.
- 메뉴를 계속 saveBasketToCookie에 저장.
- 장바구니 주문 처리
@Transactional
public OrderResponseDto basketOrder(User loginUser, Cookie basketCookie, HttpServletResponse response) {
List<BasketItemDto> basket = parseBasketFromCookie(basketCookie);
List<Store> stores = new ArrayList<>();
if (basket.isEmpty()) {
throw new CustomException(ErrorCode.EMPTY_BASKET);
}
// 주문 생성
Orders order = new Orders(loginUser,OrderStep.ORDER_COMPLETED);
Orders savedOrder = orderRepository.save(order);
for (BasketItemDto basketItem : basket) {
Menu menu = menuRepository.findByMenuOrElseThrow(basketItem.getMenuId());
Store store = menu.getStore();
stores.add(store);
orderMenuRepository.insertOrderMenu(savedOrder.getId(), menu.getId(), basketItem.getCount());
}
List<OrderDto> ordersList = orderMenuRepository.getOrderMenus(savedOrder.getId());
Integer totalPrice = ((BigDecimal) orderMenuRepository.findTotalPrice(savedOrder.getId())).intValue();
// 총 가격 업데이트
orderRepository.updateTotalPrice(totalPrice, savedOrder.getId());
orderRepository.updateStore(stores.get(0), savedOrder.getId());
// 장바구니 비우기
clearBasketCookie(response);
return OrderResponseDto.builder()
.id(savedOrder.getId())
.storeId(basket.get(0).getStoreId())
.order(ordersList)
.totalPrice(totalPrice)
.createdAt(savedOrder.getCreatedAt())
.build();
}
- 맨 처음 역시 파싱을 진행하여 JSON 데이터를 역직렬화를 통해 받기.
- 주요 메서드들
- 역직렬화 메서드
/**
* 쿠키에서 장바구니 데이터를 읽어와 객체로 변환(역직렬화)
* @param basketCookie
* @return
*/
private List<BasketItemDto> parseBasketFromCookie(Cookie basketCookie) {
//쿠키가 없으면 빈 장바구니로 반환
if (basketCookie == null) {
return new ArrayList<>();
}
try {
// URL 디코딩
String decodedBasket = URLDecoder.decode(basketCookie.getValue(), "UTF-8");
//JSON 문자열인 쿠키 값을 List<BasketItemDto>로 역직렬화, //제네릭 타입을 처리하기 위한 도구.
return objectMapper.readValue(decodedBasket, new TypeReference<List<BasketItemDto>>() {});
} catch (JsonProcessingException | UnsupportedEncodingException e) {
throw new CustomException(ErrorCode.BAD_REQUEST);
}
}
- 직렬화 메서드
/**
* 장바구니 데이터를 JSON으로 직렬화하여 쿠키에 저장
* @param basket
* @param response
*/
private void saveBasketToCookie(List<BasketItemDto> basket, HttpServletResponse response) {
try {
//List<BasketItemDto> 객체를 JSON 문자열로 직렬화
String basketJson = objectMapper.writeValueAsString(basket);
// URL 인코딩
String encodedBasket = URLEncoder.encode(basketJson, "UTF-8");
//직렬화된 JSON 데이터를 쿠키에 저장
Cookie cookie = new Cookie("basket", encodedBasket);
cookie.setMaxAge((int) COOKIE_MAX_AGE); // 쿠키 만료 시간 설정 (24시간)
cookie.setHttpOnly(true); // JavaScript 접근 불가 설정
cookie.setPath("/"); // 모든 경로에서 쿠키 접근 가능
response.addCookie(cookie); // HTTP 응답에 쿠키 추가
}
catch (JsonProcessingException | UnsupportedEncodingException e ) {
throw new CustomException(ErrorCode.BAD_REQUEST);
}
}
- 장바구니 쿠키 삭제
/**
* 장바구니 쿠키 삭제
* @param response
*/
private void clearBasketCookie(HttpServletResponse response) {
Cookie cookie = new Cookie("basket", null); // 쿠키 값을 null로 설정
cookie.setMaxAge(0); // 만료 시간을 0으로 설정 (즉시 삭제)
cookie.setPath("/"); // 모든 경로에서 쿠키 삭제
response.addCookie(cookie); // HTTP 응답에 쿠키 추가
}
- 장바구니 주문 처리에서의 주요 JPQL
@Modifying
@Query(value = "INSERT INTO order_menu(orders_Id, menu_id, food_count) VALUES (:orders_id, :menu_id, :food_count ) " , nativeQuery = true)
void insertOrderMenu(@Param("orders_id") Long orders_id, @Param("menu_id") Long menu_id, @Param("food_count") Integer food_count);
@Query(value = "SELECT new com.example.outsourcing.order.dto.OrderDto(om.menuId.id, om.menuId.menuName, om.foodCount) " +
"FROM order_menu om where om.ordersId.id = :orders_id")
List<OrderDto> getOrderMenus(@Param("orders_id") Long orders_id);
@Query(value = "SELECT SUM(m.price * om.food_count) FROM order_menu om inner join menu m on om.menu_id = m.id WHERE om.orders_Id = :orders_id ", nativeQuery = true)
Object findTotalPrice(@Param("orders_id") Long orders_id);
- 코드들에 관한 설명은 다음 TIL에서 진행.