프로젝트 개요

  • 구현 목표
    • 회원 기능 구현 목표: 안전한 로그인 및 회원가입, 다양한 메뉴 추천 및 활동 내역 관리, 다른 사용자의 매장 및 음식 평가 열람 기능을 제공한다.
    • 관리자 기능 구현 목표: 메뉴, 매장, 회원 정보, 카테고리 관리, 카테고리 추가 및 삭제로 메뉴 분류 관리, 매장 및 음식 평가 내역 확인 및 삭제 기능을 제공한다.
  • 활동 내용
    • 1. 로그인 및 회원가입 기능 구현
    • 2. 메뉴 추천 및 목록 정렬 기능 구현
    • 3. 마이페이지 및 회원 정보 관리 기능 구현
  • Category
  • Java Console 프로젝트
  • Period
  • 2023.09.06.~2023.09.25.
  • GitHub
  • https://github.com/Isaac-Seungwon/revolving-menu.git

Login.java

package com.revolving.login;

import java.util.Scanner;

import com.revolving.Main;
import com.revolving.admin.AdminMenu;
import com.revolving.data.MemberData;
import com.revolving.data.object.Member;
import com.revolving.member.MemberMenu;

/**
 * 로그인 클래스
 * 목적: ID, 비밀번호를 입력받아 로그인을 한다.
 * 
 * @author 이승원
 */
public class Login {

    static Scanner scan = new Scanner(System.in);
    public static Member user;
    
	public static void login() {
		Main.printMenu("로그인");
		System.out.print("아이디: ");
		String id = scan.nextLine().trim();

		System.out.print("비밀번호: ");
		String pwd = scan.nextLine().trim();
		
		if (id.equals("admin") && pwd.equals("admin")) { // 관리자 로그인
			System.out.println("관리자로 로그인 되었습니다.");
			System.out.println("Enter를 누르면 관리자 메뉴로 이동합니다.");
			scan.nextLine();

			AdminMenu.menu(); // 관리자 기능 실행
		} else { // 회원 로그인
			user = getMember(id, pwd);
			
			if (user != null) {
				MemberMenu.menu(); // 회원 기능 실행
			} else {
				System.out.println("Enter를 누르면 이전 화면으로 돌아갑니다.");
	            scan.nextLine();
			}
		}
	}
	
	private static Member getMember(String id, String pwd) {
        for (Member member : MemberData.list) {
            if (member.getId().equals(id) && member.getPwd().equals(pwd)) {
            	Main.printLine();
            	System.out.println(member.getName() + " 회원님 환영합니다.");
            	System.out.println();
                return member;
            }
        }
        return null;
    }
}


Join.java

package com.revolving.join;

import java.util.Scanner;

import com.revolving.Main;
import com.revolving.data.MemberData;
import com.revolving.data.object.Member;

/**
 * 회원 가입 클래스
 * 목적: ID, 비밀번호, 이름, 전화번호, 생년월일을 입력받아 회원 가입을 한다.
 * 
 * @author 이승원
 */
public class Join {

	static Scanner scan = new Scanner(System.in);
	
	/**
        * 회원 가입을 진행하는 메서드
        * ID, 비밀번호, 이름, 전화번호, 생년월일을 입력받아 회원 리스트에 회원 데이터를 추가한다.
        */
	public static void join() {
		Main.printMenu("회원 가입");
		System.out.println("회원 가입을 진행합니다.");
		System.out.println("ID, 비밀번호, 이름, 전화번호, 생년월일 정보를 입력합니다.");
		
		String id = inputId();
		String pw = inputPw();
		String name = inputName();
		String tel = inputTel();
		String birth = inputBirth();
		
		MemberData.addMember(name, id, pw, tel, birth);
		MemberData.save();
		System.out.println(name + " 회원님, 회원 가입을 축하드립니다!");
	}
	
	/**
        * 아이디를 입력받는 메서드.
        * 4자 ~ 16자의 영문 대소문자, 숫자를 포함하여 입력한다.
        * 
        * @return 아이디
        */
	private static String inputId() {
		Main.printMenu("아이디 입력");
		System.out.println("4자 ~ 16자의 영문 대소문자, 숫자를 포함하여 입력하세요.");
		System.out.print("아이디 입력: ");
		String id = scan.nextLine().trim();

		if (!isValidId(id)) {
			System.out.println("형식에 맞지 않는 아이디입니다.");
			inputId();
		}

		for (Member member : MemberData.list) {
			if (member.getId().equals(id)) {
				System.out.println("이미 존재하는 아이디입니다.");
				inputId();
				break;
			}
		}
		
		return id;
	}

	/**
        * 비밀번호를 입력받는 메서드
        * 9자 ~ 14자의 영문 대소문자, 숫자, 특수문자(!, #, ^, &, *)를 포함하여 입력한다.
        * 
        * @return 비밀번호
        */
	private static String inputPw() {
		Main.printMenu("비밀번호 입력");
		System.out.println("9자 ~ 14자의 영문 대소문자, 숫자, 특수문자(!, #, ^, &, *)를 포함하여 입력하세요.");
		System.out.print("비밀번호 입력: ");
		String pw = scan.nextLine().trim();

		if (!isValidPw(pw)) {
			System.out.println("형식에 맞지 않는 비밀번호입니다.");
			inputPw();
		}

		System.out.println("비밀번호가 저장되었습니다.");
		
		return pw;
	}

	/**
        * 이름을 입력받는 메서드
        * 2자 ~ 4자의 한글 이름을 입력한다.
        * 
        * @return 이름
        */
	private static String inputName() {
		Main.printMenu("이름 입력");
		System.out.println("2자 ~ 4자의 한글 이름을 입력하세요.");
		System.out.print("이름 입력: ");
		String name = scan.nextLine().trim();

		if (!isValidName(name)) {
			System.out.println("형식에 맞지 않는 이름입니다.");
			inputName();
		}
		
		return name;
	}

