자바 미니 프로젝트 - 키오스크 만들기

Zyoon·2025년 4월 30일

미니프로젝트

목록 보기
6/35
post-thumbnail

💡자바 콘솔로 간단한 키오스크 만들기


설계

  • 간단한 키오스크 프로그램
  • 카테고리, 메뉴를 출력하여 표시
  • 해당 메뉴를 골라 장바구니에 넣기
  • 장바구니에 있는 메뉴와 수량으로 전체 금액 계산하여 표시
  • 할인 받는 경우 할인 금액까지 계산하여 최종 금액 표시

주요 코드 정리


필드부분

메뉴 아이템 부분

public class MenuItem {
  private String menuName;
  private double menuPrice;
  private String menuDescription;

메뉴 부분 (Category 및 MenuList 정의)

public class Menu {

private static Map<Integer, String> menuCategory = new HashMap<>() {{
    put(1, "[ BURGERS MENU ]");
    put(2, "[ DRINKS MENU ]");
    put(3, "[ DESSERTS MENU ]");
}};

private static List<MenuItem> burgerMenuList = new ArrayList<>() {{
    add(new MenuItem("ShackBurger ", 6.9, "토마토, 양상추, 쉑소스가 토핑된 치즈버거"));
    add(new MenuItem("SmokeShack  ", 8.9, "베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거"));
    add(new MenuItem("Cheeseburger", 6.9, "포테이토 번과 비프패티, 치즈가 토핑된 치즈버거"));
    add(new MenuItem("Hamburger   ", 5.4, "비프패티를 기반으로 야채가 들어간 기본버거"));
}};

private static List<MenuItem> drinkMenuList = new ArrayList<>(){{
    add(new MenuItem("Coke   ", 6.9, "콜라"));
    add(new MenuItem("Sprite ", 8.9, "사이다"));
    add(new MenuItem("Fanta  ", 6.9, "환타"));
    add(new MenuItem("Beer   ", 5.4, "맥주"));
}};

private static List<MenuItem> dessertMenuList = new ArrayList<>(){{
    add(new MenuItem("Fries   ", 6.9, "감자튀김"));
    add(new MenuItem("Tender  ", 8.9, "치킨텐더"));
    add(new MenuItem("Coleslaw", 6.9, "코울슬로"));
    add(new MenuItem("Egg Tart", 5.4, "에그타르트"));
}};

Kiosk 부분

  • static 전역변수로 정의
  • 장바구니는 Map으로 관리. <선택한 메뉴, 수량> 같은 메뉴 선택 시 수량 +1
public class Kiosk {
  static int inputNumber; // 입력값 받는 변수
  static double totalPrice; // 총 금액
  static Map<MenuItem, Integer> orderItemMap; //장바구니

Enum 정의

입력값 및 에러 관리

  • 직접적인 숫자로 파라미터를 넘길 때, enum 으로 정의된 값 활용하여 오타 발생 상황을 방지하였다
private enum DefaultNumber {
    ERROR(-1),
    ZERO(0),
    ONE(1),
    TWO(2),
    THREE(3),
    FOUR(4),
    FIVE(5);

    private final int number;

    DefaultNumber(int number){
        this.number = number;
    }
    public int getValue(){
        return number;
    }
}

할인 정보

  • 할인 정보 연산을 람다식으로 관리
private enum DiscountType{
    PATRIOT(1,"1. 국가유공자 : 10%", (num) -> num * 0.9),
    SOLDIER(2,"2. 군인      : 5%", (num) -> num * 0.95),
    STUDENT(3, "3. 학생      : 3%", (num) -> num * 0.97),
    NORMAL(4,"4. 일반      : 0%", (num)-> num);

    // 필드
    private final int typeNumber;
    private final String typeName;
    private final Function<Double, Double> discountApply;

    // 생성자
    DiscountType(int typeNumber, String typeName, Function<Double, Double> discountApply) {
        this.typeNumber = typeNumber;
        this.typeName = typeName;
        this.discountApply = discountApply;
    }

    // 연산 값 리턴
    public double getDiscountedPrice(double num) {
        return discountApply.apply(num);
    }

