시스템 디자인 아틀라스사례 연구 / 트랜잭션 / Hotel Reservation학습 로드맵
CASE STUDY · ADVANCED읽기 26분검토일 2026-08-23

호텔 예약
시스템 설계

예약 버튼 하나 뒤에는 날짜별 희소 재고, 동시에 몰리는 결제, 늦게 도착하는 웹훅이 있습니다. 검색 결과를 정본으로 믿지 않고, 짧은 홀드·원자적 재고 판정·멱등 결제 확정·조정을 연결해 초과 판매를 막습니다.

재고·홀드결제 확정장애·조정면접 답변진도 저장
30초 핵심 요약

재고의 진실은 날짜별 inventory row에 둡니다. hold는 모든 숙박일을 원자적으로 검사해 짧게 차감하고, 결제 성공만 confirmed로 이동합니다. timeout·웹훅 재전송·만료 경합은 상태 전이, event inbox, payment 조회와 조정으로 풀며, 검색 cache는 최종 판매 권한이 아닙니다.

Invariantheld + confirmed ≤ sellable
Checkoutidempotent hold + confirm
Unknown paymentquery + webhook + reconcile
01 · REQUIREMENTS

희소 재고와 외부 결제를 같은 성공으로 착각하지 않는다

# 요구사항

“객실이 보였다”는 것은 검색 snapshot이고, “객실이 확보됐다”는 것은 정본 재고의 상태 변화입니다. 예약 서비스는 날짜 구간 재고와 금전 결과를 각각 검증한 뒤 사용자에게 하나의 예약 상태로 보여 줍니다.

R1날짜 구간 재고

체크인부터 체크아웃 전날까지 모든 inventory day가 판매 가능한지 원자적으로 판정합니다.

R2짧은 hold

결제 시간만큼 객실을 보류하고, 명시적 만료 release로 유령 홀드를 회수합니다.

R3한 번의 업무 효과

재시도·중복 클릭·웹훅 재전송에도 hold, 예약, 결제 확정이 각각 한 번만 적용됩니다.

R4감사·복구

상태·수량·payment reference·운영자 조정의 before/after를 삭제하지 않고 남깁니다.

02 · HIGH-LEVEL DESIGN

검색, 날짜 재고, 홀드, 결제, 조정을 분리한다

# 아키텍처

최종 hold는 cache가 아니라 inventory coordinator의 조건부 갱신에서 결정합니다. 결제의 동기 응답·조회·webhook은 같은 payment intent를 보강하는 입력이고, 예약 확정은 단조 상태 전이와 이벤트 inbox를 통과해야 합니다.

호텔 예약의 판매·확정·복구 경로 SVG DIAGRAM · inventory truth is versioned
호텔 예약 시스템 아키텍처사용자는 검색과 견적을 거쳐 예약 API에 홀드를 요청한다. 예약 API는 날짜별 재고와 홀드 레코드를 원자적으로 갱신한다. 결제 의도와 PSP 웹훅은 상태 전이 서비스를 지나 확정 예약과 조정 큐로 이어진다.Search / Quotecache · price versionReservation APIidempotency · holdInventory dayversion · conditional writePayment / PSPintent · signed webhookHold recordexpiry · quote snapshotStateinboxReconcileexception queue검색 cache는 후보를 빠르게 하지만, hold·confirm은 날짜별 정본 불변식을 다시 검사한다.
정합성 경계: 어떤 구현이든 commit 시점의 조건은 held_count + confirmed_count < sellable_count여야 합니다. 여러 숙박 날짜에서 하나라도 실패하면 전체 hold는 실패합니다. 검색 결과의 “잔여 1개”는 판매 권한이 아닙니다.
03 · REQUEST FLOW

재고를 먼저 확보하고, 모르는 결제 결과는 기다려 확인한다

# 요청 흐름
1견적 검증

호텔 현지 날짜, 인원, rate plan, 가격 version과 요청 fingerprint를 확인합니다.

2원자 hold

날짜 행을 정렬 순서로 검사·차감하고 hold, expiry, outbox를 함께 커밋합니다.

3PSP intent

동일 hold의 payment intent를 재사용합니다. timeout에는 재승인 대신 조회·webhook을 기다립니다.

4확정·회수

검증된 성공만 held→confirmed로 옮기고, 만료·실패·취소는 수량을 release합니다.

04 · TRADEOFFS

경합 제어의 선택은 재고 불변식을 대체하지 않는다

# 대안 비교

락, 버전 조건, queue, 다지역 writer 중 무엇을 선택해도 최종 판매 수량의 조건부 판정은 필요합니다. 특히 여러 숙박 날짜에 걸친 hold는 단일 key의 간단한 CAS보다 더 큰 원자성 범위를 요구합니다.

선택강점주의점권장 판단
낙관적 version 조건짧은 경합에서 lock wait가 작음인기 날짜 retry·thundering herd일반 inventory row의 기본 후보
정렬된 row lock다중 날짜 transaction을 이해하기 쉬움deadlock·긴 transaction 위험숙박일 상한이 작고 DB가 정본일 때
per-key queuehot hotel/date의 burst를 흡수queue 장애·대기 UX·처리 지연최종 조건부 write 앞의 완충 계층
다중 region writer지역 지연·가용성 개선concurrent sell의 oversell 조정명시적 buffer·보상 체계가 있을 때
hold 없이 결제 후 확정단계와 TTL이 적음결제 성공 후 객실 없음희소 객실에는 사용하지 않음
05 · FAILURE MODES

