# 개요

학원 다니면서 개인 프로젝트로 플래너 개발을 해보기로 했다.

처음에는 학원에서 배운 내용 + 내가 알고 있었는데 잊었던 것들 + 이번 개인 프로젝트로 새로 배우고 싶었던 것들 세 가지를 적절히 섞어 구현할 예정이다.

수업 내용을 기반으로 Front 화면 구현을 진행했다.

 


 

# 화면 설계

처음에는 내가 SPA(Single Page Application)에 대한 이해가 없었기 때문에 모든 기능에 대한 페이지가 구현되어야 한다고 생각했다. 그래서

  • User → 회원 가입 페이지, 로그인 페이지, 회원 정보 조회 페이지, 회원 정보 수정 페이지 등
  • Plan → 플랜 작성 페이지, 플랜 조회 페이지, 플랜 수정 페이지 등
  • DetailPlan → 디테일 작성 페이지, 디테일 조회 페이지, 디테일 수정 페이지 등

모든 페이지를 구현해야 한다고 생각했고 그에 따라 html 파일을 작성했다.

디자인이나 UI는 최대한 Bootstrap을 사용하려고 했다.

 

HTML 파일 추가 · JinHoooooou/Planner@d34b6e1

JinHoooooou committed Feb 28, 2024

github.com

각 화면에 대한 와이어 프레임을 그려보았다.

 

Figma

Created with FigJam

www.figma.com

 


 

# 클라이언트 - 서버 아키텍쳐 설계

서버의 데이터를 클라이언트로 어떻게 전달할까?를 생각해봤다.

  • 처음 계획은 JSP를 이용하여 서버 사이드 렌더링을 구현하려고 했다. 하지만 JSP는 내가 매우 싫어하는 구조이다. JSTL이 있다고는 하지만 그럴바에는 Thymeleaf나 Mustache같은 Template Engine을 사용하는게 더 낫겠다라는 생각이 들었다.
  • 그래서 다음 계획은 Thymeleaf를 사용하는 것이었다. 하지만 Thymeleaf를 위한 라이브러리를 추가해야 했고, 스프링 프레임워크를 사용하지 않았기 때문에 Embedded Tomcat에서 설정 추가해야하는 부분이 다소 어려웠다. 또한 팀원들도 Template Engine에 대해 모를 것이기 때문에 이를 설명하는 것도 문제가 될 것 같았다.
  • 마지막 계획은 ajax를 이용하는 것이었다. ajax는 수업 때 배우기도 했고, 서버와 클라이언트를 완전히 분리하여 클라이언트 사이드 렌더링을 구현하는 것이 내 개인 학습에도 도움이 되고 코드 관리하기도 더 편할 것 같다는 생각이 들었다.

그리하여 서버 - 클라이언트 아키텍쳐 구조를 다음과 같이 설계했다.

  1. 클라이언트에서 ajax 통신으로 서버에 요청을 보낸다.
  2. 서버에서는 클라이언트 요청에 대한 응답으로 JSON 형식의 데이터만 응답한다.
  3. 클라이언트는 서버의 응답 JSON 데이터를 통해 동적으로 html 요소를 렌더링하거나 다른 페이지로 이동한다. 

이 설계를 팀원들에게 얘기했는데 당시에는 서버 - 클라이언트 구조에 대한 이해를 잘 못하고 있었기 때문에 내 계획이 무슨 말인지 잘 이해하지 못한 상태에서 그냥 따랐던 것 같다.

 


 

# 화면 구현

3명의 팀원이서 각 User, Plan, Detail에 대한 화면 및 UI를 구현하기로 했는데, 나는 Detail 영역을 구현하기로 했다. 

Plan에 대한 화면 구현 설계가 나왔고 그에 따라 Detail 화면을 다음과 같이 구현해야겠다고 생각했다.

 

Figma

Created with FigJam

www.figma.com

구현 과정에서 Bootstrap 공식문서도 엄청 많이 찾아보고 Chat GPT에게도 엄청 많이 물어봤다.

지금 와서 생각해보면 기능 구현보다는 화면 배치 때문에 계속 찾아보고 검색한 것들이라 여기 정리할 의미가 없는 것 같다..

그래서 발표했던 최종 결과물 화면들중 메인 페이지로 대체한다.

팀원이 구현한 화면이다. 여기서 PlanList중 하나의 제목(빨간 박스 부분)을 클릭하면 Offcanvas영역이 나오며 내가 구현한 Detail 영역이 나온다.

