Verdict first: OMS 도입에서는 모두가 기능 체크리스트와 API 문서를 쳐다보지만, 지원 라인을 녹이는 것은 보통 가장 지루한 설정 항목――주문 상태 스테이트 머신이다. 전이를 잘못 설정하면 주문이 "사라지고", "얼어붙고", "살아 돌아오면서" 지원·창고·재무가 각자 다른 이야기를 한다. 나는 이 실패를 정확히 세 번 봤다. 매번 달랐다.

주문 상태 스테이트 머신이 이토록 중요한 이유

주문 상태는 모든 역할이 공유하는 유일한 언어다. 지원팀이 확인하고, 창고가 그것을 기준으로 일하고, 재무가 그것을 기준으로 정산하고, 고객이 추적 페이지에서 본다. 주문 상태 스테이트 머신은 문법책이다――어떤 상태가 어디로 이동하는지, 누가 그것을 움직이는지, 멈춘 주문이 어디로 가는지.

문법이 틀리면 아무도 같은 언어를 말하지 못한다. 창고는 "출고됨"이라고 하고, 지원은 여전히 "할당됨"을 보고, 고객은 "내 주문은 어디 있나요"라고 묻고, 지원은 "확인해 보겠습니다"라고만 말할 수 있다――그러고는 채팅 그룹으로 달려가 창고에 묻는다. 지원이 프로답지 못한 게 아니다. 스테이트 머신이 먼저 그녀를 배신했다.

내 세 번의 멜트다운 중 가장 교훈적인 두 가지를 소개한다.

멜트다운 1: 상태 동결, 수백 건의 주문이 "사라지다"

2년 전 나는 하루 수천 건의 주문을 처리하는 캘리포니아 3PL의 OMS 고라이브를 이끌었다. 추수감사절 피크 2주 전의 론칭이었다. 셋째 날, 지원 리드가 전화를 걸었다. "Lin Xiao, 주문 배치가 시스템에서 '할당됨'에 멈춰 있어요. 그런데 창고는 상품이 어제 출고됐다고 합니다."

근본 원인: 피킹이 끝나면 WMS가 "피킹 완료"를 반환했는데, 우리 스테이트 머신은 "할당됨"이 "피킹 중"으로 전이되도록 되어 있었고――WMS 이벤트 매핑 테이블에는 "피킹 중"에 대한 값이 전혀 없었다. 두 통합 문서는 독립적으로 작성됐고, UAT는 해피 패스만 커버했다. WMS가 보낼 수 있는 모든 이벤트를 OMS 스테이트 머신에 통과시켜 각각에 출구가 있는지 확인한 사람은 아무도 없었다.

결과: 수백 건의 주문이 "할당됨"에 얼어붙었다. 지원은 어디를 봐도 "할당됨", 창고는 이미 오래전에 출고, 운송사 추적은 "인수됨"――세 개의 소스, 세 개의 이야기, 지원 라인은 녹았다.

수정은 화려하지 않았다: 밤중에 이벤트 매핑을 패치했고, 이어서 멈춘 주문들을 실제 WMS 진행 상태로 일괄 업데이트하는 정합 작업을 작성했다. 이틀과 두 밤. 운영은 살렸지만, 피크 시즌 첫 물결의 고객 경험은 사라졌다. 그 교훈은 그 이후 모든 체크리스트에 들어갔다: 이벤트 매핑 테이블은 스테이트 머신과 한 줄씩 정렬되어야 한다. 한 줄도 건너뛰지 마라.

멜트다운 2: 취소 분기 누락, 주문이 "살아 돌아오다"

다른 고객, DTC 브랜드는 아름다운 스테이트 머신을 갖고 있었지만 순방향 흐름은 하나뿐이었다: 검토 대기 → 할당 대기 → 할당됨 → 피킹 중 → 포장됨 → 출고됨 → 배송됨. 고객이 취소하면 OMS가 WMS에 인터셉트(차단) 지시를 보냈다――하지만 스테이트 머신에는 "인터셉트 중", "인터셉트됨", "인터셉트 실패" 상태가 전혀 없었다.

인터셉트 지시는 창고에 도착했지만, 피킹 태스크는 이미 릴리스됐고, 작업자는 인터셉트 플래그를 보지 못했다(WMS 태스크 상태와 OMS 주문 상태는 다른 것이다). 상품은 어쨌든 출고됐다. OMS는 "취소됨"을 표시했고, 환불은 나갔다. 순결과: 고객은 "이미 취소된" 주문을 받았고, 회사는 반품 운임을 떠안았고, 지원은 왜 취소된 주문이 여전히 도착했는지 설명해야 했다.

나는 이 실패를 정확히 세 번 봤다. 모든 고객이 "취소에 상태가 필요할 줄은 몰랐습니다"라고 말한다. 내 답은 항상 같다: 취소는 상태의 끝이 아니다. 취소는 하나의 분기 전체다.

주문 상태 스테이트 머신 설계를 위한 3표 체크리스트

충분한 상처를 입은 뒤, 나는 이제 모든 프로젝트의 스테이트 머신을 이 세 표로 설정한다. 그대로 복사하라.