	/**
        * 전화번호를 입력받는 메서드
        * 11자의 숫자로 전화번호를 입력한다.
        * 
        * @return 전화번호
        */
        private static String inputTel() {
		Main.printMenu("전화번호 입력");
		System.out.println("11자의 숫자로 전화번호를 입력하세요.");
		System.out.print("전화번호 입력: ");
		String tel = scan.nextLine().trim();

		if (!isValidTel(tel)) {
			System.out.println("형식에 맞지 않는 전화번호입니다.");
			inputTel();
		}
		
		return tel;
	}
    
        /**
        * 생년월일을 입력받는 메서드
        * 6자의 숫자로 생년월일을 입력한다.
        * 
        * @return 생년월일
        */
        private static String inputBirth() {
		Main.printMenu("생년월일 입력");
		System.out.println("6자의 숫자로 생년월일을 입력하세요.");
		System.out.print("생년월일 입력: ");
		String birth = scan.nextLine().trim();

		if (!isValidBirth(birth)) {
			System.out.println("형식에 맞지 않는 생년월일입니다.");
			inputBirth();
		}
		
		return birth;
	}
	
        /**
        * 아이디의 유효성을 검사하는 메서드
        * 
        * @param id 아이디
        * @return 유효한 아이디면 true, 그렇지 않으면 false
        */
	public static boolean isValidId(String id) {
		return id.matches("^[a-zA-Z0-9]{4,16}$");
	}
	
	/**
        * 비밀번호의 유효성을 검사하는 메서드
        * 
        * @param pw 비밀번호
        * @return 유효한 비밀번호면 true, 그렇지 않으면 false
        */
	public static boolean isValidPw(String pw) {
		return pw.matches("^(?=.*[a-zA-Z])(?=.*\\d)(?=.*[!#^&*])[a-zA-Z\\d!#^&*]{9,14}$");
	}
	
	/**
        * 이름의 유효성을 검사하는 메서드
        * 
        * @param name 이름
        * @return 유효한 이름이면 true, 그렇지 않으면 false
        */
	public static boolean isValidName(String name) {
	    return name.matches("^[가-힣]{2,4}$");
	}
	
	/**
	 * 전화번호의 유효성을 검사하는 메서드
	 * 
	 * @param tel 전화번호
	 * @return 유효한 전화번호면 true, 그렇지 않으면 false
	 */
	public static boolean isValidTel(String tel) {
	    return tel.matches("^\\d{11}$");
	}
	
	/**
	 * 생년월일의 유효성을 검사하는 메서드
	 * 
	 * @param birth 생년월일
	 * @return 유효한 생년월일이면 true, 그렇지 않으면 false
	 */
	public static boolean isValidBirth(String birth) {
	    return birth.matches("^\\d{6}$");
	}
}

MemberMenu.java

package com.revolving.member;

import java.util.Scanner;

import com.revolving.Main;
import com.revolving.member.mypage.MyPageMenu;

public class MemberMenu {

	static Scanner scan = new Scanner(System.in);

	public static void menu() {
		
		while (true) {
			Main.printMenu("회원 메뉴");
			Main.printOption("메뉴 추천", "메뉴 목록 정렬", "매장 평가 조회", "마이페이지");
			String input = scan.nextLine().trim();
			
			if (input.equals("1")) {
				MenuRecommend.menuRecommend();
			} else if (input.equals("2")) {
				MenuSort menuSort = new MenuSort();
			    menuSort.menuSort();
			} else if (input.equals("3")) {
				MenuRate.viewStoreReview();
			} else if (input.equals("4")) {
				MyPageMenu.viewUserMenu();
			} else if (input.equals("0")) {
				return;
			}
		}
	}
}


MenuRecommend.java

package com.revolving.member;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.Scanner;

import com.revolving.Main;
import com.revolving.data.MenuChooseData;
import com.revolving.data.MenuData;
import com.revolving.data.object.Menu;
import com.revolving.data.object.MenuChoose;
import com.revolving.login.Login;

/**
 * 메뉴 추천 기능 클래스
 * 목적: 사용자에게 메뉴를 추천하는 기능을 제공한다.
 * 기능:
 * - 무작위 메뉴 추천: 무작위로 메뉴를 추천한다.
 * - 카테고리별 메뉴 추천: 카테고리를 선택하여 선택한 카테고리 중에서 메뉴를 추천한다.
 * - 계절별 제철 음식 추천: 계절을 선택하여 선택한 계절 중에서 메뉴를 추천한다.
 * 
 * @author 이승원
 */
public class MenuRecommend {

    static Scanner scan = new Scanner(System.in);

    /**
     * 메뉴 추천 기능 선택 메서드
     */
    public static void menuRecommend() {

        while (true) {
            Main.printMenu("메뉴 추천 기능 선택");
            Main.printOption("무작위 메뉴 추천", "카테고리 메뉴 추천", "계절별 제철 음식 추천");

            String sel = scan.nextLine();

            if (sel.equals("1")) {
                randomMenu();

            } else if (sel.equals("2")) {
                categoryRandom();

            } else if (sel.equals("3")) {
                seasonRandom();

            } else if (sel.equals("0")) {
                return;
            }
        }
    }

    /**
     * 계절별 메뉴 추천 메서드
     */
    private static void seasonRandom() {
        Main.printMenu("계절 선택");
        Main.printOption("여름", "겨울", "기타");
        String select = scan.nextLine();

        rndSeasonList(select);
    }