내가 구현한 Offcanvas 영역이다. 부트스트랩의 Offcanvas Component를 사용했으며 대부분 UI 구현은 부트스트랩 docs를 참고하여 만들었다. (빨간 부분)

메인 페이지의 Plan의 정보를 담고 있으며 해당 부분은 input 요소로 이루어져 있어 수정할 수 있다. input 요소의 내용을 수정 후 저장 버튼을 누르면 수정한 내용으로 Plan 데이터가 변경된다. (파란 부분)

"디테일 추가" 버튼을 누르면 위 사진처럼 버튼 아래로 Detail Create Form이 나온다. form에 대한 input들을 입력 후 "생성" 버튼을 누르면 아래 쪽에 Detail 리스트가 추가된다.

추가한 Detail 만큼 List가 추가된다. 완료 체크를 누르면 위쪽의 달성률 bar에 반영이 된다.

List중 하나의 element를 클릭하면 위와 같이 확장되어 보여준다.

확장되어 보여주는 영역은 input 영역이라 수정할 수 있다. 수정 후 다시 눌러 확장 영역을 닫으면 수정한 내용으로 변경된다.

휴지통 아이콘을 누르면 삭제할 수 있다.

 

# 간단 회고

어쨌든 서버에 요청을 보내고 받아온 응답 데이터를 통해 화면을 렌더링하거나 다른 페이지로 이동해야하기 때문에, 화면 구현에 대한 HTML/CSS/JS 파일을 다 작성하고 나서 서블릿 코드를 작성한 것이 아니고 병행 작업 했다.

그 과정을 상세하게 작성하고 싶었지만 막상 글을 쓰려니 정리도 잘 안되고, 기억도 잘 안나고, 크게 기능적인 구현을 한 것이 없다. 그래서 결과 화면만 포스팅 했다.

서블릿 코드 작성하는 글에서는 서블릿 코드 작성하면서 생각했던 점과 구현한 화면 및 기능을 최대한 상세하게 적어볼 예정이다.

진짜 화면 구현하기 위해 부트스트랩 Docs를 많이 참고하고 삽질을 많이 한 것 치고는 정리할 내용이 없어서 아쉽다.

# 개요

학원 다니면서 개인 프로젝트로 플래너 개발을 해보기로 했다.

처음에는 학원에서 배운 내용 + 내가 알고 있었는데 잊었던 것들 + 이번 개인 프로젝트로 새로 배우고 싶었던 것들 세 가지를 적절히 섞어 구현할 예정이다.

설계 했던 테이블을 기반으로 DAO 클래스를 작성했다.

 


 

# 테이블 - VO 매핑

VO 클래스 작성을 위해 회의를 다시 진행했다. 그 때 각 테이블에 당장 사용하지 않은 속성이 많기 때문에 일단은 최소한으로 테이블을 설계하고 이후에 수정하는 방식으로 가자고 강하게 어필했다. 팀원들은 잘 이해하지 못했지만 일단 따르기로 했던것 같다. 

 

ERD | Notion

SQL

tin-digit-d17.notion.site

테이블에 따라 각자 VO 클래스들을 작성했다. 각자 작성한 VO클래스를 보며 리뷰하고 리팩토링하여 최종본을 확정짓기로 했다.

 

Planner/src/main/java/com/kh/model/vo at 6f774f66ec005fe1edfe6b89ba594cf2f1aeef70 · JinHoooooou/Planner

Mini Timer For Semi Project. Contribute to JinHoooooou/Planner development by creating an account on GitHub.

github.com

사실 VO 클래스라 리팩토링 할 것은 없었다.

 


 

# DAO클래스 작성 (CRUD)

각 Plan, User, DetailPlan에 따른 DAO 클래스 그리고 그 중 가장 기본적인 CRUD메서드는 수업시간에 다 배웠다고 생각해서 각자 하나씩 맡아서 작성해보기로 했다.

내가 User 객체를 맡고, 두명씩 짝지어서 Plan과 DetailPlan을 맡아서 작성하기로 했는데, DetailPlan 작성하기로 한 두 명 중 한 명은 나가고 한 명은 잘 따라오질 못해서 일단 User와 Plan 객체만 작성됐다.

 

Planner/src/main/java/com/kh/model/dao/UserDao.java at 78bdc96406ca17a77e06c537feb06ebc3cc75319 · JinHoooooou/Planner

