Java/키오스크 구현

트러블슈팅2 - 코드 재사용성을 높이고 더 정확한 표현으로 수정

JuNo_12 2025. 4. 29. 20:33

키오스크를 처음부터 새로 만들어봤습니다.

제목에서 알 수 있듯이 코드 재사용성을 높이고 코드를 더 정확하고 안전하게 다루는것에 초점을 두어 구현해봤습니다.

 

처음 접해보는 내용들도 있어서 코드블록과 함께 코드 설명을 해보겠습니다.


1. 불변화

제가 이 개념을 접하게 된 이유는 저는 지금까지 데이터를 캡슐화시키기 위해서 접근제어자를 설정해주었습니다. 하지만 사실 외부에서 게터로 가져와서 add remove 기능을 사용할 수 있으니까 이거로는 캡슐화를 하지 못한다고 느꼈습니다. 왜냐하면 지금까지 저는 단순한 게터형식의 메서드만 만들어줬기때문이죠. 

하지만, 제가 여기서 알려드릴 '불변화'라는 개념은 정말 제가 원했던 캡슐화를 시켜줄 수 있는 개념입니다.

    public Map<String, Integer> getCart() {
        return Collections.unmodifiableMap(cart);
    }

 

사실 맨 첫줄만 보면 우리가 흔히 아는 게터 메서드 선언입니다. 하지만 내부의 코드를 설명해보자면,

cart라는 이름의 Map을 '불변'으로 반환해준 것입니다. 쉽게 말해서 이 메서드는 Map을 '읽기전용'으로만 불러올 수 있는 기능이라는 것입니다. 이를 외부에서 가져와도 아예 못건들게 설정을 해준거죠.


2. Map타입에 key를 바탕으로 value를 추가하는 메서드

제목만 봐서는 이해가 잘 안가죠? 제가 이를 설명하는 이유는 저희는 개인프로젝트에서 장바구니에 저희가 고른 메뉴를 '추가'하고 장바구니에 있는 목록들을 '계산'해야합니다. 하지만, 이를 위해서는 장바구니에 담긴 그 메뉴의 '수량' 을 데이터로 정리해주어야겠죠? 그를 위한 메서드입니다. 한줄한줄 보겠습니다.

public void addBurger(String burgerName) {
    cart.merge(burgerName, 1, Integer::sum);
}

 

merge(K key, V value, BiFunction remappingFunction) 이 메소드가 하는 일은 

키가 없으면: Key를 value로 추가 (burgerName, 1)   /   키가 있으면: 기존 값이랑 새 값(1)을 처리해주는데

여기서 Integer::sum 이 뭔가 하면, 기존값 + 새로 추가하려는 값(1)을 더해주는 기능입니다. 이를 통해 새로운 Map타입에 선택한 메뉴 이름(Key)수량(value)이 저장되는것입니다. 여기서 이제 게터 메서드를 활용하여 Map 타입에 저장된 value값을 가지고 와서 '계산'을 할 수 있는거죠.


3. Scanner로 문자열을 입력받았을 때 간단하게 예외처리를 해주는 방법

저희가 키오스크를 구현할 때, 저희는 정수타입으로 case를 나누어서 로직을 실행해주었는데 여기서 만약 문자열을 입력받으면 컴파일 에러가 납니다. 따라서 저희는 문자열을 입력받았을 때의 예외처리를 해주어야했죠. 이 때 사용하는 방법입니다. (이 개념은 아침에 알고리즘 문제 풀 때 나왔던 개념인데 접해보니 신세계였습니다.) 바로 코드 보시죠.

private int getChoice() {
        while (true) {
            try {
                return Integer.parseInt(scanner.nextLine().trim());
            } catch (NumberFormatException e) {
                System.out.println("숫자만 입력해주세요.");
            }
        }
    }
    
    while (true) {
            int choice = getChoice();

            switch (choice) {
                case 1 -> { showBurgerMenu(); return; }
                case 2, 3 -> System.out.println("준비 중인 메뉴입니다.");
                default -> System.out.println("올바른 번호를 입력해주세요.");
            }
        }

 

