키오스크 기술과제

ChoRong0824·2025년 1월 13일
0

Java

목록 보기
38/41

Kiosk

필수 기능 가이드

클래스 정의

  • Main : 시작 지점이 되는 클래스, public static void main(String[] args)
  • MenuItem: 세부 메뉴 속성 가지는 클래스
    • 햄버거의 이름, 가격설명
      • 예시: ShackBurger, 6.9, 토마토, 양상추, 쉑소스가 토핑된 치즈버거
      • new MenuItem("ShackBurger", 6.9, "토마토, 양상추, 쉑소스가 토핑된 치즈버거")
    • 햄버거의 이름, 가격설명
      • 예시: ShackBurger, 6.9, 토마토, 양상추, 쉑소스가 토핑된 치즈버거
      • new MenuItem("ShackBurger", 6.9, "토마토, 양상추, 쉑소스가 토핑된 치즈버거")
  • Kiosk: 프로그램 순서 및 흐름 제어를 담당하는 클래스
  • Menu: MenuItem 클래스를 관리하는 클래스
    • 예시 : 버거 메뉴, 음료 메뉴 등 각 카테고리 내에 여러 MenuItem을 포함합니다.

Lv 1. 기본적인 키오스크를 프로그래밍해보자

  • 요구사항이 가지는 의도
    • 입력 처리와 간단한 흐름 제어를 복습합니다. (프로그래밍 검증)
    • Scanner 활용법, 조건문, 반복문을 재확인하며 입력 데이터를 처리하는 방법 강화

  • 햄버거 메뉴 출력 및 선택하기
    • Scanner를 사용하여 여러 햄버거 메뉴를 출력합니다.
    • 제시된 메뉴 중 입력받은 숫자에 따라 다른 로직을 실행하는 코드를 작성합니다.
    • 반복문을 이용해서 특정 번호가 입력되면 프로그램을 종료합니다.
  • Lv1을 구현하면 터미널에 이렇게 보여집니다.

원래 여기까지가 기능 구현이었으나, 나는 이것보다 더 나아가서 잘 입력했는지에 대한 validation을 하고 싶었으며, 구매수량까지 물어보고, 총 금액까지 구하고 싶었다.


Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.util.List.size()" because "this.items" is null
	at com.example.kiosk.Menu.outMenu(Menu.java:22)
	at com.example.kiosk.Main.main(Main.java:13)

해당 에러가 발생햇다.
이 오류는 Menu 클래스의 items 리스트가 초기화되지 않았거나 null 상태에서 접근하려고 할 때 발생합니다.
Menu 클래스의 생성자에서 items 리스트가 올바르게 초기화되었는지 확인해야 합니다


메뉴가 존재하지 않습니다.
메뉴를 선택해주세요: 1
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.util.List.size()" because "this.items" is null
	at com.example.kiosk.Menu.getMenuSize(Menu.java:39)
	at com.example.kiosk.Main.main(Main.java:30)

수정해줬음에도 에러가 발생한다.
Menu클래스에서 기본 생성자 활용을 안해서 그런 것 같다.

 public Menu() {
        this.items = new ArrayList<>();
        Menu();
    }

이제 정상 작동하는 것을 확인할 수 있다.


그런데 너무 무겁지 않은가 ?
Main 클래스에 너무 많은 것을 담고 있다.
이것을 Kiosk 클래스에 분할하려고한다.

Main

package com.example.kiosk;

import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        Menu menu = new Menu();
        boolean running = true;

        while (running) {
            menu.outMenu();
            System.out.print("메뉴를 선택해주세요: ");
            int choice;

            try {
                choice = Integer.parseInt(br.readLine());
            } catch (NumberFormatException e) {
                System.out.println("잘못된 입력입니다. 숫자만 입력해주세요");
                continue;
            }

            if (choice == 0) {
                System.out.println("프로그램 종료합니다.");
                running = false;
                break;
            }

            if (choice < 1 || choice > menu.getMenuSize()) {
                System.out.println("잘못된 입력입니다. 다시 시도하세요. \n");
                continue;
            }

            MenuItem selectItem = menu.getMenuItem(choice - 1);
            System.out.println("주문하신 메뉴: " + selectItem);
            System.out.print("주문하신 메뉴가 맞을까요? (Y/N) 입력: ");
            String filter = br.readLine().trim().toLowerCase();

            if (filter.equals("n")) {
                System.out.println("다시 주문해주세요.");
                continue;
            } else if (filter.equals("y")) {
                System.out.print("몇 개를 주문하시겠습니까? :");
                int quantity;

                try {
                    quantity = Integer.parseInt(br.readLine());
                } catch (NumberFormatException e) {
                    System.out.println("올바른 수량을 입력해주세요");
                    continue;
                }
                if (quantity <= 0) {
                    System.out.println("올바른 수량을 입력하세요");
                    continue;
                }
                double totalPrice = selectItem.getPrice() * quantity;
                System.out.println("총 주문 금액은: W "+totalPrice+" 입니다." );
            }else System.out.println("잘못된 입력입니다. 다시 시도하세요.");
        }
    }
}

