JPA

SpringBoot JPA 쇼핑몰 주문 서비스 I

빈코 2022. 9. 15. 15:41

SpringBootJPA, Thymeleaf를 사용하여 간단한 쇼핑몰을 구현하려 합니다. 프로젝트 생성이나 Gradle 빌드는 깃허브 BincoShop을 참고해주세요! 포스팅은 도메인&테이블 설계 -> 엔티티 개발 -> 회원 서비스 -> 상품 서비스 -> 주문 서비스 순으로 진행됩니다. 포스팅의 잘못된 부분은 언제든 댓글로 남겨주시면 수정하겠습니다😀

 

로고
주문 서비스 개발

Order Entity📘

    public void setMember(Member member) {
        this.member = member;
        member.getOrders().add(this);
    }

    public void addOrderItem(OrderItem orderItem) {
        orderItems.add(orderItem);
        orderItem.setOrder(this);
    }

    public void setDelivery(Delivery delivery) {
        this.delivery = delivery;
        delivery.setOrder(this);
    }

//==생성 메서드==//
public static Order createOrder(Member member, Delivery delivery, OrderItem... orderItems){
    Order order = new Order();
    order.setMember(member);
    order.setDelivery(delivery);
    for(OrderItem orderItem : orderItems){
        order.addOrderItem(orderItem);
    }

    order.setStatus(OrderStatus.ORDER);
    order.setOrderDate(LocalDateTime.now());
    return order;
}

지난 포스팅에서 Order Entity를 설계한 코드에 생성 메서드를 추가로 작성해야 합니다. 주문 로직은 회원, 배달, 주문에 담긴 아이템들 등 복잡하게 구현되므로 생성 메서드로 따로 빼는 게 좋습니다.

 

매개변수로 받은 회원과 배달은 set을 통해 주입해주고, 고른 상품들은 여러 개 일 수 있기 때문에 반복문을 통해 Order Entity에 주입해줍니다. 여기서 OrderStatus는 기본적으로 주문에 해당하기 때문에 ORDER로 지정합니다. 날짜는 LocalDateTime을 활용하여 지금 시간을 주입해줍니다.

 

//==business logic==//
/**
 주문 취소
 */
public void cancel() {
    if (delivery.getStatus() == DeliveryStatus.COMP) {
        throw new IllegalStateException("이미 배송완료 된 상품은 취소가 불가능합니다.");
    }

    this.setStatus(OrderStatus.CANCEL);
    for (OrderItem orderItem : orderItems) {
        orderItem.cancel();
    }
}

비즈니스 로직으로는 사용자가 주문 취소하는 경우를 추가했습니다. 하지만 만약 배송이 완료 됐을 경우에는 취소가 불가능한 상태이므로 조건문을 통해 맨 앞단에서 걸러줍니다.

 

취소 버튼을 통해 주문을 취소 하면 해당 주문의 Status 값을 CANCEL로 수정하고, 주문에 포함되는 아이템들의 재고를 원복시켜야 하기 때문에 반복문을 통해 각각의 아이템을 cancel 해주는 로직도 함께 있어야 합니다.


OrderItem Entity

//==business logic==//
public void cancel() {
    getItem().addStock(count);
}

위에서 각각의 orderItem을 cancel 시킨 로직입니다. Entity 안에서 로직이 진행되기 때문에 따로 count를 매개변수로 받을 필요는 없습니다. 재고를 원복시키는 로직이기 때문에 주문 아이템에서 해당 Item을 가져와서 addStock(재고 증가) 메서드를 진행시킵니다. 이때 매개변수로 취소한 수량인 count를 넘겨줍니다.

 

Item Entity

/*
stock 증가
*/
public void addStock(int quantity) {
    this.stockQuantity += quantity;
}

지난번에 만든 Item Entity에서는 addStock을 통해 현재 남아 있는 수량(stockQuantity)에 넘겨받은 수량(quantity)을 추가시켜줍니다. 


 

비즈니스 로직 중에는 주문 취소 뿐 아니라 전체 주문 가격을 조회하는 로직도 필요합니다. 시용자에게 전체 주문 금액을 알려줘야 하기 때문입니다.

 

Order Entity

//==조회 로직==//
/**
 * 전체 주문 가격 조회
*/
public int getTotalPrice() {
    int totalPrice = 0;
    for (OrderItem orderItem : orderItems) {
        totalPrice += orderItem.getTotalPrice();
    }
    return totalPrice;
}

주문 취소와 같이 Entity에서 이와 같은 과정을 처리합니다. getTotalPrice 메서드를 통해 해당 주문의 아이템(orderItems)의 루프를 돌면서 아이템들의 가격을 더해줍니다. 이때, 한 아이템을 여러 개 구입할 수 있으므로 이 부분도 orderItem Entity로 기능을 위임할 예정입니다.

 

OrderItem Entity

public int getTotalPrice() {
    return getOrderPrice() * getCount();
}