    /**
     * 선택된 계절에 따라 메뉴 리스트를 출력하고, 사용자가 메뉴를 선택할 수 있도록 한다.
     * @param select 선택된 계절 (여름, 겨울, 기타)
     */
    private static void rndSeasonList(String select) {
        List<Menu> seasonList = new ArrayList<>();

        for (Menu seasonMenu : MenuData.list) {
            if (select.equals(String.valueOf(seasonMenu.getSeasonNo()))) {
                seasonList.add(seasonMenu);
            }
        }

        while (true) {
            List<Menu> rndSeasonList = new ArrayList<>();

            Random random = new Random();
            int i = 0;

            for (int count = 0; count < 3; count++) {
                i++;
                int randomIndex = random.nextInt(seasonList.size());
                Menu rndSeasonMenu = seasonList.get(randomIndex);
                System.out.printf("%d. " + rndSeasonMenu.getName() + "\n", i);

                rndSeasonList.add(rndSeasonMenu);
            }

            System.out.println("4. 새로운 메뉴 추천");
            System.out.println("0. 돌아가기");
            Main.printLine();
            System.out.print("번호 입력: ");
            String input = scan.nextLine();

            if (input.equals("0")) {
                return;
            } else {
                if (Integer.parseInt(input) > 0 && Integer.parseInt(input) < rndSeasonList.size() + 1) {
                    int selectedMenuIndex = Integer.parseInt(input) - 1;
                    Menu selectedMenu = rndSeasonList.get(selectedMenuIndex);
                    String todayDate = new SimpleDateFormat("yyMMdd").format(new Date());
                    MenuChoose newMenuChoose = new MenuChoose(
                        String.valueOf(MenuChooseData.list.size() + 1),
                        Login.user.getNo(),
                        selectedMenu.getNo(),
                        todayDate
                    );
                    MenuChooseData.list.add(newMenuChoose);
                    MenuChooseData.save();

                    System.out.println("선택한 메뉴: " + selectedMenu.getName());
                    System.out.println("메뉴를 결정했습니다.");
                    scan.nextLine();
                    return;
                }
            }
        }
    }

    /**
     * 카테고리별 메뉴 추천 메서드
     */
    private static void categoryRandom() {
        Main.printMenu("카테고리 선택");
        Main.printOption("한식", "양식", "일식", "중식", "기타");

        String select = scan.nextLine();
        rndCategoryList(select);
    }

    /**
     * 선택된 카테고리에 따라 메뉴 리스트를 출력하고, 사용자가 메뉴를 선택할 수 있도록 한다.
     * @param select 선택된 카테고리 번호 (1~5)
     */
    private static void rndCategoryList(String select) {
        String result = "";
        result = select;

        if (result.equals("5")) {
            result = "0";
        }

        List<Menu> categoryList = new ArrayList<>();

        for (Menu categoryMenu : MenuData.list) {
            if (result.equals(String.valueOf(categoryMenu.getCategoryNo()))) {
                categoryList.add(categoryMenu);
            }
        }

        Random random = new Random();

        while (true) {
            List<Menu> rndCategoryMenuList = new ArrayList<>();

            int i = 0;

            for (int count = 0; count < 5; count++) {
                i++;
                int randomCategoryMenuIndex = random.nextInt(categoryList.size());
                Menu rndCategoryMenu = categoryList.get(randomCategoryMenuIndex);
                System.out.printf("%d. " + rndCategoryMenu.getName() + "\n", i);

                rndCategoryMenuList.add(rndCategoryMenu);
            }

            System.out.println("6. 새로운 메뉴 추천");
            System.out.println("0. 돌아가기");
            Main.printLine();
            System.out.print("번호 입력: ");
            String input = scan.nextLine();

            if (input.equals("0")) {
                return;
            } else {
                if (Integer.parseInt(input) > 0 && Integer.parseInt(input) < rndCategoryMenuList.size() + 1) {
                    int selectedMenuIndex = Integer.parseInt(input) - 1;
                    Menu selectedMenu = rndCategoryMenuList.get(selectedMenuIndex);
                    String todayDate = new SimpleDateFormat("yyMMdd").format(new Date());
                    MenuChoose newMenuChoose = new MenuChoose(
                        String.valueOf(MenuChooseData.list.size() + 1),
                        Login.user.getNo(),
                        selectedMenu.getNo(),
                        todayDate
                    );
                    MenuChooseData.list.add(newMenuChoose);
                    MenuChooseData.save();

                    System.out.println("선택한 메뉴: " + selectedMenu.getName());
                    System.out.println("메뉴를 결정했습니다.");
                    scan.nextLine();
                    return;
                }
            }
        }
    }

    /**
     * 무작위 메뉴 추천 메서드
     */
    private static void randomMenu() {
        Random random = new Random();
        int rndMenuIndex = random.nextInt(MenuData.list.size());

        System.out.println();
        System.out.println(MenuData.list.get(rndMenuIndex).getName());
        System.out.println();

        Main.printLine();
        Main.printOption("다른 메뉴 보기", "메뉴 결정");
        String input = scan.nextLine();

        if (input.equals("1")) {
            randomMenu();
        } else if (input.equals("2")) {
            String todayDate = new SimpleDateFormat("yyMMdd").format(new Date());
            MenuChoose newMenuChoose = new MenuChoose(
                String.valueOf(MenuChooseData.list.size() + 1),
                Login.user.getNo(),
                MenuData.list.get(rndMenuIndex).getNo(),
                todayDate
            );
            MenuChooseData.list.add(newMenuChoose);
            MenuChooseData.save();

            System.out.println("선택한 메뉴: " + MenuData.list.get(rndMenuIndex).getName());
            System.out.println("메뉴를 결정했습니다.");
            scan.nextLine();
        } else if (input.equals("0")) {
            return;
        } else {
            System.out.println("잘못된 번호를 입력하셨습니다.");
        }
    }
}


MenuSort.java

package com.revolving.member;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Scanner;

import com.revolving.Main;
import com.revolving.data.CategoryData;
import com.revolving.data.MenuData;
import com.revolving.data.object.Category;
import com.revolving.data.object.Menu;

/**
 * 메뉴 정렬 기능 클래스
 * 목적: 메뉴를 다양한 기준으로 정렬하여 조회하는 기능을 제공한다.
 * 기능:
 * - 전체 정렬: 전체 메뉴를 조회한다.
 * - 카테고리별 정렬: 카테고리를 기준으로 메뉴를 조회한다.
 * - 가격 순 정렬: 가격을 기준으로 메뉴를 조회한다.
 * - 계절 별 정렬: 계절을 기준으로 메뉴를 조회한다.
 * - 이름 검색: 메뉴의 이름을 검색하여 조회한다.
 * 
 * @author 이승원
 */
public class MenuSort {

    static Scanner scan;
    static private int currentIndex;
    static private ArrayList<Menu> list;

    static {
        list = new ArrayList<Menu>();
        scan = new Scanner(System.in);
    }

