주문 가방을 중심으로 음식점, 주문서, 라이더 위치와 도착 경로가 연결된 음식 배달 시스템 설계 그림

배민급 음식 배달 시스템 설계: 주문·배차·라이더 위치·ETA

고객이 주문 버튼을 다시 눌러도 결제가 두 번 일어나지 않아야 한다. 가게가 음식을 준비하는 동안에는 적절한 라이더를 찾아야 하고, 고객 지도에는 멈춘 위치를 현재 위치처럼 그려서는 안 된다. 배민급 음식 배달 시스템 설계는 이 세 약속을 하나의 주문 여정에서 지키는 문제다.

이 글은 2026년 8월 15일 기준 공개 공식 문서와 업계 기술 자료를 대조한 가상 설계다. 실제 배민의 비공개 구조나 성능을 재현하지 않았으며, 주문량·지연 목표·보존 기간은 선택을 비교하기 위한 설계 가정이다. 모바일과 웹의 고객, 가게 운영자, 라이더가 메뉴 조회부터 취소와 알림까지 실제로 보는 결과를 따라간다.

20초 핵심 요약

  • 무엇: 주문·결제의 확정 기록과 빠르게 변하는 배차·라이더 위치·도착 예정 시간을 분리해 연결한다.
  • 왜: 중복 결제, 품절 주문, 늦은 배차, 멈춘 지도와 오래된 알림은 취소·환불과 신뢰 손실로 이어진다.
  • 어떻게: 세 사용자의 행동에서 API와 상태 변화를 따라가고, 위치 쓰기량·경로 비용·장애 복구와 단계별 확장을 계산한다.

고객·가게·라이더가 기다리는 결과부터 정한다

고객은 모바일 앱이나 웹에서 배달 주소를 정하고, 영업 중이며 주문 가능한 메뉴를 본다. 주문 버튼을 한 번 눌렀다면 통신이 끊겨 재시도해도 주문과 결제는 하나여야 한다. 주문 뒤에는 가게 확인, 조리, 픽업, 배달이라는 현재 상태와 ETA(Estimated Time of Arrival, 도착 예정 시간)를 이해할 수 있어야 한다.

가게 운영자는 새 주문을 한 번만 받아 수락하거나 거절한다. 품절·가격·영업 상태를 바꾸고 예상 준비 완료 시각을 알린다. 이 변경이 고객 화면에 늦게 반영되더라도, 결제 직전에는 최신 상태를 확인해 잘못된 주문을 막아야 한다.

라이더는 배달 가능한 동안 위치를 보내고, 이동 거리와 픽업 조건이 담긴 배차 제안을 수락하거나 거절한다. 운영체제가 백그라운드 앱을 일시 중단할 수 있으므로 위치가 정확한 간격으로 계속 도착한다고 가정하지 않는다. 고객에게는 마지막 위치의 기록 시각과 정확도를 함께 보여줘야 한다.

이 여정에서 기능 요구사항은 메뉴·영업 상태 조회, 주문과 결제, 가게 수락과 조리 이벤트, 배차 제안, 라이더 위치, ETA, 취소·환불, 관계자 알림이다. 추천·광고·리뷰·쿠폰, 라이더 정산과 부정 사용 탐지 모델의 상세 구현은 제외한다.

응답 목표도 사용자 기준으로 둔다. p95는 100개 요청을 빠른 순서로 세웠을 때 95번째 요청이 끝나는 시간이며, 평균에 가려지는 느린 사용자 경험을 보기 위해 필요하다. 아래 값은 실제 서비스 측정치가 아니라 초기 설계 목표다.

사용자가 하는 일 설계 목표 실패를 숨기지 않는 방법
메뉴를 연다 캐시 조회 p95 300ms 이내 메뉴 버전과 갱신 시각을 보낸다
주문을 접수한다 외부 결제 시간을 뺀 p95 1초 이내 처리 중인 주문을 다시 조회할 경로를 준다
라이더 위치를 본다 수신 후 화면 반영 p95 3초 이내 오래되면 위치 갱신 지연을 표시한다
ETA를 확인한다 변화 뒤 p95 10초 이내 갱신 범위와 계산 시각, 신뢰도를 함께 보낸다

