@@ -0,0 +1,141 @@
+package store.view.output;
+
+import java.util.List;
+import store.domain.GeneralProduct;
+import store.domain.PromotionProduct;
+import store.domain.Receipt;
+import store.domain.ReceiptItem;
+import store.domain.Storage;
+import store.exception.ConvenienceStoreException;
+
+public class OutputView {
+
+    public void writeStorageStatus(Storage storage) {
+        System.out.print(OutputMessage.NEW_LINE.getOutputMessage());
+        System.out.println(OutputMessage.WELCOME_MESSAGE.getOutputMessage());
+        System.out.println(OutputMessage.SHOW_CURRENT_ITEMS.getOutputMessage());
+        System.out.print(OutputMessage.NEW_LINE.getOutputMessage());
+        writeInitPromotionProducts(storage);
+        writeInitOnlyGeneralProducts(storage);
+    }
+
+    public void writeReceipt(Receipt receipt, String userAnswer) {
+        writeReceiptMenuHeader();
+        writeReceiptMenuName(receipt);
+        writePresentation(receipt);
+        System.out.println(OutputMessage.PERFORATION_LINE.getOutputMessage());
+        writeUserTotalReceipt(receipt, userAnswer);
+    }
+
+    private void writeUserTotalReceipt(Receipt receipt, String userAnswer) {
+        writeShowTotalPurchaseAmount(receipt);
+        writeShowTotalPromotionDiscountAmount(receipt);
+        writeShowTotalMembershipDiscountAmount(receipt, userAnswer);
+        writeShowTotalPaymentAmount(receipt, userAnswer);
+    }
+
+    private void writeShowTotalPaymentAmount(Receipt receipt, String userAnswer) {
+        String nameAndPrice = String.format(OutputMessage.SHOW_PAYMENT_FORMAT.getOutputMessage(), "내실돈",
+                String.format("%,d", receipt.totalPurchaseAmount() - receipt.totalPromotionDiscount() -
+                        receipt.validateMembership(userAnswer)));
+        System.out.println(nameAndPrice);
+        System.out.print(OutputMessage.NEW_LINE.getOutputMessage());
+    }
+
+
+    private void writeShowTotalMembershipDiscountAmount(Receipt receipt, String userAnswer) {
+        String name = String.format("%-14s", "멤버십할인").replace(" ", "\u3000");
+        String price = String.format("-%,d", receipt.validateMembership(userAnswer));
+        System.out.printf(OutputMessage.SHOW_DISCOUNT_FORMAT.getOutputMessage(), name, price);
+        System.out.print(OutputMessage.NEW_LINE.getOutputMessage());
+    }
+
+    private void writeShowTotalPromotionDiscountAmount(Receipt receipt) {
+        String name = String.format("%-14s", "행사할인").replace(" ", "\u3000");
+        String price = String.format("-%,d", receipt.totalPromotionDiscount());
+        System.out.printf(OutputMessage.SHOW_DISCOUNT_FORMAT.getOutputMessage(), name, price);
+        System.out.print(OutputMessage.NEW_LINE.getOutputMessage());
+    }
+
+    private void writeShowTotalPurchaseAmount(Receipt receipt) {
+        String name = String.format("%-14s", "총구매액").replace(" ", "\u3000");
+        String quantity = String.format("%-10d", receipt.getTotalPurchaseCount());
+        String price = String.format("%,-10d", receipt.totalPurchaseAmount());
+        System.out.printf(OutputMessage.SHOW_RECEIPT_INIT_FORMAT.getOutputMessage(), name, quantity, price);
+        System.out.print(OutputMessage.NEW_LINE.getOutputMessage());
+    }
+
+    private void writePresentation(Receipt receipt) {
+        System.out.println(OutputMessage.SHOW_RECEIPT_PRESENTATION_HEADER.getOutputMessage());
+        for (ReceiptItem receiptItem : receipt.getReceiptItems()) {
+            if (receiptItem.getGetQuantity() != 0) {
+                String name = String.format("%-14s", receiptItem.getItemName()).replace(" ", "\u3000");
+                String quantity = String.format("%,-10d", receiptItem.getGetQuantity());
+                System.out.printf(OutputMessage.SHOW_PRESENTATION_FORMAT.getOutputMessage(), name, quantity);
+                System.out.print(OutputMessage.NEW_LINE.getOutputMessage());
+            }
+        }
+    }
+
+    private void writeReceiptMenuName(Receipt receipt) {
+        System.out.println(OutputMessage.SHOW_RECEIPT_MENU_NAME.getOutputMessage());
+        for (ReceiptItem receiptItem : receipt.getReceiptItems()) {
+            String name = String.format("%-14s", receiptItem.getItemName()).replace(" ", "\u3000");
+            String quantity = String.format("%-10d", receiptItem.getTotalBuyQuantity());
+            String price = String.format("%,-10d", receiptItem.getPrice() * receiptItem.getTotalBuyQuantity());
+            System.out.printf(OutputMessage.SHOW_RECEIPT_INIT_FORMAT.getOutputMessage(), name, quantity, price);
+            System.out.print(OutputMessage.NEW_LINE.getOutputMessage());
+        }
+    }
+
+    private void writeReceiptMenuHeader() {
+        System.out.print(OutputMessage.NEW_LINE.getOutputMessage());
+        System.out.println(OutputMessage.SHOW_RECEIPT_HEADER.getOutputMessage());
+    }
+
+
+    private void writeInitPromotionProducts(Storage storage) {
+        for (PromotionProduct promotionProduct : storage.getPromotionProducts()) {
+            System.out.println(promotionProduct);
+            findEqualGeneralProductName(storage.getGeneralProducts(), promotionProduct.getName());
+        }
+    }
+
+    private void findEqualGeneralProductName(List<GeneralProduct> generalProducts, String name) {
+        for (GeneralProduct generalProduct : generalProducts) {
+            if (generalProduct.getName().equals(name)) {
+                System.out.println(generalProduct);
+                break;
+            }
+        }
+    }
+
+    private void writeInitOnlyGeneralProducts(Storage storage) {
+        for (GeneralProduct generalProduct : storage.getGeneralProducts()) {
+            String generalProductName = generalProduct.getName();
+            boolean flag = findEqualPromotionProductName(storage.getPromotionProducts(), generalProductName);
+            writeOnlyGeneralProduct(flag, generalProduct);
+        }
+        System.out.print(OutputMessage.NEW_LINE.getOutputMessage());
+    }
+
+    private boolean findEqualPromotionProductName(List<PromotionProduct> promotionProducts, String name) {
+        for (PromotionProduct promotionProduct : promotionProducts) {
+            if (promotionProduct.getName().equals(name)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void writeOnlyGeneralProduct(boolean flag, GeneralProduct generalProduct) {
+        if (!flag) {
+            System.out.println(generalProduct.toString());
+        }
+    }
+
+    public void displayErrorMessage(ConvenienceStoreException convenienceStoreException) {
+        System.out.println(convenienceStoreException.getMessage());
+    }
+
+}

리뷰: 저는 개인적으로 프로모션 상품이 소진된 경우에도 '재고 없음'을 표시하는 것이 중요하다고 생각합니다. 
재고 없음을 표시하지 않으면, 사용자가 해당 상품이 프로모션 기간에 포함된 것인지 아닌지를 구분하기 어려울 수 있기 때문입니다. 
따라서 프로모션 기간 중에 재고가 없다면 '재고 없음'을 명시하는 것이 사용자에게 더 명확하고 혼란을 줄일 수 있을 것 같습니다. 
물론 프로모션 기간이 아닌 경우에는 '재고 없음'이 표시되지 않는 것이 맞다고 생각합니다!

좋은 의견 감사드립니다! 말씀해주신 내용을 덕분에 UX 측면에서도 다시 한 번 고민해볼 수 있었습니다.