    /**
     * 정렬 기능 선택 메서드
     */
    public void menuSort() {

        while (true) {
            Main.printMenu("정렬 기준 선택");
            Main.printOption("전체 정렬", "카테고리별 정렬", "가격 순 정렬", "계절 별 정렬", "이름 검색");
            String input = scan.nextLine();

            if (input.equals("1")) {
                allSort();
            } else if (input.equals("2")) {
                categorySort();
            } else if (input.equals("3")) {
                Main.printMenu("메뉴 조회 > 가격 순 조회");
                System.out.println("1. 높은순");
                System.out.println("2. 낮은순");
                Main.printLine();
                System.out.print("번호 입력: ");

                input = scan.nextLine().trim();

                if (input.equals("1")) {
                    priceSort("1");
                } else if (input.equals("2")) {
                    priceSort("2");
                } else {
                    System.out.println("잘못 입력하셨습니다.");
                }
            } else if (input.equals("4")) {
                seasonSort();
            } else if (input.equals("5")) {
                nameSort();
            } else if (input.equals("0")) {
                return;
            } else {
                System.out.println("잘못 입력하셨습니다.");
            }
        }
    }

    /**
     * 이름 검색 메서드
     */
    private void nameSort() {

        Main.printMenu("메뉴 조회 > 이름 검색");
        System.out.print("단어 입력: ");
        String input = scan.nextLine().trim();

        for (Menu menu : MenuData.list) {
            if (menu.getName().contains(input)) {
                list.add(menu);
            }
        }

        while (true) {

            int endIndex = Math.min(currentIndex + 20, list.size());

            Main.printMenu(input + " 검색");

            sortResult(list, endIndex);

            if (!choiceMenu(list, endIndex)) {
                list.clear();
                return;
            }
        }
    }

    /**
     * 계절별 정렬 메서드
     */
    private void seasonSort() {

        while (true) {

            Main.printMenu("메뉴 조회 > 계절별 조회");
            System.out.println("0. 기타");
            System.out.println("1. 여름");
            System.out.println("2. 겨울");
            Main.printLine();
            System.out.print("번호 입력: ");

            String input = scan.nextLine().trim();

            if (input.matches("\\d+") && Integer.parseInt(input) >= 0 && Integer.parseInt(input) < 3) {

                for (Menu menu : MenuData.list) {
                    if (menu.getSeasonNo().equals(input)) {
                        list.add(menu);
                    }
                }

                while (true) {

                    if (input.equals("0")) {
                        Main.printMenu("기타 조회");
                    } else if (input.equals("1")) {
                        Main.printMenu("여름 조회");
                    } else {
                        Main.printMenu("겨울 조회");
                    }

                    int endIndex = Math.min(currentIndex + 20, list.size());

                    sortResult(list, endIndex);

                    if (!choiceMenu(list, endIndex)) {
                        list.clear();
                        return;
                    }
                }

            } else {
                System.out.println("잘못 입력하셨습니다.");
            }
        }
    }

    /**
     * 가격 순 정렬 메서드
     * @param order 정렬 순서 (1: 높은순, 2: 낮은순)
     */
    private void priceSort(String order) {

        for (Menu menu : MenuData.list) {
            if (menu.getName().equals("-")) {
                continue;
            }
            list.add(menu);
        }

        while (true) {

            int endIndex = Math.min(currentIndex + 20, list.size());

            if (order.equals("1")) {
                Main.printMenu("높은순 조회");

                Collections.sort(list,
                        Comparator.comparingInt(menu -> Integer.parseInt(((Menu) menu).getPrice())).reversed());

                sortResult(list, endIndex);
            } else {
                Main.printMenu("낮은순 조회");
                Collections.sort(list, Comparator.comparingInt(menu -> Integer.parseInt(menu.getPrice())));

                sortResult(list, endIndex);
            }

            if (!choiceMenu(list, endIndex)) {
                return;
            }
        }
    }

    /**
     * 카테고리별 정렬 메서드
     */
    private void categorySort() {

        while (true) {

            Main.printMenu("메뉴 조회 > 카테고리 별 조회");
            for (Category category : CategoryData.list) {
                System.out.printf("%s. %s\r\n", category.getNo(), category.getCookery());
            }
            Main.printLine();
            System.out.print("번호 입력: ");

            String input = scan.nextLine().trim();

            if (input.matches("\\d+") && Integer.parseInt(input) >= 0
                    && Integer.parseInt(input) < CategoryData.list.size()) {

                String cookery = "";

                for (Category category : CategoryData.list) {
                    if (category.getNo().equals(input)) {
                        cookery = category.getCookery();
                        break;
                    }
                }

                for (Menu menu : MenuData.list) {
                    if (menu.getCategoryNo().equals(input)) {
                        list.add(menu);
                    }
                }

                while (true) {

                    Main.printMenu(cookery + " 조회");

                    int endIndex = Math.min(currentIndex + 20, list.size());

                    sortResult(list, endIndex);

                    if (!choiceMenu(list, endIndex)) {
                        list.clear();
                        return;
                    }

                }

            } else {
                System.out.println("잘못 입력하셨습니다.");
            }
        }
    }

    /**
     * 전체 정렬 메서드
     */
    private void allSort() {

        for (Menu menu : MenuData.list) {
            if (menu.getName().equals("-")) {
                continue;
            }

            list.add(menu);
        }

        while (true) {
            Main.printMenu("메뉴 조회 > 전체 조회");

            int endIndex = Math.min(currentIndex + 20, list.size());

            sortResult(list, endIndex);

            if (!choiceMenu(list, endIndex)) {
                list.clear();
                return;
            }
        }
    }

    /**
     * 정렬된 결과 출력 메서드
     * @param list 출력할 메뉴 리스트
     * @param endIndex 표시할 메뉴의 끝 인덱스
     */
    private void sortResult(ArrayList<Menu> list, int endIndex) {

        for (int i = currentIndex; i < endIndex; i++) {

            Menu menu = list.get(i);

            System.out.printf("메뉴번호: %s\r\n", menu.getNo());
            System.out.printf("메뉴명: %s\r\n", menu.getName());
            for (Category category : CategoryData.list) {
                if (category.getNo().equals(menu.getCategoryNo())) {
                    System.out.printf("카테고리: %s\r\n", category.getCookery());
                    break;
                }
            }
            if (menu.getSeasonNo().equals("0")) {
                System.out.println("시즌: 기타");
            } else if (menu.getSeasonNo().equals("1")) {
                System.out.println("시즌: 여름");
            } else {
                System.out.println("시즌: 겨울");
            }
            System.out.printf("가격: %s원\r\n", menu.getPrice());

            if (i != endIndex - 1) {
                System.out.println();
            }
        }
    }