개인정보 기대도 명확하다. 고객 주소와 라이더의 정밀 위치는 해당 주문을 수행하는 최소한의 주체에게만 공개하고, 배달이 끝나면 접근과 보존을 줄인다.

숫자를 넣으면 주문보다 위치가 먼저 커진다

다음 계산은 실제 배민 트래픽이 아니다. 기준 입력과 계산 결과, 값이 바뀔 때의 영향을 함께 적은 설계 가정이다.

입력 가정 계산 결과와 설계 영향
하루 주문 100만 건, 피크 계수 10 1,000,000 ÷ 86,400 × 10 피크 약 116건/초다. 10배가 되어도 외부 결제 한계부터 확인하고 주문 DB 분할은 측정 뒤 결정한다.
주문당 메뉴 탐색 10회, 하루 1억 읽기 100,000,000 ÷ 86,400 × 10 피크 약 1.16만 회/초다. 탐색이 30회면 약 3.47만 회/초라서 메뉴 캐시가 먼저 필요하다.
활동 라이더 10만 명, 3초마다 위치 1회 100,000 ÷ 3 약 3.33만 건/초다. 1초 간격이면 10만 건/초, 10초면 1만 건/초가 된다.
위치 이벤트 200바이트 33,333 × 200 약 6.7MB/초, 하루 원시 데이터 약 576GB다. 복제와 인덱스 용량은 별도다.
배차 500회/초, 후보 20명 500 × 20 경로 원소 1만 개/초다. 모든 후보의 도로 경로를 바로 계산하면 비용과 호출 한계가 병목이 된다.

CDN(Content Delivery Network, 사용자 가까운 지점에서 같은 정적 응답을 전달하는 망)은 메뉴 읽기를 원본 서버에서 덜어낸다. 반대로 라이더 위치는 계속 새 값이 들어오므로 같은 방식으로 오래 캐시할 수 없다. 주문 상태 이벤트는 설계 가정상 하루 1,500만 건으로 위치보다 작지만, 같은 주문에서 순서가 뒤집히거나 중복 처리되지 않게 하는 일이 더 중요하다.

따라서 처음부터 주문 데이터베이스(DB, 구조화된 기록을 저장하고 조회하는 시스템)를 여러 조각으로 나눌 필요는 없다. 먼저 메뉴 읽기 캐시와 위치 수집 경로를 마련하고 배차 후보를 줄인다. 도시가 늘면 위치와 배차를 도시나 지리 구역별로 나누되, 한 주문의 확정 상태는 한 소유 영역에서만 바꾼다.

한 주문은 세 행동을 거쳐 완성된다

아래 흐름은 왼쪽의 고객 주문에서 시작해 가게 수락과 라이더 위치로 내려 읽는다. 위쪽 줄은 돈과 주문 상태를 확정하고, 아래쪽 줄은 빠르게 바뀌는 위치와 ETA를 갱신한다.

주문·결제 확정 경로와 라이더 위치·ETA 관측 경로를 나눈 음식 배달 요청 흐름도

두 줄을 나누면 위치 수집이 밀려도 주문과 결제 기록은 잃지 않는다. 반대로 고객 지도와 ETA는 주문 데이터베이스를 계속 읽지 않고 빠른 최신값을 사용할 수 있다.

행동 1: 고객이 메뉴를 보고 주문한다

API(Application Programming Interface, 앱과 서버가 정해진 형식으로 요청하고 응답하는 접점)는 다음처럼 시작할 수 있다. 예시는 구현 계약 초안이지 특정 기업의 실제 API가 아니다.

GET /v1/stores?lat=37.50&lng=127.03&open_now=true
GET /v1/stores/{store_id}/menu

