키오스크 프로젝트 Challenge Lv2

서연·2025년 5월 1일

Java

목록 보기
28/36

✅ Enum, Lambda & Stream 을 활용한 주문 및 장바구니 관리

⚙️ 구조

  • Enum을 활용한 사용자 유형별 할인율 관리하기
    • 사용자 유형의 Enum 정의 및 각 사용자 유형에 따른 할인율 적용
      • 예시 : 군인, 학생, 일반인
    • 주문 시, 사용자 유형에 맞는 할인율 적용해 총 금액 계산

  • Lambda & Stream을 활용한 장바구니 조회 기능
    • 기존에 생성한 MenuMenuItem을 조회 할 때 스트림을 사용하여 출력하도록 수정
    • 기존 장바구니에서 특정 메뉴 빼기 기능을 통한 스트림 활용
      • 예시 : 장바구니에 SmokeShack 가 들어 있다면, stream.filter를 활용하여 특정 메뉴 이름을 가진 메뉴 장바구니에서 제거

📄 코드

CLv2Main.java

package ChallengeLv2;

public class CLv2Main {
    public static void main(String[] args) {
        CLv2Menu cLv2Menu = new CLv2Menu();

        // CLv2Menu 에 있는 lv4Menus 라는 리스트에 getter 를 사용해 햄버거 정보를 추가
        cLv2Menu.getCLv2Menus().add(new CLv2Menu("Burgers"));
        cLv2Menu.getCLv2Menus().add(new CLv2Menu("Drinks"));
        cLv2Menu.getCLv2Menus().add(new CLv2Menu("Desserts"));

        // CLv2Menu 에 있는 burgersMenu 라는 리스트에 getter 를 사용해 햄버거 정보를 추가
        cLv2Menu.getBurgersMenu().add(new CLv2MenuItem("ShackBurger", 6.9, "토마토, 양상추, 쉑소스가 토핑된 치즈버거"));
        cLv2Menu.getBurgersMenu().add(new CLv2MenuItem("SmokeShack", 8.9, "베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거"));
        cLv2Menu.getBurgersMenu().add(new CLv2MenuItem("Cheeseburger", 6.9, "포테이토 번과 비프패티, 치즈가 토핑된 치즈버거"));
        cLv2Menu.getBurgersMenu().add(new CLv2MenuItem("Hamburger", 5.4, "비프패티를 기반으로 야채가 들어간 기본버거"));

        // CLv2Menu 에 있는 drinksMenu 라는 리스트에 getter 를 사용해 음료 정보를 추가
        cLv2Menu.getDrinksMenu().add(new CLv2MenuItem("Fountain Soda", 2.9, "코카콜라, 코카콜라 제로, 스프라이트, 환타 오렌지"));
        cLv2Menu.getDrinksMenu().add(new CLv2MenuItem("Lemonade", 4.5, "매장에서 직접 만드는 상큼한 레몬에이드"));
        cLv2Menu.getDrinksMenu().add(new CLv2MenuItem("Fresh Brewed Iced Tea", 3.7, "직접 유기농 홍차를 우려낸 아이스 티"));
        cLv2Menu.getDrinksMenu().add(new CLv2MenuItem("Fifty/Fifty", 4.0, "레몬에이드와 유기농 홍차를 우려낸 아이스 티가 만나 탄생한 시그니처 음료"));

        // CLv2Menu 에 있는 dessertsMenu 라는 리스트에 getter 를 사용해 디저트 정보를 추가
        cLv2Menu.getDessertsMenu().add(new CLv2MenuItem("Shack Attack", 6.2, "진한 초콜릿 커스터드에 퍼지 소스와 세 가지 초콜릿 토핑이 블렌딩된 콘크리트"));
        cLv2Menu.getDessertsMenu().add(new CLv2MenuItem("Classic Hand-Spun Shakes", 6.8, "쫀득하고 진한 커스터드가 들어간 클래식 쉐이크"));
        cLv2Menu.getDessertsMenu().add(new CLv2MenuItem("Cup & Cones", 5.7, "신선하게 제조하는 쫀득하고 진한 아이스크림"));

        CLv2Kiosk cLv2Kiosk = new CLv2Kiosk(cLv2Menu);
        cLv2Kiosk.start(); // CLv2Kiosk 에 있는 start 함수 실행
    }
}