    /**
     * 메뉴 선택 메서드
     * @param list 선택할 메뉴 리스트
     * @param endIndex 표시할 메뉴의 끝 인덱스
     * @return false 반환
     */
    private boolean choiceMenu(ArrayList<Menu> list, int endIndex) {

        while (true) {
            Main.printLine();
            if (endIndex < list.size()) {
                System.out.println("1. 다음 메뉴 보기");
            }
            if (currentIndex > 0) {
                System.out.println("2. 이전 메뉴 보기");
            }
            System.out.println("0. 돌아가기");
            System.out.print("번호 입력: ");
            String input = scan.nextLine().trim();

            if (input.equals("1")) {
                if (endIndex < list.size()) {
                    currentIndex += 20;
                    return true;
                } else {
                    System.out.println("다음 메뉴가 없습니다.");
                    scan.nextLine();
                }
            } else if (input.equals("2")) {
                if (currentIndex > 0) {
                    currentIndex = Math.max(0, currentIndex - 20);
                    return true;
                } else {
                    System.out.println("이전 메뉴가 없습니다.");
                    scan.nextLine();
                }
            } else if (input.equals("0")) {
                System.out.println("Enter를 누르면 이전 화면으로 돌아갑니다.");
                scan.nextLine();
                return false;
            } else {
                System.out.println("잘못 입력하셨습니다.");
            }
        }
    }
}


MenuRate.java

package com.revolving.member;

import java.util.Scanner;
import java.util.Collections;

import com.revolving.Main;
import com.revolving.data.MemberData;
import com.revolving.data.RatingData;
import com.revolving.data.StoreData;
import com.revolving.data.object.Member;
import com.revolving.data.object.Rating;
import com.revolving.data.object.Store;

/**
 * 메장 평가 조회 클래스
 * 목적: 메장 평가를 조회한다.
 * 기능:
 * - 메뉴 평가 조회: 이전 리뷰 보기/다음 리뷰 보기 기능으로 최대 20개의 리뷰를 조회한다.
 * 
 * @author 이승원
 */
public class MenuRate {

	static Scanner scan = new Scanner(System.in);
	private static int currentIndex = 0; // 현재 리뷰 인덱스 (1~20)

	/**
        * 매장 평가를 20개씩 조회하는 메서드
        */
	public static void viewStoreReview() {

		while (true) {
			Main.printMenu("매장 평가 조회");

			// 매장명 기준 매장 리스트 정렬
			Collections.sort(StoreData.list, (store1, store2) -> store1.getName().compareTo(store2.getName()));

			// 표시할 리뷰의 끝 인덱스 계산
			int endIndex = Math.min(currentIndex + 20, RatingData.list.size());

			for (int i = currentIndex; i < endIndex; i++) {
				Rating review = RatingData.list.get(i);

				System.out.printf("리뷰번호: %s\n", review.getNo());

				for (Member member : MemberData.list) {
					if (member.getNo().equals(review.getMemberNo())) {
						System.out.printf("회원명: %s\n", member.getName());
						break;
					}
				}
				for (Store store : StoreData.list) {
					if (store.getNo().equals(review.getStoreNo())) {
						System.out.printf("매장명: %s\n", store.getName());
						System.out.printf("매장주소: %s\n", store.getAddress());
						System.out.printf("매장번호: %s\n", store.getTel());
						break;
					}
				}
				System.out.printf("리뷰: %s\n", review.getReview());
				System.out.printf("평점: %s\n", review.getScore());

				// 마지막 리뷰가 아닐 경우 개행
				if (i != endIndex - 1) {
					System.out.println();
				}
			}

			// 리뷰 선택 메뉴
			if (!choiceStoreReview(endIndex)) {
				return;
			}
		}
	}

	/**
        * 다음 리뷰 보기, 이전 보기를 선택하는 메서드
        *
        * @param endIndex 표시할 리뷰의 끝 인덱스
        * @return false 반환
        */
	private static boolean choiceStoreReview(int endIndex) {
		while (true) {
			Main.printLine();
			if (endIndex < RatingData.list.size()) {
				System.out.println("1. 다음 리뷰 보기");
			}
			if (currentIndex > 0) {
				System.out.println("2. 이전 리뷰 보기");
			}
			System.out.println("0. 돌아가기");
			System.out.print("번호 입력: ");
			String input = scan.nextLine().trim();

			if (input.equals("1")) {
				if (endIndex < RatingData.list.size()) {
					currentIndex += 20;
					return true;
				} else {
					System.out.println("다음 리뷰가 없습니다.");
					scan.nextLine();
				}
			} else if (input.equals("2")) {
				if (currentIndex > 0) {
					currentIndex = Math.max(0, currentIndex - 20);
					return true;
				} else {
					System.out.println("이전 리뷰가 없습니다.");
					scan.nextLine();
				}
			} else if (input.equals("0")) {
				System.out.println("Enter를 누르면 이전 화면으로 돌아갑니다.");
				scan.nextLine();
				return false;
			}
		}
	}
}

MyPageMenu.java

package com.revolving.member.mypage;

import java.util.Scanner;

import com.revolving.Main;

public class MyPageMenu {

	static Scanner scan = new Scanner(System.in);
	
	public static void viewUserMenu() {
		
		while (true) {
			Main.printMenu("마이페이지");
			Main.printOption("나의 추천 메뉴", "나의 매장 평가", "회원 정보 수정");
			String input = scan.nextLine().trim();
			
			if (input.equals("1")) {
				MyStoreList.viewMyStoreList();
			} else if (input.equals("2")) {
				MyRate.viewUserRate();
			} else if (input.equals("3")) {
				ChangeInfo.changeUserInfo();
			} else if (input.equals("0")) {
				return;
			}
		}
	}
}


MyStoreList.java

package com.revolving.member.mypage;

import java.util.Iterator;
import java.util.Scanner;

