@@ -0,0 +1,294 @@
+package store.service;
+
+import static store.model.Promotion.parsePromotions;
+import static store.model.Store.*;
+import static store.utils.ErrorMessage.INVALID_INPUT;
+import static store.utils.ErrorMessage.INVALID_NAME;
+import static store.utils.ErrorMessage.INVALID_QUANTITY;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import store.model.Order;
+import store.model.Product;
+import store.model.Promotion;
+import store.model.Receipt;
+import store.model.Store;
+import store.view.InputView;
+import store.view.OutputView;
+
+public class StoreService {
+    private static final int NON_PROMOTION = 0;
+    private static final int GET = 1;
+    private static final int MAX_MEMBERSHIP = 8000;
+
+    public void loadProductsForm(List<Store> store) {
+        try (InputStream in = Store.class.getResourceAsStream("/products.md"); BufferedReader br = new BufferedReader(
+                new InputStreamReader(in))) {
+            String line = br.readLine();
+
+            while ((line = br.readLine()) != null) {
+                addProduct(store, line);
+            }
+        } catch (IOException e) {
+            System.out.println(e.getMessage());
+        }
+    }
+
+    public void loadPromotionsForm(List<Promotion> promotions) {
+        try (InputStream in = Store.class.getResourceAsStream("/promotions.md"); BufferedReader br = new BufferedReader(
+                new InputStreamReader(in))) {
+            String line = br.readLine();
+
+            while ((line = br.readLine()) != null) {
+                promotions.add(parsePromotions(line));
+            }
+        } catch (IOException e) {
+            System.out.println(e.getMessage());
+        }
+    }
+
+    private void addProduct(List<Store> store, String line) {
+        checkNotPromotionProduct(store, line);
+        store.add(parseStore(line));
+    }
+
+    public void checkProduct(List<Store> store, Order order) {
+        for (Product product : order.getOrder()) {
+            checkName(store, product);
+            checkQuantity(store, product);
+        }
+    }
+
+    private void checkName(List<Store> store, Product product) {
+        for (Store storeProduct : store) {
+            if (storeProduct.getName().equals(product.getName())) {
+                return;
+            }
+        }
+
+        throw new IllegalArgumentException(INVALID_NAME);
+    }
+
+    private void checkQuantity(List<Store> store, Product product) {
+        int quantity = 0;
+
+        for (Store storeProduct : store) {
+            if (storeProduct.getName().equals(product.getName())) {
+                quantity += storeProduct.getQuantity();
+            }
+        }
+
+        if (quantity < product.getQuantity()) {
+            throw new IllegalArgumentException(INVALID_QUANTITY);
+        }
+    }
+
+    public List<Receipt> parseReceipt(List<Store> store, Order order, List<Promotion> promotions) {
+        List<Receipt> receipts = new ArrayList<>();
+
+        for (Product product : order.getOrder()) {
+            String name = product.getName();
+            int indivialPrice = getPrice(store, product.getName());
+            int quantity = product.getQuantity();
+            int promotinBuy = checkPromotionProduct(store, product, promotions);
+            receipts.add(new Receipt(name, indivialPrice, quantity, promotinBuy));
+        }
+        return receipts;
+    }
+
+    private int getPrice(List<Store> store, String productName) {
+        int price = 0;
+        for (Store storeProduct : store) {
+            if (storeProduct.getName().equals(productName)) {
+                price = storeProduct.getPrice();
+            }
+        }
+
+        return price;
+    }
+
+    private int checkPromotionProduct(List<Store> store, Product product, List<Promotion> promotions) {
+        for (Store storeProduct : store) {
+            if (storeProduct.getName().equals(product.getName())) {
+                if (storeProduct.getQuantity() == 0) {
+                    return 0;
+                }
+                return getPromotion(storeProduct.getPromotion(), promotions);
+            }
+        }
+
+        return 0;
+    }
+
+    private int getPromotion(String promotion, List<Promotion> promotions) {
+        for (Promotion promotionType : promotions) {
+            if (promotionType.getName().equals(promotion)) {
+                return promotionType.checkPromotionDate();
+            }
+        }
+        return 0;
+    }
+
+    public void checkTribeQuantity(List<Receipt> receipts, List<Store> store) {
+        for (Receipt receipt : receipts) {
+            if (receipt.getPromotionBuy() != NON_PROMOTION) {
+                checkPromotionQuantity(receipt, store);
+            }
+        }
+    }
+
+    private void checkPromotionQuantity(Receipt receipt, List<Store> store) {
+        for (Store storeProduct : store) {
+            if (receipt.getName().equals(storeProduct.getName())) {
+                checkNonGet(receipt, storeProduct.getQuantity());
+                break;
+            }
+        }
+    }
+
+    private void checkNonGet(Receipt receipt, int storeQuantity) {
+        if (receipt.getQuantity() % (receipt.getPromotionBuy() + GET) == receipt.getPromotionBuy()) {
+            if (isGet(receipt, storeQuantity)) {
+                receipt.addPromotion();
+            }
+        }
+    }
+
+    private boolean isGet(Receipt receipt, int storeQuantity) {
+        if (receipt.getQuantity() + GET <= storeQuantity) {
+            while (true) {
+                try {
+                    return isAnswer(InputView.getFree(receipt.getName()));
+                } catch (IllegalArgumentException e) {
+                    OutputView.printErrorAnswer();
+                }
+            }
+        }
+        return false;
+    }
+
+    public void checkTribePromotion(List<Receipt> receipts, List<Store> store) {
+        for (Receipt receipt : receipts) {
+            if (receipt.getPromotionBuy() != NON_PROMOTION) {
+                checkStoreQuantity(receipt, store);
+            }
+        }
+    }
+
+    private void checkStoreQuantity(Receipt receipt, List<Store> store) {
+        for (Store storeProduct : store) {
+            if (receipt.getName().equals(storeProduct.getName())) {
+                checkOverGet(receipt, storeProduct);
+                break;
+            }
+        }
+    }
+
+    private void checkOverGet(Receipt receipt, Store storeProduct) {
+        int promotionProductQuantity = storeProduct.getQuantity()
+                - (storeProduct.getQuantity() % receipt.getPromotionBuy() + GET);
+
+        int nomDiscountableQuantity = receipt.getQuantity() - promotionProductQuantity;
+
+        if (nomDiscountableQuantity > 0) {
+            if (!isPaymentConfirmed(receipt, nomDiscountableQuantity)) {
+                receipt.removeNonPromotion(nomDiscountableQuantity);
+            }
+            receipt.setPromotionQuantity(promotionProductQuantity);
+            return;
+        }
+        receipt.setPromotionQuantity(
+                receipt.getQuantity() / (receipt.getPromotionBuy() + 1) * (receipt.getPromotionBuy() + 1));
+    }
+
+    private boolean isPaymentConfirmed(Receipt receipt, int nomDiscountableQuantity) {
+        while (true) {
+            try {
+                return isAnswer(InputView.askForPayment(receipt.getName(), nomDiscountableQuantity));
+            } catch (IllegalArgumentException e) {
+                OutputView.printErrorAnswer();
+            }
+        }
+    }
+
+    public int membershipDiscount(List<Receipt> receipts) {
+        int totalPrice = 0;
+        int promotionPrice = 0;
+
+        if (isMembership()) {
+            for (Receipt receipt : receipts) {
+                totalPrice += receipt.getIndividualPrice() * receipt.getQuantity();
+                promotionPrice += checkPromotionPrice(receipt);
+            }
+        }
+
+        return checkDisCount(totalPrice, promotionPrice);
+    }
+
+    private boolean isMembership() {
+        while (true) {
+            try {
+                return isAnswer(InputView.membershipMessage());
+            } catch (IllegalArgumentException e) {
+                OutputView.printErrorAnswer();
+            }
+        }
+    }
+
+    private int checkPromotionPrice(Receipt receipt) {
+        return receipt.getIndividualPrice() * receipt.getPromotionQuantity();
+    }
+
+    private int checkDisCount(int totalPrice, int promotionPrice) {
+        int disCount = (int) ((totalPrice - promotionPrice) * 0.3);
+        if (disCount > MAX_MEMBERSHIP) {
+            return MAX_MEMBERSHIP;
+        }
+
+        return disCount;
+    }
+
+    public boolean isAnswer(String answer) {
+        if (answer.equals("Y")) {
+            return true;
+        }
+        if (answer.equals("N")) {
+            return false;
+        }
+
+        throw new IllegalArgumentException(INVALID_INPUT);
+    }
+
+    public void checkStock(List<Store> store, Order order) {
+        for (Product product : order.getOrder()) {
+            handleProductDeduction(store, product);
+        }
+    }
+
+    private void handleProductDeduction(List<Store> store, Product product) {
+        Store matchedStoreProduct = findStoreProduct(store, product.getName());
+
+        int remainingQuantity = matchedStoreProduct.deduction(product.getQuantity());
+        if (remainingQuantity > 0) {
+            applyRemainingDeduction(store, store.indexOf(matchedStoreProduct) + 1, remainingQuantity);
+        }
+    }
+
+    private Store findStoreProduct(List<Store> store, String productName) {
+        return store.stream()
+                .filter(storeProduct -> storeProduct.getName().equals(productName))
+                .findFirst()
+                .orElse(null);
+    }
+
+    private void applyRemainingDeduction(List<Store> store, int startIndex, int remainingQuantity) {
+        for (int i = startIndex; i < store.size() && remainingQuantity > 0; i++) {
+            remainingQuantity = store.get(i).deduction(remainingQuantity);
+        }
+    }
+
+}

리뷰: 서비스에서 View를 호출하는건 개인적으로 layered architecture구조상 적합하지 않다고 생각됩니다!

Layered architecture는 관심사별로 계층을 나눈 구조로 알고 있습니다. Presentation Layer 는 사용자와의 상호작용을 담당하는 역할, business Layer 는 비지니스 로직만 담당하는 역할입니다. 따라서 View 객체들은  presentation layer 에 속한다고 볼 수 있습니다. 

하지만 현재 코드를 보면 business layer 가 presentation layer 를 의존하는 것으로 보이는데, 이렇게 되면 business layer에 presentation layer의 코드가 노출되기때문에, 관심사가 분리되지 않았다고 볼 수 있습니다.