강의를 들으면 들을수록 완전 초급자가 들을만한 강의는 아니라는 생각이 든다. 점점 모르는 부분이 많아져서 구글링으로 궁금증을 해소하는 데에 한계가 생기고, 받아쓰기가 되어 간다. 그래도 지금 이렇게 공부해 놓으면 처음으로 돌아가서 이론 공부를 할 때 조금이나마 도움이 될거라고 기대를 해본다. 어쨋든 TIL.
이번에는 만들어 놓은 달력에 일정을 등록하고 등록된 일정을 확인하는 추가 기능을 구현해 보려고 한다.
public void printMenu() {
System.out.println("+------------------+");
System.out.println("| 1. 일정 등록 ");
System.out.println("| 2. 일정 검색 ");
System.out.println("| 3. 달력 보기 ");
System.out.println("| h. 도움말 q. 종료 ");
System.out.println("+------------------+");
}
printMenu 메소드를 생성한 후 메뉴 목록을 작성.
public void runPrompt() {
printMenu();
Scanner scanner = new Scanner(System.in);
Calendar cal = new Calendar();
while(true) {
System.out.println("명령 (1, 2, 3, h, q)");
String cmd = scanner.next();
switch(cmd) {
case "1" : cmdRegister(); break;
case "2" : cmdSearch(); break;
case "3" : cmdCal(scanner, cal);
case "h" : printMenu();
}
if (cmd.equals("q")) break;
}
System.out.println("Thank you. Bye");
scanner.close();
}
menu를 출력한다.
while 문 안에 switch문을 사용해서 메뉴 별로 어떤 메소드를 사용해야하는지 먼저 지정해준다.
ㄱ)
강의에서는 switch문 대신에 if문을 사용했었다. 강의가 오래전 거라 그 당시에는 switch문에 문자형을 못 받았던 것 같다. 그래서 정답을 모르는 상태에서 코드를 짰는데 처음에는
switch(cmd) {
case "1" : cmdRegister(); break;
case "2" : cmdSearch(); break;
case "3" : cmdCal(scanner, cal);
case "h" : printMenu();
case "q" : break;
}
이렇게 작성하였다. 근데 unrechable statement 오류가 뜨면서 while문 이후의 코드가 작동되지 않았다. 구글링을 통해 알아낸 사실은 switch문 안의 break문은 switch문만 중단시킬뿐 while문까지 중단되지는 않는다고 한다. 반면 무한루프 안의 if문에 있는 break문은 무한루프를 중단시킬 수 있다고 한다. 그래서 switch문이 아닌 if문 만을 사용할까 하다가 case "q"만 따로 빼서 if문으로 바꾸었다. 이렇게 하니 다음 출력문장이 출력되는 것을 확인할 수 있었다.
ㄴ)
또 ㄱ) 에서의 코드를 보면 case "1"과 case "2"에 break문이 있는데 여기서도 엄청 많이 헤맸다. 중간점검을 해보기 위해 메뉴들을 다 입력해 보았는데 3,h,q는 다 작동하는데 1,2를 입력하면 3 달력보기가 같이 출력이 되었다. 그래서 구글링을 여러번 해보았지만 잘은 모르겠고 인터넷에 떠도는 다른 switch문 코드들을 보니 case마다 break문이 있길래 붙여봤더니 그 후로 해결이 되었다.
ㄷ)
참고로 if 문을 사용할 때에는
if (cmd == "q") {.....}
가 아니라
if (cmd.equals("q")) {.....}
이렇게 사용해야 한다.
ㄹ)
메소드를 새로 만드는 경우 일단 원하는 이름의 메소드를 적은 후에 에러가 나면 에러를 클릭하여 Show-Quick-Fixes를 클릭하고, create method를 클릭하면 자동으로 메소드가 만들어진다.
private void cmdCal(Scanner s, Calendar c) {
System.out.println("년을 입력하세요.");
System.out.print("year> ");
int year = s.nextInt();
System.out.println("월을 입력하세요.");
System.out.print("month> ");
int month = s.nextInt();
if (month < 1 || month > 12) {
System.out.println("잘못 입력하셨습니다.");
return;
}
c.printCalendar(year, month);
}
runPrompt 메소드에 있던 연,월 입력받는 코드를 cmdCal 메소드로 옮겨와 cmd로 3을 입력받았을 때 메뉴 '달력 보기'를 실행할 수 있게끔 한다.
ㄱ) 메소드를 새로 생성했으므로 Scanner와 Calendar클래스가 존재하지 않고 있다. 여기서 해결할 수 있는 방법이 두 가지가 있다. 첫번째방법은 cmdCal 메소드에도 중복으로
Calendar cal = new Calendar();
Scanner scanner = new Scanner(System.in);
를 작성하는 방법이 있다. 하지만 어떠한 기능을 구현할 때마다 이렇게 새로 불러오는 것이 코드를 짜기에도 매우 비효율적이고 컴퓨터 내에서도 시트템 내부에 부하가 많이 걸리기 때문에 소모적인 행동이라고 한다. 그래서 첫번째 방법은 사용하지 않았고 두번째 방법은 이미 선언된 것들을 바깥으로부터 불러오는 방법이 있다. 사용하고자 하는 기능을 괄호 안에 적음으로써 바깥에서 불러올 수 있다.
private void cmdCal(Scanner s, Calendar c)
이렇게 하면 argument가 채워졌으므로 다른 메소드에서 호출된 cmdCal에도 똑같이 argument를 채워줘야 에러가 나지 않는다.
import java.util.Date;
import java.text.SimpleDateFormat;
import java.text.ParseException;
public void registerPlan(String strDate, String plan) throws ParseException {
Date date = new SimpleDateFormat("yyyy-MM-dd").parse(strDate);
planMap.put(date);
}
Prompt 파일에 cmdRegister, cmdSearch 메소드 코드를 작성하기에 앞서 Calendar 파일에 registerPlan 메소드를 생성한다. 이 메소드의 목적은 날짜를 입력받을 때 strDate에 "yyyy-MM-dd"의 String 형태로 받게 될텐데 이 것을 Date 형식으로 바꿔주기 위함이다.
프로그램이 비정상적으로 오작동하는 경우가 있다. 이를 오류라 한다.
오류에는 두가지로 나뉘는데 발생 시점에 따라 컴파일 시 나는 오류는
컴파일 오류, 실행 시에 나는 오류는 실행 오류라고 한다.
또한 프로그램을 실행 중에도 에러가 나기도 하는데 이를 시스템 오류, 예외 두가지로 나눌 수 있다. 시스템 오류는 코드를 바꿔 수습할 수 없는 상대적으로 심각한 오류이고 예외는 상대적으로 코드를 바꿔 수습할 수 있는 오류이다. 예외의 종류에도 개발자가 어떠한 실수를 했느냐에 따라 무수히 많은데 여기서 다룰 오류는 parseException이다. 지금은 에러 자체에 대해 공부하기 보다는 예외를 처리하는 방법에 대해 공부해 보려고 한다.
강의에서 언급된 예외 처리 방법엔 두가지가 소개되었다.
ㄱ) 예외 처리 미루기 (throw)
void method() throw exception1, exception2,.. {
// method 내용
}
이와 같은 방식으로 메소드 선언부에 throw를 적고 예외들을 나열하면 된다. 이 예외들은 에러가 해결되는 것이 아니라 자신을 호출할 다른 메소드에게 예외를 떠넘기는 것이다. 이런 식으로 계속 호출 스택에 있는 메소드들을 따라 전달되고 결국 어느 한 곳에서는 반드시 try-catch문으로 해결해야 한다.
ㄴ) try-catch 문
try {
예외가 발생할 수 있는 부분
catch (처리할 예외 타입1 e1) {
try 블록 안에서 예외1이 발생했을 때 예외를 처리하는 부분
} catch (처리할 예외 타입2 e2) {
try 블록 안에서 예외2이 발생했을 때 예외를 처리하는 부분
} catch (Exception e) {
try 블록 안에서 예외가 발생했을 때 예외를 처리하는 부분
}
}
try 블록에는 예외가 발생할 가능성이 있는 코드를 작성한다. 만약 try 블록 안에서 예외가 발생하면 바로 catch 블록이 수행된다. catch문 괄호 안에 쓰는 예외 타입은 예외 상황에 따라 달라진다.
Date를 import하면 현재 날짜, 시간 등을 출력할 수 있다.
private HashMap <Date, String> planMap;
public Calendar() {
planMap = new HashMap<Date, String>();
}
날짜별 계획을 저장하기 위해 (key,value) 형식의 배열로 저장해 주는 HashMap 클래스를 사용해 palnMap이라는 메소드를 생성한다. 하나의 key에 하나 이상의 value를 저장할 수 있는 것이 HashMap이다.
public void registerPlan(String strDate, String plan) throws ParseException {
Date date = new SimpleDateFormat("yyyy-MM-dd").parse(strDate);
//System.out.println(date);
planMap.put(date, plan);
}
panMap.put(date);에서 plan을 추가하여 planMap에 date와 plan argument를 갖게 되었다. 이를 통해 사용자가 strDate와 plan을 입력하면 planMap 메소드를 통해 저장된다. (put 넣다)
public String serchPlan(String strDate) throws ParseException {
Date date = new SimpleDateFormat("yyyy-MM-dd").parse(strDate);
String plan = planMap.get(date);
return plan;
}
다음으로 특정 date에 저장된 plan을 불러오기 위한 searchPlan 메소드를 작성한다. strDate를 date형태로 바꾸어서 입력을 받고 그 date에 저장되어 있는 것이 있다면 그 것을 plan에 저장해서 plan을 return한다는 내용이다.
private void cmdRegister(Scanner s, Calendar c) throws ParseException {
System.out.println("[새 일정 등록]");
System.out.println("날짜를 입력해 주세요.(yyyy-MM-dd).");
String date = s.next();
String text = "";
s.nextLine();
System.out.println("일정을 입력해 주세요.");
text = s.nextLine();
c.registerPlan(date, text);
}
Prompt 파일로 돌아가서 cmdRegister 메소드를 작성한다. 메소드를 새로 만들었으므로 바깥에서 Scanner와 Calendar를 불러오고 ParseException 예외를 throw로 처리한다. 날짜를 strDate형태로 입력을받고 plan은 한 어절 이상이므로 nextLine()을 사용한다.
private void cmdSearch(Scanner s, Calendar c) {
System.out.println("[일정 검색]");
System.out.println("날짜를 입력해 주세요.(yyyy-MM-dd).");
String date = s.next();
String plan = "";
try {
plan = c.searchPlan(date);
} catch (ParseException e) {
e.printStackTrace();
System.err.println("일정 검색 중 오류가 발생하였습니다.");
}
System.out.println(plan);
}
다음으로 일정을 찾아보는 cmdSearch 메소드를 작성한다. plan을 불러올 date를 strDate로 입력받으면 plan을 출력해주는 코드이다.
여기서는 try-catch문으로 예외 처리를 하였다.
public void runPrompt() throws ParseException {
printMenu();
Scanner scanner = new Scanner(System.in);
Calendar cal = new Calendar();
while(true) {
System.out.println("명령 (1, 2, 3, h, q)");
String cmd = scanner.next();
switch(cmd) {
case "1" : cmdRegister(scanner, cal); break;
case "2" : cmdSearch(scanner, cal); break;
case "3" : cmdCal(scanner, cal);
case "h" : printMenu();
}
if (cmd.equals("q")) break;
}
System.out.println("Thank you. Bye");
scanner.close();
}
마지막으로 switch문에 argument에 지금껏 추가했던 scanner와 cal을 작성해준다.
+------------------+
| 1. 일정 등록
| 2. 일정 검색
| 3. 달력 보기
| h. 도움말 q. 종료
+------------------+
명령 (1, 2, 3, h, q)
1
[새 일정 등록]
날짜를 입력해 주세요.(yyyy-MM-dd).
2022-04-05
일정을 입력해 주세요.
let's have dinner!
명령 (1, 2, 3, h, q)
2
[일정 검색]
날짜를 입력해 주세요.(yyyy-MM-dd).
2022-04-05
let's have dinner!
명령 (1, 2, 3, h, q)
3
년을 입력하세요.
year> 1
월을 입력하세요.
month> 1
<<1년 1월>>
SU MO TU WE TH FR SA
--------------------------
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
+------------------+
| 1. 일정 등록
| 2. 일정 검색
| 3. 달력 보기
| h. 도움말 q. 종료
+------------------+
명령 (1, 2, 3, h, q)
q
Thank you. Bye