import com.revolving.Main;
import com.revolving.data.MemberData;
import com.revolving.data.MenuChooseData;
import com.revolving.data.MenuData;
import com.revolving.data.RatingData;
import com.revolving.data.StoreData;
import com.revolving.data.object.Member;
import com.revolving.data.object.Menu;
import com.revolving.data.object.MenuChoose;
import com.revolving.data.object.Rating;
import com.revolving.data.object.Store;
import com.revolving.login.Login;

/**
 * 나의 추천 메뉴 클래스
 * 목적: 나의 추천 메뉴에 대한 조회, 삭제 기능을 수행한다.
 * 기능:
 * - 나의 추천 메뉴 조회: 메뉴와 방문일을 출력한다.
 * - 나의 추천 메뉴 삭제: 넘버링된 번호를 입력하여 메뉴 기록을 삭제한다.
 * 
 * @author 이승원
 */
public class MyStoreList {

	static Scanner scan = new Scanner(System.in);

	/**
	 * 나의 추천 메뉴를 조회 및 삭제하는 메서드
	 */
	public static void viewMyStoreList() {

		while (true) {
			Main.printMenu("나의 추천 메뉴");
			Main.printOption("나의 추천 메뉴 조회", "나의 추천 메뉴 삭제");
			String input = scan.nextLine().trim();

			if (input.equals("1")) {

				Main.printMenu("나의 추천 메뉴 조회");

				for (Member member : MemberData.list) {
					if (member.getId().equals(Login.user.getId())) {
						viewMyMenuRecord(member);
					}
				}
				System.out.println("Enter를 누르면 이전 화면으로 돌아갑니다.");
				scan.nextLine();

			} else if (input.equals("2")) {
				Main.printMenu("나의 추천 메뉴 삭제");
				
				for (Member member : MemberData.list) {
					if (member.getId().equals(Login.user.getId())) {
						viewMyMenuRecord(member);
						break;
					}
				}
				
				deleteVisitedStoreRecord();

			} else if (input.equals("0")) {
				return;
			}
		}
	}

	/**
        * 회원의 추천 메뉴 기록을 조회하는 메서드
        * 
        * @param member 로그인 회원
        */
        private static void viewMyMenuRecord(Member member) {
		int count = 1;
		
		for (MenuChoose menuChoose : MenuChooseData.list) {
			if (menuChoose.getMemberNo().equals(member.getNo())) {
				boolean storeFound = false;

				// 매장명이 있는 경우
				for (Menu menu : MenuData.list) {
					for (Store store : StoreData.list) {
						for (Rating review : RatingData.list) {
							if (store.getMenuNo().equals(menuChoose.getMenuNo())) {
								if (store.getNo().equals(review.getStoreNo())) {
									if (menu.getNo().equals(menuChoose.getMenuNo())) {
										System.out.printf("%d. 메뉴명: %s, 매장명: %s, 방문일: %s\n", count++, menu.getName(), store.getName(), menuChoose.getDate());
										storeFound = true;
										break;
									}
								}
							}
						}
					}
				}
				
				// 매장명이 없는 경우
				if (!storeFound) {
					for (Menu menu : MenuData.list) {
						if (menu.getNo().equals(menuChoose.getMenuNo())) {
							System.out.printf("%d. 메뉴명: %s, 방문일: %s\n", count++, menu.getName(), menuChoose.getDate());
							break;
						}
					}
	            }
			}
		}
	}

	/**
        * 회원의 추천 메뉴 기록을 삭제하는 메서드
        */
	private static void deleteVisitedStoreRecord() {

		System.out.print("삭제할 번호 입력: ");
		int choice = scan.nextInt();
		
		Main.printLine();

		int count = 1;

		// 반복자로 추천 메뉴 기록을 하나씩 가져옴
		for (Iterator iterator = MenuChooseData.list.iterator(); iterator.hasNext();) {
			MenuChoose menuChoose = iterator.next();

			// 로그인한 사용자의 추천 메뉴 기록인지 확인
			if (menuChoose.getMemberNo().equals(Login.user.getNo())) {
				for (Menu menu : MenuData.list) {
					if (menu.getNo().equals(menuChoose.getMenuNo())) {
						
						// 사용자가 선택한 번호와 리스트 번호와 일치하면 해당 추천 메뉴 기록 삭제
						if (count == choice) {
							iterator.remove();
							MenuChooseData.save();
							System.out.print("선택한 메뉴 기록을 삭제했습니다.\n");
							scan.nextLine();
							
							// 해당 메뉴 기록에 대한 리뷰가 있으면 삭제할지 선택
							for (Store store : StoreData.list) {
								for (Rating review : RatingData.list) {
									if (store.getMenuNo().equals(menuChoose.getMenuNo())) {
										if (store.getNo().equals(review.getStoreNo()) && review.getMemberNo().equals(Login.user.getNo())) {
											Main.printLine();
											System.out.printf("리뷰번호: %s\n", review.getNo());
											System.out.printf("매장명: %s\n", store.getName());
											System.out.printf("매장주소: %s\n", store.getAddress());
											System.out.printf("매장번호: %s\n", store.getTel());
											System.out.printf("리뷰: %s\n", review.getReview());
											System.out.printf("평점: %s\n", review.getScore());
											Main.printLine();
											System.out.print("삭제한 메뉴에 대한 매장 리뷰를 작성한 기록이 있습니다.\n");
											System.out.print("해당 리뷰도 함께 삭제하시겠습니까? (y/n): ");
											String deleteChoice = scan.nextLine().trim();
											
											if (deleteChoice.equals("y")) {
												if (store.getNo().equals(review.getStoreNo())) {
													MyRate.deleteReview(review.getNo());
												}
											} else {
											    System.out.println("해당 리뷰를 유지합니다.");
											}
											break;
										}
									}
								}
							}
							
							System.out.println("Enter를 누르면 이전 화면으로 돌아갑니다.");
							scan.nextLine();
							return;
						}
						count++;
					}
				}
			}
		}

		System.out.println("잘못된 메뉴 기록을 입력하셨습니다.");
		System.out.println("Enter를 누르면 이전 화면으로 돌아갑니다.");
		scan.nextLine();
		scan.nextLine();
		return;
	}
}


