
이번 포스팅에서는 Order-Service 내 주문처리 관련 로직을 리팩토링 하겠습니다.
리팩토링을 진행하는 이유는 다음과 같습니다.
이전 포스팅 내용에 따르면, 인적 오류를 줄이기 위해 기존에 코드의 복잡성을 낮추기 위해 리팩토링을 진행하겠습니다.
기존의 코드는 복잡성이 높아서도 있지만, 유지 보수성이 떨어지는 가장 큰 이유는 비슷한 로직이 2개로 나눠져 있기 때문에, 로직 수정 시 실수를 유발하게 됩니다.
기존에 로직은 다음과 같습니다.
@Transactional
@Override
public OrderDto registerOrder(OrderDto orderDetails) {
log.debug("주문 등록 실행");
ResponseUser user = feignTemplate.findUserByUserId(orderDetails.getUserId());
log.debug("재고 감소 실행");
ResponseProduct product = updateStock(
StockProcess.DECREASE,
orderDetails.getProductId(),
orderDetails.getQty()
);
OrderEntity order = orderDetails.dtoToEntity(user.getResUserId(), product.getPrice());
try {
OrderProduct orderProduct = OrderProduct.createOrderedProduct(order, product, orderDetails.getQty());
order.getOrderProducts().add(orderProduct);
log.debug("결제 등록 실행");
ResponsePay pay = feignTemplate.registerPay(RequestPay.builder()
.orderId(order.getOrderId())
.payMethod(orderDetails.getPayMethod())
.totalPrice(product.getPrice() * orderProduct.getQty())
.build()
);
order.registerPay(pay.getPayId());
orderRepository.save(order);
return order.entityToDto(pay.getTotalPrice(), pay.getPayMethod());
}catch(Exception e){
ResponseProduct response = updateStock(
StockProcess.RESTORE,
orderDetails.getProductId(),
orderDetails.getQty()
);
log.debug("재고 수량 복구 성공");
log.debug("{}의 재고 : {}", response.getName(), response.getStock());
throw new CustomException(ErrorCode.FAILURE_ORDER);
}
}
@Transactional
@Override
public OrderDto registerWishlist(OrderDto orderDetails) {
log.debug("위시리스트 주문 등록 실행");
ResponseUser user = feignTemplate.findUserByUserId(orderDetails.getUserId());
List<Wishlist> wishlist = wishListRepository.findAllByUserId(user.getResUserId());
if(wishlist.isEmpty()) {
throw new CustomException(ErrorCode.NOT_EXISTS_WISHLIST);
}
Map<String, ResponseProduct> productsMap = new HashMap<>();
List<ProductInfo> productInfoList = wishlist.stream()
.map(wish -> ProductInfo.builder()
.productId(wish.getProductId())
.qty(wish.getQty())
.build())
.collect(Collectors.toList());
log.debug("위시리스트 관련 재고 감소 실행");
List<ResponseProduct> productList = feignTemplate.updateStock(
RequestStock.builder()
.stockProcess(StockProcess.DECREASE.name())
.productInfoList(productInfoList)
.build()
);
for (ResponseProduct product : productList) {
productsMap.put(product.getProductId(), product);
}
OrderEntity order = orderDetails.dtoToEntity(user.getResUserId());
try{
List<OrderProduct> orderProductList = order.getOrderProducts();
long sum = 0L;
for (Wishlist wish : wishlist) {
ResponseProduct product = productsMap.get(wish.getProductId());
OrderProduct orderProduct = OrderProduct.createOrderedProduct(order, product, wish.getQty());
sum += product.getPrice() * orderProduct.getQty();
orderProductList.add(orderProduct);
}
// 결제 생성
ResponsePay pay = feignTemplate.registerPay(RequestPay.builder()
.orderId(order.getOrderId())
.payMethod(orderDetails.getPayMethod())
.totalPrice(sum)
.build()
);
order.registerPay(pay.getPayId());
order = orderRepository.save(order);
return order.entityToDto(pay.getTotalPrice(), pay.getPayMethod());
}catch(Exception e){
log.error(e.getMessage());
List<ResponseProduct> responseList = feignTemplate.updateStock(
RequestStock.builder()
.stockProcess(StockProcess.RESTORE.name())
.productInfoList(productInfoList)
.build()
);
log.debug("위시리스트 재고 수량 복구 성공");
log.debug("재고 수량 복구 성공 갯수 : {}", responseList.size());
throw new CustomException(ErrorCode.FAILURE_ORDER);
}
}
위와 같이 2개의 메서드는 동일하게 주문을 등록하는 로직임에도, 주문하는 상품에 따라분리되어 있습니다.
이로 인해 코드의 유지 보수가 어려워지고, 중복되는 로직이 발생하게 되어 좋지 못한 코드입니다.
이를 해결하기 위해 해당 로직을 하나의 메서드로 수정한 뒤, 메서드의 매개변수를 List의 형태로 수정하여 단일 상품과 복수 상품을 모두 처리할 수 있도록 로직을 수정하겠습니다.
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OrderDto {
// 요청 들어온 정보
// private String productId;
// private Long qty;
private List<OrderProductDto> orderProductList;
...
}
위처럼 List 형태의 주문할 상품 정보를 받도록 로직을 수정했습니다.
@Transactional
@Override
public OrderDto registerOrder(OrderDto orderDetails) {
log.debug("주문 등록 실행");
long startTime = System.currentTimeMillis();
// 회원 조회
ResponseUser user = feignTemplate.findUserByUserId(orderDetails.getUserId());
// 주문할 상품 리스트
List<OrderProductDto> orderProductList = orderDetails.getOrderProductList();
if(orderProductList.isEmpty()) throw new CustomException(ErrorCode.NOT_EXISTS_PRODUCT_IN_ORDER);
// 1. 재고 감소
List<ResponseProduct> productList = feignTemplate.updateStock(
RequestStock.builder()
.stockProcess(StockProcess.DECREASE.name())
.productInfoList(orderProductList)
.build()
);
try {
// 2. 주문 정보 입력
OrderEntity order = orderDetails.dtoToEntity(user.getUserId());
long sum = 0L;
for( ResponseProduct product : productList) {
OrderProduct orderProduct
= OrderProduct.createOrderedProduct(order,product.getProductId(), product.getQty());
order.getOrderProducts().add(orderProduct);
sum += (product.getPrice() * product.getQty());
}
// 3. 결제 처리
ResponsePay pay = feignTemplate.registerPay(RequestPay.builder()
.orderId(order.getOrderId())
.payMethod(orderDetails.getPayMethod())
.totalPrice(sum)
.build()
);
order.registerPay(pay.getPayId());
orderRepository.save(order);
log.debug("정상 처리까지 걸리는 시간 : {}", System.currentTimeMillis() - startTime);
return order.entityToDto(pay.getTotalPrice(), pay.getPayMethod());
}catch(ClientException e){
feignTemplate.updateStock(
RequestStock.builder()
.stockProcess(StockProcess.RESTORE.name())
.productInfoList(orderProductList)
.build()
);
log.debug("복구까지 걸리는 시간 : {}", System.currentTimeMillis() - startTime);
throw new ClientException(ErrorCode.FEIGN_CLIENT_ERROR, e.getMessage());
}catch(Exception e){
feignTemplate.updateStock(
RequestStock.builder()
.stockProcess(StockProcess.RESTORE.name())
.productInfoList(orderProductList)
.build()
);
log.debug("복구까지 걸리는 시간 : {}", System.currentTimeMillis() - startTime);
throw new ServerException(ErrorCode.FEIGN_SERVER_ERROR, e.getMessage());
}
}

postman으로 테스트 결과, 정상적으로 주문이 등록된 것을 확인할 수 있습니다.
그 다음으로 복수 상품들 주문하기 입니다.

여러 개의 상품도 정상적으로 주문등록이 됩니다.