본문 바로가기

개발 프로젝트/Zerobase 연습프로젝트 기록

매장 테이블 예약 서비스(1) 서비스 요구사항 및 설계

고려할 관점 나열

(1) API 설계

- 기능과 Input/ Output 명확화 : 정확한 input과 output이 산출되고 이것이 견고해야 변경이 적음

- 데이터 validation : 

- 정책 및 예외 : 꼼꼼하게 예외처리를 할것

 

(2) 데이터베이스 설계

- 테이블 정규화 : 정규화 원칙에 부합되도록 테이블을 설계

- 인덱스 설정 : crud의 패턴을 고려하여 인덱스 종류 및 대상칼럼 설정

 

(3) 최적화 기술고려

- 캐쉬 : 데이터베이스 CRUD 패턴을 확인하고 캐쉬 적용포인트 고려

- 쓰레드 : 쓰레드는 현재 배우고 있는 중이므로 적용보류

- 보안 : 보안도 적용보류

 

프로그램 요구사항 (주어짐)

  • 매장을 방문할때 미리 방문 예약을 진행하는 기능"을 구현하고자 합니다.
  • 위 예약을 받기 위해서는 점장(매장의 관리자)이 매장정보를 등록하고자 하는 기능을  구현하고자 합니다.
  • 매장을 등록하기 위해서는 파트너 가입 기능"이 구현되어야 합니다.
  • 매장 이용자가 서비스를 통해서 매장을 검색하고 상세 정보를 확인하는 기능구현하고자 합니다.
  • 서비스를 통해서 예약한 이후에, “예약 10분전에 도착하여 키오스크를 통해서 방문  확인을 진행하는 기능"을 구현하고자 합니다.
  • 예약 및 사용 이후에 리뷰를 작성하는 기능을 구현하고자 합니다.

식당이나 점포를 이용하기 전에 미리 예약을 하여 편하게 식당/점포를 이용할 수 있는 서비스 개발

  •  매장의 점장은 예약 서비스 앱에 상점을 등록한다.(매장 명, 상점위치, 상점 설명)
  • 매장을 등록하기 위해서는 파트너 회원 가입이 되어야 한다.(따로, 승인 조건은 없으며 가입 후바로 이용 가능)
  • 매장 이용자는 앱을 통해서 매장을 검색하고 상세 정보를 확인한다.
  • 매장의 상세 정보를 보고, 예약을 진행한다.(예약을 진행하기 위해서는 회원 가입이 필수적으로 이루어 져야 한다.)
  • 서비스를 통해서 예약한 이후에, 예약 10분전에 도착하여 키오스크를 통해서 방문 확인을 진행한다 .
  • 예약 및 사용 이후에 리뷰를 작성할수있다.
  • 리뷰의 경우, 수정은 리뷰 작성자만, 삭제 권한은 리뷰를 작성한 사람과 매장의 관리자(점장등)만 삭제할 수 있습니다.

 

 

 

 

 

 

 

개발명세서 검토

파트너회원 partners

 

1. 파트너 회원 가입 API

  • POST /partners
  • Parameters : 파트너아이디(partnerId), 사업자명(partnerName), 사업자번호(businessId)
  • Policy: 파트너아이디 및 사업자명 10글자 초과시, 사업자번호 10자리 아닐시, 파트너 아이디 중복시 실패응답 (구현필요)
  • Success Response: partnerId: 파트너 아이디, registrationDate: 등록 일시
  • CRUD : 파트너회원 Create 진행

 파트너 회원 (DB)

  • Fields:
    • Id (Long): 파트너 ID (auto-incre)
    • partnerId (VARCHAR): 파트너 고유ID(unique)
    • userName (VARCHAR): 파트너 이름
    • buinessId (Long) : 사업자번호
    • phoneNumber(Long) : 핸드폰번호
    • registrationDate (DATETIME): 등록 일시
    • 주요 CRUD :  파트너 고유ID(unique key) 레코드 create

매장 관리

 

1. 매장 등록

  • POST /stores
  • Parameters:  파트너ID, 매장고유명, 위치, 매장설명
  • Policy: 사용자가 파트너 회원이 아닌 경우, 매장고유명 중복, 
  • Success Response: 매장고유명, 매장위치, 매장설명
  • CRUD : autoincre으로 매장등록 Create 진행

2. 파트터명으로 매장검색

  • GET /stores/partnerId
  • Parameters: query: 파트너ID
  • Policy: 파트너 ID에 해당하는 매장존재하지 않을 시에 실패, 
  • Success Response: 리스트 : 매장아이디, 매장고유명, 위치, 매장설명
  • CRUD : 파트너명 칼럼과 일치하는 매장의 리스트 Read 진행

 

3. 매장명으로 매장 검색 및 상세 정보 조회

  • GET /stores/storeId
  • Parameters: query: 매장고유명
  • Policy: 고유명 존재하지 않을 때,
  • Success Response: 매장아이디, 매장고유명, 위치, 매장설명
  • CRUD : 매장 고유명으로 매장 레코드 Read 진행