Menu

package com.example.kiosk;

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

/**
 * MenuItem 클래스를 관리하는 클래스
 */
public class Menu {
    private List<MenuItem> items;

    public Menu() {
        this.items = new ArrayList<>();
        Menu();
    }

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

    public void outMenu() {
        if (items == null || items.isEmpty()) {
            System.out.println("메뉴가 존재하지 않습니다.");
            return;
        }
        System.out.println("[ SHAKESHACK MENU ]\n");


        for (int i = 0; i < items.size(); i++) {
            System.out.println(i + 1 + ". " + items.get(i));
        }
        System.out.println("0. 종료 | 종료");
    }

    public MenuItem getMenuItem(int index) {
        return items.get(index);
    }

    public int getMenuSize() {
        return items.size();
    }
}

MenuItem

package com.example.kiosk;

/**
 * 세부 메뉴 속성
 */
public class MenuItem {
    private String burger;
    private double price;
    private String intro;

    public MenuItem(String shackBurger, double price, String intro) {
        this.burger = shackBurger;
        this.price = price;
        this.intro = intro;
    }

    public String getBurger() {
        return burger;
    }

    public double getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return burger + "\t| W " + price + " |" + intro;
    }
}

이제 무거웠던 클래스를 내 나름대로 좀 분할해봤다.
Kiosk()클래스에 구현하면서, 나는 메뉴를 보여주는 것을 해야해서, Menu 클래스에 있는 outMenu메서드를 불러와야하는데 initializeMenu()를 불러와서 메뉴판 없이 바로 메뉴를 선택하는 정말 이상한 프로그램이 개발되었던 것이다.

이제 수정된 코드를 공유하겠습니다.

package com.example.kiosk;

import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        Kiosk kiosk = new Kiosk();
        kiosk.run();
    }
}
package com.example.kiosk;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 프로그램 순서 및 흐름 제어
 */
public class Kiosk {
    private Menu menu;
    private BufferedReader br;

    public Kiosk() {
        this.menu = new Menu();
        this.br = new BufferedReader(new InputStreamReader(System.in));
    }

    public void run() throws IOException {
        boolean running = true;

        while (running) {
            // menu.initializeMenu();
            menu.outMenu();
            System.out.print("메뉴를 선택하세요: ");
            int choice;

            try {
                choice = Integer.parseInt(br.readLine());
            } catch (NumberFormatException e) {
                System.out.println("잘못된 입력입니다. 숫자를 입력해주세요.\n");
                continue;
            }

            if (choice == 0) {
                System.out.println("프로그램을 종료합니다.");
                running = false;
                break;
            }

            if (choice < 1 || choice > menu.getMenuSize()) {
                System.out.println("잘못된 입력입니다. 다시 시도하세요.\n");
                continue;
            }

            handleOrder(choice);
        }
    }

//    private void initializeMenu() {
//        menu.outMenu();
//    }

    private void handleOrder(int choice) throws IOException {
        MenuItem selectedItem = menu.getMenuItem(choice - 1);
        System.out.println("주문하신 메뉴: " + selectedItem);
        System.out.print("주문하신게 맞을까요? (Y/N): ");
        String confirm = br.readLine().trim().toUpperCase();

        if (confirm.equals("N")) {
            System.out.println("다시 주문해주세요.\n");
            return;
        } else if (confirm.equals("Y")) {
            System.out.print("몇 개를 주문하시겠습니까? : ");
            int quantity;

            try {
                quantity = Integer.parseInt(br.readLine());
            } catch (NumberFormatException e) {
                System.out.println("올바른 수량을 입력해주세요.\n");
                return;
            }

            if (quantity <= 0) {
                System.out.println("올바른 수량을 입력해주세요.\n");
                return;
            }

            double totalPrice = selectedItem.getPrice() * quantity;
            System.out.println("총 주문 금액은: W " + totalPrice + "입니다.\n");
        } else {
            System.out.println("잘못된 입력입니다. 다시 시도하세요.\n");
        }
    }
}
package com.example.kiosk;

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