POST /v1/orders
Idempotency-Key: 6f7b...
{
  "store_id": "st_123",
  "delivery_address_id": "addr_9",
  "items": [{"menu_item_id":"m_7","quantity":2,"option_ids":["o_2"]}],
  "payment_method_token": "pm_token",
  "quoted_total": 27800
}

Idempotency-Key는 같은 작업의 재시도를 알아보는 고유 키다. 같은 요청을 여러 번 처리해도 결과를 한 번 처리한 것과 같게 만드는 성질을 멱등성이라고 하며, 여기서는 응답이 끊긴 뒤 버튼을 다시 눌러도 주문과 결제가 늘어나지 않게 하는 데 필요하다.

  1. 고객이 가게와 메뉴를 요청하면 메뉴 조회 계층은 주소의 배달 가능 구역과 공개 영업 상태를 확인하고 캐시된 메뉴를 돌려준다.
  2. 주문 요청이 오면 로그인 사용자와 주소 소유권을 확인하고, 같은 중복 방지 키로 시작된 주문을 찾는다.
  3. 결제 직전에는 캐시가 아니라 현재 메뉴 버전에서 영업 상태, 품절, 옵션과 가격을 다시 검증한다. 화면 가격과 다르면 새 견적을 주고 자동 결제하지 않는다.
  4. 주문의 유일한 기록에 PENDING_PAYMENT 상태와 품목·가격 사본을 저장한다. 트랜잭션은 여러 변경을 모두 저장하거나 모두 취소하는 묶음이며, 반쪽짜리 주문을 막기 위해 필요하다.
  5. 결제 연결 계층은 order_id에서 만든 같은 중복 방지 키로 외부 결제사에 승인 요청을 보낸다. 시간 초과가 나도 실패로 단정하거나 새 키로 다시 승인하지 않는다.
  6. 승인 결과와 PLACED 상태를 저장할 때 발행 대기 이벤트도 같은 트랜잭션에 넣는다. 이 표는 DB 저장 뒤 가게 알림 메시지만 빠지는 틈을 막는다.
  7. 전달기가 OrderPlaced 이벤트를 가게 화면과 알림 경로로 보내고, 고객에게 주문 ID·현재 상태·버전·다음 조회 시점을 반환한다.

행동 2: 가게가 수락하고 라이더가 배차를 받는다

POST /v1/merchant/orders/{order_id}/accept
If-Match: "order-version-3"
{"prep_ready_at":"2026-08-15T02:08:00Z"}

POST /v1/dispatch-offers/{offer_id}/accept
{"rider_id":"r_88","offer_version":1}

If-Match는 가게가 본 주문 버전과 서버의 현재 버전이 같을 때만 변경하라는 HTTP 조건이다. 가게의 두 기기에서 수락과 거절이 동시에 들어오는 충돌을 막는다.

  1. 가게가 예상 준비 완료 시각과 함께 수락하면 주문 상태는 허용된 PLACED → ACCEPTED로만 바뀌고 버전이 하나 증가한다.
  2. 배차 조정자는 가게 주변 칸과 인접 칸에서 배달 가능한 라이더를 찾는다. H3(Hexagonal Hierarchical Spatial Index, 지구를 크기가 다른 육각형 칸 번호로 바꾸는 방식)는 도시 전체를 훑지 않고 주변 후보를 좁히기 위해 필요하다.
  3. 마지막 위치가 오래됐거나 이미 다른 업무를 수행하는 라이더를 빼고 후보를 20명 안팎으로 줄인다. 20명은 설계 가정이다.
  4. 남은 후보는 가게 도착 예상, 음식 준비 예상, 고객까지의 시간, 라이더 대기, 수락 가능성과 묶음 배달의 추가 지연으로 비교한다. DoorDash 공개 자료도 이러한 입력을 함께 고려하지만, 이 점수식이 DoorDash나 배민의 실제 방식이라는 뜻은 아니다.
  5. 상위 소수에만 도로 경로 시간을 계산한다. 출발지와 도착지의 모든 조합을 계산하는 경로 행렬은 후보 수만큼 비용과 호출량이 늘기 때문이다.
  6. 만료 시각이 있는 제안을 보내고, 수락할 때 한 주문의 활성 배차가 하나라는 데이터베이스 제약으로 승자를 정한다. 늦게 도착한 수락은 OFFER_EXPIRED로 거절한다.
  7. 거절이나 시간 초과가 나면 시도 번호를 높이고 검색 구역을 넓히거나 준비 시각에 맞춰 재시도한다. 고객 화면에는 라이더를 찾는 중 또는 예상보다 오래 걸림을 보여준다.

