본문 바로가기
AI 리더의 시대

티켓 예약 DB 설계 실습 가이드

by woojoon 2025. 12. 16.
반응형

 

 

 

티켓 예약 시스템은 “좌석 선택 → 홀드 → 결제 → 발권 → 취소/환불”의 짧은 여정을 동시에 수많은 사용자가 수행하는 환경을 전제로 합니다. 인기 공연·항공권에서는 두 사람이 같은 좌석을 잡으려는 레이스 컨디션이 상시 발생하므로, DB 설계 단계에서 중복 예약을 원천 차단해야 합니다. 최신 자료(Ticketmaster 시스템 설계, Redis 기반 좌석 홀드/TTL, GeeksforGeeks 예약 DB 가이드)가 공통으로 강조하는 세 가지는 다음과 같습니다. 첫째, 좌석 선택 시 짧은 TTL을 둔 임시 예약(HOLD) 상태를 만들어 중복 구매를 막는다. 둘째, 결제와 좌석 확정은 하나의 트랜잭션 혹은 이벤트 체인으로 묶어 “돈만 내고 좌석은 없는” 상황을 막는다. 셋째, 만료·환불·정산까지 전 과정을 로그로 남겨 운영·CS를 단순화한다. 여기에 실시간 좌석 수 알림(웹소켓·SSE)과 “결제 실패 직후 몇 초간 홀드 유지” 같은 운영 팁을 더하면 사고를 크게 줄일 수 있습니다. 특히 오픈런 상황에서는 좌석 락과 결제 처리 시간이 곧 매출과 직결되므로, 설계 단계에서 “락 유지 시간, 재시도 정책, 만료 후 복구 시간”을 함께 정의해야 합니다. 

요구사항으로 정리한 핵심 엔티티

무엇을 팔고 누가 사는지, 그리고 돈과 좌석이 어떻게 연결되는지부터 정리합니다. 필수 엔티티는 사용자(회원·비회원), 이벤트, 회차, 좌석, 예약, 결제, 환불, 정산, 쿠폰·프로모션, 운영 로그입니다. 관계는 단순합니다. 이벤트–회차는 1:N, 회차–좌석은 회차별 좌석 스냅숏을 둬 1:N으로 안전하게 만들고, 사용자–예약은 1:N, 예약–좌석은 1:N(예약당 여러 좌석) 또는 예약_좌석 테이블로 N:M을 표현합니다. 결제는 예약과 1:1 또는 1:N(추가 결제)로, 환불은 결제와 1:N으로 두어 이력을 남깁니다. 정산은 회차 매출 집계와 주최자 정산 테이블로 나누면 회계 대응이 간단해집니다. 공연장(venue)과 좌석 등급(price tier) 엔티티를 두면 동일 공연을 다른 장소에서 열 때 스키마 재사용이 쉬워지고, “동일 좌석이라도 회차별로 가격이 다를 수 있음”을 자연스럽게 모델링할 수 있습니다. 사용자 엔티티에는 국가/통화/선호 결제수단을 넣어 글로벌 판매를 대비하고, 쿠폰·포인트는 발급/사용/만료를 모두 기록해 재무 정산의 근거로 삼습니다. 운영 로그는 상태 전환, 오류, 사용자 행동까지 담아 분쟁 발생 시 근거가 되고, 데이터 마트로 보내 추천·수요 예측에도 활용할 수 있습니다. 리포트 요구가 많은 경우 예약·결제·환불을 위한 별도 요약 테이블을 배치로 생성해 대시보드 성능을 확보할 수 있습니다.

좌석 예약, 결제, 환불 스키마 설계