/**
 * MenuItem 클래스를 관리하는 클래스
 */
public class Menu {
    private List<MenuItem> items;

    public Menu() {
        this.items = new ArrayList<>();
        this.initializeMenu();
    }

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

    public void outMenu() {
        if (items == null || items.isEmpty()) {
            System.out.println("메뉴가 존재하지 않습니다.");
            return;
        }
        System.out.println("[ SHAKESHACK MENU ]");


        for (int i = 0; i < items.size(); i++) {
            System.out.println(i + 1 + ". " + items.get(i));
        }
        System.out.println("0. 종료 | 종료");
    }

    public MenuItem getMenuItem(int index) {
        return items.get(index);
    }

    public int getMenuSize() {
        return items.size();
    }
}

가독성을 위해 Menu -> initializeMenu로 수정했습니다.


Lv 2. 객체 지향 설계를 적용해 햄버거 메뉴를 클래스로 관리하기

  • 요구사항이 가지는 의도
    • 객체 지향 개념을 학습하고, 데이터를 구조적으로 관리하며 프로그램을 설계하는 방법을 익힙니다.
    • 햄버거 메뉴를 MenuItem 클래스와 List를 통해 관리합니다.

  • MenuItem 클래스 생성하기
    • 설명 : 개별 음식 항목을 관리하는 클래스입니다. 현재는 햄버거만 관리합니다.
    • 클래스는 이름, 가격, 설명 필드를 갖습니다.
  • main 함수에서 MenuItem 클래스를 활용하여 햄버거 메뉴를 출력합니다.
    • MenuItem 객체 생성을 통해 이름, 가격, 설명을 세팅합니다.
      • 키워드: new
    • List를 선언하여 여러 MenuItem을 추가합니다.
      • List<MenuItem> menuItems = new ArrayList<>();
    • 반복문을 활용해 menuItems를 탐색하면서 하나씩 접근합니다.
  • 구조 예시
    public static void main(String[] args) {
    		// List 선언 및 초기화
    		// add 함수를 통해 new MenuItem(이름, 가격, 설명) List에 삽입
    		// (add 보다 더 좋은 방법이 있다면 그렇게 해도 됩니다!)
    		
    		// Scanner 선언
    		
    		// 반복문을 활용해 List 안에 있는 MenuItem을 하나씩 출력
    		// 숫자를 입력 받기
    		// 입력된 숫자에 따른 처리
    			// 프로그램을 종료
    			// 선택한 메뉴 : 이름, 가격, 설명
    }

이제 level 2를 구현하면 된다.
멘토님이 레벨 1부터 차근히 순서대로 구현하시면 된다는 말씀에 level2는 읽어보지도 않고 1부터 진행했다. 그런데 왠걸?!
이미 레벨 1에구현이 끝났다.ㅎㅎㅎㅎ

NICE~


Lv 3. 객체 지향 설계를 적용해 순서 제어를 클래스로 관리하기

  • 요구사항이 가지는 의도
    • 객체 지향 개념을 학습하고, 데이터를 구조적으로 관리하며 프로그램을 설계하는 방법을 익힙니다.
    • main 함수에서 관리하던 전체 순서 제어를 Kiosk 클래스를 통해 관리합니다.
  • Kiosk 클래스 생성하기
    • 설명: 키오스크 프로그램의 메뉴를 관리하고 사용자 입력을 처리하는 클래스입니다.
    • MenuItem을 관리하는 리스트가 필드로 존재합니다.
    • main 함수에서 관리하던 입력과 반복문 로직은 이제 start 함수를 만들어 관리합니다.
    • List<MenuItem> menuItemsKiosk 클래스 생성자를 통해 값을 할당합니다.
      • Kiosk 객체를 생성하고 사용하는 main 함수에서 객체를 생성할 때 값을 넘겨줍니다.
  • 요구사항에 부합하는지 검토
    • 키오스크 프로그램을 시작하는 메서드가 구현되어야 합니다.
      • 콘솔에 햄버거 메뉴를 출력합니다.
      • 사용자의 입력을 받아 메뉴를 선택하거나 프로그램을 종료합니다.
      • 유효하지 않은 입력에 대해 오류 메시지를 출력합니다.
      • 0을 입력하면 프로그램이 ‘뒤로가기’되거나 ‘종료’됩니다.