가장 가까운 라이더가 늘 최선인 것은 아니다. 음식이 15분 뒤 준비되는데 바로 앞 라이더를 묶어 두면 라이더가 기다리고, 그동안 다른 주문은 늦어질 수 있다. 배차는 거리보다 음식과 라이더가 가게에 도착하는 시간을 맞추는 문제다.

행동 3: 고객이 위치와 ETA를 보고 취소한다

POST /v1/riders/{rider_id}/locations
{
  "order_id":"ord_20260815_01",
  "seq":418,
  "recorded_at":"2026-08-15T02:12:04Z",
  "lat":37.5012,
  "lng":127.0311,
  "accuracy_m":18
}

GET /v1/orders/{order_id}/tracking

POST /v1/orders/{order_id}/cancel
Idempotency-Key: cancel-ord_20260815_01-...
{"reason_code":"DELAYED"}
  1. 라이더 앱은 위치와 함께 단말 기록 시각, 계속 증가하는 순서 번호와 정확도를 보낸다. 수신 계층은 너무 오래됐거나 비정상적으로 멀리 뛴 점을 표시한다.
  2. 위치 이벤트는 rider_id를 분할 기준으로 삼는다. 많은 이벤트를 여러 처리 줄에 나누면서 같은 라이더의 순서를 유지하기 위해서다.
  3. 최신 위치 저장소는 들어온 순서 번호가 기존 값보다 클 때만 갱신한다. 고객에게는 해당 주문에 배정된 라이더의 제한된 좌표와 기록 시각만 보낸다.
  4. ETA는 위치가 충분히 바뀌거나 조리·픽업 상태가 변했을 때 다시 계산한다. 모든 위치 점마다 외부 지도 API를 호출하지 않는다.
  5. 추적 응답에는 도착 예상 범위, 계산 시각, 위치 기록 시각과 신뢰도를 넣는다. 마지막 위치가 15초 이상 오래되면 위치 갱신 지연을 표시한다. 15초는 단말 시험으로 조정할 제품 가정이다.
  6. 취소 요청이 오면 화면의 상태가 아니라 서버의 현재 상태로 가능 여부를 판정한다. 허용되면 먼저 CANCEL_REQUESTED를 기록하고 환불, 가게 보상과 배차 해제를 각각 재시도한다.
  7. 모두 끝나면 CANCELLED, 환불이 늦으면 CANCEL_PENDING_REFUND를 보여준다. 외부 결제와 배차를 한 데이터베이스 작업으로 동시에 되돌릴 수 없으므로 중간 상태를 숨기지 않는다.

확정 기록과 빠른 관측값은 보존 방식도 다르다

orders는 주문의 현재 판정을 내리는 표다. 최소 필드는 다음과 같다.

필드 역할
order_id 예측하기 어려운 주문 식별자이자 기본키
customer_id, store_id 소유 관계와 주문별 권한 확인
status, version 현재 상태와 동시 변경 충돌 방지
total_amount, currency 최소 화폐 단위 정수로 저장한 확정 금액
menu_version 주문에 사용한 메뉴 버전 추적
delivery_address_ciphertext 암호화한 배송지
idempotency_key_hash 원문 키를 노출하지 않는 중복 탐지값
created_at, updated_at 감사와 시간 제한 판정

