[배송 권역 시스템 3편] 강남 1권역에서는 괜찮았던 V1, 6개 권역에선 왜 깨졌는가

2026. 4. 28. 14:39·Project/배송 권역 시스템

"balance 0.51 — 양호해 보였던 결과"가 함정이었다.
강남 1권역에서 괜찮아 보이던 V1을 6개 권역에 동일하게 적용하니, 권역 모양에 따라 결과 품질이 최대 4배까지 떨어졌다.

 

이 글은 그걸 어떻게 측정으로 드러냈고, 그 측정이 V2 작업(일반화와 3단계 검증)의 출발점이 됐는지에 대한 이야기다.

 

요약

  • V1 은 강남 1 권역 측정 결과 (balance 0.51, span 1.89km) 만으로 채택했다.
  • 6 권역 × 5 회 = 30 시나리오로 다시 측정하니 권역 모양에 따라 결과가 갈렸다.
  • 길쭉 권역(강남·수원)은 공간 outlier, 작은 조밀 권역(부천)은 건수 불균형, 중간 둥근 권역(관악)은 우연히 잘 동작.
  • "한 권역에서 잘 동작" ≠ "알고리즘이 일반화됨". 이 차이를 인지한 게 V2 작업의 출발점.
  • 합성 데이터로 V2 채택 후 production 측정에서 V1 사례가 그대로 재현된 경험까지. 회귀 검증이 단계적이어야 하는 이유.

1. V1 채택 시점의 결과는 분명히 좋아 보였다

V1 알고리즘을 처음 측정한 권역은 강남이었다. 결과는 다음과 같았다.

지표 값
balance score 0.51
max span 1.89km
compact 비율 (span < 3km) 82%
scattered 비율 (span ≥ 5km) 8%

 

그때는 이게 양호한 결과로 보였다. 1.89km 면 한 기사가 평균 약 2km 안에서 움직인다는 의미고, balance 0.51 이면 절반 이상 균등하다고 해석할 수 있었다. scattered 4명은 운영 조정으로 흡수 가능한 수준이라고 판단했다. 이 결과로 V1 을 채택했고 시스템을 그 위에 쌓아 올렸다.

2. 그런데 한 권역만 봤다는 사실은 의외로 큰 함정이었다

V2 작업을 시작하면서 가장 먼저 한 건 "다른 권역도 잡아보자" 였다. 6 권역(강남·관악·수원·부천·종로·미추홀)을 각 5 회씩 돌려서 baseline 을 잡았다.

 

결과 패턴이 명확했다.

권역 특성 balance max span 평가
se-ga (관악) 중간 둥근 0.37 2.99km 양호
ic-mch (미추홀) 중간 0.23 2.98km 보통
se-gn (강남) 길쭉 고밀도 0.26 6.97km 공간 outlier
gg-sw (수원) 큰 외곽 0.22 5.96km 공간 outlier
se-jn (종로) 작음, 178건 0.21 3.93km 보통
gg-bc (부천) 작고 조밀 0.14 3.70km 건수 불균형

 

여기서 두 가지가 동시에 드러났다.

 

첫 번째 — 강남이 앞서 봤던 1.89km 가 아니다. 같은 강남인데 6.97km 가 나왔다. 측정 방식의 차이일까? 살펴보니 그렇지 않았다. 처음 측정은 합성 데이터였고, 이번은 production 데이터였다. 데이터 분포의 차이가 결과를 4 배 이상 벌어졌다.

 

두 번째 — 권역 모양에 따라 결과 품질이 갈린다.

  • 길쭉한 권역(강남·수원) → 한 기사가 권역 끝에서 끝까지 6~7km 를 도는 공간 outlier 발생
  • 작고 조밀한 권역(부천) → 권역이 좁아 공간 outlier 는 없지만 건수가 한쪽으로 몰림
  • 중간 둥근 권역(관악) → 우연히 잘 동작

이게 무엇을 의미할까 고민했고, 알고리즘 자체가 권역 모양에 robust 하지 않다는 뜻이라는 것을 깨달았다. 즉, 강남에서 잘 됐던 건 강남이 운 좋게 알고리즘과 맞았던 거였다.

3. "측정"과 "코드 정독"이 같이 있어야 진단이 된다

이 패턴을 본 다음에 V1 코드를 정독했다. 측정 결과만으로는 "왜 권역 모양에 따라 결과가 다르지?" 라는 질문에 답할 수 없었다. 코드 안에 답이 있었다.

원인은 크게 두 가지였다

  • 알고리즘 안의 두 결정적 결함이었고
  • 운영 측면에서는 1500 회의 noop 으로 작동하고 있었다. 어떤 결함이었는지는 별도 글에서 자세히 다루겠습니다.

여기서 배운 게 있다. 측정은 증상을 보여주고, 코드 정독은 원인을 보여준다. 둘 중 하나만 있어도 진단이 안 된다. 코드만 봤을 때는 결함이 있다는 직관이 떠오르지 않았고 (1500 회 반복 코드는 정상적으로 보였다), 측정만 봤을 때는 권역마다 결과가 다르다는 사실은 알지만 왜인지 몰랐다.

4. V2 작업의 출발점 — 채택 기준을 코드 작성 전에 합의

  1. V1 의 함정을 봤기 때문에, V2 는 다르게 시작하기로 했다.
  2. V1: "더 좋은 결과가 나오면 채택" → 강남 한 권역만 봤음.
  3. V2: "코드 작성 전에 채택 기준을 합의" → 6 권역 모두 동시 만족 시에만 채택.
