늦은 프로그래밍 이야기

230119 TIL (팀프로젝트) 본문

내일배움캠프/TIL, WIL

230119 TIL (팀프로젝트)

한정규 2023. 1. 19. 23:57

팀프로젝트

주문 요청 기능 구현

기본적인 사항을 다 거의 다 구현하고 팀원 중 Order 기능을 맡은 팀원이 아직 다른 기능들이 완성이 되지 않아서 Order 기능을 구현을 못하고 있어서 같이 해당 기능을 구현하였다.

주문 요청 기능을 맨 처음 구현하였는데, 생각보다 막막하였다. 한개의 상품이나 여러 상품들을 OrderItem 엔티티에 담아 한번에 한개의 상품이나 여러 상품을 Order 할 수 있는 구조로 구현을 해보려 했는데, 생각보다 쉽지 않았다.

엔티티의 구성은 추후에 변경하였지만, 처음에는 다음과 같이 구성하였다.

Order CLASS
@Entity(name = "orders")
@Getter
@NoArgsConstructor
public class Order extends Timestamp {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long orderId;

    @Column(nullable = false)
    private String customerId;

    @Column(nullable = false)
    private int totalAmount = 0;

    @Column(nullable = false)
    @Enumerated(value = EnumType.STRING)
    private ShippingStatusEnum shippingStatus;

OrderItem CLASS
@Entity(name = "order_items")
@Getter
@NoArgsConstructor
public class OrderItem extends Timestamp {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long itemId;

    @Column(nullable = false)
    private String customerId;

    @Column(nullable = false)
    private long orderId;

    @Column(nullable = false)
    private long productId;

    @Column(nullable = false)
    private int quantity;

    @Column(nullable = false)
    private int amount;

연관관계를 맺지 않고 진행하고 싶어서 연관관계를 맺어주지 않고 시도해 보았다.

Controller에서 리스트로 데이터를 받아오는 방법

제일 처음 마주한 문제는 controller에서 여러 상품의 데이터를 받아오는 것이었다. @RequsetBody를 사용해 리스트를 받아오는 RequestDto로 시도를 해보려다가 잘 되지 않아서 리스트를 받아오는 방법을 검색해 보았는데, @RequestParam으로 value를 콤마(,)로 구분하여 여러개의 값을 입력하면 리스트로 받아 올 수 있다는 글을 보았다.

@RequestBody로 리스트를 받아오는 방법도 시도해 보아야 할 것이다.

@PostMapping("/orders/items")
public void createOrderItem(@RequestParam List<Long> productId, 
                            @RequestParam List<Integer> quantity,
                            @AuthenticationPrincipal UserDetailsImpl userDetails) {
    orderService.createOrderItem(productId, quantity, userDetails.getUserId());
}

위와 같이 RequestParam 값의 데이터 타입을 List로 작성하고 URL을 localhost:8080/orders/items?productId=1,2,3&quantity=1,2,3 으로 작성해주니 리스트 형태로 받아올 수 있는 것을 확인하였다.

public void createOrderItem(List<Long> productId,List<Integer> quantity, String userId) {
    int totalAmount = 0;
    for (int i = 0; i < productId.size(); i++) {
        Product product = productRepository.findByProductId(productId.get(i)).orElseThrow(
                () -> new IllegalArgumentException("존재하지 않는 상품입니다.")
        );
        int amount = product.getPrice() * quantity.get(i);
        totalAmount += amount;
        OrderItem orderItem = new OrderItem(userId, product.getProductId(), quantity.get(i), amount);
        orderItemRepository.save(orderItem);
    }
    Order order = new Order(userId, totalAmount, ShippingStatusEnum.DEFAULT);
    orderRepository.save(order);
}

받아온 리스트의 값을 반복문을 사용해 하나하나 가져와서 OrderItem을 생성하고 저장을 하였는데 Order가 나중에 생성이 되서 orderId 값을 받아오지 못해서 orderId를 빼고 작성을 했다. 잘 동작은 되었지만 주문 조회를 구현하는데 애를 먹었다. 그래서 튜터님한테 질문한 결과 결국 연관관계를 맺어주는 방식으로 진행하기로 했다.

Order CLASS
@Entity(name = "orders")
@Getter
@NoArgsConstructor
public class Order extends Timestamp {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long orderId;

