본문 바로가기

프로젝트/고민

테이블 설계

글의 목적

테이블을 설계하는 것은 무척이나 어렵다.

복잡하고 동적인 현실 세계의 관계를 정적인 구조로 만드는 일은 수 많은 상황을 예측해야 하는 일이다.

테이블을 설계하며 한 고민의 과정을 정리함으로써 다음에는 좀 더 나은 생각을 할 수 있도록 글을 작성한다.

캐치 테이블의 테이블 구조

 

가장 크게 한 고민은 어떠한 요소를 필드로 둘지, 테이블로 둘지 두 결정 사이의 장단점을 비교하며 설계 당시에는 가장 나은 선택을 했다.

테이블을 설계할 때 가장 중요하게 여긴 가치

  • 확장 (기능의 확장)
  • 코드로 구현했을 때의 모습 (도메인을 분리해 다른 어플리케이션으로 작동할 수 있을지)

특이사항으로 실제 캐치테이블에서는 지원하지 않는 매장 내 테이블을 지정하여 예약을 하는 기능을 추가했다. (진짜 어려웠음)

 

복잡한 구조를 쪼개서 바라보기(애그리거트 루트를 기준으로)

이제 캐치테이블이라는 어플리케이션을 조금 더 좁혀서 바라보자. 현재로서는 다음과 같이 좁힐 수 있어 보인다.

  • 매장
  • 예약
  • 웨이팅

매장

먼저 매장으로 볼 수 있는 도메인을 살펴보자.

 

이 어플은 신기하게도 메뉴를 보여줄 때 메뉴가 아닌 메뉴 카테고리를 메인으로 보여준다. 즉, 메뉴가 없더라도 메뉴 카테고리만으로 메뉴를 표현할 수 있다.(메뉴만 존재하는 경우도 있지만 드물다.)

 

지역 테이블 같은 경우는 추후 확장을 위해 필드가 아닌 테이블로 두었으며, 메뉴에 달라 붙는 기능 중 메뉴 리뷰라는 것이 있는데 시간상 구현하지 않았으며 코드 레벨에서 어떻게 확장 가능하게 작성할 수 있는지 여기서 보여주도록 하겠다.

이외에는 해석하는데 문제 없을 것이다.

예약

이제 예약 도메인을 살펴보자. 복잡하니 잘 읽어주기를 바란다.

테이블을 설명하기에 앞서 예약을 진행하는 흐름은 다음과 같다.

  • 매장을 선택한다.
  • 매장에서 예약 가능한 날짜, 시간을 선택한다.
  • 해당 날짜와 시간에 예약 가능한 좌석을 선택한다.
  • 앞선 정보를 토대로 예약을 생성한다.

이제 테이블을 설명해보겠다.

  1. 매장과 매장 예약은 1:1로 연결 되어 있다.
    → 예약에 얽혀있는 방대한 정보를 매장과 바로 연결 짓기에 매장은 자신의 역할에 부담을 느낄 것이다.

    따라서 매장_예약이라는 중간 테이블을 만들어 예약을 위한 매장의 책임을 덜어냈다.

    매장 예약을 매장이라고 치환하고 봐도 무방할 듯

  2. 매장_예약과 매장_테이블은 1:N 관계이다. (= 매장에는 여러개의 매장 테이블이 있다. 여기서의 테이블은 db에서의 테이블 아닌 식당에 있는 식탁을 의미)

    → 위 설명만으로 충분할 것이다. 좌석이 아닌 테이블로 설정한 이유는 영화관과는 다르게 바로 옆 좌석이 같은 테이블일 수 있기 때문에 테이블로 설정했다.

  3. 매장_예약과 매장_예약_날짜_시간은 1: N 관계이다. (= 매장은 날짜 + 시간 조합으로 예약을 미리 만들어 놓는다.)

    → 캐치 테이블의 예약을 보면 매장에 들어가 예약 가능한 날짜와 시간을 선택한 뒤 예약을 할 수 있다. 따라서 이를 매장 예약 날짜 시간이라는 테이블로 만들었다.

  4. 매장_예약_날짜_시간과 매장_테이블은 N:M 관계이다. (= 매장의 특정 날짜와 특정 시간에 이용 가능한 테이블은 여러개 존재한다. 또한, 테이블은 날짜별로 여러개 존재한다. 예를 들어 1월 1일 5시에 등록된 테이블이 1월 2일 5시에 등록되어 있을 수 있음)

  5. 4번으로 인해 다대다 관계를 1:N, N:1로 풀어내기 위한 매장_예약_날짜_테이블 이라는 테이블이 만들어졌다.

  6. 예약할 때 여러개의 테이블을 선택할 수 있으므로 예약과 매장_예약_날짜_테이블은 1:N 관계가 된다. 헷갈린다면 이 또한 예약과 매장_테이블의 관계를 생각해보면 이해가 쉽다.

    → 예약을 할 때 여러개의 테이블을 선택할 수 있으며 해당 테이블이 다른 날짜의 예약에도 포함될 수 있으니 예약과 매장 테이블의 관계는 N:M이 되는 것이다.