CLv2MenuItem.java

package ChallengeLv2;

public class CLv2MenuItem {
    // 1. 속성
    private String name;
    private double price;
    private String foodDetails;

    // 2. 생성자
    public CLv2MenuItem(String name, double price, String foodDetails) {
        this.name = name;
        this.price = price;
        this.foodDetails = foodDetails;

    }

    // 3. 기능(메서드)
    // 주소값을 출력하는 것이 아닌 리스트안에 들어있는 값을 출력하기 위해 toString 메서드 작성
    public String toString() {
        return name + " | " + "W" + price + " | " + foodDetails;
    }

    // name 의 getter 메서드
    public String getName() {
        return name;
    }

    // price 의 getter 메서드
    public double getPrice() {
        return price;
    }
}

CLv2Kiosk.java

package ChallengeLv2;

import java.util.InputMismatchException;
import java.util.Scanner;

public class CLv2Kiosk {
    CLv2Menu cLv2Menu;
    CLv2Cart cLv2Cart = new CLv2Cart();
    Scanner scanner = new Scanner(System.in);
    int num1 = 0; // 메인메뉴 선택을 위한 변수 생성
    int num2 = 0; // 버거메뉴, 음료메뉴, 디저트 메뉴 선택을 위한 변수 생성

    // 2. 생성자
    public CLv2Kiosk(CLv2Menu cLv2Menu) {
        this.cLv2Menu = cLv2Menu;
    }

    // 3. 기능(메서드)
    // 메뉴 입력값을 받아 유효성 검사 후 반환하는 메서드
    public int getInput(Scanner scanner, int min, int max) {
        int input = -1;
        while (true) {
            System.out.print("원하시는 메뉴를 선택해주세요: ");
            try {
                input = scanner.nextInt();
                if (input < min || input > max) {
                    throw new IllegalArgumentException();
                }
                break;
            } catch (IllegalArgumentException | InputMismatchException e) {
                System.out.println("올바른 숫자를 입력해주세요.");
                scanner.nextLine();
            }
        }
        return input;
    }

    // 입력과 반복문 로직
    public void start() {
        while (true) { // 사용자가 종료를 누르기 전까지 계속 반복
            System.out.println("\n");
            cLv2Menu.mainMenu(); // 메인 메뉴를 출력하는 메서드

            if (!cLv2Cart.getCarts().isEmpty()) { // 장바구니에 값이 들어있을 경우 0, 1, 2, 3, 4, 5 이외의 값 입력시 예외처리
                System.out.println("\n");
                cLv2Cart.orderMenu();
                num1 = getInput(scanner, 0, 5); // 코드가 반복되는 부분을 getInput 메서드로 처리(메뉴 입력값을 받아 유효성 검사 후 반환하는 메서드)
            }

            if (cLv2Cart.getCarts().isEmpty()) { // 장바구니가 비어있을 경우 0,1,2,3 이외의 값 입력시 예외처리
                System.out.println("\n");
                num1 = getInput(scanner, 0, 3); // 코드가 반복되는 부분을 getInput 메서드로 처리(메뉴 입력값을 받아 유효성 검사 후 반환하는 메서드)
            }

            if (num1 == 0) break; // 메인 메뉴에서 0을 눌렀을 시 프로그램 종료

            while (true) { // 사용자가 종료하기 전까지 반복한다.
                System.out.println("\n");
                if (num1 == 1) {
                    cLv2Menu.burgersMenu();
                } else if (num1 == 2) {
                    cLv2Menu.drinkMenu();
                } else if (num1 == 3) {
                    cLv2Menu.dessertsMenu();
                } else if (num1 == 4) { // 메인메뉴에서 4를 입력했을 때 orders 메서드(장바구니를 확인 후 주문하는 메서드)가 실행된다.
                    cLv2Cart.orders();
                    break;
                } else if (num1 == 5) { // 메인메뉴에서 5를 입력했을 때 cancel 메서드(진행중인 주문을 취소하는 메서드)가 실행된다.
                    cLv2Cart.cancel();
                    break;
                }
                try { // 다른 값을 입력했을 시 예외처리
                    System.out.print("원하시는 메뉴를 선택해주세요: ");
                    num2 = scanner.nextInt();
                    if (num2 == 0) break; // 0을 입력하면 메인메뉴로 돌아가게 된다.
                    if (num1 == 1) {
                        System.out.println("선택한 메뉴: " + cLv2Menu.getBurgersMenu().get(num2 - 1));
                        cLv2Cart.getCarts().add(cLv2Menu.getBurgersMenu().get(num2 - 1)); // burgerMenu 리스트에 들어있는 선택한 메뉴를 cartsList 에 저장하기 위해 getter 를 사용한다.
                    } else if (num1 == 2) {
                        System.out.println("선택한 메뉴: " + cLv2Menu.getDrinksMenu().get(num2 - 1));
                        cLv2Cart.getCarts().add(cLv2Menu.getDrinksMenu().get(num2 - 1)); // burgerMenu 리스트에 들어있는 선택한 메뉴를 cartsList 에 저장하기 위해 getter 를 사용한다.
                    } else if (num1 == 3) {
                        System.out.println("선택한 메뉴: " + cLv2Menu.getDessertsMenu().get(num2 - 1));
                        cLv2Cart.getCarts().add(cLv2Menu.getDessertsMenu().get(num2 - 1)); // burgerMenu 리스트에 들어있는 선택한 메뉴를 cartsList 에 저장하기 위해 getter 를 사용한다.
                    }
                    cLv2Cart.cartsAdd(); // cartsAdd 메서드(장바구니에 메뉴를 추가하는 메서드)를 실행한다.
                    break; // 메인메뉴로 돌아가게 된다.
                } catch (IndexOutOfBoundsException | InputMismatchException e) {
                    System.out.println("올바른 숫자를 입력해주세요");
                    scanner.nextLine(); // 버퍼에 남아있는 값을 제거하기 위해 사용
                }
            }
        }
    }
}