Lv 4. 객체 지향 설계를 적용해 음식 메뉴와 주문 내역을 클래스 기반으로 관리하기

  • Menu 클래스 생성하기
    • 설명 : MenuItem 클래스를 관리하는 클래스입니다.
      예를 들어, 버거 메뉴, 음료 메뉴 등 각 카테고리 내에 여러 MenuItem을 포함합니다.
    • List<MenuItem>Kiosk 클래스가 관리하기에 적절하지 않으므로 Menu 클래스가 관리하도록 변경합니다.
    • 여러 버거들을 포함하는 상위 개념 ‘버거’ 같은 카테고리 이름 필드를 갖습니다.
    • 메뉴 카테고리 이름을 반환하는 메서드가 구현되어야 합니다.
  • 구조 예시
public static void main(String[] args) {
		// Menu 객체 생성하면서 카테고리 이름 설정
		// Menu 클래스 내 있는 List<MenuItem> 에 MenuItem 객체 생성하면서 삽입
		
		// Kiosk 객체 생성
		// Kiosk 내 시작하는 함수 호출
}

public class Kiosk {

		start() {
			// 스캐너 선언
			// 반복문 시작
			
			// List와 Menu 클래스 활용하여 상위 카테고리 메뉴 출력
			
			// 숫자 입력 받기
			
			// 입력 받은 숫자가 올바르다면 인덱스로 활용하여 List에 접근하기
				// List<Menu>에 인덱스로 접근하면 Menu만 추출할 수 있겠죠?
			
			// Menu가 가진 List<MenuItem>을 반복문을 활용하여 햄버거 메뉴 출력
			
			// 숫자 입력 받기
			// 입력 받은 숫자가 올바르다면 인덱스로 활용해서 Menu가 가지고 있는 List<MenuItem>에 접근하기
				// menu.getMenuItems().get(i); 같은 형식으로 하나씩 들어가서 얻어와야 합니다.
		}

}

public class Menu {
		// MenuItem 클래스를 List로 관리
		
		// List에 들어있는 MenuItem을 순차적으로 보여주는 함수
		// List를 리턴하는 함수
		
		// 구조에 맞게 함수를 선언해놓고 가져다 사용하세요.
}

public class MenuItem {
		// 이름, 가격, 설명 필드 선언하여 관리
		
		// 구조에 맞게 함수를 선언해놓고 가져다 사용하세요.
}
  • Lv4를 구현하면 터미널에 이렇게 보여집니다.
[ MAIN MENU ]
1. Burgers
2. Drinks
3. Desserts
0. 종료      | 종료
1 <- // 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 <- // 2를 입력
선택한 메뉴: SmokeShack | W 8.9 | 베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거

[ MAIN MENU ]
1. Burgers
2. Drinks
3. Desserts
0. 종료      | 종료
0 <- // 0을 입력
프로그램을 종료합니다.

어찌하다보니 레벨4까지 구현을 하게 된 것 같다.
level 5까지 구현하고,도전 기능 가이드를 하려고 한다.


깃헙 사용하다가 간만에 이슈를 만났다.

Image 1 Image 2

