드디어 첫 프로젝트를 진행하였다. 이 프로젝트 전에 미니 프로젝트 하나를 팀원 한분과 작업했었는데 도중에 정식으로 프로젝트를 진행하라해서 미니 프로젝트를 미뤄두었다.
처음에는 둘 다 할 수 있을 줄 알았는데, 막상 진행을 다 해보니 GitHub부터 마지막에 구현한 Socket까지 배워야 할 것이 산더미였다.
오늘부터는 프로젝트 리마인드 및 개선해야 할 점에 대해 포스팅하고자 한다.
- 해당 프로젝트의 노션 주소 https://www.notion.so/aca67bd48cd347bb87bbb9768be49d89?v=d50905aef64242d386dc9b2d4bcac9cb
- 해당 프로젝트의 Github 주소
백엔드 : https://github.com/RedPanda6801/cafe-backend
프론트엔드 : https://github.com/zi12i/cafe_front
팀원은 백엔드 둘, 프론트엔드 둘로 구성되었다. 4명이라 다양한 주제가 나왔는데, 그 중에서 가장 주목을 받았던 것은 '작은 카페들을 위한 카페 관리 시스템' 이었다.
초반 기획으로는 각 카페(고객)에 대해 쿠폰, 게시판, 고객관리 등의 서비스를 구독 형식으로 제공해준다는 것으로 잡고 시작했다.
그러나 카페마다 게시판 DB, 고객DB, 쿠폰DB를 각각 가져야 한다는 제약이 있었다.
이렇게 나눠주지 않고 한 개의 DB에 몰아 넣는다면 DB 크기가 매우 커지고, DB에서 데이터를 찾는 시간 또한 증가할 것이며, 결과적으로 서버의 품질이 저하될 우려가 있었다.
그렇다고 다른 주제를 찾자니 마땅히 좋은 아이디어가 나오지 않았다.
그래서 우리 팀은 카페 프로젝트를 하되, 기능을 줄이고 쿠폰기능만 제공해주는 방향으로 기획하였다.
우선 메인페이지가 있어야 했다. 당근마켓으로 따지면 웹사이트 처음 화면 같은 느낌이다. 당근마켓은 홈페이지가 메인이 아니라 어플이 메인이기 때문에 홈페이지에는 주요 기능이 없다. 이를 차용하여 우리 또한 홈페이지와 기능페이지를 나눠서 개발하기로 하였다.
메인페이지에서 있어야할 기능에 대한 요구사항은 이러하다.
점주 회원 관리
- 카페(지점) 등록 / 수정 / 삭제 / 조회
- 점주 정보 등록 / 수정 / 삭제 / 조회
- Q&A 및 F&Q 등록 / 수정 / 삭제 / 조회
다음은 실제 이용할 카페 페이지에 대한 요구사항이다.
카페 페이지 관리
- 등록 고객(전화번호) 등록 / 조회
- 쿠폰 개수 추가 / 조회 / 삭제
- 메뉴 조회 / 추가 / 삭제
초기에는 고객과 쿠폰과 카페의 연관관계를 잘 설정한다면 문제없이 만들 수 있을 것 같았다. 그러나, 카페와 고객은 N대M 관계이고, 이에 대해 스탬프를 어떻게 관계 맺어야 할지 고민이 많았다. 이부분 때문에 이틀을 날렸다...
첫 결론으로는 카페와 고객의 관계를 스탬프로 엮는 방법을 사용하였다.
이는 Sequelize ORM의 N대M 관계를 생성하는 방법에서 차용하였다. 아래의 예시를 보면 이해가 될 것 같다.
가운데의 테이블을 스탬프로 하여 카페와 고객의 관계를 연결시키는 방법을 사용하였다.
이대로 개발을 하다가 한가지 고민에 빠졌다.
고객 테이블에 있는 유효한 칼럼이 오직 고객의 전화번호라는 점이었다. 이는 DB의 낭비이라 보았고, 나는 스탬프 테이블에 추가하여 문제를 보완해보았다.
이에 대한 장단점이 있지만, 단점은 확실하다. 모든 고객을 조회할때 한 개의 테이블에서 조회하기 때문에 조회하는 데에 시간이 오래걸린다는 큰 단점이 있었다. 하지만 카페가 많아지는 것을 고려하는 것보다 DB 구조를 단순화하는 게 이번 프로젝트의 취지에 맞을 것 같았다. 고객을 조회하는 API는 결국 한번밖에 사용하지 않았기 때문이다. 따라서 나름대로 최선의 방법을 찾고자 노력하였다.
결과적으로 나온 DB를 ER-Diagram으로 그려보았다.
추가 설명으로, Q&A는 각각 한 질문에 한 답변만 달 수 있도록 하기 위해 1대1 관계로 구현하였다.
다음은 요구사항 분석에 대한 스키마이다.
점주(Owner)
Column | Domain | Allow Null | Primary Key | Foreign Key |
---|---|---|---|---|
id | NUMERIC | FALSE | O | X |
STRING(50) | FALSE | X | X | |
userId | STRING(20) | FALSE | X | X |
password | STRING(100) | FALSE | X | X |
name | STRING(20) | FALSE | X | X |
ownerPhone | STRING(13) | FALSE | X | X |
isManager | BOOLEAN | FALSE | X | X |
CafeId | NUMERIC | TRUE | X | Cafe - Id |
카페(Cafe)
Column | Domain | Allow Null | Primary Key | Foreign Key |
---|---|---|---|---|
Id | NUMERIC | FALSE | O | X |
cafeName | STRING(50) | FALSE | X | X |
location | STRING(20) | FALSE | X | X |
businessNum | STRING(20) | FALSE | X | X |
expireDate | DATE | FALSE | X | X |
icon | STRING(100) | TRUE | X | X |
img | STRING(100) | TRUE | X | X |
OwnerId | NUMERIC | TRUE | X | Owner - Id |
도장(Stemp)
Column | Domain | Allow Null | Primary Key | Foreign Key |
---|---|---|---|---|
id | NUMERIC | FALSE | O | X |
stackStamp | INTEGER | TRUE | X | X |
leftStamp | INTEGER | TRUE | X | X |
visit | INTEGER | TRUE | X | X |
memo | STRING(100) | TRUE | X | X |
custPhone | STRING(13) | TRUE | X | X |
CafeId | NUMERIC | TRUE | X | Cafe - Id |
질문(Question)
Column | Domain | Allow Null | Primary Key | Foreign Key |
---|---|---|---|---|
id | NUMERIC | FALSE | O | X |
category | ENUM | FALSE | X | X |
title | STRING(100) | FALSE | X | X |
text | STRING(300) | FALSE | X | X |
SolutionId | Numeric | TRUE | X | Solution - Id |
CafeId | NUMERIC | TRUE | X | Cafe- Id |
Column | Domain | Allow Null | Primary Key | Foreign Key |
---|---|---|---|---|
id | NUMERIC | FALSE | O | X |
comment | STRING(300) | FALSE | X | X |
QuestionId | NUMERIC | TRUE | X | Question - Id |
CafeId | NUMERIC | TRUE | X | Cafe - Id |
보통은 고객을 PK나 UNIQUE 값으로 두겠지만, 카페마다 중복된 고객을 가지기 때문에 UNIQUE 값을 설정해두지 않았다.
필요한 기능에 대한 API 요구사항을 나열해보았다.