CLv2Menu.java

package ChallengeLv2;

import java.util.ArrayList;
import java.util.List;

public class CLv2Menu {// MenuItem 클래스를 관리하는 클래스
    // 1. 속성
    private final List<CLv2MenuItem> burgersMenu = new ArrayList<>(); // 버거 메뉴를 저장하는 리스트
    private final List<CLv2MenuItem> drinksMenu = new ArrayList<>(); // 음료 메뉴를 저장하는 리스트
    private final List<CLv2MenuItem> dessertsMenu = new ArrayList<>(); // 디저트 메뉴를 저장하는 리스트
    private final List<CLv2Menu> cLv2Menus = new ArrayList<>(); // 메인 메뉴를 저장하는 리스트
    private String name;

    // 2. 생성자
    public CLv2Menu() {
    }

    public CLv2Menu(String name) {
        this.name = name;
    }

    // 3. 기능(메서드)
    // 메인 메뉴를 보여주는 메서드
    public void mainMenu() {
        System.out.println("[ MAIN MENU ]");
        cLv2Menus.forEach(a -> System.out.println(cLv2Menus.indexOf(a) + 1 + ". " + a));
        System.out.println("0. 종료");
    }

    // 버거 메뉴를 보여주는 메서드
    public void burgersMenu() {
        System.out.println("[ BURGERS MENU ]");
        burgersMenu.forEach(a -> System.out.println(burgersMenu.indexOf(a) + 1 + ". " + a));
        System.out.println("0. 뒤로가기");
    }

    // 음료 메뉴를 보여주는 메서드
    public void drinkMenu() {
        System.out.println("[ DRINKS MENU ]");
        drinksMenu.forEach(a -> System.out.println(drinksMenu.indexOf(a) + 1 + ". " + a));
        System.out.println("0. 뒤로가기");
    }

    // 디저트 메뉴를 보여주는 메서드
    public void dessertsMenu() {
        System.out.println("[ DESSERTS MENU ]");
        dessertsMenu.forEach(a -> System.out.println(dessertsMenu.indexOf(a) + 1 + ". " + a));
        System.out.println("0. 뒤로가기");
    }

    // 주소값을 출력하는 것이 아닌 리스트안에 들어있는 값을 출력하기 위해 toString 메서드 작성
    public String toString() {
        return name;
    }

    // 버거 메뉴의 getter 메서드
    public List<CLv2MenuItem> getBurgersMenu() {
        return burgersMenu;
    }