4. 매장명 자동완성 (추가구현필요)

  • GET /stores/autocomplete
  • Parameters: query: 매장고유명 글자
  • Policy:  매장
  • Success Response: 매장아이디, 매장고유명, 위치, 매장설명
  • CRUD : 매장 글자로 매장 레코드 Read 진행

 

5. 위치로 주변 매장 검색 및 상세 정보 조회(추가구현필요)

  • GET /stores
  • Parameters: query: 특정위치
  • Policy: 특정위치 내에 존재하지 않을때 
  • Success Response: 매장아이디, 매장고유명, 위치, 매장설명
  • CRUD : 위치로 매장 레코드 범위 Read 진행


 매장 (DB)

  • Fields:
    • Id 매장ID
    • partnerId 파트너ID
    • storeId 매장고유명
    • storeLocation 매장위치
    • storeGeolocaion 매장좌표명(향후도입)
    • storeComment 매장설명
    • registeredAt 등록일시
    • Rating 평점(reddis 항목)
    • reviewNumber 평점개수(reddis 항목)
  • 주요 CRUD
    • 매장고유명(unique key)레코드 생성
    • 매장고유명(unique key)로 레코드 검색 (Hashtable? tree?)
    • 파트너명 (Foreignkey)로 복수의 매칭 레코드 검색 
    • 위치로 위치주변 매장의 범위 검색(B-tree)
    • 별점 Rating을 update

 

사용자 관리

1. 사용자등록

 

사용저 회원 가입 API

  • POST /users
  • Parameters : 사용자아이디, 핸드폰번호
  • Policy: 10글자 초과시, 사용자 아이디 중복시 실패응답 , 핸드폰번호 010으로 시작하고 자리수 다를시 실패
  • Success Response: UserName 사용자 아이디, 
  • CRUD : 사용자 아이디 create - autoincre

 사용자 회원 (DB)

  • Fields:
    • id (Long): 사용자 ID
    • userId (String): 사용자 고유ID
    • userName(String) : 사용자 본명
    • PhoneNumber(String) : 사용자 폰번호
    • registrationDate (DATETIME): 등록 일시
    • CRUD : 사용자 아이디 create - autoincre

 

예약 관리

1. 매장 예약

  • POST /reservations
    • Parameters: 사용자의 아이디,  매장의 아이디, 예약 시간
    • Policy: 사용자가 회원이 아님, 매장 정보가 없는 경우, 예약시간이 과거일때,
    • Success Response:  예약 아이디, 예약 일시, 예약매장
    • CRUD : 위치로 매장 레코드 범위 Read 진행

2. 매장주인 예약리스트 확인

  • POST /reservations/listbypartner
    • Parameters: 파트너ID,매장ID
    • Policy: 시간순서대로 정렬,예약이 존재하지 않는경우 예외처리
    • Success Response: 예약아이디, 예약매장, 예약시간, 확정여부
    • CRUD : 사용자 ID를 통해서 예약 리스트 READ하기

 

3. 사용자 방문 조회

  • POST /reservations/listonvisit
    • Parameters: 사용자ID, 매장ID
    • Policy: 시간순서대로 정렬, 예약이 존재하지 않을때,
    • Success Response: 예약아이디, 예약매장, 예약시간, 확정여부
    • CRUD : 사용자 ID와 매장ID를 통해서 예약 ID조회
    • Note : 사용자 ID를 통해서 해당 매장에 해당하는 레코드를 받아옴

4. 방문 상태변경

  • UPDATE /reservations/reservations/statuschange
    • Parameters: 예약아이디
    • Policy: 예약이 존재하지 않거나 이미 확인된 경우 실패
    • Success Response: 예약아이디, 예약자Id, 예약매장, 예약시간, 예약상태
    • CRUD : 사용자 ID와 매장ID를 통해서 예약 ID조회
    • Note : 사용자 ID를 통해서 해당 매장에 당일 레코드를 받아오고, 그 중에서 예약을 선택해서 확정을 진행

예약 (Reservations)DB

  • Fields:
    • reservationId (UUID): 예약 고유 ID
    • userId (UUID): 예약한 사용자 ID
    • storeId (UUID): 예약된 매장 ID
    • reservationTime (DATETIME): 예약 시간
    • regissteredAt (DATETIME): 예약 생성 일시
    • confirmedAt (DATETIME, nullable): 방문 확인 일시
    • status (Enum)
    • CRUD : 사용자 ID와 매장ID를 통해서 예약 ID조회, 사용자 ID를 통해서 예약 리스트 READ하기,  위치로 매장 레코드 범위 Read 진행, 예약ID를통해서 확정진행, 

 

 

리뷰 관리

1. 리뷰 작성

  • POST /reviews
    • Parameters:  작성자아이디, 예약ID, rating: 평점, comments: 리뷰 내용
    • Policy: 예약이 존재하지 않거나 이미 확인된 경우 실패
    • Success Response: reviewId: 리뷰 아이디, postedDate: 리뷰 작성 일시
    • comment : 리뷰ID는 리뷰완료된 것들 리스트로 내려줘서 ID선택하게끔.

