ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java]STEP0 - 달력을 바탕으로 일정의 등록, 검색, 변경 기능이 있는 일정 관리 프로그램 만들기
    개발 공부/Java 2020. 8. 23. 16:59

     

    본 게시글은 인프런의 "만들어 가면서 배우는 JAVA 플레이그라운드" 에 나오는 과제물을 직접 구현한 것입니다.

    모든 저작권은 해당 강의의 강사님이신 코드스쿼드 정호영님에게 있음을 알립니다.



    <요구 사항>

    • 프로그램을 실행하면 오늘 날짜를 자동으로 인식하여 해당 월의 달력을 출력한다.
    • 간단한 콘솔 기반 사용자 UI를 만든다. (일정 검색용)
    • 달력에서 일정이 있는 날에는 점(".")을 별도로 표시한다.
    • 일정의 등록, 검색, 변경 기능을 추가한다.
    • Arraylist와 Hashmap 을 사용한다.

     

     

     

    <동작 구조>

    기본적인 아이디어는 다음과 같다.

    • 특정 날짜에 일정을 등록하는 작업은 Hashmap을 통해 구현한다.
      Hashmap은 Key와 Value의 쌍으로 이루어진 데이터를 저장한다. 즉 각 Key에 대응하는 Value가 한 개씩 있다.
      따라서 특정 날짜(ex. 2020-08-23)을 Key로 하는 일정(ex. 카페에서 공부하기) Value를 대응시킬 수 있다.
    • 여러 개의 일정을 저장하는 작업은 Arraylist를 통해 구현한다.
      Arraylist는 저장 공간이 가변적으로 변하는 선형리스트이다. 즉 내가 데이터를 넣는 만큼 저장공간이 동적할당된다.
      특정 날짜에는 일정이 여러개가 있을 수 있으므로, 내가 일정을 넣는 만큼 저장공간이 할당되어야 한다.
      이를 위해서 Hashmap 각 Value 자체를 Arraylist로 만들면 특정 날짜에 여러개의 일정을 담을 수 있다.

     

     

    1. 프로그램 실행 시 오늘 날짜를 자동으로 인식하여 해당 월을 출력하기

     

    오늘 날짜를 출력하기 위해서 먼저 java.time 라이브러리 내에 있는 LocalDate를 이용한다.

    format을 이용하여 오늘 날짜의 형식을 yyyy-MM-dd의 형태로 today라는 변수에 넣는다.

    굳이 이 형태로 넣는 이유는, 바로 밑에서 "-"을 기준으로 split하여 년, 월, 일로 쪼개기 쉽기 때문이다.

     

    다음으로 toDate라는 String 배열에 today의 날짜를 "-"를 split하여 각각 년, 월, 일로 담는다.

    미리 지정된 calender 함수에 Hashmap인 listMap과 년, 월을 argument로 보내어 오늘이 포함된 월의 달력을 출력한다.

    calender 함수는 이전 글에 설명이 더 자세히 나와있다. 사용자가 년, 월을 입력하면 해당 월의 달력을 표시해주는 함수이다.

    (아래 링크 참고)

     

     

    [Java]STEP0 - 달력 알고리즘을 이용하여, 진짜 캘린더에서 나오는 달력과 똑같이 만들기

    본 게시글은 인프런의 "만들어 가면서 배우는 JAVA 플레이그라운드" 에 나오는 과제물을 직접 구현한 것입니다. 모든 저작권은 해당 강의의 강사님이신 코드스쿼드 정호영님에게 있음을 알립니��

    kyleyj.tistory.com

     

     

     

    2. 일정 등록 기능 구현하기

    "1.일정 등록  2.일정 검색 및 변경  3.달력 보기  h.도움말  q.종료"  중에서 1.일정 등록 부분을 구현해본다.

     

    일단 각 날짜에 일정이 어떻게 저장되는지 큰 구조부터 알아볼 필요가 있다.

    간단하게 말하면 Hashmap 안에 Arraylist를 넣는 구조인데,

    대략 아래처럼 되어있는 것이다.

     

           Key                                Value                                                                    

    2014-05-06     {"1. 커피 마시기", "2. 책 읽기"}                                         

    2020-01-20     {"1. 친구 만나기", "2. 독후감 쓰기", "3. 이어폰 사기"}

    2018-09-03     {"1. 독서실 가기"}                                                                 

     

    등록하는 날짜를 통으로 key로 저장하고, 이에 해당하는 일정들을 arraylist에 value로 저장한다.

    즉 Hashmap에 Value에 해당하는 부분이 Arraylist로 되어있는 것이다.

     

    이것을 이해했다면 이제 일정 등록을 해보자.

    일정 등록을 하려면 두 가지로 나눠볼 수 있다.

     

    1. 등록 하려는 날짜에 기존 일정이 없는가?

    2. 등록 하려는 날짜에 이미 기존 일정이 있는데, 더 추가하고 싶은가?

     

    만약 1. 등록 하려는 날짜에 기존 일정이 없는가? 라면

    해당 날짜를 key로 저장하고, 빈 Arraylist를 Value로 먼저 저장해야한다. ▶  listMap.put(theDate, emptyList);

    왜냐하면, Arraylist에 일정을 등록(add) 하기 위해서는 먼저 Arraylist가 존재해야하기 때문이다.

     

    만약 2. 등록하려는 날짜에 이미 기존일정이 있는데, 더 추가하고 싶은가? 라면

    단순히 해당 날짜를 key로 하는 Arraylist를 불러와서 add 해주면 된다.  ▶  existList.add(theList);

     

     

     

    3. 일정 검색 및 변경 기능 구현하기

     

    -  먼저 일정 검색 기능에 대해 알아본다.

    일정이 있는지 없는지 알 수 있는 방법은 listMap.containsKey(findDate) 를 통해 알 수 있다.

    containsKey 메소드는 해당 Hashmap에 내가 검색하고자 하는 Key가 있는지 판별한다.

    해당 Key가 있다면 true를, 없다면 false를 반환한다.

     

    따라서 일정이 있다면 listMap.get(findDate)를 통해서 Arraylist를 가져온 후 출력하면 된다.

     

    만약 일정이 없다면

    이런 식으로 안내문을 내보내며 일정 검색 및 변경 기능을 종료한다.

     

     

    -  다음으로 일정 변경 기능에 대해 알아본다.

    먼저 change라는 변수를 통해 일정을 변경할 것인가? / 하지 않을 것인가? 에 따라 기능이 분기된다.

     

    만약 일정을 변경한다면, 일정이 들어있는 Arraylist인 schedule의 size 만큼 for 문을 돌면서

    schedule.get(i) 를 통해 모든 일정을 출력한다.

     

    그리고 변경할 일정의 번호를 선택하게 되고 이 숫자는 number라는 변수에 들어간다.

    다음 if문에서 number -1 >= schedule.size() 가 나오는데, 이때 number - 1 인 이유는

    일정의 번호는 1, 2, 3 이런식이지만, 실제로 일정이 저장된 배열에서의 index는 0, 1, 2 이런식으로 되어있기 때문이다.

     

    따라서 number -1 >= schedule.size() 즉 사용자가 변경하고자 하는 일정의 번호가 size 보다 크다면 (없는 번호라면)

    "잘 못 입력하셨습니다. 존재하는 일정을 선택하세요." 라는 문구와 함께 일정 변경 기능이 다시 시작된다. (continue;)

     

    number -1 < schedule.size() 라면 변경 내용을 입력하라는 안내문이 뜨면서

    schedule.set(number - 1, changeList); 를 통해 해당 index에 있는 일정 내용을 새로운 것으로 변경하게 된다.

     

     

     

    4. 원하는 월의 달력 출력하기     &    일정이 존재하는 날에는 점 찍기

     

    원하는 월의 달력 출력하기

    원하는 월의 달력 출력하는 것은 굉장히 간단하다.

    년도와 월을 year, month로 입력받고 calender 함수에 넘겨주면 끝이다.

    calender 함수는 위에서 언급했던 대로 이전 글에 더 자세히 나와있다.

     

     

    -  일정이 존재하는 날에는 점 찍기

    점을 찍기 전에 먼저 해결해야하는 문제점이 있다.

    바로 사용자가 원하는 년도와 월을 입력할 때 2020 3 이런식으로 입력하게 되는데

    Hashmap의 key에 들어있는 날짜의 값은 2020 03 이런식으로, 한 자릿 수에는 앞에 0이 붙어서 저장되어있다.

    이 차이를 해결하기 위해서 if문을 2개 이용하여, 10보다 작을 경우 int를 String으로 변환하여 앞에 0을 붙여준다.

     

    그렇게 date 변수 안에는 0을 붙인 깔끔한 날짜의 String이 담기게 되고

    이를 containsKey를 이용해서 해당 날짜에 일정이 있는지, 없는지를 검사하게 된다.

    만약 일정이 있다면 %02d. 의 형태로 점을 찍고, 없다면 %02d의 형태로 점을 찍지 않고 출력한다.

     

     

     

    <결과 화면>

    초기 화면은 오늘 날짜(2020-08-23)을 자동으로 인식하여 8월의 달력을 보여준다.

    0. 초기 화면

     

    1. 일정 등록

     

    2. 일정 검색 및 변경

     

    3. 달력 보기

     

    h. 도움말 및 q. 종료

     

     

    <전체 코드>

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Scanner;
    import java.time.*;
    import java.time.format.DateTimeFormatter;
    
    public class to_do_list {
    	
    	public static int monthDay(int year, int month) {
    		if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
    			return 31;
    		} else if (month == 4 || month == 6 || month == 9 || month == 11) {
    			return 30;
    		} else {
    			if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
    				return 29;
    			} else {
    				return 28;
    			}
    		}
    	}
    	
    	public static void calender(HashMap<String, ArrayList<String>> listMap, int year, int month) {		
    		System.out.println(" " + year + "년 " + month + "월의 달력");		
    
    		int sum = 0;
    
    		for (int i = 1583; i < year; i++) {
    			if ((i % 4 == 0 && i % 100 != 0) || i % 400 == 0) {
    				// 윤년이라면
    				sum += 2;
    			} else {
    				// 평년이라면
    				sum += 1;
    			}
    		}
    
    		int first = (sum + 6) % 7; // 입력한 year의 1월 1일의 요일
    
    		for (int j = 1; j < month; j++) {
    			first += monthDay(year, j) % 7;
    		}
    
    		int day = first % 7; // 입력한 month의 1일의 요일
    		int num = 1; // month의 일 표시
    		int max = monthDay(year, month); // 해당 month가 가지는 최대 일 수
    		boolean start = false;
    
    		System.out.println(" Sun  Mon  Tue  Wed  Thu  Fri  Sat ");
    
    		loop: for (int row = 0; row <= 5; row++) {
    			for (int column = 0; column <= 6; column++) {
    				if (row == 0 && !start) {
    					// 달력의 첫 행
    					if (column == day) {
    						// 시작 일에 도달하면
    						start = true;
    					} else {
    						// 시작 일에 도달 전에는 공백
    						System.out.print("     ");
    						continue;
    					}
    				}	
    				
    				// 10이하의 month, num과 listMap에 저장된 month, day와의 자릿수 차이를 해결(ex. 2와 02의 자릿수 차이)
    				String monthString = Integer.toString(month);
    				String dayString = Integer.toString(num);
    				
    				if(month < 10) {
    					monthString = "0" + Integer.toString(month);
    				}
    				
    				if(num < 10) {
    					dayString = "0" + Integer.toString(num);
    				}
    				
    				String date = year + "-" + monthString + "-" + dayString;
    				
    				if (listMap.containsKey(date)) {
    					// if plans exist
    					System.out.printf("  %02d.", num);
    				} else {
    					// if plans not exist
    					System.out.printf("  %02d ", num);
    				}				
    				
    				num++;
    
    				if (num > max) {
    					// 최대 일 수에 도달하면 break loop
    					System.out.println("");
    					break loop;
    				}
    			}
    			System.out.println("");
    		}
    
    	}
    
    	public static void main(String[] args) {
    		@SuppressWarnings("resource")
    		Scanner scanner = new Scanner(System.in);
    		HashMap<String, ArrayList<String>> listMap = new HashMap<String, ArrayList<String>>();
    			
    		String today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); // 오늘 날짜
    		String[] toDate = today.split("-"); // 오늘 날짜를 년, 월, 일로 나눔
    		calender(listMap, Integer.parseInt(toDate[0]), Integer.parseInt(toDate[1])); // 오늘 날짜가 포함 된 월의 달력 자동 출력
    		
    		System.out.println("+---------------------------------+");	
    		System.out.println("| 1. 일정 등록");
    		System.out.println("| 2. 일정 검색 및 변경");
    		System.out.println("| 3. 달력 보기");
    		System.out.println("| h. 도움말    q. 종료");
    		System.out.println("+---------------------------------+");
    
    		while (true) {
    			System.out.print("명령 (1.일정 등록  2.일정 검색 및 변경  3.달력 보기  h.도움말  q.종료)\n> ");
    			char order = scanner.next().charAt(0); // choose menu
    			scanner.nextLine();
    
    			switch (order) {
    			case '1':
    				System.out.print("[일정 등록] 날짜를 입력하세요.(ex.2020-01-01)\n> ");
    				String theDate = scanner.nextLine();
    
    				if (!listMap.containsKey(theDate)) {
    					// 기존의 일정이 없다면, 빈 Arraylist를 추가한다.
    					ArrayList<String> emptyList = new ArrayList<String>();
    					listMap.put(theDate, emptyList);
    				}
    
    				System.out.print("[일정 등록] 일정을 입력하세요.\n> ");
    				String theList = scanner.nextLine();
    
    				ArrayList<String> existList = listMap.get(theDate);
    
    				existList.add(theList);
    				listMap.put(theDate, existList);
    				break;
    
    			case '2':
    				System.out.print("[일정 검색] 날짜를 입력하세요.(ex.2020-01-01)\n> ");
    				String findDate = scanner.nextLine();
    
    				if (listMap.containsKey(findDate)) {
    					// 해당 날짜에 일정이 있다면
    					ArrayList<String> schedule = listMap.get(findDate);
    
    					System.out.printf("%d개의 일정이 있습니다.\n", schedule.size());
    
    					for (int i = 0; i < schedule.size(); i++) {
    						// 존재하는 일정 전체 출력
    						System.out.printf("%d.%s\n", i + 1, schedule.get(i));
    					}
    
    					while (true) {
    						System.out.print("일정을 변경하시겠습니까?(예:1 / 아니오:2)\n> ");
    						String change = scanner.nextLine();
    
    						if (change.equals("1")) {
    							// 일정 변경한다.
    							System.out.println("현재 등록되어 있는 일정입니다.");
    							for (int i = 0; i < schedule.size(); i++) {
    								// 존재하는 일정 전체 출력
    								System.out.printf("%d.%s\n", i + 1, schedule.get(i));
    							}
    
    							System.out.print("변경할 일정의 번호를 입력하세요.(숫자만 입력)\n> ");
    							int number = scanner.nextInt();
    							scanner.nextLine();
    
    							if (number - 1 >= schedule.size()) {
    								// index not exists
    								System.out.println("잘 못 입력하셨습니다. 존재하는 일정을 선택하세요.");
    								continue;
    							} else {
    								// index exists
    								System.out.print("변경 내용을 입력하세요.\n> ");
    								String changeList = scanner.nextLine();
    
    								schedule.set(number - 1, changeList);
    								System.out.println("정상적으로 일정이 변경되었습니다.");
    							}
    						} else if (change.equals("2")) {
    							// 일정 변경하지 않는다.
    							break;
    						} else {
    							// 1이나 2 외에 엉뚱한 값을 입력하면
    							System.out.println("잘 못 입력하셨습니다. 다시 선택해주세요.");
    						}
    					}
    
    				} else {
    					// 해당 날짜에 일정이 없다면
    					System.out.println("해당 날짜에 일정이 존재하지 않습니다.");
    				}
    				break;
    
    			case '3':				
    				System.out.print("년도를 입력하세요: ");
    				int year = scanner.nextInt();
    				System.out.print("월을 입력하세요: ");
    				int month = scanner.nextInt();
    				System.out.println("");
    				calender(listMap, year, month);
    				break;
    
    			case 'h':
    				System.out.println("");
    				System.out.println("<일정 관리 프로그램 도움말>");
    				System.out.println("(1. 일정 등록)을 선택하시면 일정을 등록하고 싶은 날짜와 일정 내용을 입력하시면 해당 일정이 저장됩니다.");
    				System.out.println("(2. 일정 검색 및 변경)을 선택하시면 검색하고 싶은 날짜를 입력하시면 해당 날짜에 저장 된 일정을 보여줍니다. 일정 변경 또한 가능합니다.");
    				System.out.println("(3. 달력 보기)를 선택하시면 원하시는 년도와 월의 달력을 보여줍니다.");
    				System.out.println("(h. 도움말)을 선택하시면 지금 보고 계시는 이 도움말을 보여줍니다.");
    				System.out.println("(q. 종료)를 선택하시면 일정 관리 프로그램이 종료됩니다.");
    				System.out.println("");
    				break;
    
    			case 'q':
    				System.out.println("종료합니다. 이용해 주셔서 감사합니다.");
    				System.exit(0);
    
    			default:
    				System.out.println("잘 못 입력하셨습니다. 다시 선택해주세요.");
    			}
    		}
    	}
    
    }
    

     

     

    본 게시물은 여기서 마치겠습니다. 읽어주셔서 감사합니다.

    수정사항이나 건의사항은 댓글로 남겨주시면 감사하겠습니다.

    댓글

Designed by Tistory.