표 1: 메인 흐름――주문의 평범한 일생. 각 행에 네 열이 필요하다: 상태, 누가 그것을 움직이는지, 타임아웃 시 어디로 가는지, 어디로 롤백할 수 있는지.

  • 검토 대기: 시스템 자동화 또는 사람이 움직임; 타임아웃 시 수동 검토로; 이것이 시작이며 롤백 없음.
  • 할당 대기: waving 또는 룰 엔진이 움직임; 재고 부족 시 보류로; 검토 반려 시 검토 대기로 롤백.
  • 할당됨: WMS 콜백이 움직임; WMS가 너무 오래 침묵하면 예외로; 할당 해제 시 할당 대기로 롤백.
  • 피킹 중: WMS 콜백이 움직임; 피킹 예외로 타임아웃; 태스크 취소 시 할당됨으로 롤백.
  • 포장됨: WMS 콜백이 움직임; 인터셉트 성공으로 상품이 스테이징으로 돌아오면 (승인 하에) 롤백.
  • 출고됨: 운송사 또는 WMS 콜백이 움직임; 결승선 전 마지막으로 추적 가능한 지점.
  • 배송됨: 운송사 콜백이 움직임; 끝.

표 2: 분기 상태――취소와 인터셉트는 전용 레인을 갖는다.

취소 요청 → 검토 중 → 취소됨, 또는 취소 반려 (원래 상태로 복귀). 인터셉트 중 → 인터셉트됨 (상품은 포장됨으로 복귀, 처리 대기) → 인터셉트 실패 (원래 흐름 계속, 지원이 개입).

핵심: 취소와 인터셉트는 서로 다른 액션이다――하나의 상태로 합치지 마라. "취소됨"은 돈과 주문의 결과, "인터셉트됨"은 물리적 상품의 결과다. 상품 출고 후의 취소와 아무것도 움직이기 전의 취소는 완전히 다른 상황이며, 상태는 분리된 채로 있어야 한다. 합치면 "취소된 주문이 출고됐다" 같은 괴담이 생긴다.

표 3: 예외 상태――모든 상태에 탈출구를.

검토 반려, 재고 부족 보류, 주소 예외, 결제 예외, WMS 콜백 타임아웃, 운송사 예외. 모든 예외 상태에 세 가지 할당이 주어진다: 소유자, 처리 SLA, 해결 불가 시 에스컬레이션 대상. 소유자 없는 예외 상태는 없는 것과 같다.

각 상태를 설정할 때는 세 가지 질문을――이것은 내 고라이브 당일 밤 체크리스트의 고정 페이지다:

  1. 이 상태를 누가 움직이는가? 소유자 없는 상태는 얼어붙는다. 시스템 구동: 어떤 잡인지, 스케줄을 문서화하라. 사람 구동: 어떤 역할인지 문서화하라.
  2. 타임아웃 시 어디로 가는가? 모든 상태에 타임아웃 출구가 필요하다――없으면 미래의 자신을 위해 지뢰를 심는 셈이다.
  3. 어디로 롤백할 수 있고, 롤백에 승인이 필요한가? 롤백은 스테이트 머신에서 가장 잊히는 부분이지만, 프로덕션에서는 매일 일어난다.

주문 상태 스테이트 머신이 실패하는 가장 흔한 세 가지 방식

첫째, 이벤트와 상태의 불일치. OMS 상태는 외부 이벤트――WMS, 운송사, 결제 콜백――에 의해 움직이며, 모든 이벤트는 정확히 하나의 전이에 매핑되어야 한다. 해피 패스만 테스트하는 프로젝트는 늦든 이르든 여기서 실패한다. 수정: 외부 시스템이 보낼 수 있는 모든 이벤트를 나열하고, 각각을 스테이트 머신에 통과시키고, 빈틈을 그 자리에서 메운다.

둘째, 표시 상태와 워크플로 상태의 혼합. 지원은 고객용 뷰를 원하고, 창고는 운영 뷰가 필요하고, 재무는 정산 뷰가 필요하다. 셋을 하나의 스테이트 머신에 강제하면 모두가 화낸다. 수정: 워크플로 스테이트 머신은 내부 전이만 다루고, 별도의 "고객 가시 상태" 매핑 테이블이 표시를 다룬다. 두 개의 표――하나의 진실의 원천, 하나의 번역층.

셋째――직관에 반하지만――더 정교한 스테이트 머신이 더 좋은 것은 아니다. 나는 한때 30개가 넘는 상태를 가진 프로젝트를 봤다; 지원은 3일을 훈련해도 주문을 조회하지 못했고, 모든 신입은 전이 다이어그램을 외웠다. 가장 안정적인 프로덕션 스테이트 머신은 메인 흐름과 분기를 합쳐 열두 개 안팎의 상태로 돌아간다. 여분의 상태는 장식이며, 장식은 유지보수 임대료를 청구한다: 모든 상태에 타임아웃, 롤백 경로, 권한, 테스트 케이스가 필요하다. 상태를 하나 추가할 때마다 비용이 더해지는 게 아니라――곱해진다.

한 가지 트레이드오프 더: 자동 전이와 수동 확인의 비율. 자동화가 많을수록 효율은 오르지만 예외는 어려워진다. 내 하한선: 검토, 인터셉트, 대규모 환불은 항상 인간 확인을 유지한다. 완전 자동화가 원해? 예외 분기 폴백 로직을 먼저 완성하라.

고라이브 당일 밤에 이 체크리스트를 실행하라

고라이브 당일 밤에는 세 가지를 한다: 테스트 환경에서 모든 상태를 걸어 다니며 "여기서 멈추면 어떻게 되나"라고 묻는다; 모든 외부 이벤트가 스테이트 머신에 출구를 갖고 있는지 확인한다; 취소와 인터셉트 분기를 각각 세 번씩 실행한다――멜트다운은 거기 산다.

답할 수 없으면 오늘 밤은 고라이브하지 마라. 론칭 전 스테이트 머신의 두 시간이, 그 뒤의 스무 건의 불만 전화를 절약한다. 나는 그 셈을 세 번 해봤다. 매번 본전이다.

Lin Xiao, WMS 구현 컨설턴트 지음 영어판 번역본입니다. 내용이 다를 경우 영어판을 우선합니다.