UNIQUE(customer_id, idempotency_key_hash)는 같은 고객의 중복 주문을 데이터베이스에서 마지막으로 막는다. 가게 화면에는 (store_id, status, created_at), 고객 주문 목록에는 (customer_id, created_at DESC) 인덱스를 둔다. 인덱스는 자주 찾는 순서로 별도 찾아보기를 만들어 전체 행을 훑는 일을 줄이는 구조다.

order_items에는 주문 당시 메뉴명·옵션·단가·수량을 사본으로 남긴다. 현재 메뉴 가격이 바뀌어도 과거 결제 금액이 바뀌지 않게 한다. order_transitions에는 이전 상태, 다음 상태, 버전, 변경 주체와 시각을 남긴다. 결제 표에는 결제사 참조값과 승인·환불 금액, 중복 방지 키를 두되 카드 번호 원문은 저장하지 않는다.

메뉴는 store_id:menu_version으로 캐시한다. TTL(Time To Live, 캐시나 메시지가 살아 있을 수 있는 최대 시간)은 품절 정보가 무한히 남지 않게 하는 만료 규칙이며, 1~5분을 초기 가정으로 두고 품절이나 영업 종료 이벤트가 오면 바로 무효화한다. 이벤트가 유실돼도 TTL 뒤에는 최신 버전을 읽고, 결제 직전 재검증이 잘못된 주문을 막는다.

배차에는 delivery_tasksdispatch_offers를 둔다. 제안마다 라이더, 시도 번호, 점수에 사용한 값, 제안·만료 시각과 응답을 남긴다. 한 주문에 활성 배차가 하나뿐이라는 조건과 (rider_id, state) 인덱스가 동시 수락과 라이더 업무 조회를 각각 지킨다.

rider_latest_location은 라이더별 최신 좌표, 지리 셀, 순서 번호, 기록·수신 시각과 정확도만 짧게 보존한다. 원시 위치 이벤트는 재처리에 필요한 기간만 스트림에 두고, 장기 분석이 꼭 필요한 값만 정밀도를 낮추고 접근을 제한한 저장소로 옮긴다. 배달이 끝나면 고객의 실시간 구독을 닫는다. 법정 보존 기간은 이 글에서 임의로 정하지 않고 실제 사업자 지위와 목적에 맞춰 법무 검토로 확정한다.

ETA 이력은 주문, 예측 버전, 단계, 하한·대표값·상한, 계산 시각, 사용한 위치 순서 번호와 모델 버전을 연결한다. 고객에게는 최신 범위만 보여주되, 운영자는 당시 예측과 실제 준비·도착 시각을 비교할 수 있어야 한다.

주문 직후 ETA = 가게 수락 대기 + 조리 + 배차 대기
              + 라이더→가게 이동 + 픽업 + 가게→고객 이동 + 완충

픽업 뒤 ETA = 현재 위치→고객 이동 + 현관·전달 완충

한 숫자를 약속처럼 고정하면 조리 지연과 위치 공백이 생길 때 이유 없는 번복처럼 보인다. 범위와 마지막 계산 시각을 주고, 후보 탐색에는 빠른 근사 경로를, 최종 배차와 픽업 뒤에는 교통을 반영한 경로를 쓰는 편이 비용과 품질을 맞추기 쉽다.

취소 상태도는 되돌림이 한 번에 끝나지 않음을 보여준다

아래 그림은 왼쪽의 결제 대기에서 정상 배달 흐름을 오른쪽으로 읽고, 수락·조리 중 취소가 시작되면 아래쪽 가지를 따라 읽는다.

정상 배달 흐름과 환불 대기 단계를 포함한 주문 취소 흐름 상태도

CANCEL_PENDING_REFUND가 필요한 이유는 주문 상태, 외부 결제 환불과 라이더 배차 해제가 같은 순간에 끝나지 않기 때문이다. 주문의 현재 상태와 버전은 한 행에서 최신 확정값만 보여준다. 반면 메뉴·지도 점·ETA는 잠깐 이전 값일 수 있어 갱신 시각과 버전을 함께 보낸다.

