1단계 문제 이해 및 설계 범위 확정
기능 요구사항
- 호텔 정보 페이지 표시
- 객실 정보 페이지 표시
- 객실 예약 지원
- 호텔이나 객실 정보를 추가/삭제/갱신하는 관리자 페이지 지원
- 초과 예약 지원
- 10% 초과 예약 가능, 실제 객실 수보다 더 많은 객실을 판매할 수 있어야 함
- 객실 가격 유동적
비기능 요구사항
- 높은 수준의 동시성 지원 : 성수기, 대규모 이벤트 기간에는 일부 인기 호텔의 특정 객실을 예약하려는 고객이 많이 몰릴 수 있다.
- 적절한 지연 시간 : 사용자가 예약을 할 때는 응답 시간이 빠르면 이상적이겠으나 예약 요청 처리에 몇 초 정도 걸리는 것은 괜찮다.
개략적 규모 추정
- 총 5,000개 호텔, 100만 개의 객실이 있다고 가정한다.
- 평균적으로 객실의 70%가 사용 중이고, 평균 투숙 기간은 3일이라고 가정한다.
- 일일 예상 예약 건수 : 3 ( 초당 예약 트랜잭션 수 - TPS)
다음으로 시스템 내 모든 페이지의 QPS(Queries-per-seoned)를 계산해 보자.
일반적으로 고객이 이 웹사이트를 사용하는 흐름에는 세 가지 단계가 있다.
- 호텔/객실 상세 페이지: 사용자가 호텔/객실 정보를 확인한다 (조회 발생)
- 예약 상세 정보 페이지: 사용자가 날짜, 투숙 인원, 결제 방법 등의 상세 정보를 예약 전에 확인한다 (조회 발생)
- 객실 예약 페이지: 사용자가 '예약' 버튼을 눌러 객실을 예약한다 (트랜잭션
발생)
대략 10%의 사용자가 다음 단계로 진행하고 90%의 사용자는 최종 단계에 도달하기 전에 흐름을 이탈한다고 하자. 아울러 다음 단계에 표시될 내용을 미리 계산해 두는 방안은 고려하지 않는다고 가정할 것이다.
최종 예약 TPS(Transaction Per Second)는 3이라는 것을 알고 있으므로, 그 수치에서 역산한 결과다. 예약 페이지의 QPS는 30, 그리고 객실 정보 확인 페이지의 QPS는 300이다.
2단계: 개략적 설계안 제시 및 동의 구하기
이번 절에서는 다음 사항을 살펴본다.
API 설계
호텔 예약 시스템의 API 설계안을 살펴보자. 가장 중요한 APT를 RESTFul 관례에 따라 나열해 보았다.
호텔 관련 API
객실 관련 API
예약 관련 API
신규 예약 접수는 아주 중요한 기능이다. 새 예약을 만들 때 API(POST /V1/ reservations)에 전달하는 인자의 형태는 다음과 같다.
{
"startDate": "2021-04-28",
"endDate": "2021-04-30"
"hotelID": "245",
"roomID": "U12354673389",
"reservationID": "13422445"
}
researvationID는 이중 예약을 방지하고 동일한 예약은 단 한번만 이루어지도록 보증하는 멱등 키(idempotent key)다. 여기서 이중 예약은 같은 날 같은 객실에 예약이 중복으로 이루어지는 것을 말한다.
데이터 모델
어떤 데이터베이스를 사용할지 결정하기 전에 데이터 접근 패턴부터 자세히 살펴보자. 호텔 예약 시스템은 다음 질의를 지원해야 한다.
- 질의 1: 호텔 상세 정보 확인
- 질의 2: 지정된 날짜 범위에 사용 가능한 객실 유형 확인
- 질의 3: 예약 정보 기록
- 질의 4: 예약 내역 또는 과거 예약 이력 정보 조회
대략적인 추정 과정을 통해 시스템 규모가 크지 않은 것은 알았으나 대규모 이벤트가 있는 경우에는 트래픽이 급증할 수도 있으니 대비해야 한다. 이런 요구 사항을 종합적으로 고려하였을 때 본 설계안에서는 관계형 데이터베이스를 선택할 것이다.
- 관계형 데이터베이스는 읽기 빈도가 쓰기 연산에 비해 높은 작업 흐름을 잘 지원한다. 호텔 웹사이트/앱을 방문하는 사용자의 수는 실제로 객실을 예약 하는 사용자에 비해 압도적으로 많다. NosQL 데이터베이스는 대체로 쓰기 연산에 최적화되어 있다. 관계형 데이터베이스는 읽기가 압도적인 작업 흐름은 충분히 잘 지원한다.
- 관계형 데이터베이스는 ACID 속성(원자성, 일관성, 격리성, 영속성)을 보장한다. ACID 속성은 예약 시스템을 만드는 경우 중요하다. 이 속성이 만족되지 않으면 잔액이 마이너스가 되는 문제, 이중 청구 문제, 이중 예약 문제 등을 방지하기 어렵다. ACID 속성이 충족되는 데이터베이스를 사용하면 애플리케이션 코드는 훨씬 단순해지고 이해하기 쉬워진다. 관계형 데이터베이스는 일반적으로 ACID 속성을 보장한다.
- 관계형 데이터베이스를 사용하면 데이터를 쉽게 모델링할 수 있다. 비즈니스 데이터의 구조를 명확하게 표현할 수 있을 뿐 아니라 엔티티(호텔, 객실, 객실 유형 등) 간의 관계를 안정적으로 지원할 수 있다.
아래는 스키마 설계이다.
reservation 테이블의 status 필드는 pending, paid, refunded, canceled, rejected 즉 결제 대기, 결제 완료, 환불 완료, 취소, 승인 실패의 다섯 상태 가운데 하나를 값으로 가질 수 있다.
하지만 이 스키마 디자인에는 큰 문제가 있다. room_id가 있기 때문에 에어비앤비 같은 회사에는 적합하지만 호텔은 그렇지 않다.
사용자는 특정 객실을 예약하는 것이 아니라 특정 호텔의 특정 객실 유형을 예약하기 때문이다.
여기서 객실 유형은 스탠다드 룸, 킹 사이즈 룸, 퀸 사이즈 룸 등이 될 수 있다.
객실 번호는 예약할 때가 아닌, 투숙객이 체크인 하는 시점에 부여된다. 이 요구사항을 반영하려면 데이터 모델을 손볼 필요가 있다.
개략적 설계안
이 호텔 예약 시스템에는 마이크로서비스(microservice) 아키텍처를 사용한다.
지난 몇 년 동안 이 아키텍처는 많은 인기를 끌었다. 마이크로서비스 아키텍처를 채택한 회사로는 아마존, 넷플릭스, 우버, 에어비앤비, X(구 트위터) 등이 있다.
위 설계안의 각 컴포넌트는 아래와 같다.
- 사용자: 휴대폰이나 컴퓨터로 객실을 예약하는 당사자다.
- 관리자(호텔 직원): 고객 환불, 예약 취소, 객실 정보 갱신 둥의 관리 작업을 수행할 권한이 있는 호텔 직원이다.
- CDN(콘텐츠 전송 네트워크): 자바스크립트 코드 번들, 이미지, 동영상, HTML 등 모든 정적 콘텐츠를 캐시하여 웹사이트 로드 성능을 개선하는 데 이용된다.
- 공개 API 게이트웨이: 처리율 제한(rate limiting), 인증 등의 기능을 지원하는 완전 관리형 서비스(fully managed service)다. 엔드포인트 기반으로 특정 서비스에 요청을 전달할 수 있도록 구성된다. 예를 들어 호텔 홈페이지 요청은 호텔 서비스로, 호텔 객실 예약 요청은 예약 서비스로 전달하는 역할을 담당한다.
- 내부 API: 승인된 호텔 직원만 사용 가능한 API로, 내부 소프트웨어나 웹사이트를 통해서 사용 가능하다. VPN(Virtual Private Network, 즉 가상 사설 망) 등의 기술을 사용해 외부 공격으로부터 보호한다.
- 호텔 서비스: 호텔과 객실에 대한 상세 정보를 제공한다. 호텔과 객실 데이터는 일반적으로 정적이라서 쉽게 캐시해 둘 수 있다.
- 요금 서비스: 미래의 어떤 날에 어떤 요금을 받아야 하는지 데이터를 제공하는 서비스다. 재미있는 것은 객실의 요금은 해당 날짜에 호텔에 얼마나 많은 손님이 몰리느냐에 따라 달라진다는 것이다.
- 예약 서비스: 예약 요청을 받고 객실을 예약하는 과정을 처리한다. 객실이 예약되거나 취소될 때 잔여 객실 정보를 갱신하는 역할도 담당한다.
- 결제 서비스: 고객의 결제를 맡아 처리하고, 절차가 성공적으로 마무리되면 예약 상태를 결제 완료로 갱신하며 실패한 경우에는 승인 실패로 업데이트 한다.
- 호텔 관리 서비스: 승인된 호텔 직원만 사용 가능한 서비스다. 임박한 예약 기록 확인, 고객 객실 예약, 예약 취소 등의 기능을 제공한다.
아래는 각 서비스 간의 연결을 간략하게 나타낸 도식이다.