코드 그라데이션

shop 구현 (17) 장바구니에서 상품 주문하기 본문

Spring/SpringShop

shop 구현 (17) 장바구니에서 상품 주문하기

완벽한 장면 2023. 7. 24. 23:28

@PostMapping(value = "/cart/orders")
public @ResponseBody ResponseEntity orderCartItem(@RequestBody CartOrderDto cartOrderDto, Principal principal){
   
System.out.println(cartOrderDto.getCartItemId());
   
List<CartOrderDto> cartOrderDtoList = cartOrderDto.getCartOrderDtoList();

   
if(cartOrderDtoList == null || cartOrderDtoList.size() == 0){ // 카트 주문 상품이 없으면 실행
       
return new ResponseEntity<String>(주문할 상품을 선택해주세요.",HttpStatus.FORBIDDEN);
    }
   
for(CartOrderDto cartOrder : cartOrderDtoList){ //Cart에 담긴 item 시킬 수 있는 권한이 있는지 확인
       
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);
}

이건 장바구니에 있는 아이템을 조회하는 메서드

DTO를 받아서 DTO를 검증.

 

Long orderId = cartService.orderCartItem(cartOrderDtoList, principal.getName()); 요기가 사실상 주문 로직.

 

원래 검증은 @Valid, BindingResult, if문 @Max/@Min 같은 4가지 조합으로 했는데, 왜 여기 (DTO 검증로직)에서는 if문만 가지고 하는가?

사실 일관성을 이용해서 전자의 방법을 사용하는 게 논리적으로는 맞다.

Valid로 하는 게 한계가 있다.

=> 어노테이션으로 표현할 수 있는 부분만 할 수 있다.

 

예를 들면 검색기능에서 날짜를,

startDate를 2023년 10월 10일, endDate를 2022년 10월 10일로 해놔버렸으면.

잘못 설정한 것.(선후관계 나타내는 경우)

아니면 검증 로직을 반드시 남겨야 하는 상황인 경우

어노테이션으로 하기에는 빈 틈이 존재한다.

 

역할을 최대한 분리하는 게 좋다고 했다.

즉,     if(cartOrderDtoList == null || cartOrderDtoList.size() == 0){ // 카트 주문 상품이 없으면 실행
        
return new ResponseEntity<String>(주문할 상품을 선택해주세요.",HttpStatus.FORBIDDEN);
    }
    
for(CartOrderDto cartOrder : cartOrderDtoList){ //Cart에 담긴 item 시킬 수 있는 권한이 있는지 확인
        
if(!cartService.validateCartItem(cartOrder.getCartItemId(), principal.getName())){
            
return new ResponseEntity<String>("주문 권한이 없습니다.",HttpStatus.FORBIDDEN);
        }

이 검증 로직도 여기에 붙여놓는 게 아니라 다른 곳에 만들어놓고, 불러다 쓰게 만드는 게 더 맞다.

 

검증도 여러번 다양한 곳에서 쓸 것이니 반복

 

서비스 부분 코드

public Long orderCartItem(List<CartOrderDto> cartOrderDtoList, String email){
   
List<OrderDto> orderDtoList = new ArrayList<>();
   
for(CartOrderDto cartOrderDto : cartOrderDtoList){
       
CartItem cartItem = cartItemRepository.findById(cartOrderDto.getCartItemId())
                .
orElseThrow(EntityExistsException::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(EntityExistsException::new);
       
cartItemRepository.delete(cartItem); // 카트에 있는 상품 지웁니다.
    }
   
return orderId;
}

 카트 오더 디티오로부터 오더 디티오를 만들려고 하는 상황

 

Long orderId = orderService.orders(orderDtoList, email); //주문 실행 

이걸 보면 주문을 장바구니에서 하나, 상품페이지에서 하나 동일한 주문

이건 결국 내가 얼마나 order를 다른데 불필요하게 의존하지 않고 가져다 쓸 수 있느냐.

어디서 오든 형식만 맞춰줘서 처리하게 만들면 된다는 이야기.

이렇게 설계를 하는게 order는 변하지 않으니까 일관성을 유지할 수 있다는 장점.

 

주문은 카트가 들어갈 필요가 없고, order만 들어가면 된다.

주문 완료 이후에

cartItemRepository.delete(cartItem); // 카트에 있는 상품 지웁니다.

 

그런데 주문 완료되고 그 아이템을 제거하는 과정에서 예외가 발생하면?

트랜잭션이 어노테이션으로 달려 있으므로 위 코드 전체가 롤백이 되면서, 주문이 안 된 상태로 들어가게 되고, 그대로 남아있는다.

 

 

아까 봤듯이 예외 발생하지 않게 처리하려면

cartItemRepository.deleteById(cartId); 하면 된다.

 

 

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); // order 데이터베이스에 적재

   
return order.getId();
}

 

이거 왜 필요하는지는 잘 모르겠다.

굳이 바꾸자면 

List<OrderItem> orderItemList = new ArrayList<>(); //주문상품리스트 객체 생성 

여기서 계속 새로 객체를 만들지 말고, 만드는 역할을 따로 밖에서 하고, 

여기서는

orders(List<OrderItem> orderItems, String email) {}

이렇게 디자인하는게 낫다.

 

이렇게 되면 다 밖에서 해주는 것이므로, order나 orders나 분리할 필요가 없다.

728x90
Comments