이벤트는 유실을 줄이기 위해 최소 한 번 전달한다고 가정한다. 이는 같은 이벤트가 두 번 올 수 있다는 뜻이므로, 처리자는 이미 본 event_id를 기억하거나 더 큰 위치 순서 번호만 받아들인다. 메시지 시스템 안의 중복 방지 기능이 주문 DB·결제사·푸시까지 자동으로 한 번만 처리해 주는 것은 아니다.

병목은 위치 쓰기·경로 조합·외부 결제에서 나타난다

첫째, 위치 처리량이 소비 속도를 넘으면 오래된 좌표가 쌓인다. 역압은 들어오는 속도가 처리 속도보다 빠를 때 생산자에게 늦추게 하거나 낮은 가치의 반복 작업을 합치는 제어다. 지연이 커지면 정지한 라이더의 같은 위치를 합치고 ETA 재계산 빈도를 낮추되, 주문·결제 이벤트와 자원은 분리한다.

둘째, 특정 라이더나 주문의 구독이 한 처리 조각에 몰릴 수 있다. 핫키는 특정 키에 요청이 몰려 한 조각만 과부하되는 현상이다. 라이더별 순서를 유지하면서 도시와 지리 셀로 상위 분할하고, 고객에게는 자기 주문 한 건만 전달해 퍼지는 범위를 제한한다.

셋째, 후보 100명과 주문 100개의 도로 경로를 모두 조합하면 1만 원소가 된다. 완화 순서는 인접 지리 셀 → 직선 거리와 상태 → 상위 후보의 근사 시간 → 최종 소수의 교통 반영 경로다. 같은 짧은 시간대와 지리 셀의 결과는 캐시할 수 있지만 교통 변화가 크면 만료 시간을 줄인다.

넷째, 결제 시간 초과는 성공인지 실패인지 모르는 상태를 만든다. 외부 서비스 실패가 이어질 때 새 호출을 잠시 막는 회로 차단기로 내부 자원 고갈을 줄이고, 주문은 PAYMENT_PENDING으로 표시하거나 새 접수를 제한한다. 새 승인 키를 만들지 말고 기존 키로 결제 결과를 조회해야 한다.

운영 화면에서는 서버 CPU보다 사용자가 받은 결과를 먼저 본다. 메뉴 버전 불일치율, 불명확한 결제 비율, 가게 수락 지연, 배차 거절·시간 초과율, 신선한 위치 비율, ETA 과소 예측, 환불 대기 시간과 오래된 알림 폐기율을 지역·시간대·주문 단계별로 나눈다.

사용자가 본 실패부터 감지·완화·복구한다

사용자가 보는 현상 감지 즉시 완화 복구와 최종 확인
주문 버튼이 멈춘다 같은 중복 방지 키의 동시 요청, 결제 시간 초과 기존 처리 결과에 연결하고 주문 조회 경로 제공 같은 키로 결제 조회 후 주문 1개·승인 1개·금액 일치 확인
메뉴에는 있는데 결제 직전 품절이다 메뉴 버전 불일치, 무효화 이벤트 지연 결제 전 최신 상태 재검증과 새 견적 캐시 무효화 재전송 후 실패 주문이 결제되지 않았는지 확인
라이더 찾는 중이 오래 간다 후보 수, 거절률, 제안 시간 초과 검색 구역 확대와 ETA 범위 재계산 재배차 또는 취소 선택을 주고 활성 배차가 하나인지 확인
지도 점이 멈추거나 뒤로 간다 기록·수신 시각 차이와 순서 번호 역전 이전 번호를 버리고 마지막 갱신 시각 표시 최신 위치를 다시 읽고 배정 라이더와 순서가 맞는지 확인
ETA가 갑자기 늘어난다 조리 오차, 교통 변화, 위치 공백 범위와 갱신 이유 표시 새 조리·위치 이벤트로 재계산하고 실제 도착과 편향 비교
취소 뒤 배달 중 알림이 온다 주문 버전 역전과 푸시 지연 알림에 버전·짧은 TTL·최신 교체 키 사용 앱에서 현재 주문을 다시 읽고 주문·환불·배차 버전 대조
환불이 바로 보이지 않는다 환불 처리 지연과 재시도 적체 취소 완료환불 처리 중을 분리 같은 환불 키로 재시도하고 환불 합계가 승인액을 넘지 않는지 확인