좌석 충돌을 막는 핵심은 “짧은 홀드 + 자동 해제”입니다. 회차별 좌석 테이블에 상태(AVAILABLE/HOLD/BOOKED)와 만료시각을 두고, 좌석 선택 시 트랜잭션에서 SELECT … FOR UPDATE SKIP LOCKED로 행을 잡아 HOLD와 만료시각을 기록합니다. 분산 환경이면 Redis 락에 TTL을 더해 중복 홀드를 차단합니다. 만료된 HOLD는 워커가 AVAILABLE로 복구하고, 결제가 끝나면 BOOKED로 전환합니다. 예약 테이블은 사용자·회차·금액·상태(PENDING/PAID/CANCELED/REFUNDED)와 만료시각, 결제 ID를 담아 흐름을 한눈에 보이게 합니다. 좌석 연결 테이블에는 좌석 ID와 가격 스냅숏(등급·금액·수수료)을 함께 저장해 정책 변경 후에도 과거 영수증을 재현합니다. 결제는 수단·통화·거래 ID·상태(REQUESTED/SUCCESS/FAILED/REFUNDED)를 기록하고, 환불은 원 결제와 금액·사유·상태를 남깁니다. 정산 테이블은 회차 매출, 수수료, 주최자 몫, 세금, 정산 예정일을 저장해 회계 요청에 즉시 응답하도록 합니다. 좌석 상태 변경 이벤트를 메시지 큐로 발행하면 실시간 좌석 수 UI와 알림(웹소켓/SSE) 구현이 쉬워지고, 부하가 많을 때는 “홀드 시간 5분 → 3분”처럼 운영자가 설정만 바꿔 즉시 반영할 수 있습니다. 결제 실패나 타임아웃이 발생했을 때 홀드가 즉시 풀려버리면 사용자 경험이 나쁘므로, 10~20초 정도 짧은 그레이스 기간을 두고 재시도를 허용하는 것도 실무에서 효과적입니다. 전체 흐름은 “좌석 HOLD → 예약 생성 → 결제 → 성공 시 BOOKED, 실패·만료 시 AVAILABLE”로 단순화하고, 상태 전환을 이벤트 로그에 남기면 운영/CS 추적이 쉬워집니다.

성능과 무결성을 지키는 DB 관리 기준

성능의 핵심은 좌석 조회와 상태 전환입니다. 회차 좌석 테이블에 session_id + status, session_id + section + row_num 복합 인덱스를 두면 잔여 좌석 검색이 빨라지고, 예약·결제·환불은 시계열이므로 월 단위 파티셔닝을 검토해 백업·아카이브를 단순화합니다. 캐싱은 이벤트 목록, 회차 스케줄, 잔여 좌석 수 집계에 적용하되, 좌석 상태는 DB 우선으로 관리해 불일치를 막습니다. 무결성은 FK와 UNIQUE로 중복 예약을 차단하고, 동시성은 DB 락 또는 Redis 락으로 보호하며 필요시 버전 칼럼을 둔 OCC를 적용합니다. 운영 측면에서는 만료 HOLD 회수 워커, 결제 실패 재시도 큐, 환불·정산 배치, 좌석 불일치 리포트를 준비하고, 좌석 락 실패율·HOLD 만료 회수·결제 실패율·환불 지연·FK 위반·캐시 미스율을 모니터링 지표로 삼습니다. 보안은 비밀번호 해시, 결제 토큰화, PII 최소 수집·암호화, 감사 로그가 기본이며, 오픈런 전에는 쿼리 계획·인덱스·캐시 만료 정책을 점검하고 스테이징 부하 테스트로 “한 좌석 두 번 판매”를 예방합니다. 카드 한도 초과나 3D 인증 실패가 잦은 구간에서는 “좌석 홀드 유지 시간 단축 + 결제 재시도 횟수 제한”으로 부정 결제와 좌석 독점을 줄일 수 있고, 장애 시에는 좌석 상태를 “부분 차단”으로 전환해 새로운 홀드를 잠시 막고, 기존 결제 흐름만 마무리하는 세이프 모드를 마련해 두면 손실을 최소화할 수 있습니다. 마지막으로, SLA 관점에서 “홀드 생성~결제 완료 3초 내, 좌석 상태 불일치 0건, 환불 처리 24시간 이내” 같은 목표를 정하고 대시보드에 노출하면 팀이 동일한 지표를 보며 운영 품질을 관리할 수 있습니다. 규제 준수를 위해 결제 기록 보존 기간, PII 보존·파기 정책, 환불 처리 기한을 스키마와 함께 문서화하고, 분기마다 복구 리허설을 실행하면 감사를 대비한 신뢰성을 확보할 수 있습니다. 마지막으로, QA 환경에서 실제 결제 샌드박스와 동일한 좌석 락·만료 로직을 반복 검증하고, 퍼포먼스 테스트 시 “좌석 락 성공률, 결제 완료율, 만료 복구 시간”을 함께 측정하면 배포 리스크를 크게 낮출 수 있습니다. 이런 반복 검증과 모니터링이 쌓이면, 좌석과 돈이 맞지 않는 희귀한 오류도 조기에 발견하고 선제적으로 조치할 수 있는 체계가 완성됩니다. 결국 이 모든 체크리스트는 “좌석은 정확히 한 번만 팔리고, 돈은 정확히 한 번만 청구된다”는 단순한 목표를 지키기 위해 존재한다는 점을 기억하면 설계·운영 의사결정이 한결 수월해집니다.

반응형