메트릭 V1 baseline V2 채택 기준
max span — se-gn (강남) 6.97km ≤ 4.9km (30% 감소)
max span — gg-bc (부천) 3.70km ≤ 3.0km
balance — gg-bc (최악) 0.14 ≥ 0.30
balance — se-gn 0.26 ≥ 0.40
balance — se-ga (회귀 방지) 0.37 ≥ 0.36

 

 

기준선이 코드 변경의 방향을 결정한다. 측정 → 진단 → 설계 → 측정 사이클이 V2 작업 전체를 관통하는 구조가 됐다.

5. 그런데 합성 측정은 또 한 번의 함정이 됐다

V2 알고리즘 설계 후 합성 데이터로 V1 vs V2 를 비교했다 (6 권역 × 5 회 = 30 시나리오).

 

결과는 V2 의 승.

  • balance 12승 / 2패 / 16 동률,
  • span 7-7 동률.
  • "회귀 없음"

으로 V2 채택을 결정했다.

 

그런데 production 데이터(2,870건/45명)에서 측정해보니 V1 의 사례가 그대로 재현됐다. 강남 6.97km, 그리고 "한 기사가 강남역 전체를 도는 span 5.89km 의 기사" 문제도 그대로였다. 합성 데이터(90건/3명)에서는 단일 점수 함수 통합으로도 충분히 동작했지만, production scale (큰 H3 셀이 다수 발생, 다수 드라이버가 밀집하게 위치) 에서는 다른 차원의 문제가 있었다.

 

여기서 또 하나의 명제가 도출할 수 있었다. 

합성 측정에서 회귀 없음 ≠ production 안전성.

 

이후 검증은 항상 3 단계로 한다.

  1. 합성 데이터 — 90건/3명 단위. 알고리즘 정합성 확인.
  2. 소규모 production — 1 권역 production data. 도메인 데이터 품질 영향 확인.
  3. 전 권역 production — 6+ 권역 production data. 권역 모양 일반화 확인.

6. V1 의 함정이 V2 의 자산이 됐다

돌이켜보면 V1 의 한계를 빠르게 잡아낸 게 V2 작업의 가장 큰 자산이었다. V1 이 처음부터 6 권역에서 잘 됐다면 V2 작업의 동기 자체가 약했을 거고, 합성 측정의 함정도 그 다음 사이클에서야 발견했을 거다.

 

배운 점을 정리해 본다.

  • 한 권역 측정으로 채택은 위험하다. 알고리즘이 권역 모양과 무관하게 일반화됨을 보장하지 않는다.
  • 측정과 코드 정독은 한 쌍이다. 측정만 보면 증상은 알지만 원인은 모르고, 코드만 보면 직관이 안 온다.
  • 합성 데이터는 production 안전성을 보장하지 않는다. scale 이 달라지면 알고리즘의 다른 면이 노출된다.
  • 채택 기준은 코드 작성 전에 합의해 둔다. 그래야 결과가 안 나왔을 때 "재설계 복귀" 라는 옵션이 살아있다.

다음 글은 이번 프로젝트에서 가장 신경 써서 설계한 영역인 실시간 추적 알림에 대해 쓸 예정이다. 음식 배달 식의 위치 노출이 새벽 배송에서는 왜 안 맞는지, 어떻게 우회 설계했는지에 대한 이야기다.

'Project > 배송 권역 시스템' 카테고리의 다른 글

[배송 권역 시스템 4편] 새벽 배송 알림 설계 — 기사 위치를 직접 노출하지 않는 이유  (0) 2026.04.28
[배송 권역 시스템 2편] 수도권 새벽 배송 자동 배차 시스템 – 구현을 하면서 깨달은 것들  (0) 2026.04.11
[배송 권역 시스템 1편] 배송 권역 시각화 시스템을 설계하며 고민한 것들  (0) 2026.04.05
'Project/배송 권역 시스템' 카테고리의 다른 글
  • [배송 권역 시스템 4편] 새벽 배송 알림 설계 — 기사 위치를 직접 노출하지 않는 이유
  • [배송 권역 시스템 2편] 수도권 새벽 배송 자동 배차 시스템 – 구현을 하면서 깨달은 것들
  • [배송 권역 시스템 1편] 배송 권역 시각화 시스템을 설계하며 고민한 것들
Log Cat
Log Cat
잊어버리지 않기 위한 메모장
  • Log Cat
    개발 메모장
    Log Cat
  • 전체
    오늘
    어제
    • 전체보기 (59) N
      • Book Review (6)
      • Language (20)
        • Java (13)
        • Kotlin (1)
        • Go (2)
        • JavaScript (1)
        • TypeScript (3)
      • Computer Science (6) N
        • Network (1)
        • Database (4) N
        • Design Pattern (0)
      • Spring Framework (11)
        • Spring & Spring Boot (5)
        • Spring Batch (4)
        • Servlet & JSP (2)
      • Python Framework (3)
        • FastAPI (3)
      • Infra (4)
        • Dcoker (1)
        • Kafka (2)
        • Redis (1)
      • ORM (1)
        • JPA (1)
      • Project (5)
        • 배송 권역 시스템 (4)
      • Error (2)
      • Retrospective (0)
      • Certificate (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • Github
  • 인기 글

  • 태그

    프로그래머스
    배송
    우아한테크코스
    jvm
    자바 풀이
    프로그래머스 문제
    코딩테스트
    spring boot
    Java
    spring
    개발서적리뷰
    네트워크
    escape-analysis
    leetcode
    자바
    백준
    fastapi
    programmers
    Typescript
    코테
    공간인덱싱
    배정
    Python
    BOJ
    권역
    jmm
    리트코드
    개발서적
    go
    프로그래머스문제
  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
Log Cat
[배송 권역 시스템 3편] 강남 1권역에서는 괜찮았던 V1, 6개 권역에선 왜 깨졌는가
상단으로

티스토리툴바