    // 음료 메뉴의 getter 메서드
    public List<CLv2MenuItem> getDrinksMenu() {
        return drinksMenu;
    }

    // 디저트 메뉴의 getter 메서드
    public List<CLv2MenuItem> getDessertsMenu() {
        return dessertsMenu;
    }

    // 메인 메뉴의 getter 메서드
    public List<CLv2Menu> getCLv2Menus() {
        return cLv2Menus;
    }
}

CLv2Cart.java

package ChallengeLv2;

import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Scanner;

public class CLv2Cart {

    // 1. 속성
    // 선택한 메뉴의 정보를 저장하는 리스트
    private final List<CLv2MenuItem> cartsList = new ArrayList<>();
    Scanner scanner = new Scanner(System.in);

    // 2. 생성자

    // 3. 기능(메서드)
    // 메뉴 입력값을 받아 유효성 검사 후 반환하는 메서드
    private int getInput(Scanner scanner, int min, int max) {
        int input = -1;
        try {
            input = scanner.nextInt();
            if (input < min || input > max) {
                throw new IllegalArgumentException();
            }
        } catch (IllegalArgumentException | InputMismatchException e) {
            System.out.println("올바른 숫자를 입력해주세요.");
            scanner.nextLine();
        }
        return input;
    }

    // 장바구니에 메뉴를 추가하는 메서드
    public void cartsAdd() {
        System.out.println("\n");
        System.out.println("\"" + cartsList.get(cartsList.size() - 1) + "\""); // cartsList 에 담겨있는 마지막 값을 보여준다.(선택한 메뉴가 cartsList 마지막에 저장되어 있다.)
        System.out.println("위 메뉴를 장바구니에 추가하시겠습니까?");
        System.out.println("1. 확인        2. 취소");

        int cartNum = 0; // 장바구니에 추가 여부를 위한 변수 생성
        while (true) {
            cartNum = getInput(scanner, 1, 2); // 코드가 반복되는 부분을 getInput 메서드로 처리(메뉴 입력값을 받아 유효성 검사 후 반환하는 메서드)

            if (cartNum == 1) { // 1을 입력하면 장바구니에 추가된 메뉴 이름을 출력한다.
                System.out.println(cartsList.get(cartsList.size() - 1).getName() + " 이 장바구니에 추가되었습니다.");
                break; // 바른 값을 입력했을 시 while 문에서 빠져나온다.
            } else if (cartNum == 2) { // 2를 입력하면 장바구니에 추가하지 않기 때문에 cartsList 에 있는 마지막 값을 삭제한다.
                cartsList.remove(cartsList.size() - 1);
                System.out.println("메뉴 선택이 취소되었습니다.");
                break; // 바른 값을 입력했을 시 while 문에서 빠져나온다.
            }
        }
    }

    // ORDER MENU 를 출력하는 메서드
    public void orderMenu() {
        System.out.println("[ ORDER MENU ]\n" +
                "4. Orders       | 장바구니를 확인 후 주문합니다.\n" +
                "5. Cancel       | 진행중인 주문을 취소합니다.");
    }

    // 진행중인 주문을 취소하는 메서드
    public void cancel() {
        cartsList.clear(); // cartsList 에 담겨 있는 모든 값을 제거한다.
    }

