1편에서는 왜 카페 운영 시스템을 만들게 되었는지 정리해봤습니다. 이번에는 이 앱에서 가장 중요했던 예약 구조에 대해 적어보려고 합니다.
처음에는 예약 기능을 단순하게 생각했습니다. 사용자가 자리를 고르고, 날짜를 선택하고, 예약을 신청하면 끝이라고 생각했습니다. 그래서 처음에는 모든 예약을 거의 같은 방식으로 처리해보려고 했습니다. 좌석 번호, 날짜, 이용 기간 정도만 있으면 될 것 같았습니다.

그런데 실제로 만들어보니까 3층/4층 예약과 M층 예약은 완전히 다른 방식으로 봐야 했습니다.
3층과 4층은 좌석권 중심이었습니다. 예를 들어 1일권, 7일권, 30일권처럼 특정 좌석을 며칠 동안 사용하는 구조였습니다. 그래서 중요한 값은 “어떤 좌석을”, “언제부터”, “며칠 동안” 사용하는지였습니다.

반면 M층은 시간권 중심이었습니다. 좌석 하나를 며칠 동안 잡아두는 방식이 아니라, 특정 날짜의 특정 시간대에 공간을 예약하는 방식이었습니다. 그래서 “몇 월 며칠”, “몇 시부터”, “몇 시까지”가 중요했습니다. 이걸 3층/4층 좌석권과 똑같이 처리하려고 하니까 바로 문제가 생겼습니다.
처음에는 M층도 그냥 날짜와 이용 시간만 저장하면 되겠다고 생각했습니다. 그런데 그렇게 해보니까 같은 시간대에 예약이 겹치는 문제가 생길 수 있었습니다. 예를 들어 누군가 오후 2시부터 4시까지 예약했는데, 다른 사용자가 오후 3시부터 5시까지 예약하면 둘 다 같은 시간대를 쓰게 됩니다. 단순히 날짜만 비교하면 이런 충돌을 막을 수 없었습니다.

그래서 M층 예약은 시작 시간과 종료 시간을 따로 저장하고, 새 예약이 들어올 때 기존 예약들과 시간이 겹치는지 검사하도록 바꿨습니다. 기준은 단순했습니다. 새 예약의 시작 시간이 기존 예약의 종료 시간보다 빠르고, 새 예약의 종료 시간이 기존 예약의 시작 시간보다 늦으면 겹치는 예약으로 보는 방식입니다. 이렇게 바꾸니까 같은 날짜 안에서 시간대가 겹치는 예약을 막을 수 있었습니다.
또 하나 막혔던 부분은 이용 시간 단위였습니다. 처음에는 1시간 단위로만 예약하면 충분할 줄 알았습니다. 그런데 실제 운영에서는 1시간 30분 같은 예약도 필요할 수 있었습니다. 그래서 M층은 30분 단위 예약을 지원하도록 바꿨습니다. 시작 시간도 30분 단위로 선택할 수 있게 하고, 이용 시간도 1시간, 1시간 30분처럼 처리할 수 있게 수정했습니다.
이 과정에서 생각보다 자잘한 버그도 많이 나왔습니다. 예를 들어 M층은 일반 좌석처럼 좌석 번호가 명확하지 않아서 내부적으로 0 같은 값을 쓰는 경우가 있었는데, 처음 유효성 검사에서는 0을 잘못된 값처럼 처리하는 문제가 있었습니다. JavaScript에서 0은 falsy 값이라 단순 조건문으로 검사하면 없는 값처럼 판단될 수 있었습니다. 그래서 좌석 번호가 있는지 없는지를 대충 검사하지 않고, 숫자인지와 허용 범위 안에 있는지를 명확하게 검사하도록 바꿨습니다.
예약 상태도 처음보다 더 세분화했습니다. 처음에는 예약이 있냐 없냐 정도만 생각했는데, 실제 운영에서는 그걸로 부족했습니다. 사용자가 예약을 신청했지만 아직 관리자가 확인하지 않은 상태, 관리자가 승인한 상태, 거절된 상태, 사용자가 취소한 상태, 이용이 끝난 상태가 모두 달랐습니다. 그래서 예약 상태를 대기, 승인, 거절, 취소, 완료처럼 나눴습니다.
이렇게 상태를 나누니까 관리자 페이지도 훨씬 명확해졌습니다. 관리자는 어떤 예약을 승인해야 하는지, 어떤 좌석이 실제 사용 중인지, 어떤 예약은 매출에 반영해도 되는지를 구분할 수 있었습니다. 특히 매출 계산에서는 단순히 예약 데이터가 있다고 전부 더하면 안 됐습니다. 거절된 예약이나 취소된 예약, 테스트 예약까지 매출에 들어가면 실제 운영 데이터가 틀어지기 때문입니다.

반복 예약도 쉽지 않았습니다. M층은 한 번만 예약하는 경우도 있지만, 매주 같은 요일에 반복해서 예약하는 경우도 필요했습니다. 처음에는 반복 예약을 단순히 여러 개의 예약으로 생성하면 된다고 생각했습니다. 그런데 실제로는 각 날짜마다 기존 예약과 시간이 겹치는지 모두 확인해야 했습니다. 하나라도 겹치면 전체 예약을 막거나, 사용자가 다시 선택할 수 있게 해야 했습니다. 그래서 반복 예약도 결국 “여러 개를 한 번에 만드는 기능”이 아니라, 각각의 날짜와 시간대에 대해 충돌 검사를 하는 구조로 봐야 했습니다.

결국 예약 기능을 만들면서 가장 크게 느낀 점은, 같은 “예약”이라는 단어를 쓰더라도 운영 방식에 따라 데이터 구조가 달라져야 한다는 것이었습니다. 3층/4층은 좌석과 기간이 핵심이고, M층은 날짜와 시간이 핵심이었습니다. 이 차이를 제대로 분리하지 않으면 화면은 그럴듯하게 보여도 실제 운영에서는 바로 문제가 생겼습니다.
처음에는 단순히 예약 폼을 만드는 작업이라고 생각했지만, 실제로는 카페 운영 정책을 데이터 구조로 옮기는 작업이었습니다. 어떤 예약을 허용할지, 어떤 예약을 막을지, 어떤 상태를 관리자에게 보여줄지 하나씩 정리하면서 시스템이 점점 실제 운영에 맞게 바뀌었습니다.
다음 글에서는 관리자 페이지를 어떻게 만들었는지 정리해보려고 합니다. 예약 목록만 보여주는 화면에서 시작해서, 좌석 모니터, 회원 관리, 매출 정리, 환불 처리까지 어떻게 확장됐는지를 다뤄보겠습니다.