인텔리제이에서 깃헙을 연동을 해서 추가와 커밋, 브랜치 생성까지 해서 작업했다.
원래 나는 터미널에서 작업을 했었는데, 그냥 갑자기 인텔리제이랑 깃헙 연동해서 있는 VCS를 쓰고 싶었다.
그래서 연결하고 깃 올릴 주소랑 연결해서 feature 브랜치를 파고 개발하고 푸쉬했는데, main에 merge가 안되는 상황이 발생한 것이다.
분명 pr은 뜨고, pr누르면 창으로 이동해야하는데, 코드 변경 내역은 확인되는데, pr 및 머지 하는 창이 안뜨고 그냥 비교할 내용이 없다고만 뜨는 상황이 발생한 것이었다. 이런 상황은 나도 처음봤다. 결국 난 다시 터미널에서 해결해보고자 했다.
rebase도 해보고 이것 저것 많이 해봤는데 추가하고 커밋까지 했는데, history는 없고 이게 뭔가..?
리모트 브랜치(origin/main)와 로컬 브랜치(main) 간에 충돌이 발생한 상황으로 파악하고,
리모트 브랜치와 로컬 브랜치를 병합으로 해결하려 했는데,리모트 브랜치에 로컬 브랜치에 없는 변경 사항이 있기 때문인 줄 알고 git pull origin main --allow-unrelated-histories
로 리모트 브랜치에서 변경 사항을 가져오고 병합하려고 했는데,

➜  KioskProject git:(main) git pull origin main --allow-unrelated-histories
https://github.com/sjMun09/Kiosk URL에서
 * branch            main       -> FETCH_HEAD
힌트: You have divergent branches and nee]\d to specify how to reconcile them.
힌트: You can do so by running one of the following commands sometime before
힌트: your next pull:
힌트: 
힌트:   git config pull.rebase false  # merge
힌트:   git config pull.rebase true   # rebase
힌트:   git config pull.ff only       # fast-forward only
힌트: 
힌트: You can replace "git config" with "git config --global" to set a default
힌트: preference for all repositories. You can also pass --rebase, --no-rebase,
힌트: or --ff-only on the command line to override the configured default per
힌트: invocation.
fatal: Need to specify how to reconcile divergent branches.

깃 헙, 너란녀석 간만에 말썽이구나,,
현재 로컬 브랜치와 리모트 브랜치 간에 히스토리가 달라 병합 방법을 명확히 지정하지 못해서 발생한 문제였다.
이를 해결 하기 위해 git pull 명령어에서 병합 방식 옵션을 명시적으로 지정하거나 기본 설정을 설정을 진행해줬다.

해결 방법으로는 알아보니 총 3가지 방법이 있었다.

1. 병합 방식 선택

병합(Merge) 방식으로 히스토리를 합치기

git pull origin main --allow-unrelated-histories --no-rebase

리베이스(Rebase) 방식으로 히스토리를 재구성하기

git pull origin main --allow-unrelated-histories --rebase

Fast-Forward 방식만 허용

git pull origin main --ff-only

2. 글로벌 기본 설정 변경 (선택 사항)

병합 방식을 항상 고정하고 싶다면 git config를 사용해 글로벌 설정을 변경할 수 있습니다.

병합(Merge) 방식을 기본으로 설정

git config --global pull.rebase false

리베이스(Rebase) 방식을 기본으로 설정

git config --global pull.rebase true

Fast-Forward 방식만 허용

git config --global pull.ff only

3. 병합 후 충돌 해결

위 명령어를 실행하면 병합이나 리베이스 중 충돌이 발생할 수 있습니다. 충돌이 생기면 Git이 충돌 난 파일을 알려줍니다. 해당 파일을 열어 충돌을 해결하고 저장한 뒤 add, commit, push까지 순차적으로 해주면 된다.


Lv 5. 캡슐화 적용하기

  • MenuItem, Menu 그리고 Kiosk 클래스의 필드에 직접 접근하지 못하도록 설정합니다.
  • Getter와 Setter 메서드를 사용해 데이터를 관리합니다.

lv5를 구현하기에 앞서, 레벨 5를 적용하는 이유를 먼저 알고 저의 코드를 확인해야된다는 생각하여 저의 코드를 분석해봤습니다. 충분히 캡슐화했다고 생각하고, 유지보수 및 확장성도 어느정도 구현되었다고 생각했었습니다.

1. 적용해야하는 이유

1 - 데이터 보호
클래스의 필드를 private로 설정함으로써 외부에서 직접 접근을 차단하고, 데이터 무결성을 보장합니다. 외부 클래스가 필드에 잘못된 값을 설정하거나 무분별하게 수정하는 것을 방지할 수 있습니다.
2 - 변경에 유연
필드를 Getter와 Setter로 관리하면 내부 구현이 변경되더라도 외부 코드에 영향을 주지 않고 수정할 수 있습니다. 예를 들어, 가격을 계산하는 로직을 getPrice() 메서드에 추가해도 호출하는 코드에는 영향을 주지 않습니다.