    public int getTypeNumber(){
        return typeNumber;
    }

}

연산 부분

입력 값 받는 메서드

  • Scanner 입력을 모두 하나의 메서드로 관리
  • 입력 받는 숫자의 범위를 파라미터로 같이 넘겨준 뒤, 해당 범위 안에서 입력값이 나올 경우 리턴
  • 범위를 벗어나거나 입력값이 올바르지 않은 경우 에러 발생
inputNumber = DefaultNumber(scanner, DefaultNumber.ZERO.getValue(), DefaultNumber.THREE.getValue());

//번호 입력받는 부분
private int DefaultNumber(Scanner scanner, int minNumber, int maxNumber){

    try{
        //스캐너 입력
        inputNumber = scanner.nextInt();
        scanner.nextLine();

        //입력받은 숫자가 max 와 min 사이일 경우 리턴 -> 아닐경우 에러
        if(inputNumber <= maxNumber && inputNumber >= minNumber){
            return inputNumber;
        }else {
            System.out.println("숫자를 제대로 입력해 주세요. (" + minNumber +" ~ " + maxNumber + ")");
            return DefaultNumber.ERROR.getValue();
        }
    //값이 이상할 경우 에러
    } catch (Exception e) {
        System.out.println("숫자를 제대로 입력해 주세요. (" + minNumber +" ~ " + maxNumber + ")");
        scanner.nextLine();
        return DefaultNumber.ERROR.getValue();
    }
}

메뉴 선택 및 선택 메뉴 장바구니 추가

  • 입력값으로 메뉴 선택
  • 선택된 메뉴가 장바구니에 있을 시, 기존 수량에서 +1 하여 대입
  • 없다면 해당 객체와 수량 1로 대입
//메뉴 확인 후 선택
private MenuItem getSelectedMenuByNumber(List<MenuItem> menuList){

    //입력받은 숫자에 해당하는 번호로 선택한 메뉴 정보 가져옴 (인덱스번호 == 선택한 번호 - 1)
    MenuItem selectedMenu = menuList.get(inputNumber - DefaultNumber.ONE.getValue());

    //메뉴 출력
    System.out.println("선택한 메뉴 : " + menuList.get(inputNumber - DefaultNumber.ONE.getValue()).toString());

    return selectedMenu;
}

//선택한 메뉴 장바구니에 넣기
private void addToOrderList(MenuItem selectedMenu) {

    System.out.println("장바구니에 추가 : " + selectedMenu.getMenuName() + "\n");

    //장바구니에 메뉴가 있을경우 수량 가져옴. 없을경우 0 가져옴
    int quantity = orderItemMap.getOrDefault(selectedMenu, DefaultNumber.ZERO.getValue());

    //메뉴가 있을경우 + 1, 없을경우 1.
    orderItemMap.put(selectedMenu, quantity + DefaultNumber.ONE.getValue());
}

장바구니 총 금액 계산

  • 장바구니 MapentrySet 로 변환한다.
  • map 으로 key에서 가격 가져오고, value 에서 수량 가져와서 곱셈 연산을 해준다.
  • reduce 함수로 연산한 결과 값 모두 더해준다.
private double getTotalPrice(){
  return orderItemMap.entrySet().stream()
          .map(entry -> entry.getKey().getMenuPrice() * entry.getValue())
          .reduce(0.0,(sum, now) -> sum + now);
}

할인 적용