위에 코드부터 설명을 해보겠습니다. getChoice()라는 메서드는 정수형 메서드입니다. 반복문 내에서 try-catch문을 사용해주었습니다. 저희가 주목해야할 점은 return Integer.parseInt(Scanner.nextLine().trim()); 부분인데

  • Scanner.nextLine() : 이 부분은 저희가 잘 아는 '문자열'로 입력을 받는 기능입니다.
  • .trim() : 이거는 입력받은 문자열의 앞 뒤에 있는 '공백'을 지워주는 기능입니다.
  • Integer.parseInt : 자 여기가 핵심입니다. 저희는 분명 '문자열'로 입력을 받았죠? 근데 보시면 Integer, Int가 적혀있습니다. 이것의 의미는 저희가 받은 '문자열''정수'로 반환해주는 것입니다. 

자 만약에 저희가 문자열을 입력해주었다면,  정수형으로 반환을 해주는 시점에서 예외가 발생합니다. 이 예외를 처리해주는 로직입니다. 그리고 아래 코드를 보면, int coice = getChoice(); 이 코드는 간단합니다. 위에서 예외가 발생되지 않은, 즉 '정수형'으로 입력받은 값을 choice라는 변수에 넣어준 것입니다.


4. 지금까지 만든 기능들을 활용하여 장바구니에 들어있는 버거의 수량을 찾는 기능

저희가 2번에서 새로운 Map타입에 선택한 버거이름과 수량을 넣어주었죠? 이제 이 저장된 데이터에서 수량을 꺼내어보겠습니다.

for (String burgerName : shoppingCartList.getShoppingCart().keySet()) { // 1.
    int count = shoppingCartList.getShoppingCart().get(burgerName);     // 2.
    Optional<Burger> burger = burgerMenu.findBurgerByName(burgerName);  // 3.
 
    if (burger.isPresent()) {                           // 4.
        double price = burger.get().getBurgerPrice();   // 5.
        totalPrice += price * count;                    // 6.
        System.out.println(burgerName + " | W " + price + " | " + count + "개");
    }
}

 

코드가 복잡해보이긴하지만, 사실 하나하나 끊어서보면 간단한 코드입니다. 하나하나 봐보죠.

1. 장바구니(Map형태)를 가져와서 안에 든 모든 키(버거 이름)을 가져옵니다. 각 버거 이름에 대해 반복합니다.

2. Map에서 걸러진 해당 버거의 주문 수량을 가져옵니다. 이를 count 라는 변수에 넣어주었습니다

3. 버거 메뉴에서 해당 이름의 버거 정보를 찾습니다. (Optional을 사용하여 버거가 존재하지 않을 수 있는 상황을 안전하게 처리)

4. 만약 버거가 존재한다면, 

5. 그 버거의 가격을 가져와서 price 라는 변수에 넣어주고

6. 가격과 수량을 곱하여 총 금액을 계산해주었습니다.


5. 4번에서 사용된 findBurgerByName 메서드

public Optional<Burger> findBurgerByName(String burgerName){ // 1.
    return burgers.stream()                                  // 2.
            .filter(burger -> burger.getBurgerName().equals(burgerName))  // 3.
            .findFirst();                                    // 4.

 

한줄한줄 보겠습니다.

1. 값이 없을 수 있는 상황을 안전하게 처리하기 위하여 반환타입을 Optional로 해주었습니다. 

이는 매개변수로 찾고자 하는 버거 이름을 받습니다.

2. burgers(List형태) 를 스트림으로 변환 : 스트림은 데이터를 연속적으로 처리할 수 있게 해주는 기능

3. 각 버거에 대해 이름을 확인하여 필터링해줍니다. 버거의 이름을 가져와서, 찾고자 하는 이름과 일치하는지 확인합니다.

4. 조건에 맞는 '첫 번째' 요소를 Optional로 반환해줍니다.

 

사실 이렇게 따로 메서드를 안만들어주고 위에서 한번에 처리해줘도되긴합니다.


새로 만들어보니, 확실하게 처음 만든 키오스크보다 훨씬 깔끔하고 코드 재사용성도 높아졌습니다. 자바에는 정말 수많은 문법들이 있다고 생각이 들었습니다. 하지만 저희가 모든 문법을 알 수는 없기에 앞으로 코드를 구현할 때 "이런 상황에서는 이런 방식으로 처리해주면 되겠다." 를 계속 생각해보는 노력을 해야겠다고 정말 크게 느낀 하루였습니다.