3 - 유지보수성 증가
캡슐화를 통해 코드의 변경 범위를 줄이고, 특정 클래스의 내부 구현을 외부로부터 숨김으로써 코드의 안정성을 높입니다.

4 - 확장성 증가
Getter와 Setter를 통해 필드에 접근하면, 향후에 데이터 검증 로직, 로깅, 혹은 값 변환 등의 추가 작업을 쉽게 구현할 수 있습니다.

라는 이유 때문에, MenuItem에 게더세터를 생성했었는데, 이것 만으로는 부족한가 ? 라는 생각이 문득 들었다. 오히려 사용하지 않는 게더세터를 생성해주면서 코드가 길어지고 불편해진 느낌이 있었기 때문이다.

캡슐화로 인해 코드가 길어지고 불편하다고 느낀 이유 ?

  • 단순한 프로그램에서는 필드를 직접 사용하는 것이 더 간단하고 직관적일 수 있습니다. Getter와 Setter를 추가하면 코드가 길어지고 복잡해지는 것처럼 보일 수 있습니다.
  • 그러나 프로젝트가 커지거나, 팀에서 협업을 하는 경우, 혹은 요구사항이 변경될 가능성이 있는 상황에서는 캡슐화가 큰 장점으로 작용합니다.

반드시 구현해야 하는 경우

  • 데이터 검증이 필요한 경우
    예를 들어, 가격이 음수가 되지 않도록 보장해야 하는 경우, 저의 코드에선, setPrice() 메서드에서 이를 검증할 수 있었습니다.
  • 데이터 변경의 영향을 최소화해야 하는 경우
    여러 곳에서 필드를 직접 수정하는 대신 Setter 메서드로 한곳에서 관리하면 변경 범위가 줄어듭니다.
  • 다른 개발자와 협업
    팀 프로젝트에서 필드를 직접 접근하지 못하도록 하고, Getter와 Setter로 데이터 흐름을 명확히 하면 오류를 줄일 수 있습니다.

그렇다면 이전에 내가 구현한 코드는 캡슐화가 적용 안된건가?
-> 캡슐화가 부분적으로 적용된 코드입니다. 다음과 같은 이유에서 "캡슐화가 완벽히 적용되지 않았다"고 말 할 수 있습니다.

<캡슐화의 상태>

적용된 부분

모든 클래스(MenuItem, Menu, Kiosk)의 필드가 private로 설정되어 있습니다. 외부에서 필드에 직접 접근하지 못하도록 제한한 것은 캡슐화의 기본 원칙 중 하나입니다.
데이터를 읽거나 수정할 수 있도록 Getter와 Setter를 통해 데이터를 관리하고 있습니다.

부족한 부분

  • 직접 필드 사용
    Kiosk 클래스가 Menu 클래스의 메서드를 호출하고 있지만, 내부적으로 Menu가 가진 데이터를 직접 조작하려는 코드가 보입니다. 예를 들어 getMenuSize()와 getMenuItem()은 Menu 내부 데이터를 외부로 노출시키는 메서드입니다.
  • 캡슐화의 완전성 부족
    getItems()와 setItems() 메서드를 통해 Menu의 전체 데이터 구조인 ArrayList를 외부에서 완전히 제어할 수 있습니다. 이는 Menu의 내부 데이터가 외부에서 쉽게 변조될 수 있다는 뜻입니다.

정리
이전 코드도 부분적인 캡슐화는 적용되어 있었지만, 내부 데이터 구조를 외부에 노출하는 방식은 캡슐화의 원칙에 위배됩니다.
단순히 돌아가는 코드가 아닌, 확장성과 유지보수성이 높은 코드를 작성하기 위해 캡슐화를 강화하는 것이 필요합니다.

profile
백엔드를 지향하며, 컴퓨터공학과를 졸업한 취준생입니다. 많이 부족하지만 열심히 노력해서 실력을 갈고 닦겠습니다. 부족하고 틀린 부분이 있을 수도 있지만 이쁘게 봐주시면 감사하겠습니다. 틀린 부분은 댓글 남겨주시면 제가 따로 학습 및 자료를 찾아봐서 제 것으로 만들도록 하겠습니다. 귀중한 시간 방문해주셔서 감사합니다.

0개의 댓글