Mini Timer For Semi Project. Contribute to JinHoooooou/Planner development by creating an account on GitHub.

github.com

내가 작성한 UserDao이고 Create, Read (One/All) 메서드 밖에 없는데, 이것을 참고해서 작성하라고 했다.

 

Planner/src/main/java/com/kh/model/dao/PlanDao_Minseok.java at f73a0df8af32f181ff9cbeca255b10da27584deb · JinHoooooou/Planner

Mini Timer For Semi Project. Contribute to JinHoooooou/Planner development by creating an account on GitHub.

github.com

 

 

Planner/src/main/java/com/kh/model/dao/PlanDao_Sim.java at 6e876137389f505eb6186f4f728ab7cb2b8c50b0 · JinHoooooou/Planner

Mini Timer For Semi Project. Contribute to JinHoooooou/Planner development by creating an account on GitHub.

github.com

 


 

# DAO 클래스 리팩토링

내가 작성한 UserDAO와 팀원들이 작성한 PlanDAO 클래스를 책(자바 웹 프로그래밍 Next Step)을 참고하여 리팩토링을 진행했다. DetailPlan까지 작성 후 할까 리팩토링을 진행할까 생각했지만, 먼저 리팩토링을 하면 그 구조에 맞게 DetailPlan도 작성하는게 더 편할 것 같고 코드 리뷰할 때 클린코드, 리팩토링 관련 설명을 해주고 싶어서 회의 전에 미리 했다.

 

## JDBC Template 리팩토링

  1. 기존의 JdbcTemplate에서 DB와 연결하는 클래스를 작성했었는데, 이를 ConnectionManager로 옮겼다.
  2. DAO 클래스를 잘 보면 각 메서드마다 공통적으로 작성되는 부분과 메서드마다 다르게 작성되는 부분이 있다.
    • 쿼리를 작성하고 그 쿼리에 맞게 변수들을 setting하는 부분은 메서드마다 다르게 작성되는 부분이다.
    • ConnectionManager를 통해 Connection 인스턴스를 가져오고 그 Connection 인스턴스를 통해 Statement 인스턴스를 가져오고 Statement의 executeUpdate()나 executeQuery()를 호출하는 부분은 공통적으로 작성되는 부분이다.
    • 이에 따라 JdbcTemplate에 INSERT, UPDATE, DELETE 쿼리를 담당하는 executeUpdate() 메서드와 SELECT 쿼리를 담당하는 executeQuery(), executeQueryForOne()을 작성했다.

3. SQLException 대신 RuntimeException을 상속받는 DataAccessException을 추가했다.

 

Jdbc 템플릿 추가 · JinHoooooou/Planner@3358d07

JinHoooooou committed Feb 28, 2024

github.com

 

## UserDAO, PlanDAO 리팩토링

DAO의 CRUD 메서드들을 위에서 리팩토링한 JdbcTemplate을 사용하는 것으로 리팩토링했다.

 

User 관련 코드 리팩토링 · JinHoooooou/Planner@d1943f3

JinHoooooou committed Feb 28, 2024

github.com

 

 

Plan 관련 코드 리팩토링 · JinHoooooou/Planner@fb31201

JinHoooooou committed Feb 28, 2024

github.com

 


 

# 코드 리뷰

내가 리팩토링한 코드를 토대로 코드 리뷰를 진행했다...만 다들 잘 이해를 못했던것 같다. 제네릭을 사용한 것, ResultMap을 이용해서 테이블 → VO로 파싱하는 부분을 RowMap이라는 인터페이스를 통해 해결한 것 등 어려워 했다. 그래서 "나중에 더 배우게 되면 이해할 수 있으니 지금은 테스트 코드와 리팩토링을 통해 이런식으로 더 깔끔하게 코드를 작성할 수 있고, 이전 코드보다 가독성이 더 좋아졌다고 느낀다면 오케이다." 라고 설명했다.

내가 팀원들보다 쪼오오오오금 더 안다고 "내가 작성한 코드가 옳다." 라고 설명하고 싶진 않았는데, 설명하다보니 그렇게 된 것 같아 아쉬웠다;

 


 

# DetailPlan VO, DAO 클래스 작성

리팩토링하여 구조화 된 DAO 클래스에 따라 DetailPlan에 대한 VO, DAO 클래스도 쉽게 작성할 수 있었다.

 