FCM(Firebase Cloud Messaging, 앱 단말에 푸시 메시지를 전달하는 서비스)이 메시지를 받아들였다는 응답은 단말 전달 완료가 아니다. TTL은 여기서 오래된 알림이 살아 있을 최대 시간이며, 같은 주문의 최신 메시지로 이전 것을 교체하는 키와 함께 쓴다. 앱이 열리면 푸시 문구를 상태의 유일한 기록으로 믿지 않고 주문 API에서 현재 버전을 다시 읽는다.

정밀 위치를 줄이면 보안과 비용도 함께 낮아진다

모든 주문 조회와 변경 요청은 로그인만 확인해서는 부족하다. 요청한 사람이 해당 주문의 고객, 가게, 배정 라이더 또는 허용된 상담원인지 주문별로 확인한다. 예측하기 어려운 주문 ID는 보조 수단일 뿐 권한 검사를 대신하지 않는다.

응답 범위도 역할에 따라 다르게 정한다. 고객은 활성 배달에 필요한 제한된 라이더 위치만 보고, 가게는 배송에 필요한 주소 일부만 보며, 라이더는 제안 수락 전후에 서로 다른 범위를 본다. 주소·정밀 좌표·결제 토큰은 일반 로그와 분석 이벤트에 복제하지 않는다. 위치 수집은 배달 가능 상태와 활성 업무에 필요한 때로 제한하고 권한 철회, 수집 중지와 삭제 요구를 처리할 경로를 둔다.

비용의 큰 축은 지도 경로 원소, 위치 전송·저장, 실시간 연결, 알림과 결제다. Google Routes의 요청·경로 행렬은 종량 과금과 사용 제한이 있으므로 2026년 8월 15일 문서의 숫자를 고정 계약처럼 쓰지 않는다. 후보를 먼저 줄이고, 외부 공급자가 늦거나 한도를 넘으면 근사 ETA로 낮춰 서비스한다.

국내 위치정보와 주문배달 개인정보 의무는 사업자 지위, 처리 목적과 제3자 제공 관계에 따라 달라진다. 개인정보보호위원회의 주문배달 분야 자료는 책임과 안전 조치의 출발점이지만, 실제 출시 전 최신 법률과 전문 검토로 동의·제공 기록·파기 요건을 확정해야 한다.

대안은 정상 속도보다 실패 형태와 운영 부담으로 고른다

WebSocket은 서버와 앱이 한 연결에서 계속 메시지를 주고받는 방식이며 활성 배달의 위치를 빠르게 보내기 위해 쓴다. 폴링은 앱이 정해진 간격으로 서버에 다시 묻는 방식으로, 느린 주문 상태를 단순하게 확인할 때 유용하다.

선택 장점 단점 채택 기준
주문·결제·배차를 하나의 관계형 DB에서 시작 유일성 제약과 상태 변경이 단순하다 위치 쓰기까지 넣으면 주문 쿼리를 방해한다 위치 최신값과 원시 이벤트만 처음부터 분리한다
초기 주문 상태는 폴링, 활성 배달은 WebSocket 단순한 조회와 빠른 위치 전송을 구간별로 쓸 수 있다 실시간 연결은 재연결·순서·누락 복구가 복잡하다 연결이 끊기면 마지막 버전 이후를 받거나 현재 상태를 다시 읽는다
가까운 후보에게 제한적으로 제안 구현과 승자 판정이 쉽다 거절마다 시간이 늘거나 동시 수락이 경합한다 활성 배차 유일성 제약과 제안 만료를 먼저 둔다
지역 전체 주문·라이더를 함께 최적화 묶음과 공급 균형을 개선할 여지가 있다 입력 예측 오류의 범위와 계산·설명 비용이 커진다 관측과 시뮬레이션이 쌓인 고밀도 지역에서만 실험한다
외부 지도 API 도로·교통 데이터 운영을 맡길 수 있다 원소별 비용, 호출 한도와 공급자 장애에 묶인다 후보를 계층적으로 줄이고 시간 초과와 근사값 대체 경로를 둔다
자체 경로 엔진 호출 단가와 한도를 직접 제어한다 지도 갱신과 한국 이륜차 경로 품질을 직접 책임진다 외부 비용과 품질을 실제 트래픽으로 측정한 뒤 검토한다