    // 장바구니를 확인 후 주문하는 메서드
    public void orders() {
        int finalOrd = 0; // 최종으로 메뉴를 주문하기 위한 변수 생성
        double sumResult = 0; // 주문할 가격의 합을 나타내기 위한 변수 생성

        while (finalOrd < 1 || finalOrd > 3) { // 1, 2, 3을 입력하기 전까지 반복
            System.out.println("\n" + "아래와 같이 주문 하시겠습니까?");
            System.out.println("[ Orders ]\n" + cartsList + "\n"); // 장바구니에 담겨 있는 메뉴를 보여준다.

            int size = cartsList.size(); // cartsList 의 사이즈 변수 생성
            double sum = 0; // total 금액 변수 생성
            for (int i = 0; i < size; i++) { // cartsList 에 있는 가격의 합을 for 문을 활용해 구한다.
                sum += cartsList.get(i).getPrice();
            }
            sumResult = Math.round(sum * 10) / 10.0; // 가격의 합을 소숫점 첫째자리까지 나타내기 위해 Math.round 를 사용한다.
            System.out.println("[ Total ]" + "\n" + "W " + sumResult + "\n");

            System.out.println("1. 주문   2. 메뉴 삭제   3. 메뉴판");

            finalOrd = getInput(scanner, 1, 3); // 코드가 반복되는 부분을 getInput 메서드로 처리(메뉴 입력값을 받아 유효성 검사 후 반환하는 메서드)
        }
        if (finalOrd == 1) {
            int per = 0;
            while (per < 1 || per > 4) {
                DiscountRatePerType.discountinfo();
                per = getInput(scanner, 1, 4); // 코드가 반복되는 부분을 getInput 메서드로 처리(메뉴 입력값을 받아 유효성 검사 후 반환하는 메서드)
            }

            for (int i = 0; i <= 4; i++) {
                if (per == i) {
                    sumResult -= sumResult * (DiscountRatePerType.discountPer.get(i - 1));
                    sumResult = Math.round(sumResult * 10) / 10.0;
                }
            }
            System.out.println("\n" + "주문이 완료되었습니다. 금액은 W " + sumResult + "입니다.");
            cartsList.clear();

        } else if (finalOrd == 2) { // 장바구니에 담긴 메뉴 중 입력받은 메뉴 삭제
            System.out.print("무슨 메뉴를 삭제하시겠습니까?: ");
            String menu = scanner.next();

            //입력받은 menu 와 이름이 동일하다면 메뉴를 삭제
            cartsList.stream()
                    .filter(a -> a.getName().equals(menu))
                    .toList()
                    .forEach(cartsList::remove);
            System.out.println("메뉴가 삭제되었습니다.");

        }
    }

    // cartsList 의 getter 메서드
    public List<CLv2MenuItem> getCarts() {
        return cartsList;
    }

}

DiscountRatePerType.java

package ChallengeLv2;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public enum DiscountRatePerType {
    국가유공자("국가유공자", 0.1),
    군인("군인", 0.05),
    학생("학생", 0.03),
    일반("일반", 0);

    // 1. 속성
    private String name;
    private double per;

    // Enum 에서 정의한 name 을 리스트로 저장
    static List<String> discountName = Stream.of(DiscountRatePerType.values())
            .map(DiscountRatePerType::getName)
            .collect(Collectors.toList());

    // Enum 에서 정의한 per(할인율) 을 리스트로 저장
    static List<Double> discountPer = Stream.of(DiscountRatePerType.values())
            .map(DiscountRatePerType::getPer)
            .collect(Collectors.toList());

    // 2. 생성자
    DiscountRatePerType(String name, double per) {
        this.name = name;
        this.per = per;
    }

    // 3. 기능(메서드)
    // 할인정보를 출력하는 메서드
    public static void discountinfo() {
        System.out.println("\n할인 정보를 입력해주세요");
        for (int i = 0; i < discountName.size(); i++) {
            System.out.print(i + 1 + ". ");
            System.out.println(discountName.get(i) + ": " + Math.round(discountPer.get(i) * 100) + "%");
        }
    }

    // per 의 getter 메서드
    public double getPer() {
        return per;
    }

    // name 의 getter 메서드
    public String getName() {
        return name;
    }
}

💡 느낀점

중복되는 코드를 메서드로 정리하면서 코드의 재사용성과 유지보수성이 크게 향상된 것을 실감했다. 메서드를 사용해 반복적인 로직을 하나로 묶을 수 있다는 점에서 코드가 훨씬 간결해졌다.
Enum을 활용하면서 각 사용자 유형에 맞는 할인율을 손쉽게 적용할 수 있다는 점이 매우 유용하다는 것을 알게 되었다. Enum으로 정의하고 이를 통해 할인율을 적용하는 구조는 코드의 가독성을 높이고 확장성을 고려한 설계라는 점에서 매우 효율적이었다.
LambdaStream을 활용하여 장바구니 기능을 구현한 것이 이전에 코드를 짰던 방식보다 직관적인 것을 알 수 있었다. filter를 활용해 특정 메뉴를 장바구니에서 제거하는 기능을 구현하면서 Stream을 자세히 알게되었다.

🔗 GitHub

프로젝트 코드 보러 가기

0개의 댓글