5 minute read

시스템 디자인을 공부하다 보면 대부분의 자료가 영어로 작성되어 있고, 주로 영어권 서비스를 대상으로 하고 있습니다. 이 때문에 한국 개발자들은 시스템 디자인 자체뿐만 아니라 해당 서비스에 대한 이해도 함께 필요하게 되어 학습 과정이 더 복잡해지는 경우가 많습니다. 이를 해결하기 위해, 이번 글에서는 한국의 대표적인 서비스를 기반으로 시스템 디자인을 공부하고, 유사한 해외 서비스를 함께 연결해 보려 합니다.

첫 번째로 다룰 서비스는 인터파크 티켓입니다. 현재 야놀자가 인수해 운영하는 티켓 구매 서비스로, 공연부터 전시회까지 다양한 종류의 티켓을 판매합니다. 유사한 국내 서비스로는 멜론 티켓, Yes24 티켓 등이 있으며, 영어권에서는 시스템 디자인 인터뷰에 자주 등장하는 Ticketmaster가 대표적입니다. 또한, CGV, 메가박스 등의 영화 예매 앱도 유사한 예매 절차를 가지고 있습니다.

이번 글에서는 좌석이 지정된 공연(예: 콘서트, 뮤지컬)의 티켓 구매 절차를 따라가 보겠습니다. 이 글은 Hello Interview의 System Design Interview: Design Ticketmaster 영상을 참고하여, 한국 서비스에 맞게 재구성한 것입니다. 목표는 약 40분 동안 시스템 디자인 인터뷰를 진행할 수 있도록 준비하는 것입니다.

먼저, 경쟁 상태가 발생하지 않는 경우를 가정하여 기본적인 서비스 설계를 진행한 후, 트래픽이 몰리는 상황에서 필요한 기능을 추가하는 방식으로 확장해 보겠습니다.

티켓 예매 시스템의 기본 흐름 따라가기Permalink

먼저, 일반적인 티켓의 예매 절차를 따라가보도록 하겠습니다. image image image

사용자는 앱에 접속한 후, 검색창에서 원하는 공연명이나 가수명을 입력하여 공연을 검색할 수 있습니다. 검색 결과 페이지에서는 사용자의 키워드와 일치하는 공연 목록이 표시되며, 사용자는 원하는 공연을 선택하여 예매 화면으로 이동합니다.

image 여기서 예매하기를 누르면 원하는 날짜 / 회차를 선택합니다. image image

그럼 해당 공연을 하는 공연장의 좌석표가 뜨고, 현재 구매 가능한 티켓들의 위치가 표시됩니다. 그 중 하나를 선택하면 배송지를 지정하고 결제를 할 수 있습니다.

image

결제까지 완료한 티켓은 유저의 ‘예매 확인’창에서 확인 가능합니다.

RequirementsPermalink

시스템을 설계하려면 먼저 요구사항을 명확히 정리하는 것이 중요합니다. 시스템이 가져야 할 핵심 기능을 정의하고, 트래픽 증가나 동시성 문제에 대한 비기능 요구사항도 함께 고려해야 합니다. 앞서 정리한 티켓 예매 흐름을 바탕으로, 시스템 설계를 위한 요구사항을 분석해 보겠습니다.

Functional RequirementsPermalink

티켓 예매 시스템이 기본적으로 제공해야 할 기능을 정리하면 다음과 같습니다.

  • 유저는 공연을 검색할 수 있습니다.
  • 유저는 특정 공연의 상세 정보를 조회할 수 있습니다.
  • 유저는 날짜 및 좌석을 선택하여 티켓을 구매할 수 있습니다.

단, 이 글에서는 핵심적인 예매 흐름에 집중하기 위해, 다음 기능들은 다루지 않습니다.

  • 좌석 없이 예매하는 이벤트(전시회, 페스티벌 등)
  • 유저 회원가입
  • 예매 서비스에 새 공연을 추가하는 기능
  • 공연마다/좌석마다 다른 가격을 설정하는 기능
  • 선예매
  • 유저 후기 기능
  • 할인 서비스
  • 간편결제