PlanDao 리팩토링 · JinHoooooou/Planner@316e1be

JinHoooooou committed Feb 29, 2024

github.com

커밋 메시지 잘못 작성함...

 


 

# 간단 회고

지금 블로그 글을 작성하면서 가장 크게 후회하는 것은 역시 commit 메시지 관련이다.

  • 팀원들이 git, github의 기본 flow? (코드 작성 → git add → git commit으로 커밋 메시지 작성 → git push로 remote에 push)도 이해하기 어려워 했어서 branch를 구분하여 작업하고 PR을 통해 Merge하는 방식을 알려주면 과부하가 올 것 같았다.
  • 그래서 organization에 Repo를 하나 생성하고 각 개인 Repo로 fork하게 했고, fork한 Repo에서 작업 후 push하여 main 브랜치에서 main 브랜치로의 PR을 하도록 했다.
  • 문제는 팀원들이 commit 메시지를 전혀 작성하지 않아서 팀장인 나의 입장에서는 이해하기가 매우 어려웠다. 그렇다고 팀원들한테 "commit 메시지 다시 작성해서 PR 주세요"라고 하기에도 좀 그래서 그냥 '내가 감수하자'라는 마인드로 일일이 다 보기로 했다.
  • 지금 시간이 지나고 나서 커밋들을 보는데 가관이다 ㅋㅋ... 물론 나도 이상하게 작성한게 많긴 하다. 다음 파이널 프로젝트 때는 이런것을 미리 정하고 하는게 나을 것 같다..

 

# 개요

학원 다니면서 개인 프로젝트로 플래너 개발을 해보기로 했다.

처음에는 타이머로 시작했다가 내가 생각했던 방향이 아닌거 같아서 플래너로 바꿨다.