초과 판매, 유령 홀드, 알 수 없는 결제를 각각 복구한다

# 장애 9가지
동시 hold 경합

같은 날짜·객실 유형에 여러 checkout이 몰리면 낡은 count를 읽은 요청이 함께 성공할 수 있습니다.

대응 · commit 시 조건부 갱신, 정렬 lock, 날짜별 불변식 검사로 sold-out을 결정합니다.
재시도·중복 클릭

네트워크 재전송이나 두 탭이 동일 예약을 여러 hold로 만들 수 있습니다.

대응 · idempotency key와 request fingerprint로 최초 hold·응답을 재사용하고 mismatch는 거절합니다.
?PSP timeout

고객에게는 응답이 없지만 PSP에서 실제 승인됐을 수 있습니다.

대응 · 신규 결제를 만들지 않고 payment ID 조회·서명된 webhook으로 unknown 상태를 해소합니다.
webhook 재전송·역순

늦은 실패 이벤트가 이미 confirmed인 예약을 덮어쓰거나 확정이 반복될 수 있습니다.

대응 · event inbox dedupe, signature 검증, 단조 상태 전이와 payment reference unique 제약을 둡니다.
expiry worker 지연

실패한 checkout의 객실이 TTL 이후에도 held로 남아 판매 가능 수량을 막습니다.

대응 · scan·retry와 compare-and-set release, expired-hold age alarm으로 회수합니다.
결제 성공·만료 경합

만료 처리와 늦은 payment success가 같은 hold를 반대 상태로 바꾸려 합니다.

대응 · 상태 우선순위·외부 조회·운영 exception queue로 단일 결과를 결정합니다.
DB failover/부분 처리

inventory commit은 됐지만 notification이나 분석 이벤트만 누락될 수 있습니다.

대응 · transaction rollback과 outbox replay를 분리하고 정본 version으로 재생성합니다.
검색 cache stale

객실이 보이지만 hold 시점에는 이미 매진되어 고객 혼란이 생깁니다.

대응 · hold의 정본 재검사를 진실로 두고 cache age·quote mismatch를 관측합니다.
!수동 재고 변경

운영자 변경이 audit 없이 들어가면 음수 재고와 보상 책임을 설명할 수 없습니다.

대응 · 역할 분리, 승인 ticket, append-only adjustment와 inventory reconciliation을 사용합니다.
06 · OPERATIONS

PII, 정합성 지표, 결제 비용을 판매 흐름과 함께 운영한다

# 운영
보안·개인정보

투숙객 PII는 inventory와 분리하고 최소 권한·암호화·audit·삭제 보존 정책을 적용합니다. 카드 원문은 저장하지 않으며, webhook은 secret·서명·timestamp를 검증합니다.

PII / inventory / payment scope 분리
관측 가능성

날짜별 불변식, conditional failure, lock wait, hold→confirm, expiry lag, unknown payment age, webhook duplicate, outbox lag를 hotel·room type·day bucket으로 관측합니다.

held + confirmed ≤ sellable
비용 모델

달력 재고·감사·백업, hotspot retry/queue, cache purge, PSP 승인·조회·환불, reconciliation와 고객 지원이 함께 비용을 만듭니다. timeout 재승인은 비용 절감책이 아닙니다.

storage + contention + PSP + ops
면접 모드 · 추가 질문07:00
“프로모션 시작 1분에 특정 호텔의 토요일 Deluxe King 1개에 수천 명이 결제를 시도합니다. 초과 판매 없이 hold, 결제 timeout, 만료, webhook 재전송, hot key 처리와 운영자 조정을 어떤 순서로 설명하시겠습니까?”
날짜별 불변식정렬 lock / conditional writeidempotent holdpayment unknownexpiry + reconcile
SOURCES

공식·1차 출처와 설계 가정

LEARNING ROADMAP다음 시스템 디자인 주제 보기
EDITORIAL NOTES

작성·검토·참고 자료

콘텐츠 원칙
이 문서는 독립적으로 재작성한 한국어 학습 자료입니다. 사실과 학습용 설계 가정을 구분합니다.
최종 검토
예상 학습 시간
26분

참고 자료

  • Stripe API — Idempotent requests — POST 요청 재시도에서 멱등 키의 동작과 보존 경계를 확인했다.
  • Stripe Docs — Webhooks — webhook 서명 검증, 재전송과 endpoint 처리 지침을 확인했다.
  • PostgreSQL — Transaction Isolation — 격리 수준과 동시 transaction의 가시성·재시도 고려를 확인했다.
  • PostgreSQL — Explicit Locking — row-level lock과 deadlock 회피를 위한 lock 순서 고려를 확인했다.
  • Amazon DynamoDB — Condition expressions — 조건을 만족할 때만 write를 수행하는 조건식의 공식 예시를 확인했다.
  • PCI SSC — PCI DSS v4.0.1 — 결제 계정 데이터 보호와 접근 통제의 기준 문서를 확인했다. 이 문서의 수요, QPS, TTL, cache 정책, shard 기준, 상태 우선순위와 비용은 학습용 **설계 가정/제안**이다. 실제 서비스에서는 호텔 계약, 지역 법률, 결제사 규칙, 부하·장애 시험과 운영 역량으로 재검증해야 한다.

사실 오류·출처 정정은 문의·정정 페이지로 알려 주세요.