OrderItem Entity에서는 getTotalPrice 메서드를 통해 해당 아이템의 가격과 갯수를 곱한 금액을 return 시켜줍니다.

 

OrderItem Entity📙

//생성 메서드//
public static OrderItem createOrderItem(Item item, int orderPrice, int count) {
    OrderItem orderItem = new OrderItem();
    orderItem.setItem(item);
    orderItem.setOrderPrice(orderPrice);
    orderItem.setCount(count);

    item.removeStock(count);
    return orderItem;
}

Order Entity와 마찬가지로 OrderItem Entity도 추후에 할인, 쿠폰 같은 기능이 추가될 수 있기 때문에 생성 메서드를 따로 만들었습니다. 매개변수로 Item, orderPrice, count를 받아서 해당 orderItem을 만들어주고, 주문을 한 상황이기 때문에 Item의 재고는 줄여주는 로직도 함께 추가되었습니다.

 

Item Entity

/*
stock 감소
 */
public void removeStock(int quantity){
    int restStock = this.stockQuantity - quantity;
    if (restStock < 0 ){
        throw new NotEnoughStockException("need more stock");
    }
    this.stockQuantity = restStock;
}

removeStock은 지난번에 만든 Item Entity에서 현재 재고(stockQuantity)에서 넘겨받은 quantity를 빼주는 로직입니다.

 

Order Repostiory📔

@Repository
@RequiredArgsConstructor
public class OrderRepository {

    private final EntityManager em;

    public void save(Order order) {
        em.persist(order);
    }

    public Order findOne(Long id) {
        return em.find(Order.class, id);
    }

}

 

Repository는 간단하게 구현됩니다. 엔티티 매니저를 통해서 persist하여 저장해주는 save() 메서드나 id를 통해 주문 내역을 찾아주는 findOne() 메서드를 구현하였습니다.

 

 


👨‍👩‍👦‍👦 오픈채팅방 운영

취업을 준비하는 예비 개발자분들을 위한 질문&답변할 수 있는 공간을 만들었습니다. 취업과 이직을 하기 위해서 어떤 걸 중점적으로 준비해야 하는지부터 포트폴리오&이력서 작성법 등 다양한 질문들을 받고 답변을 드립니다. 참여하셔서 다양한 정보 얻고 가시면 좋을 것 같네요😁

 

참여코드 : 456456

https://open.kakao.com/o/gVHZP8dg

 

비전공 개발자 취업 준비방(질문&답변)

#비전공 #개발자 #취업 #멘토링 #부트캠프 #국비지원 #백엔드 #프론트엔드 #중소기업 #중견기업 #자바 #Java #sql

open.kakao.com

 


👨‍💻 전자책 출간

아울러 제가  🌟비전공자에서 2년만에 보안 전문 중견기업으로 이직 한 방법들을 정리한 전자책을 출간 하게 되었습니다. 어떤 걸 공부해야 하는지, 이직을 위해서 무엇을 준비해야 하는지, 제가 받았던 기술 면접 리스트 등 다양한 목차로 구성되어 있습니다. 또한, 구매 시 1:1 채팅을 이용하여 포트폴리오 첨삭을 도와드리고 있습니다. 🐕전자책으로 얻은 모든 수익은 유기견 센터 '팅*벨 입양센터'에 후원될 예정입니다. 관심 있으신 분들은 아래 링크를 참고해주세요😁

https://kmong.com/gig/480954

 

비전공개발자 2년만에 중견기업 들어간 방법 | 14000원부터 시작 가능한 총 평점 0점의 전자책, 취

0개 총 작업 개수 완료한 총 평점 0점인 Binco의 전자책, 취업·이직 전자책 서비스를 0개의 리뷰와 함께 확인해 보세요. 전자책, 취업·이직 전자책 제공 등 14000원부터 시작 가능한 서비스

kmong.com


 

마치며

주문 로직은 생각보다 많이 복잡한 것 같습니다. 기존에는 Service단에서 주문이 들어오면 DB에서 상품을 꺼내 재고를 줄이고 다시 집어넣고를 반복했는데, 이번 강의를 통해 Entity 내부에서 로직들을 처리하면서 코드는 많이 간결해졌지만 아직 익숙하지는 않은 것 같네요😅

 

주문 서비스는 생각보다 설명해야 할 부분이 많아서 3개의 포스팅으로 나누어서 할 예정입니다.

 

관련 포스팅

SpringBoot JPA 쇼핑몰 도메인&테이블 설계

SpringBoot JPA 쇼핑몰 엔티티 개발 I

SpringBoot JPA 쇼핑몰 엔티티 개발 II

SpringBoot JPA 쇼핑몰 회원 서비스 개발

SpringBoot JPA 쇼핑몰 상품 서비스 개발

SpringBoot JPA 쇼핑몰 주문 서비스 II

SpringBoot JPA 쇼핑몰 주문 검색 개발

 

Reference

스프링 부트와 JPA 활용 - 김영한

반응형