Design Starbucks, 모바일 주문을 매장 생산 순서와 재고 상태에 연결하는 시스템
고객이 앱에서 결제를 완료해도 주문이 곧바로 준비되는 것은 아닙니다. 매장의 현재 작업량, 음료 제조 설비, 재료 재고, 드라이브스루 대기열, 예약 픽업 시간을 함께 확인해야 주문 시간을 약속할 수 있습니다.
Introduction & Requirements
이 글에서 사용하는 Store Capacity Service, Order Promise Service, Smart Queue Orchestrator, Store Edge Gateway, Production Task Service 같은 이름도 공개된 Starbucks 내부 서비스 이름이 아닙니다. 모바일 주문, 매장 주문, 드라이브스루, 배달 주문을 함께 처리하는 대규모 커피 매장 플랫폼을 설명하기 위해 이 글에서 정의한 컴포넌트입니다.
Starbucks 앱에서는 사용자가 주변 매장을 찾고, 메뉴를 확인하고, 음료를 원하는 방식으로 변경한 뒤 주문할 수 있습니다. 참여 매장에서는 즉시 픽업뿐 아니라 최대 한 시간 뒤의 픽업 시간을 선택하는 예약 주문도 제공할 수 있습니다.
주문 기능만 보면 일반적인 전자상거래 시스템처럼 보일 수 있습니다.
메뉴 조회
-> 장바구니
-> 결제
-> 주문 완료하지만 커피 매장 주문은 창고에서 완성된 상품을 꺼내 배송하는 과정과 다릅니다.
음료는 주문이 들어온 뒤 매장에서 제조하게 됩니다. 같은 라테라도 크기, 우유 종류, espresso shot, syrup, cold foam, 온도와 얼음 양에 따라 제조 시간이 달라질 수 있습니다.
매장에는 동시에 여러 주문 채널이 들어옵니다.
카운터 주문
모바일 즉시 픽업
모바일 예약 픽업
드라이브스루
배달각 채널의 주문을 단순히 도착 순서대로 처리하면 문제가 생길 수 있습니다.
드라이브스루 차량이 계속 들어오면 모바일 주문이 오랫동안 밀릴 수 있습니다. 반대로 모바일 주문을 모두 먼저 처리하면 매장 안에서 기다리는 고객과 드라이브스루 고객의 대기 시간이 증가하게 됩니다.
예약 주문을 너무 많이 받으면 해당 시간에 현장 주문을 처리할 여유가 사라질 수 있습니다. 주문을 적게 받으면 매장의 생산 능력을 충분히 사용하지 못하게 됩니다.
그래서 이 시스템은 결제 요청을 받기 전에 다음 상태를 확인해야 합니다.
매장이 현재 주문을 받을 수 있는가?
요청한 상품이 해당 매장에서 판매 중인가?
필요한 재료가 남아 있는가?
요청한 픽업 시간에 생산 여유가 있는가?
해당 채널이 현재 운영 중인가?
결제 금액과 리워드 사용 결과가 유효한가?“우리가 설계하는 것은 음료 주문 API인가요, 아니면 여러 주문 채널과 매장 생산 능력을 함께 제어하는 매장 운영 시스템인가요?”
주문 API만 설계한다면 주문 row를 저장하고 결제를 처리하면 됩니다.
하지만 매장 운영까지 포함하면 메뉴 availability, 제조 작업, 픽업 시간, 재고, 매장별 설비, 채널별 대기열, POS 상태, 배달 기사 도착 시간까지 함께 관리해야 합니다.
주문 승인과 제조 시작은 같은 상태가 아닙니다
앱에서 결제가 승인되었다고 해서 바리스타가 이미 음료를 만들고 있다는 뜻은 아닙니다. 주문은 다음과 같은 상태를 거칠 수 있습니다.
cart_created
quote_ready
payment_pending
confirmed
queued_at_store
in_preparation
ready_for_pickup
picked_up
completed
cancelled
refund_pending
refundedconfirmed는 결제와 매장 수용 가능 여부가 확인되어 주문이 확정된 상태입니다.
queued_at_store는 주문이 매장 시스템에 전달되어 제조 순서를 기다리는 상태입니다.
in_preparation은 하나 이상의 상품이 실제 제조 작업에 들어간 상태입니다.
앱에서는 이 상태를 구분해서 보여줘야 합니다.
주문을 확인하고 있습니다
매장에서 주문을 받았습니다
음료를 만들고 있습니다
픽업할 수 있습니다결제 승인 직후 앱에 “제조 중”이라고 표시하면 실제 매장 시스템 전달이 지연된 상황을 정확히 표현하지 못하게 됩니다.
현재 재고와 앱 메뉴는 완전히 동시에 바뀌지 않을 수 있습니다
매장에 oat milk가 한 개 남았다고 가정하겠습니다. 두 사용자가 거의 같은 시각에 oat milk 음료를 주문할 수 있습니다.
User A
-> oat milk 주문
User B
-> oat milk 주문메뉴 화면의 availability만 보고 두 주문을 모두 허용하면 매장은 한 주문을 만들 수 없게 됩니다. 반대로 모든 메뉴 조회에서 POS 재고를 실시간으로 읽으면 앱 메뉴 조회가 매장 네트워크와 POS 상태에 의존하게 됩니다. 그래서 조회용 availability와 주문 확정 시점의 재검증을 나눠야 합니다. 메뉴 화면에서는 최근 재고 상태를 기반으로 상품을 표시합니다. 결제 직전에는 매장 재고와 수용 가능 시간을 다시 확인합니다.
조회용 데이터가 실제 원본보다 잠시 늦게 갱신될 수 있는 구조를 최종적 일관성(eventual consistency)이라고 부릅니다. 이 구조에서는 사용자가 메뉴를 보는 동안 상품이 품절될 수 있습니다. 따라서 결제 단계에서 다음과 같은 응답을 제공할 수 있어야 합니다.
Oat milk is no longer available.
Choose another milk option to continue.즉시 주문과 예약 주문은 생산 능력을 다르게 사용합니다
즉시 주문은 현재 대기열 뒤에 제조 작업을 추가합니다.
예약 주문은 미래의 특정 시간 구간에 제조 작업을 배정하게 됩니다.
예를 들어 사용자가 9시 20분에서 9시 25분 사이의 픽업을 선택했다고 가정하겠습니다.
시스템은 9시 20분에 제조를 시작하면 안 됩니다. 음료 제조 시간, 상품 수, 매장까지 전달되는 지연 시간을 고려해 더 이른 시점에 작업을 시작해야 합니다.
선택한 픽업 시간
09:20–09:25
예상 제조 시간
4분
준비 여유 시간
1분
예상 제조 시작
09:15이 글에서는 매장이 일정한 시간 구간에 처리할 수 있는 작업량을 생산 가능량(capacity)이라고 부르겠습니다. 예약 주문을 받을 때는 5분 단위와 같은 시간 구간별로 남은 생산 가능량을 확인할 수 있습니다.
09:10–09:15
남은 생산 점수: 18
09:15–09:20
남은 생산 점수: 4
09:20–09:25
남은 생산 점수: 11모든 주문을 단순 주문 건수로 계산하면 정확도가 낮아질 수 있습니다.
드립 커피 한 잔과 여러 customization이 포함된 Frappuccino 네 잔의 제조 시간이 같지 않기 때문입니다. 그래서 상품과 customization에 예상 작업량을 부여할 수 있습니다.
Brewed Coffee
work_units = 1
Latte
work_units = 3
Customized Frappuccino
work_units = 6
Heated Food
work_units = 2이 수치는 절대적인 제조 시간이 아니라 매장별 처리 가능량을 계산하기 위한 상대적인 값입니다.
주문 순서는 접수 시간만으로 결정하기 어렵습니다
다음 주문이 같은 매장에 들어왔다고 가정하겠습니다.
08:00:00 모바일 픽업, 음료 2개
08:00:05 드라이브스루, 음료 1개
08:00:10 카운터 주문, 음료 3개
08:00:15 배달 주문, 음료 5개모바일 주문을 무조건 먼저 처리하면 드라이브스루 대기 시간이 늘어날 수 있습니다. 드라이브스루만 우선하면 모바일 앱에서 약속한 픽업 시간이 지켜지지 않을 수 있습니다.그래서 제조 순서에는 다음 정보가 들어갈 수 있습니다.
주문 접수 시간
약속한 준비 시간
주문 채널
상품별 제조 시간
현재 station의 작업량
배달 기사 예상 도착 시간
주문 지연 시간
고객이 이미 매장에 도착했는지 여부여러 주문 채널의 작업을 하나의 제조 순서로 정리하는 컴포넌트를 이 글에서는 Smart Queue Orchestrator라고 부르겠습니다.
Smart Queue Orchestrator는 모든 상품을 하나의 줄에 넣지 않습니다. 음료와 음식은 서로 다른 설비에서 동시에 준비될 수 있습니다.
Espresso Station
Cold Bar
Brewed Coffee Station
Oven Station
Packaging Station주문 하나는 여러 제조 작업으로 나뉠 수 있습니다.
Order #991
-> Latte: Espresso Station
-> Cold Foam: Cold Bar
-> Sandwich: Oven Station
-> Final Assembly: Packaging Station각 작업이 완료된 뒤 주문 전체가 준비 상태로 전환됩니다.
매장 시스템은 인터넷 연결이 불안정해도 주문을 처리해야 합니다
매장 인터넷이 잠시 끊길 수 있습니다.
이미 매장에 전달된 주문까지 사라지면 안 됩니다. 바리스타가 제조를 완료했는데 중앙 서버에 상태를 전송하지 못할 수도 있습니다.
그래서 매장에는 중앙 서버와 주문을 동기화하는 로컬 컴포넌트가 필요합니다.
이 글에서는 이를 Store Edge Gateway라고 부르겠습니다.
Store Edge Gateway는 다음 데이터를 짧은 기간 보관할 수 있습니다.
현재 주문 대기열
상품별 제조 작업
최근 완료 주문
매장 메뉴 availability
중앙 서버에 전송하지 못한 상태 변경
마지막 동기화 이벤트연결이 복구되면 중앙 서버와 상태를 다시 맞추게 됩니다.
다만 매장이 오프라인일 때 새로운 모바일 주문을 계속 받아서는 안 됩니다. 주문은 결제되었지만 매장에 전달되지 않는 상황이 생길 수 있기 때문입니다.
연결 상태가 일정 시간 이상 확인되지 않으면 해당 매장을 앱 주문 불가 상태로 전환할 수 있습니다.
Functional Requirements
사용자는 현재 위치나 검색어를 기준으로 주변 매장을 찾을 수 있어야 합니다.
사용자는 매장의 영업시간, 지원 주문 채널, 예상 준비 시간과 메뉴를 확인할 수 있어야 합니다.
사용자는 음료 크기, 우유, syrup, shot, 온도, 얼음과 같은 옵션을 선택할 수 있어야 합니다.
시스템은 매장별 가격, 판매 상품, 품절 상태와 customization 가능 여부를 제공할 수 있어야 합니다.
사용자는 즉시 픽업 또는 제공되는 시간 구간 중 하나를 선택해 예약 주문을 만들 수 있어야 합니다.
시스템은 주문 확정 전에 매장 생산 가능량, 상품 availability, 가격, 리워드 사용 가능 여부를 다시 확인할 수 있어야 합니다.
사용자는 저장된 결제 수단, Starbucks Card 또는 지원되는 외부 결제 수단으로 결제할 수 있어야 합니다.
동일한 결제 요청이 재전송되어도 주문과 결제가 중복 생성되지 않아야 합니다.
매장 시스템은 모바일, 카운터, 드라이브스루, 배달 주문을 하나의 제조 계획으로 변환할 수 있어야 합니다.
음료와 음식 작업을 제조 station에 배정하고 진행 상태를 추적할 수 있어야 합니다.
사용자는 주문 접수, 제조, 준비 완료 상태를 앱에서 확인할 수 있어야 합니다.
매장은 상품이나 재료를 품절로 전환하고 특정 주문 채널을 일시 중단할 수 있어야 합니다.
주문 완료 후 리워드 적립과 전자 영수증 생성을 처리할 수 있어야 합니다.
주문 취소, 전체 환불과 부분 환불을 처리할 수 있어야 합니다.
배달 주문은 외부 배달 제공자와 연결하고 기사 배정 및 도착 상태를 반영할 수 있어야 합니다.
매장 네트워크가 복구되면 중앙 서버와 주문 진행 상태를 중복 없이 동기화할 수 있어야 합니다.
Non-Functional Requirements
Latency
주변 매장 조회는 p95 300ms 이내를 목표로 둘 수 있습니다.
매장 메뉴 조회는 p95 300ms 이내를 목표로 둡니다.
주문 가격과 예상 픽업 시간을 계산하는 요청은 p95 500ms 이내를 목표로 둘 수 있습니다.
결제와 주문 확정은 외부 결제 제공자의 응답 시간을 제외하고 p95 1초 이내를 목표로 설정할 수 있습니다.
주문 확정 후 매장 시스템 전달은 p95 2초 이내를 목표로 둘 수 있습니다.
위 수치는 실제 Starbucks SLA가 아니라 시스템 디자인을 위한 가정입니다.
Availability
메뉴 이미지나 추천 기능이 실패해도 주문 생성 경로는 계속 동작해야 합니다.
리워드 적립이 지연되어도 결제와 매장 주문 전달이 함께 실패하면 안 됩니다.
특정 매장 시스템이 오프라인이면 해당 매장만 모바일 주문을 제한하고 다른 매장의 주문은 계속 받아야 합니다.
Scalability
매장 검색, 메뉴 조회, 주문 생성, 주문 상태 stream, 매장 dispatch, 리워드 처리를 독립적으로 확장할 수 있어야 합니다.
아침 피크 시간에 특정 도시와 매장으로 요청이 집중되어도 다른 지역의 주문 처리 시간이 증가하지 않아야 합니다.
Consistency
결제 승인과 주문 확정 상태를 추적할 수 있어야 합니다.
같은 주문이 매장에 두 번 전달되거나 같은 결제가 두 번 승인되지 않아야 합니다.
재고와 메뉴 availability는 잠시 차이가 날 수 있지만 주문 확정 시점에는 다시 확인해야 합니다.
Durability
결제 상태, 주문 상태, 환불 상태, 제조 완료 상태는 서버와 worker가 재시작되어도 복구할 수 있어야 합니다.
여기서 영속 상태(durable state)는 실행 중인 서버의 메모리가 사라진 뒤에도 데이터베이스에서 다시 읽을 수 있는 상태를 의미합니다.
Security
카드 번호를 애플리케이션 데이터베이스에 직접 저장하지 않아야 합니다.
결제 제공자가 발급한 token을 사용하고 민감한 데이터 접근을 제한해야 합니다.
매장 직원과 관리자의 권한을 매장 단위로 확인해야 합니다.
Auditability
가격 변경, 상품 품절, 주문 취소, 환불, 리워드 조정, 채널 중단과 관리자 개입을 추적할 수 있어야 합니다.
Store Isolation
한 매장의 주문량 증가와 장애가 다른 매장의 제조 순서와 주문 조회에 영향을 주지 않아야 합니다.
Scope
이 글에서는 다음 기능을 다룹니다.
매장 검색
영업시간과 주문 채널
매장별 메뉴
상품 customization
즉시 픽업
예약 픽업
주문 견적
매장 생산 가능량
결제
리워드 적립과 사용
주문 상태
전자 영수증
모바일 주문
카운터 주문
드라이브스루 주문
배달 주문
매장 제조 순서
상품 제조 작업
매장 품절 처리
주문 취소와 환불
매장 Edge 시스템
약한 네트워크 복구
운영자 모니터링다음 기능은 범위에서 제외합니다.
커피 원두 공급망 전체
직원 근무 일정 전체
결제 네트워크 내부 구현
배달 제공자의 기사 배차 내부 구현
개인화 추천 모델 학습
Starbucks Card 발행 회계 전체
세금 신고 시스템 전체
실제 음료 제조 장비 제어Traffic & Load Assumptions
아래 수치는 실제 Starbucks 트래픽이 아닙니다.
시스템 디자인을 위해 전 세계 참여 매장을 40,000개로 가정하겠습니다.
매장 하나가 하루 평균 1,000건의 전체 주문을 처리한다고 가정하면 하루 주문은 4,000만 건이 됩니다.
40,000 stores × 1,000 orders
= 40,000,000 orders/day평균 주문 생성 QPS는 약 463입니다.
40,000,000 / 86,400
≈ 463 orders/second평균 QPS는 낮아 보일 수 있지만 주문은 지역별 아침 시간에 집중됩니다.
전 세계 피크 계수 30배를 적용하면 약 13,900 QPS가 됩니다.
463 × 30
≈ 13,890 orders/second매장 하나의 피크를 별도로 봐야 합니다. 말이 안될수도 있지만, 도심의 대형 매장이 30분 동안 600건의 주문을 처리한다고 가정하겠습니다.
600 / 1,800
≈ 0.33 orders/second주문 QPS 자체는 높지 않습니다. 하지만 주문 하나가 여러 상품과 제조 작업으로 나뉩니다. 주문 한 건에 평균 2.3개의 상품이 있고, 상품 하나가 평균 1.4개의 제조 작업을 만든다고 가정하겠습니다.
40,000,000 × 2.3 × 1.4
= 128,800,000 production tasks/day평균 약 1,491개의 제조 작업 이벤트가 초당 생성됩니다.
주문 상태 이벤트는 더 많아질 수 있습니다.
주문 한 건이 평균적으로 다음 이벤트를 만든다고 가정하겠습니다.
order_created
payment_approved
order_confirmed
store_received
preparation_started
item_completed
order_ready
order_picked_up
order_completed
reward_posted주문당 평균 12개의 이벤트가 발생하면 하루 4억 8천만 개의 이벤트가 생성됩니다.
40,000,000 × 12
= 480,000,000 events/day평균 약 5,556 event QPS입니다.