좌석을 선택할 수 있다는 기능을 하나 추가함으로써 테이블 구조가 많이 복잡해졌다.. 읽히지 않는다면 예약 흐름을 생각하고 스스로 테이블 구조를 그려본 뒤 읽으면 더 잘 읽힐 것이다. (이는 캐치 테이블이라는 서비스에 국한하지 않고 다른 서비스의 테이블을 설계 할 때에도 그림을 그려보는 것을 추천한다. 암산 하는 것 보다 훨씬 나을 것이다.)

웨이팅

 

다음으로 웨이팅이다. 웨이팅은 예약에 비해 비교적 간단하다.

  1. 예약과 마찬가지로 매장과 웨이팅 사이에 웨이팅이라는 중간 테이블을 둬 매장은 매장의 역할에, 웨이팅은 웨이팅의 역할에 충실하게 하려고 했으며 매장 웨이팅이라는 테이블에 두 관계를 위해 존재하는 필드를 넣어 뒀다.
    → 매장 웨이팅의 필드를 매장에 들어갈 필드로 보는 것이 이해가 더 쉬울수도 있다.
  2. 사용자는 여러개의 웨이팅을 등록할 수 있다. 1 : N
    (웨이팅 등록은 한번에 한개만 되지만 취소한 웨이팅, 이미 입장한 웨이팅 등을 포함하면 여러개의 웨이팅을 가질 수 있는 셈이다.)
  3. 매장은 여러개의 웨이팅을 받을 수 있다. 1 : N

단순히 캐치테이블의 기능을 구현하는 것은 그렇게 높은 난이도의 개발은 아니었지만 예약 시 좌석(테이블) 을 선택할 수 있다는 기능을 추가하니 개발 난이도가 급격하게 올라갔다.

테이블 구조가 복잡해지고 외래키 제약조건이 많아 테이블 생성 순서, 삭제 순서 데이터를 넣는 순서 모두 지켜야 했으며 개발을 할수록 속도가 느려지는 것 같았다.

이런 복잡한 구조를 코딩할 때 외래키를 안쓰는 것이 더 낫겠다는 생각이 들었으며, 데이터 무결성은 어플리케이션 단에서 지키게 하는 것이 효율적이라고 생각이 들었다.

외래키가 주는 장점이 있지만 장점에 비해 단점이 더 큰 것 같다..

 

 

이번 프로젝트를 통해 배운점

  • 다대다 관계의 설계법
    → 두 관계를 봤을 때 다대다 관계라는 것을 인지하기 조차 어려운 경우가 많지만 좀 극복한듯
  • 외래키를 선택적으로 쓰자
    → 데이터 무결성이 오히려 생산성을 너무 떨어지게 함
  • 이 정도를 얻은 것 같다.

'프로젝트 > 고민' 카테고리의 다른 글

우리의 문제 상황에 맞는 락 구현  (0) 2023.10.06
이벤트 기반으로 책임 분리  (0) 2023.10.05
주문 코드 리팩토링  (0) 2023.08.19
OpenFeign 적용기  (0) 2023.07.12
elasticsearch에 형태소 분석기 적용하기  (0) 2023.06.12