@@ -0,0 +1,80 @@
+package store.controller;
+
+import store.controller.dto.OrderStateDTO;
+import store.controller.dto.StateContextDTO;
+import store.domain.Command;
+import store.service.OrderService;
+import store.service.ProductService;
+import store.view.InputView;
+import store.view.OutputView;
+import store.view.dto.RequestOrder;
+import store.view.dto.ResponseOrder;
+import store.view.dto.ResponseProducts;
+
+import java.util.function.Supplier;
+
+public class OrderController {
+    private final OrderService orderService;
+    private final ProductService productService;
+    private final InputView inputView;
+    private final OutputView outputView;
+
+    public OrderController(OrderService orderService, ProductService productService, InputView inputView, OutputView outputView) {
+        this.orderService = orderService;
+        this.productService = productService;
+        this.inputView = inputView;
+        this.outputView = outputView;
+    }
+
+    public void createOrder() {
+        Command retryCommand;
+        do {
+            printProductInfo();
+            OrderStateDTO orderStateDTO = getOrderStateDTO();
+            checkOrderState(orderStateDTO);
+            Command command = inputCommand(inputView::askMemberShipDiscount);
+            orderService.determinedMembership(command);
+            getReceipt();
+            retryCommand = inputCommand(inputView::askAdditionalPurchase);
+        } while (retryCommand.isYes());
+    }
+
+    private void checkOrderState(OrderStateDTO orderState) {
+        if(orderState.state().equals("PENDING")) {
+            for (StateContextDTO stateContext : orderState.stateContexts()) {
+                Command command = inputCommand(() -> inputView.askReviewPending(stateContext));
+                orderService.solvePending(command, stateContext.name());
+            }
+        }
+    }
+
+    private void printProductInfo() {
+        ResponseProducts products = productService.findAll();
+        outputView.printProducts(products);
+    }
+
+    private OrderStateDTO getOrderStateDTO() {
+        try {
+            RequestOrder requestOrder = inputView.askItem();
+            return orderService.createOrder(requestOrder);
+        } catch (IllegalArgumentException e) {
+            outputView.printError(e.getMessage());
+            return getOrderStateDTO();
+        }
+    }
+
+    private Command inputCommand(Supplier<String> supplier) {
+        try {
+            String input = supplier.get();
+            return Command.find(input);
+        } catch (IllegalArgumentException e) {
+            outputView.printError(e.getMessage());
+            return inputCommand(supplier);
+        }
+    }
+
+    public void getReceipt() {
+        ResponseOrder orderReceipt = orderService.getOrderReceipt();
+        outputView.printOrder(orderReceipt);
+    }
+}

리뷰: 혹시 의존성의 방향에 대해서 고민해보셨나요?
물론 지금은 계층이 구조적으로 명확하게 나뉘어져있는 것은 아니지만(`interface`를 이용해서)
DTO를 사용하신 것으로 보아서 계층을 분리하려고 시도하시지 않았나 싶어요.
만약 그렇다고 하면, '분리된 컨트롤러가 뷰를 바라보고 있는(`import store.view....`) 의존성의 방향이 바람직한가?'를 고민해봐도 좋을 것 같아요.

제 생각에는 뷰와 컨트롤러의 코드를 분리할 때는 뷰의 변경사항으로부터 컨트롤러가 영향을 받지 않게 하려는 의도가 있는 거라구 생각을 하고 있거든요. (꼭 뷰와 컨트롤러가 아니라 하더라도 계층을 분리할 때는...)
그런데 그렇게 해서 분리한 컨트롤러가 다시 뷰를 의존하게 되면, 결국 분리한 의미가 희석되는 것이 아닌가 하는 생각이 들어요.

모호한 이야기네요 😅
지금 상황에서 꼭 지켜야 하는 내용이라고 생각하진 않지만, 계층 분리를 염두에 두고 코딩을 하셨던 것 같아 남겨보아요.