실시간 연결은 끊긴 사이의 값을 복구하는 별도 규칙이 필요하다. 폴링은 바뀐 값이 없어도 요청 비용이 들고 지연이 조회 간격보다 짧아질 수 없다.

정밀 위치를 보내는 주기에도 같은 교환 관계가 있다. 1초 간격은 움직임이 부드럽지만 배터리·데이터·저장비와 개인정보 노출이 커진다. 10초 간격은 싸지만 도심에서 화면이 뒤처질 수 있다. 1~10초 범위에서 속도·주문 단계·화면 구독 여부에 따라 조절한다는 가정으로 시작하고 단말 현장 시험으로 바꾼다.

확장은 한 도시의 정확성에서 지역 격리로 진행한다

1단계는 한 도시에서 정확한 상태를 지키는 일이다. 소수 서비스와 관계형 주문 DB, 메뉴 캐시로 시작한다. 주문·결제 중복 방지 키, 허용된 상태 변화, 배차 유일성, 위치 순서 번호와 알림 버전을 먼저 구현한다. 가게가 준비 시간을 직접 입력하고, 인접 지리 셀과 규칙 기반 점수로 라이더를 찾으며 고객은 짧은 폴링으로 상태를 본다.

2단계는 여러 도시에서 위치와 이벤트를 분리한다. 주문 DB의 발행 대기 표에서 이벤트 스트림으로 전달하고, 소비자는 중복을 제거한다. 위치 수집·최신값·장기 분석 저장소를 나누고 도시별로 처리한다. 활성 배달만 실시간 연결을 열며, 조리 시간과 수락 가능성 예측을 추가해도 규칙 기반 대체 경로는 남긴다.

3단계는 고밀도 지역 최적화와 장애 격리다. 한 지역의 위치·배차 장애가 전국 주문을 막지 않게 소유 영역을 나눈다. 묶음 배달과 지역 최적화는 오프라인 시뮬레이션 뒤 제한된 지역에서 시험한다. ETA 범위의 편향, 지도·결제·알림 공급자 장애 뒤의 자동 대조, 정밀 위치 삭제와 권한 감사를 운영한다.

단계가 커져도 여러 저장소가 주문 상태의 주인인 것처럼 쓰면 안 된다. 빠른 위치와 ETA 화면은 다시 만들 수 있지만, 주문·결제·취소의 확정 기록은 하나여야 한다.

선택한 설계와 아직 측정해야 할 것

선택한 설계의 경계는 명확하다. 돈과 주문 상태는 중복 방지 키, 상태 버전과 데이터베이스 제약으로 보수적으로 확정한다. 배차는 가까운 후보를 값싸게 줄인 뒤 조리·이동·수락 가능성을 비교하고, 위치와 ETA는 빠르게 갱신하되 오래된 값임을 사용자에게 드러낸다.

아직 한국 도로와 라이더 단말에서 위치 오차, 가게별 조리 시간, 지도 API 비용과 배터리 영향을 측정하지 않았다. 국내 법률이 요구하는 동의와 보존 범위도 서비스 조건에 따라 달라진다. 구현 전에는 한 도시의 합법적인 표본으로 위치 갱신 주기와 ETA 편향을 시험하고, 아래 공식 계약을 다시 확인한 뒤 관측된 병목만 분리해야 한다.

참고 링크

비슷한 글

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다