이 레포지토리는 원티드 프리온보딩 백엔드 코스 6차 과제를 위해 만들어졌습니다.
- 일정 : 2021년 11월 18일(목) 오후 6시 ~ 11월 22일(월) 오후 2시
팀장 박상수 | 팀원 김성연 | 팀원 최준호 |
---|---|---|
blog: Plus Ultra github: epitone |
blog: sudocorp github: SibaDoge1 |
blog: raejun92.log github: raejun92 |
변동요금 조건 연산 | 확장성설계, 코드퀄리티관리 / DevOps | 조회, 요금계산 API |
프로젝트 회고 | 프로젝트 회고 | 프로젝트 회고 |
팀원 어유선 | 팀원 김현길 | 팀원 이동훈 |
---|---|---|
blog: Makkiato github: Makkiato |
blog: gusrlf14 github: hyunghilkim |
blog: dongjay00.log github: dongjay00 |
테스트 데이터 세팅, DB 쿼리 | 변동요금 조건 연산 | 변동요금 조건 연산 |
프로젝트 회고 | 프로젝트 회고 | 프로젝트 회고 |
- README 작성
- 프로젝트 빌드, 자세한 실행 방법 명시
- 구현 방법과 이유에 대한 간략한 설명
- 완료된 시스템이 배포된 서버의 주소
- Swagger나 Postman을 통한 API 테스트할때 필요한 상세 방법
- 해당 과제를 진행하면서 회고 내용 블로그 포스팅
- Swagger나 Postman을 이용하여 API 테스트 가능하도록 구현
✔️ API 목록
- 요금계산 API
[고려 사항 및 상세설명]
- 주어진 정보를 기술적으로 설계하고 구현할 수 있는 역량
- 확장성을 고려한 시스템 설계 및 구현
디어는 사용자의 요금을 계산하기 위해 다양한 상황을 고려합니다.
- 할인 조건도 있습니다. 사용자가 파킹존에서 반납하는 경우 요금의 30%를 할인해주며, 사용자가 마지막 이용으로부터 30분 이내에 다시 이용하면 기본요금을 면제해줍니다.
- 우선 지역별로 다양한 요금제를 적용하고 있습니다. 예를 들어 건대에서 이용하는 유저는 기본요금 790원에 분당요금 150원, 여수에서 이용하는 유저는 기본요금 300원에 분당요금 70원으로 적용됩니다.
- 벌금 조건도 있습니다. 사용자가 지역 바깥에 반납한 경우 얼마나 멀리 떨어져있는지 거리에 비례하는 벌금을 부과하며, 반납 금지로 지정된 구역에 반납하면 6,000원의 벌금을 요금에 추가로 부과합니다.
- 예외도 있는데, 킥보드가 고장나서 정상적인 이용을 못하는 경우의 유저들을 배려하여 1분 이내의 이용에는 요금을 청구하지 않고 있습니다.
최근에 다양한 할인과 벌금을 사용하여 지자체와 협력하는 경우가 점점 많아지고 있어 요금제에 새로운 할인/벌금 조건을 추가하는 일을 쉽게 만드려고 합니다. 어떻게 하면 앞으로 발생할 수 있는 다양한 할인과 벌금 조건을 기존의 요금제에 쉽게 추가할 수 있는 소프트웨어를 만들 수 있을까요?
우선은 사용자의 이용에 관한 정보를 알려주면 현재의 요금 정책에 따라 요금을 계산해주는 API를 만들어주세요. 그 다음은, 기능을 유지한 채로 새로운 할인이나 벌금 조건이 쉽게 추가될 수 있게 코드를 개선하여 최종 코드를 만들어주세요.
다음과 같은 정보들이 도움이 될 것 같아요.
- 요금제가 사용자 입장에서 합리적이고 이해가 쉬운 요금제라면 좋을 것 같아요.
- 앞으로도 할인과 벌금 조건은 새로운 조건이 굉장히 많이 추가되거나 변경될 것 같아요.
- 가장 최근의 할인/벌금 조건의 변경은 '특정 킥보드는 파킹존에 반납하면 무조건 무료' 였습니다.
- Node.js, express, Sequelize를 이용하여 요금계산 Api를 구현하였습니다.
- 코드 컨벤션, 커밋 컨벤션, Git Flow를 지켜가며 작업했습니다.
- Github Project, 마일스톤을 활용해서 백로그, 이슈 관리를 진행했습니다.
- Service, Controller 등의 계층 분리를 통해 코드의 가독성을 높였습니다.
- 리팩토링을 통해 가독성을 높이고, 유지보수를 편하게 하기 위해 노력 했습니다.
- 결제요금을 계산해주는 라이브러리를 독립적으로 만들고, 이를 사용하여 최종요금을 조회하는 API를 구현했습니다.
-
확인 시의 편의를 위해 DB 테이블마다 전체조회를 할 수 있는 API를 추가했습니다.
-
또한 DB상의 공간정보를 시각화한 스프레드시트를 아래와 같이 제작했습니다.
-
아래는 테스트에 사용한 이용내역에 대한 설명입니다.
[테스트 데이터 사용내역]
[id] - [지역(기본요금/시간요금)],[사용시간],[기타],[예상요금]1번 - 2번지역(400/80), 25분, 그외없음, expect 2400
2번 - 2번지역(400/80), 20분, 그외없음, expect 2000
4번 - 2번지역(400/80), 30초, 1분이내무료, expect 0
5번 - 2번지역(400/80), 10분, 6번 환승적용을 위한 용도, expect 1200
6번 - 2번지역(400/80), 35분, 30분이내환승용도, expect 2800
8번 - 2번지역(400/80), 10분, 9번 환승적용을 위한용도, expect 1200
9번 - 2번지역(400/80), 35분, 환승, 지역이탈(92005m),금지구역, expect 9209300
10번 - 4번지역(630/100), 10분, 지역이탈(97817m),주차구역, expect 9782841
11번 - 4번지역(630/100),15분,주차구역, expect 1491
- 자바스크립트 자체 내장 Error 클래스를 상속 받아서, 커스텀 에러를 생성해서 관리했습니다.
-
과제분석을 통해 요금조건이 다양하고, 추가와 제거의 가능성이 많으며 조건의, 적용순서도 변경될 여지가 많다는 특징을 확인했습니다.
-
고민을 거쳐 해당 특징들에 적합한 책임연쇄패턴을 적용하기로 했습니다.
src/libs/costCalculator/ 폴더 내의 클래스 다이어그램
-
CostChainBase는 Chain을 추상화한 인터페이스의 용도로 만들었습니다. 다만, 자바스크립트는 Duck Typing으로인해 인터페이스 개념이 존재하지 않으므로 어쩔 수 없이 클래스 상속을 이용했습니다.
-
ConcreteChain클래스들은 CostChainBase를 상속하여 구체화된 클래스들입니다. calculateCost 메소드를 통해 각자의 요금조건을 계산하고 goToNextChain을 통해 다음 체인으로 넘깁니다.
-
CostChainManager는 체인연결, 데이터 전처리 등 종합적으로 체인을 관리하는 클래스입니다. addChain으로 체인을 추가하고 최종적으로는 이 클래스의 calculateCost 메소드를 호출하여 요금을 계산합니다.
(인터페이스 용도) CostChainBase 실제 코드
CostChainManager 실제 코드
-
추가적으로 다른 부분과의 의존성을 낮추기 위해, node.js의 index.js파일 모듈 기능을 이용해 독립적인 폴더로 구분하기로 했습니다.
/src/libs/costCalculator/index.js 파일의 내용
const costCalculator = require('../libs/costChains'); let finalCost = await costCalculator.calculateCost(historyId);
실제 사용 예
- 위와 같은 설계를 통해 요금조건의 추가, 삭제, 순서변경 등의 변화에 있어서 기존코드의 변경을 최소화 할 수 있었습니다.
- SOLID원칙들 중 기능의 확장에는 열려있고 기존 코드의 변경에는 닫혀있는 Open-Closed 원칙을 만족할 수 있었습니다. SOLID의 일부 다른 원칙 또한 만족하는 것으로 보입니다.
- 구성원들의 전체 의견을 반영하여 API 명세와 DB모델을 구축 하였습니다.
- 긴밀하게 소통하여, 신속하게 문제를 인식하고, 신속하게 해결 하기 위해 노력 하였습니다.
- 전체 작업을 도메인 단위로 분리하여 구성원 간의 병목현상을 줄였습니다.
- 과제에 사용한 노션 페이지
- 레포지토리를 clone 받거나, 압축을 해제한 후 npm install을 통해 환경 셋팅을 진행합니다.
- npm start를 통해 서버를 구동합니다.
- src 폴더에 .env 파일을 설정해서, 환경변수를 설정합니다.
- npm start로 서버를 구동시키고, npm test를 입력하면 단위 테스트가 가능합니다.
- .env설정 노션 링크
-
링크 접속불가 시 .env 파일 설정 방법
RDS_DATABASE=DB 스키마 명 RDS_USERNAME=DB 유저 명 RDS_PASSWORD=DB 유저 비밀번호 RDS_HOSTNAME=DB 주소 RDS_PORT=DB 포트 PORT=http서버 포트 IS_SQLLITE=false
-
- Postman을 활용하여 API 작동 테스트를 진행했습니다.
- 배포된 서버 주소 및 자세한 API 명세는 아래에서 확인 가능합니다.
- 🗂 API Description Link
- 을 클릭하여 웹브라우저 혹은 Postman 클라이언트에 콜렉션이 로드되면
-
Variables 탭에서 서버 Host와 Port를 지정합니다. (기본값이 지정되어 있습니다.)
-
그후 우측 상단의 Run 버튼을 눌러 RUN ORDER 화면에 진입한 뒤 Run [Collection Name]을 클릭하면, 이상적인 상황에서의 테스트가 진행됩니다.
-
요청마다 여러 이상적이지 않은 상황의 테스트에 대한 예시가 있습니다.
-
📦src
┣ 📂bin
┃ ┗ 📜www.js
┣ 📂configs
┃ ┣ 📜cost.js
┃ ┣ 📜db.js
┃ ┗ 📜index.js
┣ 📂controllers
┃ ┣ 📜historyController.js
┃ ┗ 📜listingController.js
┣ 📂globals
┃ ┣ 📜index.js
┃ ┣ 📜responseMessage.js
┃ ┣ 📜routes.js
┃ ┗ 📜statusCode.js
┣ 📂libs
┃ ┗ 📂costCalculator
┃ ┃ ┣ 📂chains
┃ ┃ ┃ ┣ 📜DefaultCostChain.js
┃ ┃ ┃ ┣ 📜ForbiddenAreaFineChain.js
┃ ┃ ┃ ┣ 📜index.js
┃ ┃ ┃ ┣ 📜MalfunctionFreeChain.js
┃ ┃ ┃ ┣ 📜OutOfAreaFineChain.js
┃ ┃ ┃ ┣ 📜ParkingDiscountChain.js
┃ ┃ ┃ ┗ 📜TransportChain.js
┃ ┃ ┣ 📜CostChainBase.js
┃ ┃ ┣ 📜CostChainManager.js
┃ ┃ ┗ 📜index.js
┣ 📂middlewares
┣ 📂models
┃ ┣ 📜area.js
┃ ┣ 📜deer.js
┃ ┣ 📜forbiddenArea.js
┃ ┣ 📜history.js
┃ ┣ 📜index.js
┃ ┣ 📜parkingZone.js
┃ ┗ 📜user.js
┣ 📂routes
┃ ┣ 📜globalRouter.js
┃ ┣ 📜historyRouter.js
┃ ┗ 📜index.js
┣ 📂services
┃ ┣ 📜areaService.js
┃ ┣ 📜deerService.js
┃ ┣ 📜historyService.js
┃ ┗ 📜listingService.js
┣ 📂test
┃ ┣ 📂data
┃ ┃ ┗ 📂dto
┃ ┃ ┃ ┣ 📜postAccount.json
┃ ┃ ┃ ┣ 📜signup.json
┃ ┃ ┃ ┗ 📜token.json
┃ ┗ 📂unit
┃ ┃ ┗ 📂controllers
┃ ┃ ┃ ┣ 📂accountController
┃ ┃ ┃ ┃ ┗ 📜postAccount.test.js
┃ ┃ ┃ ┣ 📂transactionController
┃ ┃ ┃ ┃ ┣ 📜getTransaction.test.js
┃ ┃ ┃ ┃ ┗ 📜postTransaction.test.js
┃ ┃ ┃ ┗ 📂userController
┃ ┃ ┃ ┃ ┣ 📜postToken.test.js
┃ ┃ ┃ ┃ ┗ 📜postUser.test.js
┣ 📂utils
┃ ┣ 📂errors
┃ ┃ ┣ 📜commonError.js
┃ ┃ ┗ 📜errors.js
┃ ┣ 📜index.js
┃ ┣ 📜logger.js
┃ ┣ 📜resFormatter.js
┃ ┗ 📜time.js
┣ 📜.env
┣ 📜.eslintrc.json
┣ 📜.gitignore
┣ 📜.prettierrc.json
┣ 📜app.js
┣ 📜package-lock.json
┗ 📜package.json