    @Column(nullable = false)
    private String customerId = "";

    @Column(nullable = false)
    private String sellerId = "";

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    privateList<OrderItem> orderItemList = new ArrayList<>();

    @Column(nullable = false)
    private int totalAmount = 0;

    @Column(nullable = false)
    @Enumerated(value = EnumType.STRING)
    private ShippingStatusEnum shippingStatus = ShippingStatusEnum.DEFAULT;

OrderItem CLASS
@Entity(name = "order_items")
@Getter
@NoArgsConstructor
public class OrderItem extends Timestamp {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long itemId;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id")
    private Order order;

    @Column(nullable = false)
    private String sellerId;

    @Column(nullable = false)
    private long productId;

    @Column(nullable = false)
    private int quantity;

    @Column(nullable = false)
    private int amount;

@OneToMany가 필요한 것 같아서 양방향 연관관계를 맺어주고 테스트 해보았는데 @Transaction이 잘 작성되어 있는데도 Order를 넣어주지 못하는 문제가 계속하여 발생하였다.

Order의 orderId 값이 존재하지 않아 OrderItem에 넣어줄 수 없어서 발생하는 문제

양방향 연관관계를 맺어주고 나머지 코드들도 연관관계에 맞게 수정을 하고 실행을 시켜 보았다. 실행은 되는데 주문 요청 메소드를 실행하니 주문 요청이 되지 않고 object references an unsaved transient instance - save the transient instance before flushing 에러가 출력 되었다. 찾아보니 저번에도 마주한 문제였다. Order의 값이 존재하지 않아 OrderItem에 넣어줄 수 없어서 발생하는 문제였다. cascade도 잘 설정이 되어 있고, @Transaction 어노테이션도 잘 적용이 되어 있는데도 에러가 계속하여 발생해서 Order와 OrderItem의 생성 순서를 바꿔주었더니 해결이 되었다.

@Transactional
public void createOrder(List<Long> productId,List<Integer> quantity, String userId) {
    int totalAmount = 0;
    String sellerId = "";
    Order order = new Order();
    orderRepository.save(order);

List<OrderItem> orderItemList = new ArrayList<>();
    for (int i = 0; i < productId.size(); i++) {
        Product product = productRepository.findByProductId(productId.get(i)).orElseThrow(
                () -> new IllegalArgumentException("존재하지 않는 상품입니다.")
        );
        sellerId = product.getUserId();
        int amount = product.getPrice() * quantity.get(i);
        totalAmount += amount;
        OrderItem orderItem = new OrderItem(sellerId, product.getProductId(), quantity.get(i), amount, order);
        orderItemList.add(orderItem);
        orderItemRepository.save(orderItem);
    }

    order.putDatasInOrder(userId, sellerId, totalAmount);
    orderRepository.save(order);
}

저번에 비슷한 문제를 해결할 때에는 OneToOne에서 발생한 문제여서 save를 두번쓰지 않고 생성자에서 해결을 한 것 같은데 조금 찾아보고 나중에 리팩토링 해봐야 할 것 같다.


'내일배움캠프 > TIL, WIL' 카테고리의 다른 글

12주차 WIL  (0) 2023.01.23
230120 TIL (팀프로젝트)  (0) 2023.01.22
230118 TIL (팀프로젝트)  (0) 2023.01.19
230117 TIL (팀프로젝트)  (0) 2023.01.18
230116 TIL (팀프로젝트)  (0) 2023.01.16
Comments