@@ -0,0 +1,201 @@
+package store.controller;
+
+import store.model.Product;
+import store.model.receipt.PurchaseProducts;
+import store.service.ReceiptService;
+import store.service.StockManageService;
+import store.view.InputView;
+import store.view.OutputView;
+
+import java.io.IOException;
+
+public class StoreController {
+    private final InputView inputView;
+    private final OutputView outputView;
+
+    private boolean nextPlay;
+    private PurchaseProducts purchaseProducts;
+    private ReceiptService receiptService;
+    private StockManageService stockManageService;
+
+    private int countPurchasePromotion;
+    private int countPurchaseNormal;
+    private int giftCount;
+    private boolean getBenefit;
+
+    public StoreController(InputView inputView, OutputView outputView) {
+        this.inputView = inputView;
+        this.outputView = outputView;
+        nextPlay = true;
+    }
+
+    public void run() throws IOException {
+        while (nextPlay) {
+            play();
+        }
+    }
+
+    private void play() throws IOException {
+        generateService();
+        stockManageService.bringStock();
+
+        outputView.printStoreMenu();
+        repeatUntilProductAndPriceValid();
+        calculateProducts();
+        printReceipt();
+
+        if (!repeatUntilAdditionalPlayValid()) {
+            nextPlay = false;
+        }
+    }
+
+    private void calculateProducts() {
+        for (Product product : purchaseProducts.getPurchaseProducts()) {
+            initCount(product);
+            calculateCount(product);
+
+            stockManageService.manageStock(countPurchaseNormal, countPurchasePromotion, product);
+
+            int stockCount = countPurchasePromotion + countPurchaseNormal;
+
+            receiptService.make(stockCount, giftCount, product, getBenefit);
+        }
+    }
+
+    private void calculateCount(Product product) {
+        if (!product.isPromotionDuration()) { //프로모션 기간이 아닐 때
+            if (product.isPromotionProduct()) {
+                getBenefit = false;
+            }
+
+            if (product.getNormalStockCount() >= product.getQuantity()) {
+                countPurchaseNormal = product.getQuantity();
+                return;
+            }
+            countPurchaseNormal = product.getNormalStockCount();
+            countPurchasePromotion = product.getQuantity() - countPurchaseNormal;
+            return;
+        }
+
+        if (product.isAvailableOnlyPromotion()) {
+            calculateCaseOne(product);
+            return;
+        }
+
+        if (product.isSameWithQuantity()) {
+            calculateCaseTwo(product);
+            return;
+        }
+
+        calculateCaseThree(product);
+    }
+
+    private void calculateCaseThree(Product product) {
+        countPurchasePromotion = product.getPromotionStockCount();
+        countPurchaseNormal = product.getQuantity() - countPurchasePromotion;
+
+        if (product.countPromotionDisable() > 0 & product.isPromotionProduct()) {
+            if (!repeatUntilPurchaseValid(product)) {
+                countPurchasePromotion = product.buyOnlyPromotion();
+                countPurchaseNormal = 0;
+            }
+            getBenefit = false;
+        }
+    }
+
+    private void calculateCaseTwo(Product product) {
+        countPurchasePromotion = product.getQuantity();
+
+        if (product.countPromotionDisable() > 0 & product.isPromotionProduct()) {
+            if (!repeatUntilPurchaseValid(product)) {
+                countPurchasePromotion = product.buyOnlyPromotion();
+            }
+            getBenefit = false;
+        }
+    }
+
+    private void calculateCaseOne(Product product) {
+        countPurchasePromotion = product.getQuantity();
+
+        if (product.canReceiveMoreFreeGift()) { //프로모션 기간, 프로모션 적용 가능
+            if (repeatUntilOneMoreFreeValid(product)) {
+                countPurchasePromotion += product.getPromotionGetCount();
+                giftCount += product.getPromotionGetCount();
+                product.increaseQuantity(product.getPromotionGetCount());
+                return;
+            }
+            getBenefit = false;
+        }
+    }
+
+    private void generateService() {
+        receiptService = new ReceiptService();
+        stockManageService = new StockManageService();
+    }
+
+    private void initCount(Product product) {
+        getBenefit = true;
+        countPurchasePromotion = 0;
+        countPurchaseNormal = 0;
+        giftCount = product.getGiftCount();
+    }
+
+    private void repeatUntilProductAndPriceValid() {
+        while (true) {
+            try {
+                purchaseProducts = new PurchaseProducts(inputView.getProductAndPrice());
+                return;
+            } catch (IllegalArgumentException | IOException e) {
+                System.out.println(e.getMessage());
+            }
+        }
+    }
+
+    private boolean repeatUntilPurchaseValid(Product product) {
+        while (true) {
+            try {
+                return inputView.getPurchaseOrNot(product.getName(), product.countPromotionDisable());
+            } catch (IllegalArgumentException e) {
+                System.out.println(e.getMessage());
+            }
+        }
+    }
+
+    private boolean repeatUntilOneMoreFreeValid(Product product) {
+        while (true) {
+            try {
+                return inputView.getOneMoreFree(product);
+            } catch (IllegalArgumentException e) {
+                System.out.println(e.getMessage());
+            }
+        }
+    }
+
+    private boolean repeatUntilMembershipDiscountValid() {
+        while (true) {
+            try {
+                return inputView.getMembershipDiscountOrNot();
+            } catch (IllegalArgumentException e) {
+                System.out.println(e.getMessage());
+            }
+        }
+    }
+
+    private boolean repeatUntilAdditionalPlayValid() {
+        while (true) {
+            try {
+                return inputView.getAdditionalPurchase();
+            } catch (IllegalArgumentException e) {
+                System.out.println(e.getMessage());
+            }
+        }
+    }
+
+    private void printReceipt() throws IOException {
+        receiptService.calculateDiscount(repeatUntilMembershipDiscountValid());
+        outputView.printPurchaseProduct(purchaseProducts);
+        outputView.printGiftProducts(receiptService.getGiftProducts());
+        outputView.printAmountInfo(receiptService.getAmountInfo());
+        stockManageService.reflectStock();
+    }
+}

리뷰: ``repeatUntil`` 메서드가 같은 패턴으로 반복되고 있어 코드 중복이 많게 느껴져요.
저도 3주 차 로또 미션 구현하면서 반복을 어떻게 하면 없앨 수 있을까 많이 찾아보다가 함수형 인터페이스를 접하게 됐는데요!
[람다식, 메서드 참조를 이용하여 예외 발생시 성공할 때 까지 반복 구현 - 함수형 인터페이스](https://dev-dongmoo.tistory.com/14)
위 블로그 포스팅이 참고가 될 수 있을 것 같아요☺️