처음에 타이머로 생각한 이유는 단순히 뽀모도로 타이머(https://chromewebstore.google.com/detail/focus-to-do-%EB%BD%80%EB%AA%A8%EB%8F%84%EB%A1%9C-%ED%83%80%EC%9D%B4%EB%A8%B8-+-%EC%97%85%EB%AC%B4/ngceodoilcgpmkijopinlkmohnfifjfb?pli=1)를 개발하려고 했기 때문인데.. 코드 작성하다보니 타이머가 돌아가는 로직은 프론트에서 간단하게 처리 할 수 있다고 생각했기 때문이다.

원래는 프로젝트 개발진행하면서 블로그도 같이 쓰려고했는데, 개발 중간에서야 작성하게 됐다 ㅎㅎ;

 


# CRUD의 R(Read) 기능 추가

Read one / Read All에 대한 View 클래스 추가와 Controller에 메서드를 추가했다

Read One기능은 TimerController의 필드인 List의 Index 기반으로 했다. 나중에 데이터베이스 추가되면 Primary Key인 Id로 조회하거나 Front에 해당 객체 선택할 수 있도록 구현할 수 있겠지?

리팩토링을 위해 Create와 마찬가지로 Read기능에 대한 View와 Controller 테스트 코드를 작성했다. 

https://github.com/JinHoooooou/MiniTimer/commit/8b7c49d965d0a6cf870733550104a31537bb6650

 

TimerController의 필드인 List에 대한 캡슐화가 제대로 이루어진것 같지 않아서 isEmpty(), size()메서드를 따로 작성했다.

https://github.com/JinHoooooou/MiniTimer/commit/b104fe410aa618d69b0ed644c7780ebafd2f7aa2

 


# CRUD의 U(Update) 기능 추가

Update에 대한 View 클래스와 Controller에 메서드를 추가했다.

https://github.com/JinHoooooou/MiniTimer/commit/a03e217edf16138eedf27ccdded28b94a82d1293

 


# CRUD의 D(Delete) 기능 추가

Delete에 대한 View 클래스와 Controller에 메서드를 추가했다.

https://github.com/JinHoooooou/MiniTimer/commit/d37bd9a292c2eee7898cd19eac4b18228e77399d

https://github.com/JinHoooooou/MiniTimer/commit/e63dcb2ab82b5ef598241e522f4617fb258b5bd3

 


# 간단 회고

기능을 추가할 때 마다 테스트 코드를 작성했다. TDD로 구현한 것 까지는 아니지만, 테스트가 있어야 공격적인 리팩토링이 가능하기 때문에 작성했다.

근데 작성할 수록 View에 대한 테스트 코드를 작성하는것에 대해 회의감을 느꼈다. '이렇게 까지 View 테스트 코드를 작성해야 하나?" 라는 생각이 들었다. 그도 그럴 것이, View 부분은 비즈니스 로직 없이 단순히 콘솔에 보여주는 화면만 구성하는데 이것을 위한 테스트 코드까지 작성하는 것은 시간 낭비라는 생각이 계속 나를 지배했다. 그럼에도 불구하고 '테스트 코드 작성 연습하고 공부하자'라는 생각으로 작성했다.

Controller에서 getList()메서드로 List필드를 가져오고, 그 List에대한 메서드를 호출하는 것 보다. Controller에게 물어보는 메서드를 작성하는것이 더 올바른 캡슐화라고 생각해서 size(), isEmpty() 메서드를 작성했다. 근데 지금와서 보니 이 메서드들은 테스트 코드에서만 사용하는 메서드가 돼버려서 잘 못 작성한것 같다.

# 개요

학원 다니면서 개인 프로젝트로 플래너 개발을 해보기로 했다.

처음에는 타이머로 시작했다가 내가 생각했던 방향이 아닌거 같아서 플래너로 바꿨다.

처음에 타이머로 생각한 이유는 단순히 뽀모도로 타이머(https://chromewebstore.google.com/detail/focus-to-do-%EB%BD%80%EB%AA%A8%EB%8F%84%EB%A1%9C-%ED%83%80%EC%9D%B4%EB%A8%B8-+-%EC%97%85%EB%AC%B4/ngceodoilcgpmkijopinlkmohnfifjfb?pli=1)를 개발하려고 했기 때문인데.. 코드 작성하다보니 타이머가 돌아가는 로직은 프론트에서 간단하게 처리 할 수 있다고 생각했기 때문이다.

원래는 프로젝트 개발진행하면서 블로그도 같이 쓰려고했는데, 개발 중간에서야 작성하게 됐다 ㅎㅎ;

 


# 타이머 개발

기본적으로 학원 수업 진행 방향을 따라서 개발을 진행했고 거기에 추가적으로 내가 공부하고싶은 부분을 적용시켰다.

객체(Timer)에 대한 필드 설계를 하고 콘솔창에 View를 출력하고 사용자 입력 (Scanner)을 통해 실행되는 어플리케이션을 만드는 것으로 시작했다.

뽀모도로 타이머 어플을 보고 처음 설계한 것은 Timer 객체에 Hour, Minute, Second가 있고 Title과 Memo가 있어서 해당 요소들을 필드로 추가했다.

MainView에서 Timer 생성, 조회, 수정, 삭제 (CRUD)에 대한 View들을 사용자 입력으로 받고 그 기능들은 TimerController의 메서드로 구현했다.

처음에는 command 입력에 대한 기능들을 분기처리로 구현했다가 다형성을 이용하여 클래스를 분리하여 구현했다.

https://github.com/JinHoooooou/MiniTimer/commit/f3135fcb067d836ef2b534a3909c484dd39582e4

 

각 View에 대한 출력을 어플리케이션 실행이 아닌 단위 테스트 실행으로 확인할 수 있게 테스트 코드를 사용했다.

https://github.com/JinHoooooou/MiniTimer/commit/75d45a5b7524028afc3dc15f5a21e326c4b281ba 

 


# CRUD의 C(Create) 기능 추가

그리고 Create Timer에 대한 테스트 코드, View, Controller를 구현했다. 각 객체에 대한 저장은 아직 데이터베이스 연결을 하지 않았기 때문에 Controller에 있는 List에 저장하도록 구현했다.

https://github.com/JinHoooooou/MiniTimer/commit/eb696ee727a3b18035f14e2b03c6dbd7dfee3234

https://github.com/JinHoooooou/MiniTimer/commit/554c813326b17bd83210f36429371bdf8533ecca

 


# 간단 회고

지금 블로그에 작성하는건 커밋한지 약 1~2개월 후에 작성하는 것인데... 커밋 메시지 내용이 너무 개판이고 커밋 파일들도 중구난방이라 확인하는 것이 너무 힘들다..

아무리 혼자 하는 개발이라고 하지만 커밋 정리를 잘 하자.. 앞으로 한동안 커밋 개판인거 블로그에 옮기려니 벌써 어지럽다... ㅜㅜ

+ Recent posts