  • Enum 에 정의된 할인 정보를 stream 으로 모두 출력
  • Enum 에 정의된 할인 정보 중, 입력된 숫자에 대응되는 하나의 결과만 가져온 후, 정의되어 있는 값으로 연산 후 리턴.
//할인정보 보여줌
private void showDiscountInfo(){
    Arrays.stream(DiscountType.values())
            .forEach(value -> System.out.println(value.typeName));
    System.out.println();
}

//할인 정보 포함하여 계산 후 결과 값 리턴
private double getDiscountedPriceByNumber(){
    //선택된 숫자로 enum 의 discountType 중 하나 선택 후 할인률 계산
    return Arrays.stream(DiscountType.values())//enum 을 스트림으로 사용
            .filter(value -> value.getTypeNumber() == inputNumber) //조건
            .findAny()//하나의 값만 반환 -> 없으면 에러
            .map(type -> type.getDiscountedPrice(totalPrice))
            .orElse(totalPrice);// 계산 없을경우 기존 값 반화 -> 없으면 에러
}

출력결과

[ MAIN MENU ]
1. Burgers
2. Drinks
3. Desserts
0. 종료    | 종료합니다.

1
[ BURGERS MENU ]
1. ShackBurger    |   W 6.9   |   토마토, 양상추, 쉑소스가 토핑된 치즈버거
2. SmokeShack     |   W 8.9   |   베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거
3. Cheeseburger   |   W 6.9   |   포테이토 번과 비프패티, 치즈가 토핑된 치즈버거
4. Hamburger      |   W 5.4   |   비프패티를 기반으로 야채가 들어간 기본버거
0. 뒤로가기
2
선택한 메뉴 : SmokeShack     |   W 8.9   |   베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거
위 메뉴를 장바구니에 추가하시겠습니까?
1. 확인     2. 취소

1
장바구니에 추가 : SmokeShack  

[ MAIN MENU ]
1. Burgers
2. Drinks
3. Desserts
0. 종료    | 종료합니다.

[ ORDER MENU ]
4. Orders       | 장바구니를 확인 후 주문합니다.
5. Cancel       | 진행중인 주문을 취소합니다.

4
아래와 같이 주문 하시겠습니까?

[ Orders ]
SmokeShack     |   W 8.9   |   베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거  |  1ShackBurger    |   W 6.9   |   토마토, 양상추, 쉑소스가 토핑된 치즈버거  |  2Tender     |   W 8.9   |   치킨텐더  |  1Fanta     |   W 6.9   |   환타  |  1[ Total ]
W 38.5

1. 주문     2. 메뉴판

1
1. 국가유공자 : 10%
2. 군인      : 5%
3. 학생      : 3%
4. 일반      : 0%

1
주문이 완료되었습니다. 금액은 W 34.6 입니다.

이슈 사항


1. Map 에서 객체를 Key 로 사용할 경우 중복 계산

문제점

  • 장바구니 MapKey 는 객체로 되어있는데, equals() 함수로는 중복된 값이 제대로 확인되지 않는다.

원인

  • Object 의 비교에서는 equals()== 가 동일하게 작동한다.
  • 즉, 논리적 비교(객체 안에 있는 데이터가 같은지 비교)를 해야하지만 실제로는 물리적 비교(같은 메모리에 있는지 비교) 로 작동하는 것이다.

해결

  • 직접 재정의(Override) 해야 논리적 비교가 제대로 작동한다.
  • 이렇게 재정의 후 equal() 가 논리적으로 제대로 판별을 하며, Map, Set 에 중복되어 있는 값도 판별할 수 있다.
@Override
public boolean equals(Object o) {
    if (o == null || getClass() != o.getClass()) return false;
    MenuItem menuItem = (MenuItem) o;
    return Objects.equals(menuName, menuItem.menuName);
}

@Override
public int hashCode() {
    return Objects.hashCode(menuName);
}

📗 Java 동등성과 동일성 정리


2. 자바 네이밍 컨벤션 활용

  • 이 전까지는 변수명 및 클래스명을 간단하게 혹은 자신만 알아볼 수 있게 했었지만, 이 프로젝트는 자바의 명명규칙 및 자주 사용되는 반의어에 따라 이름을 생성하였다.
  • 당사자가 아닌 다른 사람이 보아도 해당 변수 및 메서드가 어떤 기능을 하는지 확실히 알 수 있게 설정해 놓았다.
//변수
int inputNumber // 입력값 받는 변수
double totalPrice // 총 금액
int categoryNumber // 카테고리 번호

//메서드
private int getInputNumber // 입력값 받는 메서드
private MenuItem getSelectedMenuByNumber // 번호로 선택된 메뉴 리턴
private void addToOrderList // 장바구니에 객체 넣기
private double getDiscountedPriceByNumber // 금액 할인 적용 후 결과 리턴

📗 Java 명명규칙

Github

📓키오스크 만들기 Github

profile
기어 올라가는 개발

0개의 댓글