Non-functional RequirementsPermalink

성공적인 시스템 설계를 위해서는 기능적인 요구사항뿐만 아니라, 시스템의 성능과 안정성을 고려한 비기능 요구사항도 중요합니다.

  • 티켓 판매는 반드시 일관성을 유지해야 합니다.
    • 같은 공연, 같은 날짜, 같은 좌석이 두 명에게 동시에 팔리는 일이 있어서는 안 됩니다.
    • 즉, 티켓 판매에 있어서는 일관성(Consistency)이 가장 중요한 요소입니다.
  • 반면, 공연 검색 및 상세 정보 조회는 고가용성을 유지해야 합니다.
    • 검색 및 조회 기능에서는 최신 데이터가 아니어도 크게 문제가 되지 않습니다.
    • 따라서 가용성(Availability)을 우선 고려합니다.
  • 인기 있는 공연의 경우, 대규모 트래픽을 감당할 수 있어야 합니다.
    • 특정 인기 공연은 수십만 명이 동시에 예매를 시도할 수 있음을 고려해야 합니다.
    • 이를 해결할 수 있도록 수평 확장(Scalability)이 가능해야 하며, 부하를 줄이는 기술(예: 큐잉 시스템, 캐싱, 레이트 리미팅 등)이 필요합니다.

Core EntitiesPermalink

예상되는 Core Entity들을 정의합니다. 여기서 떠오르는거 먼저 간단히 정의한 후, 이후 실제 디자인하는데 필요하다면 추가하도록 합니다. 각 Entity의 Id는 생략합니다.

  • User
    • 예약을 진행하는 사용자
    • Bookings[]
  • Event
    • 공연 Entity
    • Title, PerformanceDateTime, Venue, Performer, Description, Tickets[] (해당 Event에서 판매하는 티켓 리스트)
  • Ticket
    • 티켓 정보
    • EventId, Status
  • Venue
    • 공연장
    • Events[] (해당 공연장에서 이루어지는 이벤트), Seatmap
  • Performer: 공연자
  • Booking
    • 예약정보
    • UserId, EventId, TicketId, Status

High level DesignPermalink

시스템 디자인 인터뷰에서는 제한된 시간 내에 전체적인 아키텍처를 설계해야 합니다. 일반적으로 1시간 이내의 인터뷰에서, 소개나 마지막 질의응답, 그리고 Behavior Questions(행동 관련 질문) 등을 제외하면 실제 설계에 주어진 시간은 약 30~40분 정도입니다.

이 짧은 시간 안에 완벽한 이상적인 디자인을 만드는 것은 현실적으로 어렵기 때문에, High-Level Design 단계에서는 핵심 컴포넌트와 API 설계에 집중하고, 이후 Low-Level Design에서 세부적인 최적화를 논의하자고 합의를 하는 것이 좋을 것 같습니다.

유저가 공연을 검색하게 하는 기능Permalink

검색 기능부터 디자인해보겠습니다. 위의 스크린샷을 다시 보면 검색창에 공연명을 쳐서 검색을 합니다. (여기엔 없지만 카테고리를 선택해 해당 카테고리 - 예를 들면 뮤지컬 - 의 공연 목록을 보는 방법도 있습니다.) 검색이 된 화면을 보면 ‘장르’, ‘날짜’, ‘지역’, ‘할인’과 같은 필터링이 가능하고 이에 매치되는 검색결과의 공연 리스트가 표시되네요. 공연 리스트의 공연 정보는 공연명, 장소, 날짜, 평점, Status(판매중) 등입니다. 우리의 Event Entity에 Status와 Rating을 추가할 수 있겠습니다.

image

맨 먼저 왼쪽에 Client를 두고 맨 오른쪽에 DB를 뒀습니다. DB는 일단 SQL database를 가정했습니다. 다른 대안이 있다면 이후에 면접관과 이야기하기로 하고요. Microservice를 디자인하기로 가정하고, 먼저 Client의 요청이 API Gateway로 갔다가 Search Service로 가고, 이 Search Service가 DB를 조회해 돌려주는 그림을 그려보았습니다.

