현재 상황 및 문제 정의
쇼핑몰 프로젝트를 함에 있어 유저가 장바구니에 담아서 결제하는 경우, 상품 상세페이지 칸에서 직접 주문하는 경우 2가지로 방식을 나눌 수 있다. 모든 주문 방식을 하나로 처리하게 되면, 의존성이 커지게 된다. 또한, 결제 페이지까지 가게되면 공통된 부분들이 많으므로 팩토리 메소드 패턴을 이용해서 주문을 구현할 수 있다.
- 장바구니 기본 주문
- 즉시결제에 따른 개별 주문
Factory Method Pattern을 이용한 주문 방식 구현
주문 방식에 맞는 Factory 구현
public interface OrderFactory {
Order createOrder(Customer customer, List<Product> products);
}
public class CartOrderFactory implements OrderFactory {
@Override
public Order createOrder(Customer customer, Map<Product, Integer> productMap) {
Order order = new Order(customer, OrderStatus.ORDERED);
for (Map.Entry<Product, Integer> entry : productMap.entrySet()) {
Product product = entry.getKey();
int quantity = entry.getValue();
order.addItem(new OrderItem(order, product, product.getPrice(), quantity));
}
return order;
}
}
public class DirectOrderFactory implements OrderFactory {
@Override
public Order createOrder(Customer customer, Map<Product, Integer> productMap) {
Map.Entry<Product, Integer> entry = productMap.entrySet().iterator().next();
Product product = entry.getKey();
int quantity = entry.getValue();
Order order = new Order(customer, OrderStatus.ORDERED);
order.addItem(new OrderItem(order, product, product.getPrice(), quantity));
return order;
}
}
CreateOrder 메소드는 구매자의 정보와 구매하려는 상품의 정보와 수량이 담긴 map을 파라미터로 받는다는 점에서 공통점을 갖고 있으므로 주문생성의 책임을 OrderFactory로 분리한다.
Factory 선택 Provider 구현
public enum OrderType {
CART, DIRECT
}
public class OrderFactoryProvider {
public static OrderFactory getOrderFactory(OrderType type) {
return switch (type) {
case CART -> new CartOrderFactory();
case DIRECT -> new DirectOrderFactory();
};
}
}
서비스에서 Factory 사용
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
@Transactional
public Order createOrder(OrderType orderType, Customer customer, Map<Product, Integer> productQuantityMap) {
OrderFactory factory = OrderFactoryProvider.getOrderFactory(orderType);
Order order = factory.createOrder(customer, productQuantityMap);
return orderRepository.save(order);
}
}
패턴을 적용하지 않은 경우
public Order createOrder(OrderType type, Customer customer, Map<Product, Integer> productMap) {
Order order = new Order(customer, OrderStatus.ORDERED);
if (type == OrderType.CART) {
for (Map.Entry<Product, Integer> entry : productMap.entrySet()) {
Product product = entry.getKey();
int quantity = entry.getValue();
order.addItem(new OrderItem(order, product, product.getPrice() quantity));
}
} else if (type == OrderType.DIRECT) {
Map.Entry<Product, Integer> entry = productMap.entrySet().iterator().next();
Product product = entry.getKey();
int quantity = entry.getValue();,
order.addItem(new OrderItem(order, product, product.getPrice(), quantity));
}
return orderRepository.save(order);
}
- OrderService가 객체 생성 책임까지 떠안음 → 단일 책임 원칙(SRP) 위배
- 조건문 분기 로직이 복잡해짐
- 새로운 주문 방식 추가 시 Service 수정 필요 → 개방 폐쇄 원칙(OCP) 위배
- 테스트 어려움: 주문 방식마다 서비스 테스트 필요
그냥 동작만 하게 만들고 끝나는 구조는 확장성과 유지보수성 측면에서 문제를 야기할 수 있다. 팩토리 메소드 패턴을 도입하면서 주문 방식이 많아져도 코드 변경에 유연하게 대응할 수 있고, 서비스 측면에서 주문 자체를 생성하는 로직에 집중할 수 있다는 장점을 얻을 수 있다.
'ShoppingMall Project' 카테고리의 다른 글
[Shop Project] HTTP vs HTTPS, SSL인증서 생성하기(2) (0) | 2025.05.29 |
---|---|
[Shop Project] HTTP vs HTTPS, SSL인증서 생성하기 (1) (1) | 2025.05.28 |
[Shop Project] 전략 패턴(Strategy Pattern)을 활용한 결제 방식 구현 (0) | 2025.05.28 |