2. 리뷰 수정

  • PATCH /reviews/{reviewId}
  • Parameters: reviewId: 리뷰 아이디, rating: 수정할 평점, comments: 수정할 내용
  • Policy: 리뷰 작성자 또는 매장 관리자만 삭제 가능
  • Success Response: updatedDate: 수정 일시
  • comment : 리뷰ID는 리뷰완료된 것들 리스트로 내려줘서 ID선택하게끔.

3. 리뷰 삭제

  • DELETE /reviews/{reviewId}
  • Parameters: reviewId: 리뷰 아이디
  • Policy: 리뷰 작성자 또는 매장 관리자만 삭제 가능
  • Success Response: deletedDate: 삭제 일시
  • comment : 리뷰ID는 예약완료된 것들 리스트로 내려줘서 ID선택하게끔.

 리뷰DB (Reviews)

  • Fields:
    • Id
    • reviewId (UUID): 리뷰 고유 ID
    • 예약ID
    • 사용자ID
    • 매장ID
    • rating (INT): 평점 (1-5)
    •  reviewContents : 리뷰 내용
    • postedDate (DATETIME): 리뷰 작성 일시
    • updatedDate (DATETIME, nullable): 리뷰 수정 일시
    • deletedDate (DATETIME, nullable): 리뷰 삭제 일시

 

설계고려사항

1. 테이블 정규화 설계고려

  예약과 방문확인과 리뷰는 비즈니스상 연속적 과정인데 예약/ 방문확인/ 리뷰를 별도의 테이블로 나눠야할 것인가 하나의 테이블로 둘것인가? 

  A.테이블을 합쳐서 관리 : 예약테이블에 예약확정데이터+리뷰데이터를 추가해서 사용하면 예약레코드 리스트에서 예약확인과 리뷰작성d을 하지않는 데이터까지 관리를 해야하기 때문에 데이터량이 확연히 늘어날 수 있음. 

  B. 테이블을 별도로 분리 : 별도로 관리할 경우, 예약확정이 되지 않을 데이터와 리뷰작성을 안할 데이터에 대해서, 해당 데이터를 관리하지 않아서 데이터량 자체는 줄일 수 있음. 단, 예약레코드를 찾아볼때, 예약확정여부를 확인하려면 별도로 다른 테이블을 확인해야함. 

  결론 :  쿼리의 양상을 보면, 예약레코드와 예약확정여부를 같이 확인할 필요가 많아보임, 그러나 테이블을 별도로 분리하면 예약레코드 조회와 예약확정테이블을 별도로 조회한 후 Join쿼리를 사용해야해서 DB에 부하를 줄 수 있음. 그러므로 예약확정 데이터는 예약레코드의 한 column으로 status로서 관리할 것. 리뷰데이터는 예약과 함께 조회할 일이 적고 리뷰데이터 자체의 칼럼이 양이 많아서 정규화를 위한 분리를 할 것. 

 

2. 캐쉬도입

- 예약당일 가까이에 예약조회가 빈번할 것같으니 캐쉬도입 (키오스크)

- 매장데이터의 경우 예약의 relatedENtity로서 검색이 빈번하고 데이터 크기가 작으며 변경이 거의 없을 것으로 보여

- partnerId는 데이터량이 작고 relatedEntity로써 검색이 빈번하고 변경이 거의 없을 것으로 보여 캐쉬도입

 

3. store 테이블의 rating 데이터 관리

  store 테이블에 있는 Rating column review 테이블의 rating을 평균으로 산출하는 데이터인데 이 경우, 해당 store rating을 어떻게 유지보수할 지 고민일 필요하다.

A. 매장의 리뷰가 작성될때마다 리뷰 table내에 store에 해당하는 row를 select하여 평균을 구하고 업데이트할것인지

B. 정기적인 간격으로 peaktime이 아닐때 rating을 업데이트하고 실시간 동기화는 하지 않을 것인지

결론 : A와 B를 절충하여, DB의 store 테이블의 rating column은 peak가 아닌 시간에 정기적으로 업데이트를 하고, store의 rating column과 rating개수를 캐쉬에 저장한다. 그리고 review 데이터가 업데이트 될때마다 해당 데이터를 서버사이드에서 계산하고 reddis 내에 실시간으로 동기화한다.

 

4. index설정

 

자주사용되는 foreign key의 경우 별도로 index를 설정하지 않으면 primarykey만 index로 설정됨.

 

 

5. 복잡한 Entity관계 최적화를 위한 LazyLoading

 

어떤 데이터베이스가 어떤식으로 CRUD될 것인지 미리 예측하고 CRUD될때 파급효과를 최소화하기위해서 redis사용과 lazyloading을 적절히 혼합하여서 사용하고자한다. redis도입정도만 우선은 고려하고, 그외 필요사항은 grinder을 통해서 확인해보고 필요에따라 도입해보자