백엔드 부트캠프/TIL

[내일배움캠프Spring-14일차] 키오스크 문제

sintory-04 2025. 3. 7. 21:51

    ✅ 문제를 풀기 전

    오늘은 키오스크 문제를 풀어보았다.

    이전에 풀었던 계산기 과제에서 내가 좀 .. 과하게 풀었나? 싶어서 이번에는 요구사항에 맞게 천천히! 풀고자 한다. 🫠

     대학교에서 과제를 풀었을 때, 항상 느낀 것이 요구사항은 철저히 지켜야 한다는 것인데 저번에는 조금 부족하지 않았나 싶다. 기본 흐름대로 따라가야지 요구사항을 지킬 수 있을 거 같다. 애초에 자바 과제의 목표는 자바라는 것을 이해하기 위해 푸는 것이기 때문에 쓸데없는 기술 빼고 객체 지향이라는 것에 집중하면서 풀고자 했다.

     키오스크 문제 발제를 들었을 때 튜터님께서 내가 혼자서 고민하고 해석하는 것이 원래의 의도와 높은 확률로 다를 수 있다고 하셨다. 살짝 뜨끔했다... 이번에는 이해가 안 되는 부분이 있으면 혼자 고민하고 해석하지 말고 이해가 안 될때에는 바로바로 요구사항을 체크하고 풀이하기로 다짐했다 ! 실제 현업에서도 요구사항이 맞나 안 맞나 맞추어가는 과정이 중요하다했기 때문에 그 점 유의하면서 문제를 풀었다.

    ✅ 1단계

    1단계에서는 가볍게 아래 두가지 조건만 맞으면 됐다.

    • 입력 처리와 간단한 흐름 제어를 복습합니다. 
    • Scanner 활용법, 조건문, 반복문을 재확인하며 입력 데이터를 처리하는 방법 강화

    그래서, 하나의 클래스에서 입력처리, 반복문, 조건문을 적절히 처리했다.

    - Main.java

    package level1;
    
    import java.util.Scanner;
    
    public class Main {
      public static void main(String[] args) {
        int exit = 1;
        while(exit!= 0){
          System.out.println("[ Burger King Menu ]");
          System.out.println("1. Whopper               | 7,100원 | 버거킹의 대표 메뉴로, 두툼한 패티와 신선한 채소가 어우러져 클래식한 맛을 제공하는 와퍼");
          System.out.println("2. Quattro Cheese Whopper| 7,900원 | 네 가지 치즈가 어우러져 진한 치즈 풍미를 느낄 수 있는 와퍼");
          System.out.println("3. Monster Whopper       | 9,300원 | 두툼한 더블 패티와 바삭한 베이컨, 매콤한 소스로 강렬한 맛을 자랑하는 와퍼");
          System.out.println("4. Whole Shrimp Whopper  | 7,900원 | 탱글탱글한 통새우와 신선한 야채가 어우러져 해산물의 풍미를 느낄 수 있는 버거");
          System.out.println("5. Whopper Jr            | 4,700원 | 클래식 와퍼의 맛을 그대로 담은 작은 사이즈의 버거");
          System.out.println("0. Exit                  | 종료");
          Scanner sc = new Scanner(System.in);
          int inputNumber = sc.nextInt();
    
          switch (inputNumber){
            case 1 -> System.out.println("[Whopper] 을/를 선택하셨습니다.");
            case 2 -> System.out.println("[Quattro Cheese Whopper] 을/를 선택하셨습니다.");
            case 3 -> System.out.println("[Monster Whopper] 을/를 선택하셨습니다.");
            case 4 -> System.out.println("[Whole Shrimp Whopper] 을/를 선택하셨습니다.");
            case 5 -> System.out.println("[Whopper Jr] 을/를 선택하셨습니다.");
            case 0 -> exit =0;
            default -> System.out.println("잘못입력하셨습니다.");
          }
          System.out.println("**********************");
        }
        System.out.println("프로그램을 종료합니다.");
      }
    }

    - 1단계는 계산기 과제와 흡사한 거 같다 !

    ✅ 2단계 

    2단계에서는 새로운 클래스를 만들어서 접근해야 됐다.

    • MenuItem 클래스 생성하기
      • 설명 : 개별 음식 항목을 관리하는 클래스입니다. 현재는 햄버거만 관리합니다.
      • 클래스는 이름, 가격, 설명 필드를 갖습니다.
    • main 함수에서 MenuItem 클래스를 활용하여 햄버거 메뉴를 출력합니다.
      • MenuItem 객체 생성을 통해 이름, 가격, 설명을 세팅합니다.
        • 키워드: new
      • List를 선언하여 여러 MenuItem을 추가합니다.
        • List<MenuItem> menuItems = new ArrayList<>();
      • 반복문을 활용해 menuItems를 탐색하면서 하나씩 접근합니다.

    1. Main.java

    package level2;
    
    import java.awt.*;
    import java.util.*;
    import java.util.List;
    
    public class Main {
      public static void main(String[] args) {
        // List 선언 및 초기화
        List<MenuItem> menuItems = new ArrayList<>();
    
        // add 함수를 통해 new MenuItem() List 에 삽입
        menuItems.add(new MenuItem("Whopper™",7100,"버거킹의 대표 메뉴로, 두툼한 패티와 신선한 채소가 어우러져 클래식한 맛을 제공하는 와퍼"));
        menuItems.add(new MenuItem("Quattro Cheese Whopper",7900,"네 가지 치즈가 어우러져 진한 치즈 풍미를 느낄 수 있는 와퍼"));
        menuItems.add(new MenuItem("Monster Whopper",9300,"두툼한 더블 패티와 바삭한 베이컨, 매콤한 소스로 강렬한 맛을 자랑하는 와퍼"));
        menuItems.add(new MenuItem("Whole Shrimp Whopper",7900,"탱글탱글한 통새우와 신선한 야채가 어우러져 해산물의 풍미를 느낄 수 있는 버거"));
        menuItems.add(new MenuItem("Whopper Jr",4700,"클래식 와퍼의 맛을 그대로 담은 작은 사이즈의 버거"));
    
        // Scanner 선언, 변수 선언
        Scanner sc = new Scanner(System.in);
        int exit = 1;
        int inputNumber;
    
        // 반복문을 활용해 List 안에 있는 Menu Item 하나씩 출력
        while(exit != 0){
          System.out.println("[ Burger King Menu ]");
          int i = 1;
          for (MenuItem a:menuItems){
            System.out.printf("%-25s | %d원 | %s\n", i + ". " + a.menuName, a.menuPrice, a.menuDescription);
            i++;
          }
          System.out.printf("%s | %s","0. 종료","0\n");
          System.out.println("주문을 입력해주세요.");
    
          inputNumber = sc.nextInt();
    
          System.out.print("선택한 메뉴: ");
          switch (inputNumber){
            case 1 -> System.out.printf("%s | 가격: %d | 설명: %s\n", menuItems.get(0).menuName, menuItems.get(0).menuPrice, menuItems.get(0).menuDescription);
            case 2 -> System.out.printf("%s | 가격: %d | 설명: %s\n", menuItems.get(1).menuName, menuItems.get(1).menuPrice,menuItems.get(1).menuDescription);
            case 3 -> System.out.printf("%s | 가격: %d | 설명: %s\n", menuItems.get(2).menuName, menuItems.get(2).menuPrice,menuItems.get(2).menuDescription);
            case 4 -> System.out.printf("%s | 가격: %d | 설명: %s\n", menuItems.get(3).menuName, menuItems.get(3).menuPrice,menuItems.get(3).menuDescription);
            case 5 -> System.out.printf("%s | 가격: %d | 설명: %s\n", menuItems.get(4).menuName, menuItems.get(4).menuPrice,menuItems.get(4).menuDescription);
            case 0 -> exit =0;
            default -> System.out.println("잘못입력하셨습니다.");
          }
        }
        System.out.println("주문이 종료되었습니다. ");
      }
    }

    2.MenuItem.java

    package level2;
    
    public class MenuItem {
      // 이름
      String menuName;
      // 가격
      Integer menuPrice;
      // 설명
      String menuDescription;
      MenuItem(String menuName, Integer menuPrice, String menuDescription){
        this.menuName = menuName;
        this.menuPrice = menuPrice;
        this.menuDescription = menuDescription;
      }
    }

    - 이렇게 MenuItem 를 작성하다 보니 DTO 처럼 만들어졌다. 확실히 Spring 연습하는 기분이긴 하다.

    - 그때는 왜 생성자를 만들어야하고 왜 Getter와 Setter를 만드는지 몰랐다면, 지금은 어느정도 이해가 되는 거 같다.

    ✅ 3단계

    • Kiosk 클래스 생성하기
      • 설명: 키오스크 프로그램의 메뉴를 관리하고 사용자 입력을 처리하는 클래스입니다.
      • MenuItem을 관리하는 리스트가 필드로 존재합니다.
      • main 함수에서 관리하던 입력과 반복문 로직은 이제 start 함수를 만들어 관리합니다.
      • List<MenuItem> menuItems 는 Kiosk 클래스 생성자를 통해 값을 할당합니다.
        • Kiosk 객체를 생성하고 사용하는 main 함수에서 객체를 생성할 때 값을 넘겨줍니다.

    1. Main.java

    package level3;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Main {
      public static void main(String[] args) {
        // 메인에서는 키오스크 실행만을 만들기
        List<MenuItem> menuItems = new ArrayList<>();
    
        // add 함수를 통해 new MenuItem() List 에 삽입
        menuItems.add(new MenuItem("Whopper™",7100,"버거킹의 대표 메뉴로, 두툼한 패티와 신선한 채소가 어우러져 클래식한 맛을 제공하는 와퍼"));
        menuItems.add(new MenuItem("Quattro Cheese Whopper",7900,"네 가지 치즈가 어우러져 진한 치즈 풍미를 느낄 수 있는 와퍼"));
        menuItems.add(new MenuItem("Monster Whopper",9300,"두툼한 더블 패티와 바삭한 베이컨, 매콤한 소스로 강렬한 맛을 자랑하는 와퍼"));
        menuItems.add(new MenuItem("Whole Shrimp Whopper",7900,"탱글탱글한 통새우와 신선한 야채가 어우러져 해산물의 풍미를 느낄 수 있는 버거"));
        menuItems.add(new MenuItem("Whopper Jr",4700,"클래식 와퍼의 맛을 그대로 담은 작은 사이즈의 버거"));
    
        Kiosk kiosk = new Kiosk(menuItems);
        kiosk.start();
      }
    }

    - 원래 있었던 main 자바의 순서제어를 제거하고 키오스크 클래스의 시작함수만이 담겨있다.

    - 위의 add 는 main 에서 유지하라는 거 같아 유지하였다 ! -> 따로 튜터님한테 여쭈어보니 역시 메인에는 실행 함수만 존재하는게 낫기는 하다라고 하셔서 추후에는 메인에서 제거할 생각이다.

    2. Kiosk.java

    package level3;
    
    import java.util.ArrayList;
    import java.util.InputMismatchException;
    import java.util.List;
    import java.util.Scanner;
    
    public class Kiosk {
      // MenuItem 관리하는 리스트
      List<MenuItem> menuItems;
    
      // Kiosk 클래스 생성자
      Kiosk(List<MenuItem> menuItems){
        this.menuItems = menuItems;
      }
    
      // 입력과 반복문 로직
      public void start(){
        int exit = 1;
        int inputNumber=-1;
        Scanner sc = new Scanner(System.in);
        while(exit != 0){
          System.out.println("[BBQ Menu]");
          int i = 1;
          for (MenuItem a:menuItems){
            System.out.printf("%s | %s | %s\n",i+". "+a.menuName,a.menuPrice ,a.menuDescription);
            i++;
          }
          System.out.printf("%s | %s","0. 종료","0\n");
          System.out.println("주문을 입력해주세요.");
          try{
            inputNumber = sc.nextInt();
          } catch (InputMismatchException e){ // 숫자 입력이 아닐 시
            System.out.println("숫자를 입력하셔야 합니다.");
            sc.nextLine();
            continue;
          }
    
          // inputNumber 가 0 일 경우 반복문 탈출
          if(inputNumber == 0) {
            exit = 0;
          } else{
            // 값이 올바를 경우 출력
            try {
              System.out.printf("%s | 가격: %d | 설명: %s\n", menuItems.get(inputNumber-1).menuName, menuItems.get(inputNumber-1).menuPrice, menuItems.get(inputNumber-1).menuDescription);
            } catch (Exception e) { // 번호 밖이면, 오류 출력
              System.out.println("번호 내에서 입력 부탁드립니다.");
            }
          }
        }
        System.out.println("주문이 종료되었습니다. ");
    
      }
    }

    - 오류도 출력했다. 먼저 숫자 값이 제대로 오지 않을 경우, 범위 밖의 숫자를 받을 경우에 대응했다. 지금 다시 코드를 살펴보니 나중은 IO 패키지에 따로 오류 메시지를 담아내는 게 좋을 거 같다. (그럴 경우 반복문을 어떻게 처리할지도 생각해야겠다.) 

    - 전체적인 흐름은 이러하다. 메뉴 출력 -> 입력값 받기 - > 옳은 입력일 시 해당 메뉴 출력 -> 특정 값인 0을 받을 동안 무한 반복

    3. MenuItem.java

    - 해당 클래스에서는 큰 변화는 없다.

    package level3;
    
    public class MenuItem {
      // 이름
      String menuName;
      // 가격
      Integer menuPrice;
      // 설명
      String menuDescription;
      MenuItem(String menuName, Integer menuPrice, String menuDescription){
        this.menuName = menuName;
        this.menuPrice = menuPrice;
        this.menuDescription = menuDescription;
      }
    }

     

    ✅ 4단계

    • Menu 클래스 생성하기
      • 설명 : MenuItem 클래스를 관리하는 클래스입니다. 예를 들어, 버거 메뉴, 음료 메뉴 등 각 카테고리 내에 여러 MenuItem을 포함합니다.
      • List<MenuItem> 은 Kiosk 클래스가 관리하기에 적절하지 않으므로 Menu 클래스가 관리하도록 변경합니다.
      • 여러 버거들을 포함하는 상위 개념 ‘버거’ 같은 카테고리 이름 필드를 갖습니다.
      • 메뉴 카테고리 이름을 반환하는 메서드가 구현되어야 합니다.

    1. Main.java

    package level4;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    public class Main {
      public static void main(String[] args) {
        // Menu 카테고리 이름 설정
        Menu burger = new Menu("Burger");
        Menu drink = new Menu("Drink");
        Menu desert = new Menu("Desert");
    
        // Burger
        burger.menuItems.add(new MenuItem("Whopper™",7100,"버거킹의 대표 메뉴로, 두툼한 패티와 신선한 채소가 어우러져 클래식한 맛을 제공하는 와퍼"));
        burger.menuItems.add(new MenuItem("Quattro Cheese Whopper",7900,"네 가지 치즈가 어우러져 진한 치즈 풍미를 느낄 수 있는 와퍼"));
        burger.menuItems.add(new MenuItem("Monster Whopper",9300,"두툼한 더블 패티와 바삭한 베이컨, 매콤한 소스로 강렬한 맛을 자랑하는 와퍼"));
        burger.menuItems.add(new MenuItem("Whole Shrimp Whopper",7900,"탱글탱글한 통새우와 신선한 야채가 어우러져 해산물의 풍미를 느낄 수 있는 버거"));
        burger.menuItems.add(new MenuItem("Whopper Jr",4700,"클래식 와퍼의 맛을 그대로 담은 작은 사이즈의 버거"));
    
        // Drink
        drink.menuItems.add(new MenuItem("Americano",1500,"진하고 깔끔한 커피 본연의 맛을 즐길 수 있는 음료"));
        drink.menuItems.add(new MenuItem("Vanilla Sundae Ice Cream",2000,"부드러운 바닐라 아이스크림"));
        drink.menuItems.add(new MenuItem("Choco Sundae Ice Cream",2200,"진한 초콜릿 아이스크림에 달콤한 초콜릿 시럽을 더한 디저트"));
        drink.menuItems.add(new MenuItem("Soft Drink",2000,"시원하고 청량감 넘치는 탄산이 가득한 음료"));
    
        // Desert
        desert.menuItems.add(new MenuItem("French Fries",2100,"클래식 와퍼의 맛을 그대로 담은 작은 사이즈의 버거"));
        desert.menuItems.add(new MenuItem("21cm Cheese Sticks",2500,"클래식 와퍼의 맛을 그대로 담은 작은 사이즈의 버거"));
        desert.menuItems.add(new MenuItem("Nugget King 4ea",2200,"클래식 와퍼의 맛을 그대로 담은 작은 사이즈의 버거"));
        desert.menuItems.add(new MenuItem("Coconut Shrimp 3ea",3900,"클래식 와퍼의 맛을 그대로 담은 작은 사이즈의 버거"));
    
        List<Menu> menus = new ArrayList<>(Arrays.asList(drink,burger,desert));
    
        // 키오스크 실행
        Kiosk kiosk = new Kiosk(menus);
        kiosk.start();
      }
    }

    - 문제 속에서는 Main 코드 안에 add 로 리스트 추가가 적혀 있어서 일단 여기에 데이터를 추가했지만, 추후에는 따로 뺄 예정이다 : >

    - 일단 나는 카테고리 별로 객체를 만들고 그 객체 안에 메뉴를 넣었다. 이게 원하는 바가 맞는지는 모르겠다. 나중에 5단계 까지 완성한 후 카테고리를 Stirng으로만 만들고 연결할지 아니면 객체 안에 객체를 삽입하는 식으로 접근하는게 맞을지 물어봐야겠다 !

    2. Kiosk.java 

    package level4;
    
    import io.Output;
    import org.w3c.dom.ls.LSOutput;
    
    import java.util.ArrayList;
    import java.util.InputMismatchException;
    import java.util.List;
    import java.util.Scanner;
    
    
    public class Kiosk {
      // start 라는 함수
      List<Menu> menus;
    
      // 리스트를 받게 합니다.
      Kiosk(List<Menu> menus){
        this.menus = menus;
      }
    
      public void start() {
        Scanner sc = new Scanner(System.in);
        int orderCategory;
        Menu menu;
        while (true){
          // 리스트와 Menu 클래스 활용하여, 상위 카테고리 메뉴를 출력해야하는 겁니다.
          Output.printOutput("[ Burger King MAIN MENU ]");
          for (int i=1; i <= menus.size(); i++){
            System.out.printf("%d. %s\n", i, menus.get(i-1).getCategoryName());
          }
          Output.printOutput("0. 종료");
    
          // 값을 받아야한다.
          try {
            orderCategory = sc.nextInt();
            if(orderCategory==0){
              break;
            }
            menu = menus.get(orderCategory-1);
          } catch (InputMismatchException e){
            Output.printOutput("잘못입력했습니다.");
            sc.nextLine();
            continue;
          } catch (RuntimeException e) {
            Output.printOutput("번호 내에서 입력부탁드립니다.");
            sc.nextLine();
            continue;
          }
    
    
          Output.printOutput("[ "+menu.getCategoryName()+" ]");
          menu.viewMenuItems();
          Output.printOutput("0. 뒤로가기.");
          int subExit = 1;
          while(subExit !=0){
            // int 값 받고 !
            int orderMenuItem;
            try {
              // 이상 없을 경우 출력
              orderMenuItem = sc.nextInt();
              if(orderMenuItem == 0){
                Output.printLineDivider();
                Output.printOutput("메인 메뉴로 이동합니다.");
              } else {
                // 번호를 받으면 menu.items Get 
                List<MenuItem> itemList = menu.getMenuItems();
                System.out.printf("선택한 메뉴: %s | %s | %s \n",itemList.get(orderMenuItem-1).menuName,itemList.get(orderMenuItem-1).menuPrice,itemList.get(orderMenuItem-1).menuDescription);
              }
              break;
            } catch (Exception e){ // 값이 이상할 경우 오류 출력
              System.out.println("값이 이상합니다.");
              sc.nextLine();
            }
          }
          Output.printStepDivider();
        }
        Output.printOutput("종료되었습니다.");
      }
    }

    - 카테고리가 새로 생겼기 때문에 반복문이 조금 더 복잡해진 걸 볼 수 있다.

    - 아직은 Getter 와 Setter 가 없기 때문에 순수하게 리스트에서 .get 을 해오는 걸 볼 수 있다.

    - 확실히 이렇게 되면 여기저기서 값을 다 가져올 수 있다는 부분이 문제인 거 같다.

    3. Main.java

    - 이번 요구사항에 맞추어 새로운 클래스인 Main.java를 만들었다. 객체 지향 설계를 위해 만든 클래스이다.

    - List<MenuItem> 을 Kiosk 클래스가 관리하기에는 적절하지 않기 때문에 새로운 클래스인 Menu 클래스를 만든 것이다. 

    package level4;
    
    import java.util.*;
    
    public class Menu {
      // 필드 선언
      String categoryName;
    
      List<MenuItem> menuItems = new ArrayList<>();
    
      // 생성자
      Menu(String categoryName){
        this.categoryName = categoryName;
      }
    
      // 리스트를 순차적으로 보여주는 함수
      public void viewMenuItems(){
        for (int i=0; i < menuItems.size(); i++){
          System.out.printf("%-30s | %s원 | %s\n",(i+1)+ ". "+ menuItems.get(i).menuName,menuItems.get(i).menuPrice,menuItems.get(i).menuDescription);
        }
      }
    
      // List를 리턴하는 함수
      public List<MenuItem> getMenuItems(){
        return this.menuItems;
      }
    
      // 카테고리 이름을 반환
      public String getCategoryName(){
        return this.categoryName;
      }
    }

    4. MenuItem

    - 해당 클래스에서는 변화가 없다.

    package level4;
    
    public class MenuItem {
      // 이름
      String menuName;
      // 가격
      Integer menuPrice;
      // 설명
      String menuDescription;
    
      MenuItem(String menuName, Integer menuPrice, String menuDescription){
        this.menuName = menuName;
        this.menuPrice = menuPrice;
        this.menuDescription = menuDescription;
      }
    }

    ✅ 오늘의 회고

    - 문제를 풀다보니 시간이 금방 지나간다. 그래서 마지막에 가면 TIL 쓰는 시간 까지도 문제를 풀고있다.

    - 그러다 보니, TIL 을 자세히 못 쓰는 거 같다. 이제부터라도 저녁을 먹고 난 후는 배운것을 다시 되돌아보는 복습 시간을 가져야겠다고 생각했다.  

    - 문제도 생각보다는.. 계산기를 풀고 난 후라 그런가 해볼만은 하다. 하지만 요구사항을 어떻게 만족시켜야 할지에 대한 고민은 계속 하게된다...