image

실제로 어떤 API가 호출되고 어떤 쿼리가 날아갈지 구체적으로 상상해보았습니다. GET /search API가 존재하고 여기에 여러 가지 파라미터가 들어가는 것이 합당할 것으로 보입니다. 이 API는 파라미터에 맞는 이벤트 정보를 리턴합니다.

유저가 공연의 상세 정보를 조회하는 기능Permalink

검색을 해서든 메인화면의 배너를 보고서든 특정 이벤트에 진입하면 위의 검색결과보다 자세한 공연 정보와 함께 공연 예매를 진행할 수 있는 상세 페이지로 넘어가야 합니다. 이 상세 페이지에는 위에서 나왔던 공연명 등의 기본 정보에 더해서 공연 기획사에서 등록한 상세 설명이나 리뷰 등이 보입니다.

image

역시 이벤트를 담당하는 Event Service를 만들고, 유저의 요청이 이 Event Service로 가고, Event Service가 DB를 조회해서 원하는 정보를 한 번에 리턴합니다. path parameter로 검색 기능에서 리턴받은 Id를 다시 넘기는 것이 가장 합리적일 것으로 보입니다.

유저가 좌석을 조회하고 예매하는 기능Permalink

티켓 예매 서비스에서 가장 중요한 기능이라고 할 수 있습니다. 위의 화면으로 돌아가보면, 상세 정보 화면에서 ‘예매하기’를 누르면 공연일자 중 하루를 선택하고, 하루 안에 회차가 두 번 이상이라면 회차까지 선택합니다.

그러면 이번에는 좌석표와 함께 구매할 수 있는 좌석들이 표시됩니다. 예매할 좌석을 누르고 다음을 누르면 예매 내용 확인 및 결제 페이지로 넘어갑니다. 결제까지 완료하고 나면 유저는 본인의 예매 내역에서 예매 정보를 볼 수 있습니다.

예매하기 버튼을 눌렀을 때 날짜/회차/좌석 등을 선택하는 API를 Event Service에 둬야 하나 잠깐 고민했는데, 이후 대기열 기능을 생각해보니 확실히 Booking Service 쪽에 두는 게 맞을 것 같다고 생각했습니다.

image

먼저 좌석표와 예매 가능 좌석을 가져오는 GET API를 만들었습니다. 이를 호출해서 공연장의 좌석표와 현재 예매 가능한 좌석을 그려줘야겠습니다.

예매할 티켓을 선택해서 다음 화면으로 넘어가기 위한 API도 설계합니다. reserve API로, 이 API가 호출되면 일시적으로 해당 좌석을 선점한 상태가 됩니다. Booking Service는 eventId, 회차, 그리고 선택한 티켓 정보를 담아 Booking 엔티티를 생성하고 이 정보를 리턴해줍니다. 여기에는 기존에 유저가 설정해둔 배송지 정보나 결제 정보 등이 포함되어 있습니다.

마지막으로 예매를 완료하기 위한 confirm API를 설계합니다. 위에서 받은 Booking 정보를 유저가 수정한 후 확정 요청을 하는 API입니다. Booking Service는 이 정보에 있는 결제 정보를 기반으로 결제 모듈과 연동하여 결제를 처리합니다. 결제까지 완료된다면 Booking과 Ticket Status를 완료로 변경합니다.

이제 Functional Requirements들은 대충 완성한 것 같습니다. 하지만 Non-Functional Requirements를 고려하지 못해서 문제가 있을 것 같습니다. 예를들면 두명이 비슷한 시기에 좌석표를 조회해서 같은 좌석을 예매하려고 시도하는 경우, 둘 중 한명은 예약에 실패해야 합니다.

다음 글에서 예상되는 문제와 해결하기 위해 인터파크가 사용하고 있는 방법들을 살펴보고, Low-level design을 완성해보도록 하겠습니다.

한국 서비스를 기반으로 시스템 디자인 공부하기 – 인터파크 티켓 (하)