MyRate.java

package com.revolving.member.mypage;

import java.util.Collections;
import java.util.Iterator;
import java.util.Scanner;

import com.revolving.Main;
import com.revolving.data.MemberData;
import com.revolving.data.RatingData;
import com.revolving.data.StoreData;
import com.revolving.data.object.Member;
import com.revolving.data.object.Rating;
import com.revolving.data.object.Store;
import com.revolving.login.Login;

/**
 * 나의 매장 평가 클래스
 * 목적: 나의 매장 평가에 대한 조회, 추가, 수정, 삭제 기능을 수행한다.
 * 기능:
 * - 나의 매장 평가 조회: 나의 매장 평가를 조회한다.
 * - 나의 매장 평가 추가: 매장 번호, 리뷰 내용, 평점을 입력하여 평가를 추가한다.
 * - 나의 매장 평가 수정: 나의 매장 평가를 수정한다.
 * - 나의 매장 평가 삭제: 나의 매장 평가를 삭제한다.
 * 
 * @author 이승원
 */
public class MyRate {

	static Scanner scan = new Scanner(System.in);

	/**
        * 나의 매장 평가 기능을 선택하는 메서드
        */
	public static void viewUserRate() {

		while (true) {
			Main.printMenu("나의 매장 평가");
			Main.printOption("나의 매장 평가 조회", "나의 매장 평가 추가", "나의 매장 평가 수정", "나의 매장 평가 삭제");
			String input = scan.nextLine().trim();

			if (input.equals("1")) {
				Main.printMenu("나의 매장 평가 조회");
				viewMyStoreReview();

			} else if (input.equals("2")) {
				Main.printMenu("나의 매장 평가 추가");
				addReview();

			} else if (input.equals("3")) {
				Main.printMenu("나의 매장 평가 수정");
				viewMyStoreReview();
				editReview();

			} else if (input.equals("4")) {
				Main.printMenu("나의 매장 평가 삭제");
				viewMyStoreReview();
				deleteReview();
			} else if (input.equals("0")) {
				return;
			}
			
			System.out.println("Enter를 누르면 이전 화면으로 돌아갑니다.");
			scan.nextLine();
		}
	}

	/**
        * 나의 매장 평가를 조회하는 메서드
        */
	private static void viewMyStoreReview() {
		int count = 0;
		int endIndex = 0;

		// 사용자 리뷰 개수 계산
		for (Member member : MemberData.list) {
			if (member.getId().equals(Login.user.getId())) {
				for (Rating review : RatingData.list) {
					if (review.getMemberNo().equals(member.getNo())) {
						count++;
					}
				}
			}
		}
		endIndex = count;
		count = 0;

		Collections.sort(RatingData.list,
				(review1, review2) -> Integer.parseInt(review1.getNo()) - Integer.parseInt(review2.getNo()));

		for (Member member : MemberData.list) {
			if (member.getId().equals(Login.user.getId())) {
				for (Rating review : RatingData.list) {
					if (review.getMemberNo().equals(member.getNo())) {
						count++;

						System.out.printf("리뷰번호: %s\n", review.getNo());

						for (Store store : StoreData.list) {
							if (store.getNo().equals(review.getStoreNo())) {
								System.out.printf("매장명: %s\n", store.getName());
								System.out.printf("매장주소: %s\n", store.getAddress());
								System.out.printf("전화번호: %s\n", store.getTel());
								break;
							}
						}

						System.out.printf("리뷰: %s\n", review.getReview());
						System.out.printf("평점: %s\n", review.getScore());

						if (count != endIndex) {
							System.out.println();
						}
					}
				}

				Main.printLine();
				if (count == 0) {
					System.out.println("작성한 리뷰가 없습니다.");
				}
			}
		}
	}

	/**
        * 나의 매장 평가를 추가하는 메서드
        */
	private static void addReview() {
		System.out.printf("1 ~ %d의 매장 번호를 입력하세요.\n", StoreData.list.size());
		System.out.print("매장 번호 입력: ");
		String storeNo = scan.nextLine().trim();
		if (Integer.parseInt(storeNo) < 1 || (Integer.parseInt(storeNo) > StoreData.list.size())) {
			System.out.println("잘못된 매장 번호를 입력하셨습니다.");
			return;
		}

		System.out.print("리뷰 내용 입력: ");
		String review = scan.nextLine().trim();

		System.out.print("0 ~ 5의 평점을 입력하세요.\n");
		System.out.print("평점 입력: ");
		String score = scan.nextLine().trim();
		if (Integer.parseInt(score) < 0 || Integer.parseInt(score) > 5) {
			System.out.println("잘못된 평점을 입력하셨습니다.");
			return;
		}

		Rating newReview = new Rating(String.valueOf(RatingData.list.size() + 1), Login.user.getNo(), storeNo, review,
				score);
		RatingData.list.add(newReview);
		RatingData.save();
		System.out.println("리뷰를 작성했습니다.");
	}

	/**
        * 나의 매장 평가를 수정하는 메서드
        */
	private static void editReview() {
		System.out.print("수정할 리뷰 번호 입력: ");
		String reviewNo = scan.nextLine().trim();

		for (Rating review : RatingData.list) {
			if (review.getNo().equals(reviewNo) && review.getMemberNo().equals(Login.user.getNo())) {
				System.out.print("새로운 리뷰 입력: ");
				String newReview = scan.nextLine().trim();

				System.out.print("새로운 평점 입력: ");
				String newScore = scan.nextLine().trim();
				if (Integer.parseInt(newScore) < 0 || Integer.parseInt(newScore) > 5) {
					System.out.println("잘못된 평점을 입력하셨습니다.");
					return;
				}

				review.setReview(newReview);
				review.setScore(newScore);

				RatingData.save();
				System.out.println("리뷰를 수정했습니다.");
				return;
			}
		}

		System.out.println("잘못된 리뷰 번호를 입력하셨습니다.");
	}

