만들고 있던 delete 기능부터 들어가서 parsing을 하는 방법에 대해서 뭔가 이야기 하셨다.
update나 detail에도 같은 방법을 사용할 수 있고 아마 더 효율적으로 코드를 짜는 것을 설명하시려고 하는 것 같았다.
public class Rq {
private String actionMethod;
private Map<String, String> params;
// Rq == request
public Rq(String cmd) {
//parsing
String[] cmdBits = cmd.split("\\?", 2);
actionMethod = cmdBits[0]; //delete
params = new HashMap<>();
String[] paramBits = cmdBits[1].split("&");
for (String paramStr : paramBits) {
String[] paramStrBits = cmdBits[1].split("=", 2);
String key = paramStrBits[0];
String value = paramStrBits[1];
params.put(key, value);
}
}
public Map<String, String> getParams() {
return params;
}
public String getActionMethod() {
return actionMethod;
}
}
그래서 이 기능을 처리하는 클래스를 하나 만들었다.
계속 반복하다 보니까 뭔가 클래스를 새로 생성하는데 기준이 보이는 것 같다. 어떤 기능을 도입할 때 그게 여러번 반복해서 쓰이거나 담당하는 기능이 나중에 다시 뽑아다가 쓸 수 있을 것 같을 때, 그리고 뭔가 아예 다른 기능을 할 때 보기 좋게나, 쓰기 좋게 따로 분리해주고 리모컨으로 연결해주는 그런 느낌이다. 이게 말로 설명이 잘 되지 않는데 딱 그런 느낌으로 클래스를 분리하고 만들고 하는 것 같다.
제대로 쓸 수 있게 Rq 클래스를 수정했다.
import java.util.HashMap;
import java.util.Map;
public class Rq {
private String actionMethod;
private Map<String, String> params;
// Rq == request
public Rq(String cmd) {
//parsing
String[] cmdBits = cmd.split("\\?", 2);
actionMethod = cmdBits[0]; //delete
params = new HashMap<>();
String[] paramBits;
try {
paramBits = cmdBits[1].split("&");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("명령어 확인 요망.");
return;
}
for (String paramStr : paramBits) {
String[] paramStrBits = cmdBits[1].split("=", 2);
String key = paramStrBits[0];
String value = paramStrBits[1];
params.put(key, value);
}
}
public String getActionMethod() {
return actionMethod;
}
public String getParams(String paramName) {
return params.get(paramName);
}
}
else if (cmd.startsWith("delete")) {
Rq rq = new Rq(cmd);
// motivationController.delete(cmd);
}
그리고 기존의 App의 delete를 실행하는 곳에는 클래스 Rq의 생성자를 받는 코드를 추가했다.
App를 더 효율적으로 수정했다.
public class App {
byte system_status = 1;
public App() {
}
public void run() {
System.out.println("== motivation execution ==");
SystemController systemController = new SystemController();
MotivationController motivationController = new MotivationController();
while (system_status == 1) {
System.out.print("commend) ");
String cmd = Container.getScanner().nextLine().trim();
if (cmd.length() == 0) {
System.out.println("명령어를 입력해주세요.");
continue;
}
Rq rq = new Rq(cmd);
switch (rq.getActionMethod()) {
case "exit":
systemController.exit();
system_status = 0;
break;
case "add":
motivationController.add();
break;
case "list":
motivationController.list();
break;
case "delete":
// motivationController.delete();
break;
default:
System.out.println("사용할 수 없는 명령어.");
break;
}
}
}
}
이 코드가 '라우터' 역할을 맡는다고 한다. 코드에 길잡이 역할이라고 하는데 각 기능을 명령할 때 어디로 가서 어떤 코드를 진행시켜야하는지 길을 안내해줘서 이런 이름이 붙는 것 같다. 그래서 지금 우리가 한 것은 '라우팅 코드 간결화' 정도가 되겠다. switch 는 처음 써 봤는데 되게 간결하고 보기 좋다. 잘 보면 add할 때 우리가 만들어 놓은 motivationController를 타고 가서 add 메서드를 실행하는 모습을 확인 할 수 있다. 코드의 가독성이 좋다. 앞으로 무언가를 개발할 때 이런 모양이 될 수 있도록 노력해야겠다.
parsing할 때 key의 예외상황 처리에 대한 코드
public class Rq {
private String actionMethod;
private Map<String, String> params;
private String errMsg = "";
@Override
public String toString() {
return "Rq{" +
"actionMethod='" + actionMethod + '\'' +
", params=" + params +
'}';
}
// Rq == request
public Rq(String cmd) {
//parsing
String[] cmdBits = cmd.split("\\?", 2);
actionMethod = cmdBits[0]; //delete
params = new HashMap<>();
String[] paramBits;
if (cmdBits.length == 1) {
return;
}
try {
paramBits = cmdBits[1].split("&");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("명령어 확인 요망.");
return;
}
for (String paramStr : paramBits) {
String[] paramStrBits = cmdBits[1].split("=", 2);
String key = paramStrBits[0];
if (key.equals("id") == false) {
System.out.println("오타 있음(id)");
errMsg = "오타 있음(id)";
}
String value = paramStrBits[1];
params.put(key, value);
}
}
public String getActionMethod() {
return actionMethod;
}
public String getParams(String paramName) {
return params.get(paramName);
}
public String getErrMsg() {
return errMsg;
}
}
public class MotivationController {
int lastId;
List<Motivation> motivations;
public MotivationController() {
lastId = 0;
motivations = new ArrayList<>();
}
public void add() {
int id = lastId + 1;
System.out.print("motivation :");
String body = Container.getScanner().nextLine();
System.out.print("source :");
String source = Container.getScanner().nextLine();
Motivation motivation = new Motivation(id, body, source);
motivations.add(motivation);
System.out.printf("%d번 motivation 이 등록 되었습니다.\n", id);
lastId++; //마지막 번호 증가
}
public void list() {
if (motivations.size() == 0) {
System.out.println("등록된 motivation 없음.");
return;
}
System.out.println("== motivation list ==");
System.out.print(" id // source // body \n");
System.out.println("=".repeat(40));
// System.out.println("있음.");
// System.out.println("등록된 motivation 개수 : " + motivations.size());
for (int i = motivations.size() - 1; i >= 0; i--) {
Motivation motivation = motivations.get(i);
if (motivation.getSource().length() > 7) {
System.out.printf(" %d // %s // %s \n", motivation.getId(), motivation.getSource().substring(0, 5) + "...", motivation.getBody());
continue;
}
System.out.printf(" %d // %s // %s \n", motivation.getId(), motivation.getSource(), motivation.getBody());
}
}
public void delete(String cmd) {
System.out.println("delete 실행");
}
}
를 이렇게 짰다.
public void delete(Rq rq) {
System.out.println("delete 실행");
int id;
try {
id = Integer.parseInt(rq.getParams("id"));
}
catch (NumberFormatException e) {
System.out.println("정수 입력 오류");
return;
}
Motivation motivation = findById(id);
if (motivation == null) {
System.out.printf("%d번 motivation은 없어\n", id);
return;
}
motivations.remove(motivation);
System.out.printf("%d번 motivation을 삭제\n", id);
}
private Motivation findById(int id) {
for (Motivation motivation : motivations) {
if (motivation.getId() == id) {
return motivation;
}
}
return null;
}
}
이후 삭제 기능을 구현했다. 기능을 구현하면서 안되는 경우에 대한 것에 더 많이 시간을 쏟았다. 아마 에러가 발생할 수 있는 경우를 최대한 줄이기 위해서 이런 부분에 대해 더 신경써서 구현해야된다고 말씀하시는 것 같다.
수정 기능 구현!
case "edit":
motivationController.edit(rq);
break;
App클래스의 swich문에 해당 기능을 넣어주고
public void edit(Rq rq) {
System.out.println("edit 실행");
int id;
try {
id = Integer.parseInt(rq.getParams("id"));
} catch (NumberFormatException e) {
System.out.println("정수 입력 오류");
return;
}
Motivation motivation = findById(id);
if (motivation == null) {
System.out.printf("%d번 motivation은 없어\n", id);
return;
}
System.out.println("body(기존): "+motivation.getBody());
System.out.println("source(기존): "+motivation.getSource());
System.out.print("새로운 motivation 내용: ");
String newBody = Container.getScanner().nextLine();
System.out.print("새로운 source 내용: ");
String newSource = Container.getScanner().nextLine();
// 업데이트
motivation.setBody(newBody);
motivation.setSource(newSource);
System.out.printf("%d번 motivation을 수정\n", id);
}
이후에 MotivationController클래스에 해당 기능이 실행될 메서드를 선언하고 실행될 수 있게끔 코드를 만들어서 넣었다.
swich문을 처음 써서 익숙하지 않았다. break를 쓰지 않아 default의 출력문이 함께 나온 다는 것을 알고 break를 추가해줬다.
이렇게 CRUD를 다 구현해 보았고, 직접 하나하나 단계를 거쳐가며 연습해보기 위해서 강사님의 깃 리포지토리를 가져와서 할 수 있는 연습 팁을 배웠고 그대로 한번 해 볼 예정이다.
터미널을 열고 프로젝트들이 있는 폴더로 cd를 사용해 접근, git clone (강사님의 git url) 이후, 다시 cd로 해당 폴더 접근, git checkout (접근할 과거 리포지토리 복붙) 하면 코드가 내가 지정한 과거로 간다. 거기서 다시 목표에 대한 코드를 짜 보면 수월하게 연습할 수 있다.
그리고 이 프로그램의 문제점을 하나 말 해보자면 우리가 아무리 데이터를 저장한다고 한들 ArrayList에 저장하기 때문에(전역변수) 프로그램을 끄면 데이터는 날라간다. 이제 DB에 대해서 공부할 때가 왔을거다. 이 문제는 DB를 배우면서 해결하지 않을까 싶다.
각 클래스에 대해서 설명하시면서 하신 말이다. main 메서드는 모듈로 보면 되는데 응집도는 높게 결합도는 낮게 설정하는게 좋다고 하셨다. 통신을 최소화 하자라는 개념. M.C(MotivationController)에서 직접 데이터를 저장하고 있는데 이는 프로그램이 종료되면 유실된다. M.C는 DB와 연결되어야 한다. 그래서 구조 변경이 들어갈 것이라고 예고하셨다.
Main, App, Controller(motivation, system), Service(motivation, system),DAO(Repository), DB. 여기서 명령어나 객체가 왔다 갔다 하는 것이 Dto. 이를 MVC 구조(패턴)이라 한다.
핵심기능을 service 클래스에 넘겨주었다.
public class MotivationService {
private int lastId;
private List<Motivation> motivations;
public MotivationService() {
lastId = 0;
motivations = new ArrayList<>();
}
// 입력된 id와 일치하는 motivation 찾기
private Motivation findById(int id) {
for (Motivation motivation : motivations) {
if (motivation.getId() == id) {
return motivation;
}
}
return null;
}
public int doAdd(String body, String source) {
int id = lastId + 1;
Motivation motivation = new Motivation(id, body, source);
motivations.add(motivation);
lastId = id; // 방금 만들어진 새 motivation의 id로 갱신
return id;
}
public void showList() {
if (motivations.size() == 0) {
System.out.println("등록된 motivation 없음");
return;
}
System.out.println("== motivation list ==");
System.out.printf(" id // source // body \n");
System.out.println("=".repeat(35));
for (int i = motivations.size() - 1; i >= 0; i--) {
Motivation motivation = motivations.get(i);
if (motivation.getSource().length() > 7) {
System.out.printf(" %d // %s // %s \n", motivation.getId(), motivation.getSource().substring(0, 5) + "...", motivation.getBody());
continue;
}
System.out.printf(" %d // %s // %s \n", motivation.getId(), motivation.getSource(), motivation.getBody());
}
}
public int doDelete(Rq rq) {
int id;
try {
id = Integer.parseInt(rq.getParams("id"));
} catch (NumberFormatException e) {
return -1;
}
Motivation motivation = findById(id);
if (motivation == null) {
return -2;
}
motivations.remove(motivation);
return id;
}
public int doEdit(Rq rq) {
int id;
try {
id = Integer.parseInt(rq.getParams("id"));
} catch (NumberFormatException e) {
return -1;
}
Motivation motivation = findById(id);
if (motivation == null) {
return -2;
}
// 불러온 motivation의 인스턴스변수에 접근
System.out.println("body(기존) : " + motivation.getBody());
System.out.println("source(기존) : " + motivation.getSource());
System.out.print("body : ");
String body = Container.getScanner().nextLine();
System.out.print("source : ");
String source = Container.getScanner().nextLine();
// 불러온 motivation의 인스턴스변수 수정
motivation.setBody(body);
motivation.setSource(source);
return id;
}
}
코드들이 이사하면서 MotivationController의 코드도 약간씩 수정이 필요했다.
public class MotivationController {
private MotivationService motivationService;
public MotivationController() {
motivationService = new MotivationService();
}
public void add() {
System.out.print("body : ");
String body = Container.getScanner().nextLine();
System.out.print("source : ");
String source = Container.getScanner().nextLine();
int id = motivationService.doAdd(body, source);
System.out.printf("%d번 motivation이 등록 되었습니다\n", id);
}
public void list() {
motivationService.showList();
}
public void delete(Rq rq) {
System.out.println("delete 실행");
int id = motivationService.doDelete(rq);
if (id == -1) {
System.out.println("정수 입력 오류");
return;
} else if (id == -2) {
System.out.printf("해당 motivation은 없어\n");
return;
}
System.out.printf("%d번 motivation을 삭제했습니다\n", id);
}
public void edit(Rq rq) {
System.out.println("edit 실행");
int id = motivationService.doEdit(rq);
if (id == -1) {
System.out.println("정수 입력 오류");
return;
} else if (id == -2) {
System.out.printf("해당 motivation은 없어\n");
return;
}
System.out.printf("%d번 motivation을 수정했습니다\n", id);
}
}
이렇게 일단은 구조를 이해하는 것이 먼저인 것 같다. 나중에 다시 공부할 때는 구조파악과 문제풀이나 MVC에 대해 공부 할 예정이다.
다음 시간에는 DB와 함께 프로젝트에 연결하는 것을 배우거나 아니면 Service Class를 추가 해서 로그인이나 아웃 기능을 배우지 않을까 싶은데 아마 DB가 아닐까 조심스레 짐작해본다.
'Coding History' 카테고리의 다른 글
2024. 07 .01 문제풀이 (0) | 2024.07.01 |
---|---|
MVC 패턴이란? (0) | 2024.07.01 |
국비 지원 IT(웹앱개발) 취업반 강의 17일차 (motivation 앱 만들기) (1) | 2024.06.30 |
2024. 06. 27. 시험 문제 풀이 (0) | 2024.06.27 |
국비 지원 IT(웹앱개발) 취업반 강의 16일차 (다항식 계산기 만들기 과정) (0) | 2024.06.27 |