죽음의 프로젝트 주제 💀
진행 기간 2022-11-25~2022-12-07 (저는 구체적인 프로젝트 설명이 진행되기 이전에 제 실력을 기르고자 먼저 시작했고, 2일 동안 진행했습니다.)
고객 분류 프로그램 개발 💡
스토어 사장님들을 위한 고객을 이용시간 별로 분류하고 나눠서 원하는 정보대로 정렬 / 출력 할 수 있는 프로그램을 개발한다.
유의사항 💡
- 컬렉션 프레임워크 사용 금지 (단 Arrays 는 허용) 🚫 List,Set,Map 등에 대한 API를 직접 구현해야 한다.
- 고객별로 이용시간, 이용금액에 따라서 분류가 가능해야 한다.
- 분류된 데이터를 이름순, 이용금액, 이용시간 기반으로 정렬이 가능해야 한다.
- 고객의 정보를 추가,수정,삭제,조회가 가능해야 한다.
- 분류 기준을 수정,초기화 할 수 있어야 하고 수정될 시에 수정된 기준으로 다시 분류가 가능해야 한다.
- 객체지향 성격이 잘 드러날 수 있도록 클래스를 설계해야 한다.
클래스 설계 🧑🏻💻
(클래스 관계는 기존에 프로젝트 정의서에 있던 내용들을 참고하였으며, 역할은 제 맘대로 지정했습니다.)
- Customer 도메인 (고객 정보에 대한 모든것을 담당한다.) 🧑🏻💻
- Customer : 고객들의 정보를 담고있는 객체를 생성하기 위한 클래스
- 객체 생성자, 기본적인 Getter, Setter 그리고 자바빈 규약을 따른 멤버변수를 선언했습니다.
- Customers : Customer 객체를 저장하고 관리하기위한 클래스
- Customer 객체를 생성, 수정, 조회, 삭제 하기 위한 배열을 갖고 있고, 그룹 기준별로 등급을 설정, 등급별로 그룹을 나눠서 리턴해주는 기능까지 구현했습니다.
- ClassifiedCustomers : 등급별로 분류된 Customer 를 정렬하고 출력하기 위한 클래스
- 기존의 ClassifiedCustomers 는 분류된 고객들 이라는 뜻을 갖고있기 때문에 분류된 고객리스트가 관리를 하는것은 이상하지 않을까? 라는 생각으로 Customer 가 해당 그룹핑을 관리하도록 역할을 정했습니다.
- Customer : 고객들의 정보를 담고있는 객체를 생성하기 위한 클래스
- Group 도메인 🧑🏻💻
- Group : Customer과 마찬가지로 그룹에대한 정보를 담고있는 객체를 생성하기 위한 클래스
- Enum타입인 GroupType 과 , 분류 기준인 Parameter 를 멤버변수로 갖고 있습니다.
- GroupType : Enum타입으로 분류가 안된 NONE 타입, 일반 등급인 GENERAL, 그리고 VIP, VVIP 이렇게 4개의 타입이 정의되어있다.
- Parameter : 그룹 기준을 설정할 객체로, 멤버변수로는 기준이 될 이용시간과 이용금액이 선언되어있고, 해당 기준을 초과하면 해당 등급이 된다.
- Groups : Group을 담을 배열을 갖고있는 관리 클래스. 등급을 분류할 그룹은 이미 정해져있고, 초기화가 되지 않아서 각각 0시간과 0만원의 파라미터를 담고있는 그룹이 4개가 배열에 들어가있다.
- Group : Customer과 마찬가지로 그룹에대한 정보를 담고있는 객체를 생성하기 위한 클래스
- Menu 도메인 🧑🏻💻
- Menu 인터페이스 : 각각 메뉴가 기본적으로 가지고 있는 행동들을 메소드로 선언한다.
- Menu를 구현한 구현클래스 : 기본적인 첫 메뉴를 담당할 클래스
- CustomerMenu : Customer에 대한 입력 유도문과, Customer 객체를 생성하기 전 데이터 검증과, 비즈니스 검증까지 담당한다.
- GroupMenu : 등급에 대한 기준 초기화, 수정, 조회 를 입력유도문을 통해 원하는 등급을 입력받고 해당 결과물을 출력해준다.
- SummaryMenu : 분류된 등급에 따라 고객 명단을 출력해주고, 원하는 데이터에 따라 오름차순/내림차순 으로 목록을 출력해준다.
- Menu 인터페이스 : 각각 메뉴가 기본적으로 가지고 있는 행동들을 메소드로 선언한다.
본격 코딩 들어가기 🤦🏻♂️
해당 토이프로젝트 주제는 무엇보다 객체지향 설계와 프로그래밍이 가장 큰 주제였다고 생각했습니다.
그리고 +@로 싱글톤으로 관리할 수 있는 객체들은 싱글톤 패턴으로 관리해, 스프링에서 자주나오는 객체 주입까지 체험해볼 수 있는 아주 유익한 프로젝트라 생각했습니다.
Customer 도메인 🗓️
일단 해당 클래스가 어떠한 역할을 갖고 있는지, 그리고 어떠한 부분까지 담당할 수 있을지를 처음에 고민을 많이 했습니다.
저는 일단 객체를 만들기 위한 Customer 클래스는 단순히 Customer객체에 대한 설계도 라는 역할만 갖고 있길 바랐으며,
Customers 클래스는 해당 객체! 만 관리! 하는 클래스 정도의 역할만 갖고 있길 바랐습니다.
(기본적인 ~~를 입력해주세요. 라는 부분은 Menu에서 담당하고, 말 그대로 등록/조회/수정/삭제에서 조금 더 나아가 해당 클래스에서 분류/ 그룹핑, 그리고 직접 API를 구현해야 했기 때문에 그에 따른 배열 관리 메서드 (trimToSize 나 더블링) 정도까지만 갖고 있도록 설계했습니다.)
ClassifiedCustomers 클래스에 대한 생각이 꽤 많았는데요, 강사님께서 제시해주신 클래스 다이어그램에서는
싱글톤으로 관리해야할 Customers를 배열에 담고 계셨었습니다.
최대한 주어진 설계도에 맞춰서 작업하는 게 추후에 제가 서비스를 제작해주길 원하는 고객이 기본적인 설계도를 가져온다면, 그거에 최대한 맞게 작업하는게 맞다고 생각을 했었습니다.
고민되었던 문제.. 🫠
- Customers 객체를 싱글톤으로 관리하는데 (왜냐하면 하나의 리스트로 관리하고 싶기 때문에) 해당 객체를 배열에 저장할 수 있을까?
- 해당 부분은 아직 해결되지 않을 상태였습니다. 하지만 제 나름대로 생각을 좀 해서 2차원 배열로 그룹핑을 해 ClassifiedCustomers에 해당 배열을 저장하도록 진행했습니다. 🥹
- Customer객체를 생성하기 위한 검증을 Customers에서 진행해야 할까 아니면 Menu에서 진행해야 할까?
- 담당 역할에 대해서 고민이 많았습니다. 스프링으로 따지자면 Customers는 Repository 같은 경우였고, menu는 service계층 이라고 생각했기때문에 Menu에서 검증을 진행하도록 설계했습니다. 😇
- ClassifiedCustomers의 존재이유 .. 🤔
- 이미 Customer 객체가 Group이라는 변수를 갖고있는데 그걸 또 다시 나눠야하는 이유가 궁금했습니다.
- 설계는 알아서 해도 되기때문에 1차적으로는 ClassifiedCustomers 클래스를 생성하지 않고, 그룹별 정렬/출력을 해당 클래스에서 담당하도록 설계했습니다.
- 하지만 추후에 생각을 해보니 이 방식은 매 그룹마다 처음부터 끝까지 탐색을 하며 값을 골라서 출력하도록 하는 로직이었기 때문에 비효율적이라 생각해 Classified 클래스를 생성해 배열을 전달받도록 구현했습니다.
Group 도메인 👨👩👧👧
해당 도메인은 하나하나가 큰 역할을 갖고 있다기보다는Customer가 필요하기 때문에 있어야 하는 Class 정도로 다가왔습니다.
단순히 Group은 분류 기준과 타입(어떻게 보면 그룹 이름)만 갖고 있기 때문에, 해당 Group들을 관리할 Groups 클래스는 Customers처럼 조회, 삭제, 수정, 추가를 구현하지 않고 이미 있는 그룹의 기준을 초기화, 수정, 조회만 가능하도록 구현했습니다. 그리고 별 차이 없이 프로젝트 정의서 속 설계도와 거의 일치하게 설계했습니다.
Group 클래스는 단순히 해당 그룹을 정의할 Enum타입 GroupType과, Parameter를 멤버 변수로 갖고 있습니다.
Parameter는 고객들을 분류하기 위한 기준을 담고 있습니다. 기준 이용금액, 기준 이용시간을 멤버 변수로 갖고 있으며 해당 기준을 초과하면 해당 등급의 멤버가 됩니다.
Groups 클래스는 Customers와 같이 Group을 관리할 클래스로서, 해당 객체가 생성될 때 생성자는 각 배열 인덱스에 0,0 값(이용 기준)을 담고 있는 깡통 그룹 객체를 담고 생성하도록 해줍니다.
고민되었던 문제..🫠
- Group에 대한 역할이 애매모호 했습니다. 이것으로 고객을 담아야할지? 아니면 단순히 그룹타입을 Customer 클래스에 선언해서 해당 Enum으로만 관리하고 다른 설계도만 Group에 담아도 되지않을까.. 하는 고민이 있었으나, 단순 이름표 같은거라고 생각하고 최대한 설계도에 맞게 설계했습니다.
Menu 도메인 🖱️
Menu도메인은 각각 클래스별로 실행하는 행동들은 같지만, 그 내용물은 다를 것이기 때문에 Menu인터페이스를 구현하도록 설계했습니다.
Menu 인터페이스는 기본적인 메뉴를 출력하는 ShowMenu와, 해당 메뉴를 골랐을 때 나올 반응인 SelectMenu 메서드를 선언했고,
나머지 메뉴 클래스들은 해당 역할에 맞도록 재정의해서 메서드를 선언해줬습니다.
첫 번째 화면이 될 MenuImpl 클래스는 어떻게 보면 다른 메뉴들의 조상이라고 할 수 있어서 부모 클래스로 생성할지를 고민했지만, 그렇게 하면 다른 메뉴 객체를 싱글톤으로 관리할 수 없기 때문에 단순 구현 클래스로 지정해주었습니다.
기본적인 첫 스타트 화면 (고객 메뉴, 그룹 메뉴, 조회 메뉴)를 선택하도록 입력 유도 문이 출력되며, 해당 메뉴를 선택하면 메뉴 값이 리턴되어 selectMenu메서드가 switch 문으로 해당 메뉴의 showmenu를 호출하도록 구현했습니다.
CustomerMenu 클래스는 Customers에 객체를 저장하기 전에, 해당 객체를 생성하고 검증하고 , 수정/삭제를 한다면 해당 아이디가 존재하는지, 그리고 배열에 아무것도 들어있지 않아서 Null예외가 발생할 수는 없을지.. 정도의 역할을 부여했습니다.
Customer객체는 생성이 되면 바로 static 변수인 count가 생성자가 호출될 때 ++되도록 되어있기 때문에, 데이터 검증을 해당 메뉴에서 진행하도록 설계했습니다.
GroupMenu 클래스는 단순 그룹의 기준을 초기화, 수정, 조회하기 전에 원하는 그룹의 이름을 받아내고, 초기화가 되어있는지 확인이 된다면 수정/조회를 할 수 있도록 해 나름의 예외처리를 진행했습니다.
SummaryMenu 클래스는 말 그대로 요악 메뉴 이기 때문에, 해당 메뉴가 직접적으로 수행할 것은 출력 정도만 있도록 역할을 부여했습니다. classifiedCustomers 클래스에서 리스트를 가져와 단순히 내림차순/오름차순 정도만 고르도록 했습니다.
고민되었던 문제...🫠
- CustomerMenu 의 역할을 어떻게 지정해야 할지 고민이 되었습니다. MVC패턴으로 치자면 CustomerMenu 를 Controller 의 역할을 부여해야 할지.. 아니면 비즈니스 로직을 담당할 Model 의 역할을 부여해야 할지..
- 하지만 그정도로의 과한 디테일을 요구하는 설계가 필요한 작업은 아니라고 생각됐고, 해당 프로젝트가 장기 프로젝트가 아니고 어떠한 기능과 목표가 이미 정해진 프로젝트이기 때문에 단순설계로 끝내도록 했습니다.
Customers가 주방이라고 치자면 그 전을 담당하는 카운터 정도의 역할을 부여하도록 했습니다.
고려했던 부분 😪
- 최대한 객체지향의 개념을 살려 어떠한 객체가 무엇을 갖고 어떠한 작업을 진행합니다. 의 내용을 갖고있도록 메소드를 선언했습니다.
Customers가 고객 고유번호로 삭제를 합니다.(주어지는 값은 고유번호)
- 메소드의 이름을 최대한 해당 메소드가 담당하는 역할을 명시하도록 작성해주었습니다.
GroupMenu 가 Parameter를 만든다. / 입력값을 토대로 그룹의 인덱스를 가져온다.
- 객체가 하나로만 생성되어 관리가 될 필요가 있는 객체들은 싱글톤으로 관리하고, 객체주입을 많이 활용했습니다.
스프링 개념에 자주 등장하던 의존성 주입에 대한 활용을 자바에서 연습해볼 수 있도록 진행했습니다.
아쉬웠던 점과 좋았던 점 ⭕️❌
아쉬웠던 점❌
- 이번 토이프로젝트를 진행하면서, 기존에 다른 토이프로젝트를 진행해왔을때 깃 사용에 대한 아쉬움이 항상 있었어서 다음 토이프로젝트는 시작부터 깃으로 관리를 꾸준히 해가면서 진행해야겠다.. 하는 생각이 있었지만 실패했습니다.(?) 급한 성격이 문제인듯 합니다..... 다음엔 꼭 이러지 않도록 노력해야겠습니다.
- 예외처리에 대한 기본적인 지식이 부족했습니다. 처음에 뭣모르고 예외관리 클래스를 만들어 모든 예외에 대한 정의를 마치 프린트문 출력하듯이 메소드를 선언했고, 아는 지인에게 피드백을 받고 찾아본결과,
예외를 직접 생성하면 단순히 생성만으로 많은 자원을 이용하게 되고, 이것과 마찬가지로 throw를 많이할수록 또한 메모리 낭비가 심해진다는걸 알게되었습니다. 이부분에 대한 공부를 더 할 예정입니다.
좋았던 점⭕️
- 진행하면서 개인적으로 애매모호했던 스프링의 역전제어와 의존성주입, 그리고 빈이 스프링에서 어떠한 역할을 하는지 이해가 더 잘된 느낌입니다. 스프링을 들어가기 전에 이런 토이프로젝트를 진행하고 들어갔으면 얼마나 좋았을까 라는 생각도 들지만, 강사님의 프로젝트 구성이 정말 알차다고 느껴졌습니다. 우리 국비 기수 복받았다..!
- 클래스 설계를 거의 처음으로 진행해보고 하나부터 열까지 신경쓰려고 했던 첫 프로젝트였고, 성공적이었던것 같습니다. 객체지향이 무엇인지 단순히 말로 설명하기는 어렵고, 그걸 이해하기도 어렵지만, 이번에 확실히 개념을 챙겨가는 느낌입니다.
- 코드가 더 깔끔해진것 같습니다. 강사님께서 강의에서 알려주신 내용을 바탕으로 더 깔끔한 메소드를 작성하려고 노력했고 그 결과 가독성이 좋고 해당 부분이 어떤 재료를 가지고 어떠한 행동을 하는지 표기할 수 있게 되었습니다.
- 성능에 대한 고민을 할 수 있는 시간이었습니다. 그룹핑이라는건 어떠한 목록을 탐색해서 짚어내는 행동이기도 한데, 그에 따른 생각을 많이 해볼 수 있는 시간이었습니다.
시연 화면
마무리
글이 굉장히 길지만, 제가 공부를 시작한 이후로 나름 기록을 남길 수 있는 첫 토이 프로젝트라고 생각해 기록을 남기게 되었습니다.
9월 초, 제가 처음으로 자바를 시작했었을 때 JDBC API를 이용해 게시판 프로그램을 만든 적이 있었습니다.
그때의 설계와 지금의 설계는 정말 말로 설명할 수 없을 정도로 다릅니다.
공부라는 건 항상 내가 잘하고 있는지 많이 힘들 수밖에 없는 부분인데.. 이에대한 체크를 하려면 보통 과거의 나와 비교를 하는 수 밖에 없다고 생각합니다.
이번 토이 프로젝트로 인해서 내가 정말 잘하고 있구나.. 굉장히 많이 늘었구나.. 나 나름 똑똑하구나..(?)라는 생각을 갖게 되었고 공부가 더 재밌어진 것 같습니다.
물론 해야 할게 많지만 멀리 본다면 제가 배워야 할 것들이 넘어야 할 산들이 아니라 오히려 더 겪어볼 수 있는 어떠한 놀이가 될 수 있겠다는 생각에 설레는 것 같습니다.
(해당 글은 블로그 이전으로 인하여 붙여 넣기 된 게시글입니다.)