	/**
        * 나의 매장 평가를 삭제하는 메서드
        */
	private static void deleteReview() {
		System.out.print("삭제할 리뷰 번호 입력: ");
		String reviewNo = scan.nextLine().trim();

		Iterator iterator = RatingData.list.iterator();

		while (iterator.hasNext()) {
			Rating review = iterator.next();
			if (review.getNo().equals(reviewNo) && review.getMemberNo().equals(Login.user.getNo())) {
				iterator.remove();
				RatingData.save();
				System.out.println("리뷰를 삭제했습니다.");
				return;
			}
		}

		System.out.println("잘못된 리뷰 번호를 입력하셨습니다.");
	}
	
	/**
	 * 나의 매장 평가를 삭제하는 메서드
	 * 
	 * @param reviewNo 삭제할 리뷰 번호
	 */
	public static void deleteReview(String reviewNo) {
		Iterator iterator = RatingData.list.iterator();

		while (iterator.hasNext()) {
			Rating review = iterator.next();
			if (review.getNo().equals(reviewNo) && review.getMemberNo().equals(Login.user.getNo())) {
				iterator.remove();
				RatingData.save();
				System.out.println("리뷰를 삭제했습니다.");
				return;
			}
		}

		System.out.println("잘못된 리뷰 번호를 입력하셨습니다.");
	}
}


ChangeInfo.java

package com.revolving.member.mypage;

import java.util.Scanner;

import com.revolving.Main;
import com.revolving.data.MemberData;
import com.revolving.data.object.Member;
import com.revolving.join.Join;
import com.revolving.login.Login;

/**
 * 회원 정보 수정 클래스
 * 목적: 사용자의 회원 정보(ID, 비밀번호, 이름, 전화번호, 생년월일)를 수정한다.
 * 
 * @author 이승원
 */
public class ChangeInfo {

	static Scanner scan = new Scanner(System.in);
	
	/**
	 * 회원 정보를 수정하는 메서드
	 * 아이디, 비밀번호, 이름, 전화번호, 생년월일 중 하나를 선택하여 수정할 수 있다.
	 */
	public static void changeUserInfo() {

		while (true) {
			Main.printMenu("회원 정보 수정");
			Main.printOption("아이디 수정", "비밀번호 수정", "이름 수정", "전화번호 수정", "생년월일 수정");
			String input = scan.nextLine().trim();

			if (input.equals("1")) {
				changeId();
			} else if (input.equals("2")) {
				changePw();
			} else if (input.equals("3")) {
				changeName();
			} else if (input.equals("4")) {
				changeTel();
			} else if (input.equals("5")) {
				changeBirthdate();
			} else if (input.equals("0")) {
				return;
			}
			scan.nextLine();
		}
	}

	/**
	 * 아이디를 수정하는 메서드
	 * 새로운 아이디를 입력받고 유효성을 검사하여 아이디를 수정한다.
	 */
	private static void changeId() {
		Main.printMenu("아이디 수정");
		System.out.println("4자 ~ 16자의 영문 대소문자, 숫자를 포함하여 입력하세요.");
		System.out.print("새로운 아이디 입력: ");
		String newId = scan.nextLine().trim();

		if (!Join.isValidId(newId)) {
			System.out.println("형식에 맞지 않는 아이디입니다.");
			return;
		}

		for (Member member : MemberData.list) {
			if (member.getId().equals(newId)) {
				System.out.println("이미 존재하는 아이디입니다.");
				return;
			}
		}

		Login.user.setId(newId);
            MemberData.save();
		System.out.println("아이디를 수정했습니다.");
	}

	/**
	 * 비밀번호를 수정하는 메서드
	 * 새로운 비밀번호를 입력받고 유효성을 검사하여 비밀번호를 수정한다.
	 */
	private static void changePw() {
		Main.printMenu("비밀번호 수정");
		System.out.println("9자 ~ 14자의 영문 대소문자, 숫자, 특수문자(!, #, ^, &, *)를 포함하여 입력하세요.");
		System.out.print("새로운 비밀번호 입력: ");
		String newPw = scan.nextLine().trim();

		if (!Join.isValidPw(newPw)) {
			System.out.println("형식에 맞지 않는 비밀번호입니다.");
			return;
		}

		Login.user.setPwd(newPw);
            MemberData.save();
		System.out.println("비밀번호를 수정했습니다.");
	}

	/**
	 * 이름을 수정하는 메서드
	 * 새로운 이름을 입력받고 유효성을 검사하여 이름을 수정한다.
	 */
	private static void changeName() {
	    Main.printMenu("이름 수정");
	    System.out.println("2자 ~ 4자의 한글 이름을 입력하세요.");
	    System.out.print("새로운 이름 입력: ");
	    String newName = scan.nextLine().trim();

	    if (!Join.isValidName(newName)) {
	        System.out.println("형식에 맞지 않는 이름입니다.");
	        return;
	    }

	    Login.user.setName(newName);
            MemberData.save();
	    System.out.println("이름을 수정했습니다.");
	}

	/**
	 * 전화번호를 수정하는 메서드
	 * 새로운 전화번호를 입력받고 유효성을 검사하여 전화번호를 수정한다.
	 */
	private static void changeTel() {
	    Main.printMenu("전화번호 수정");
	    System.out.println("11자의 숫자로 전화번호를 입력하세요.");
	    System.out.print("새로운 전화번호 입력: ");
	    String newTel = scan.nextLine().trim();

	    if (!Join.isValidTel(newTel)) {
	        System.out.println("형식에 맞지 않는 전화번호입니다.");
	        return;
	    }

	    Login.user.setTel(newTel);
            MemberData.save();
	    System.out.println("전화번호를 수정했습니다.");
	}

	/**
	 * 생년월일을 수정하는 메서드
	 * 새로운 생년월일을 입력받고 유효성을 검사하여 생년월일을 수정한다.
	 */
	private static void changeBirthdate() {
	    Main.printMenu("생년월일 수정");
	    System.out.println("6자의 숫자로 생년월일을 입력하세요.");
	    System.out.print("새로운 생년월일 입력: ");
	    String newBirthdate = scan.nextLine().trim();

	    if (!Join.isValidBirth(newBirthdate)) {
	        System.out.println("형식에 맞지 않는 생년월일입니다.");
	        return;
	    }

	    Login.user.setBirth(newBirthdate);
            MemberData.save();
	    System.out.println("생년월일을 수정했습니다.");
	}
}