-
[과제] 키오스크 도전 Lv2내일배움캠프/과제 2025. 3. 13. 16:43
✅ Lv2. Enum, 람다 & 스트림을 활용한 주문 및 장바구니 관리
1️⃣ 과제 조건
- 요구사항이 가지는 의도
- 의도 : 고급 자바 기능을 활용해 프로그램의 효율성과 코드의 가독성을 개선하는 것을 목표로 합니다.
- 목적
- Enum을 통해 상수를 안전하게 관리하고, 프로그램 구조를 간결하게
- 제네릭을 활용하여 데이터 유연성을 높이고, 재사용 가능한 코드를 설계
- 스트림 API를 사용하여 데이터를 필터링하고, 간결한 코드로 동작을 구현
- Enum을 활용한 사용자 유형별 할인율 관리하기
- 사용자 유형의 Enum 정의 및 각 사용자 유형에 따른 할인율 적용
- 예시 : 군인, 학생, 일반인
- 주문 시, 사용자 유형에 맞는 할인율 적용해 총 금액 계산
- 사용자 유형의 Enum 정의 및 각 사용자 유형에 따른 할인율 적용
- 람다 & 스트림을 활용한 장바구니 조회 기능
- 기존에 생성한 Menu의 MenuItem을 조회 할 때 스트림을 사용하여 출력하도록 수정
- 기존 장바구니에서 특정 메뉴 빼기 기능을 통한 스트림 활용
- 예시 : 장바구니에 SmokeShack 가 들어 있다면, stream.filter를 활용하여 특정 메뉴 이름을 가진 메뉴 장바구니에서 제거
2️⃣ 구현 포인트
1. 할인 정보를 저장할 Enum 생성
public enum DiscountType { VETERAN("국가유공자", 10), // 20% 할인 SOLDIER("군인", 5), // 15% 할인 STUDENT("학생", 3), // 10% 할인 GENERAL("일반", 0); // 할인 없음 private final String value; // 값 private final double discountRate; // 할인율 DiscountType(String value, double discountRate) { this.value = value; this.discountRate = discountRate; } public String getValue() { return value; } public double getDiscountRate() { return discountRate; } public static void printDiscountTypeList() { IntStream.range(0, DiscountType.values().length) .forEach(i -> System.out.printf("%d. %-10s : %.1f%%%n", i + 1, DiscountType.values()[i].getValue(), DiscountType.values()[i].getDiscountRate())); System.out.println(); } public static DiscountType getDiscountTypeByIndex(int index) { DiscountType[] types = DiscountType.values(); if (index < 0 || index > types.length - 1) { throw new IllegalArgumentException("잘못된 인덱스입니다. 1부터 " + types.length + " 사이의 값을 입력하세요."); } return types[index]; // 1-based index 대응 } }
2. 할인 적용
- 주문 진행 시 할인 정보를 입력 받아 최종 금액에서 할인율 적용
private void processOrder(BufferedReader br) throws IOException { System.out.println("\n아래와 같이 주문 하시겠습니까?\n"); order.printOrderList(); // 장바구니 List 출력 order.printTotalPrice(); // 총 금액 출력 System.out.println("\n1. 주문 2. 메뉴판"); int no = inputHandler.getNumberInput(br, 3, false); // 번호 입력 if (no == 1) { // 주문 진행 System.out.println("\n할인 정보를 입력해주세요."); DiscountType.printDiscountTypeList(); // 할인 정보 출력 int dcNo = inputHandler.getNumberInput(br, DiscountType.getDiscountTypeCount(), false); // 선택된 할인정보 DiscountType discountType = DiscountType.getDiscountTypeByIndex(dcNo - 1); // DiscountType 찾기 order.order(discountType); // 주문 진행 } }
public void order(DiscountType discountType) { double discountRate = discountType.getDiscountRate(); // 할인율 double totalPrice = this.getTotalPrice(); // 총 금액 double finalPrice = totalPrice - (totalPrice * (discountRate / 100)); // 할인율이 적용된 최종금액 System.out.printf("\n주문이 완료되었습니다. %.1f%% 할인이 적용되어 최종 금액은 W %.1f 입니다.\n", discountRate, finalPrice); this.clearOrder(); // 장바구니 초기화 }
3. Stream 을 활용하여 print
- 기존의 for 을 사용하여 print 했던 것들은 stream() 함수를 활용
private void printMainMenu() { System.out.println("\n[ MAIN MENU ]"); menuList.stream().forEach(menu -> System.out.println((menuList.indexOf(menu) + 1) + ". " + menu.getCategory())); } public void printMenuItemList() { menuItems.stream().forEach(menuItem -> System.out.println((menuItems.indexOf(menuItem) + 1) + ". " + menuItem)); System.out.println("0. 뒤로가기\n"); } public void printOrderList() { System.out.println("[ Orders ]"); orderList.stream().forEach(System.out::println); }
4. stream filter 를 이용한 장바구니 삭제
- InputHandler 에 메뉴명을 입력받는 함수 추가
- orderList에서 입력 받은 메뉴명을 filter로 제외한 List로 재구성 만약 filterList와 기존 List의 사이즈가 같으면 filter되지 않은 것 false return
public String getMenuNmInput(BufferedReader br) throws IOException { while (true) { System.out.print("\n삭제하실 메뉴 이름을 입력해주세요 : "); String input = this.readInput(br); if (input == null || "".equals(input.replaceAll(" ", ""))) { System.out.println("\n! 메뉴명을 입력해주세요 !"); continue; } return input; } }
public boolean removeOrder(String menuNm) { List<MenuItem> filterList = orderList .stream() .filter(order -> !order.getMenuNm().equalsIgnoreCase(menuNm)) .toList(); if (orderList.size() == filterList.size()) { return false; } this.clearOrder(); orderList.addAll(filterList); return true; }
private void processOrder(BufferedReader br) throws IOException { System.out.println("\n아래와 같이 주문 하시겠습니까?\n"); order.printOrderList(); // 장바구니 List 출력 order.printTotalPrice(); // 총 금액 출력 System.out.println("\n1. 주문 2. 메뉴판 3. 메뉴 삭제\n"); int no = inputHandler.getNumberInput(br, 3, false); // 번호 입력 if (no == 1) { // 주문 진행 System.out.println("\n할인 정보를 입력해주세요."); DiscountType.printDiscountTypeList(); // 할인 정보 출력 int dcNo = inputHandler.getNumberInput(br, DiscountType.getDiscountTypeCount(), false); // 선택된 할인정보 DiscountType discountType = DiscountType.getDiscountTypeByIndex(dcNo - 1); // DiscountType 찾기 order.order(discountType); // 주문 진행 } else if (no == 3) { // 장바구니 삭제 String menuNm = inputHandler.getMenuNmInput(br); // 삭제 메뉴 명 입력 if (order.removeOrder(menuNm)) { // 메뉴 삭제 System.out.println("\n메뉴 삭제가 완료되었습니다."); } else { System.out.println("\n해당하는 메뉴가 없습니다."); } } }
3️⃣ 최종 코드
1. Main
import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<Menu> menuList = new ArrayList<>(); // Burgers menuList.add(new Menu("Burgers", List.of( new MenuItem("ShackBurger", 6.9, "토마토, 양상추, 쉑소스가 토핑된 치즈버거"), new MenuItem("SmokeShack", 8.9, "베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거"), new MenuItem("Cheeseburger", 6.9, "포테이토 번과 비프패티, 치즈가 토핑된 치즈버거"), new MenuItem("Hamburger", 5.4, "비프패티를 기반으로 야채가 들어간 기본버거") ))); // Drinks menuList.add(new Menu("Drinks", List.of( new MenuItem("Coke", 2.5, "탄산이 가득한 코카콜라"), new MenuItem("Cider", 2.5, "청량미 넘치는 사이다"), new MenuItem("Lemon juice", 3.0, "생과일 레몬이 들어간 레몬 쥬스"), new MenuItem("PowerAde", 3.0, "수분을 보충시켜 줄 파워에이드") ))); // Desserts menuList.add(new Menu("Desserts", List.of( new MenuItem("Cheese Cake", 5.0, "꾸덕한 치즈가 들어간 치즈 케이크"), new MenuItem("Chocolate cake", 5.5, "초코가 흘러내리는 달콤한 초콜릿 케이크"), new MenuItem("IceCream", 3.0, "시원한 아이스크림"), new MenuItem("Cookie", 2.0, "갓 구운 바삭한 쿠키") ))); new Kiosk(menuList).start(); } }
2. Menu
import java.util.List; public class Menu { private final String category; // 카테고리 private final List<MenuItem> menuItems; // menu Item 목록 public Menu(String category, List<MenuItem> menuItems) { this.category = category; this.menuItems = menuItems; } public String getCategory() { return category; } public int getMenuItemsSize() { return menuItems.size(); } // MenuItem 목록 출력 public void printMenuItemList() { menuItems.stream().forEach(menuItem -> System.out.println((menuItems.indexOf(menuItem) + 1) + ". " + menuItem)); System.out.println("0. 뒤로가기\n"); } // menuItem detail 출력 public void printMenuItemDetails(int index) { MenuItem item = this.getMenuItemByIndex(index); System.out.printf("선택한 메뉴 : %s | 가격 : W %.1f | 설명 : %s\n", item.getMenuNm(), item.getPrice(), item.getDesc()); } public MenuItem getMenuItemByIndex(int index) { return menuItems.get(index); } }
3. MenuItem
public class MenuItem { private final String menuNm; // 메뉴명 private final double price; // 가격 private final String desc; // 설명 public MenuItem(String menuNm, double price, String desc) { this.menuNm = menuNm; this.price = price; this.desc = desc; } public String getMenuNm() { return menuNm; } public double getPrice() { return price; } public String getDesc() { return desc; } @Override public String toString() { return String.format("%-15s | W %-4.1f | %s", menuNm, price, desc); } }
4. Order
import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class Order { private final List<MenuItem> orderList; public Order() { this.orderList = new ArrayList<>(); } // 장바구니 추가 public void addOrder(MenuItem order) { this.orderList.add(order); } // 장바구니 비우기 public void clearOrder() { this.orderList.clear(); } // 장바구니 empty 여부 public boolean isEmptyOrder() { return this.orderList.isEmpty(); } // 메뉴 목록 출력 함수 public void printOrderList() { System.out.println("[ Orders ]"); orderList.stream().forEach(System.out::println); } // 총 금액 출력 함수 public void printTotalPrice() { System.out.println("\n[ Total ]"); System.out.printf("W %.1f\n", this.getTotalPrice()); } // 총 금액 sum 함수 public Double getTotalPrice() { return orderList.stream().mapToDouble(MenuItem::getPrice).sum(); } // 주문 진행 public void order(DiscountType discountType) { double discountRate = discountType.getDiscountRate(); // 할인율 double totalPrice = this.getTotalPrice(); // 총 금액 double finalPrice = totalPrice - (totalPrice * (discountRate / 100)); // 할인율이 적용된 최종금액 System.out.printf("\n주문이 완료되었습니다. %.1f%% 할인이 적용되어 최종 금액은 W %.1f 입니다.\n", discountRate, finalPrice); this.clearOrder(); // 장바구니 초기화 } public boolean removeOrder(String menuNm) { List<MenuItem> filterList = orderList .stream() .filter(order -> !order.getMenuNm().equalsIgnoreCase(menuNm)) .toList(); if (orderList.size() == filterList.size()) { return false; } this.clearOrder(); orderList.addAll(filterList); return true; } }
5. InputHandler
import java.io.BufferedReader; import java.io.IOException; public class InputHandler { private static final String EXIT_COMMAND = "exit"; public String getMenuNmInput(BufferedReader br) throws IOException { while (true) { System.out.print("\n삭제하실 메뉴 이름을 입력해주세요 : "); String input = this.readInput(br); if (input == null || "".equals(input.replaceAll(" ", ""))) { System.out.println("\n! 메뉴명을 입력해주세요 !"); continue; } return input; } } // 숫자 입력 받기 public int getNumberInput(BufferedReader br, int maxOption, boolean useZero) throws IOException { while (true) { System.out.print("번호를 선택해주세요 : "); String input = this.readInput(br); if (isValidInput(input, maxOption, useZero)) { return Integer.parseInt(input); } System.out.println("\n! 올바른 번호를 입력해주세요 !\n"); } } public String readInput(BufferedReader br) throws IOException { String input = br.readLine().trim(); if (EXIT_COMMAND.equalsIgnoreCase(input)) { System.out.println("프로그램이 종료되었습니다."); System.exit(0); } return input; } // valid input private boolean isValidInput(String input, int maxOption, boolean useZero) { try { int number = Integer.parseInt(input); return useZero ? (number >= 0 && number <= maxOption) : (number > 0 && number <= maxOption); } catch (NumberFormatException e) { return false; } } }
6. DiscountType
import java.util.stream.IntStream; public enum DiscountType { VETERAN("국가유공자", 10), // 20% 할인 SOLDIER("군인", 5), // 15% 할인 STUDENT("학생", 3), // 10% 할인 GENERAL("일반", 0); // 할인 없음 private final String value; // 값 private final double discountRate; // 할인율 DiscountType(String value, double discountRate) { this.value = value; this.discountRate = discountRate; } public String getValue() { return value; } public double getDiscountRate() { return discountRate; } public static void printDiscountTypeList() { IntStream.range(0, DiscountType.values().length) .forEach(i -> System.out.printf("%d. %-10s : %.1f%%%n", i + 1, DiscountType.values()[i].getValue(), DiscountType.values()[i].getDiscountRate())); System.out.println(); } public static int getDiscountTypeCount() { return DiscountType.values().length; } public static DiscountType getDiscountTypeByIndex(int index) { DiscountType[] types = DiscountType.values(); if (index < 0 || index > types.length - 1) { throw new IllegalArgumentException("잘못된 인덱스입니다. 1부터 " + types.length + " 사이의 값을 입력하세요."); } return types[index]; // 1-based index 대응 } }
7.Kiosk
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; public class Kiosk { private final List<Menu> menuList; // 메뉴 목록 private final InputHandler inputHandler; // inputHandler private final Order order; // 장바구니 public Kiosk(List<Menu> menuList) { this.menuList = menuList; this.inputHandler = new InputHandler(); this.order = new Order(); } public void start() { int menuListSize = menuList.size(); try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) { while (true) { this.printMainMenu(); // MAIN MENU 목록 출력 함수 실행 int menuNo; int maxOption = menuListSize; if (!order.isEmptyOrder()) { // 장바구니에 물건이 있을 경우 this.printOrderMenu(); // ORDER MENU 출력 maxOption = menuListSize + 2; } System.out.printf("%-13s | 종료\n\n", "0. Exit"); menuNo = inputHandler.getNumberInput(br, maxOption, true); // inputHandler 실행 if (menuNo == 0) { // 0 이면 프로그램 종료 break; } if (!order.isEmptyOrder() && menuNo > menuListSize) { // ORDER MENU 선택시 if (menuNo == menuListSize + 1) { // Orders 버튼 this.processOrder(br); // 주문 프로세스 실행 } else { // Cancel 버튼 order.clearOrder(); // 장바구니 초기화 System.out.println("\n! 주문이 취소되었습니다 ! "); } } else { // MAIN MENU 선택 시 this.processMenuSelection(br, menuList.get(menuNo - 1)); // 선택한 Menu 진행 } } } catch (IOException e) { System.out.println("\n! 에러가 발생했습니다 !\n"); } System.out.println("\n프로그램을 종료합니다."); } // MAIN MENU 목록 출력 함수 private void printMainMenu() { System.out.println("\n[ MAIN MENU ]"); menuList.stream().forEach(menu -> System.out.println((menuList.indexOf(menu) + 1) + ". " + menu.getCategory())); } // ORDER MENU 목록 출력 함수 private void printOrderMenu() { System.out.println("\n[ ORDER MENU ]"); int i = menuList.size() + 1; System.out.printf("%-13s | 장바구니를 확인 후 주문합니다.\n", i++ + ". Orders"); System.out.printf("%-13s | 진행중인 주문을 취소합니다.\n", i + ". Cancel"); } // 주문 process private void processOrder(BufferedReader br) throws IOException { System.out.println("\n아래와 같이 주문 하시겠습니까?\n"); order.printOrderList(); // 장바구니 List 출력 order.printTotalPrice(); // 총 금액 출력 System.out.println("\n1. 주문 2. 메뉴판 3. 메뉴 삭제\n"); int no = inputHandler.getNumberInput(br, 3, false); // 번호 입력 if (no == 1) { // 주문 진행 System.out.println("\n할인 정보를 입력해주세요."); DiscountType.printDiscountTypeList(); // 할인 정보 출력 int dcNo = inputHandler.getNumberInput(br, DiscountType.getDiscountTypeCount(), false); // 선택된 할인정보 DiscountType discountType = DiscountType.getDiscountTypeByIndex(dcNo - 1); // DiscountType 찾기 order.order(discountType); // 주문 진행 } else if (no == 3) { // 장바구니 삭제 String menuNm = inputHandler.getMenuNmInput(br); // 삭제 메뉴 명 입력 if (order.removeOrder(menuNm)) { // 메뉴 삭제 System.out.println("\n메뉴 삭제가 완료되었습니다."); } else { System.out.println("\n해당하는 메뉴가 없습니다."); } } } // 메뉴 선택 후 process private void processMenuSelection(BufferedReader br, Menu menu) throws IOException { while (true) { System.out.printf("\n[ %s MENU ]\n", menu.getCategory().toUpperCase()); // 선택한 메뉴 출력 menu.printMenuItemList(); // 선택한 메뉴의 menuItem List 출력 int itemNo = inputHandler.getNumberInput(br, menu.getMenuItemsSize(), true); // 입력받기 if (itemNo != 0) { // 메뉴 선택시 menu.printMenuItemDetails(itemNo - 1); // menuItem Detail 출력 MenuItem menuItem = menu.getMenuItemByIndex(itemNo - 1); // menuItem 가져 옴 System.out.println("\n위 메뉴를 장바구니에 추가하시겠습니까?"); System.out.println("1. 확인 2. 취소\n"); int orderNo = inputHandler.getNumberInput(br, 2, false); // 입력받기 if (orderNo == 1) { // 장바구니 추가 System.out.println(menuItem.getMenuNm() + " 이 장바구니에 추가되었습니다."); order.addOrder(menuItem); break; } System.out.println("취소 되었습니다."); } else { // 돌아가기 선택 시 break; } } } }
'내일배움캠프 > 과제' 카테고리의 다른 글
[과제] 일정 관리 앱 만들기 필수 Lv2 (0) 2025.03.25 [과제] 일정 관리 앱 만들기 필수 Lv1 (0) 2025.03.25 [과제] 키오스크 도전 Lv1 (0) 2025.03.13 [과제] 키오스크 필수 Lv5 (1) 2025.03.13 [과제] 키오스크 필수 Lv4 (0) 2025.03.13 - 요구사항이 가지는 의도