감으로 하던 재고 판단을, 데이터에 기반하여 정확하게 예측할 수 있도록.. — TMS 트래킹 집계
배경: 집계되지 않던 회수·발주 데이터
내가 현재 개발 하고 있는 시스템은 식기를 세척해서 배송하고 다시 회수하는 서비스다. 매일 엄청난 양의 배송(DELIVERY)과 회수(RETURN)가 일어나는데, 정작 이 회수량·발주량 데이터가 집계·통계화되지 않고 있었다.
공장의 재고 판단은 사실상 감에 의존하고 있었다. "이 정도 나갔으니 이만큼 있겠지" 수준이었다. 그런데 배송/회수 이벤트는 이미 외부 Argo TMS(배송 관제 시스템)에 다 찍히고 있었다. 데이터는 있는데 안 쓰고 있던 상황이었다.
고민 1: 이 데이터, 누가 먼저 쓰자고 안 했는데?
이건 위에서 시킨 일이 아니었다. 회수/발주 데이터가 흘러다니는 걸 보다가 **"이걸 공장 재고 관리에 쓰면 되겠다"**는 걸 먼저 발견하고 제안했다.
기능을 만드는 것보다, 데이터의 활용 가치를 먼저 알아보고 제안하는 것도 개발자의 일이라고 생각했다.
고민 2: 재고 시스템을 우리가 다 만들어야 하나?
제안이 받아들여지자 두 번째 고민이 왔다. 재고 관리 시스템을 우리가 직접 만들 것인가?
현장(공장)은 요구사항과 변수가 자주 바뀐다. 품목이 추가되고, 재고 산정 기준이 계절/거래처에 따라 달라진다. 이런 걸 우리가 다 떠안으면, 현장이 바뀔 때마다 우리 코드를 고쳐야 한다. 결합도가 너무 높아진다.
그래서 역할을 이렇게 나눴다.
현장이 재고 시스템을 자체 구축한다.
우리는 정제된 데이터 공급만 전담한다.
각자 자기가 잘 바꿀 수 있는 부분을 책임지는 구조. 이게 현장의 잦은 변화를 우리 시스템에서 분리하는 방법이었다.
해결: TMS 트래킹 이벤트를 SQS로 수집·집계
Argo TMS의 배송/회수 트래킹 이벤트를 SQS 큐로 실시간 수집했다. 규모는 하루 약 3만 박스·20여 개 품목.
// TMS 트래킹 이벤트 컨슈머
@SqsMessageHandler(TMS_TRACKING_QUEUE)
async handleTracking(message: TmsTrackingEvent) {
// DELIVERY / RETURN 구분
const { type, itemCode, quantity, trackedAt } = message;
await this.aggregationService.accumulate({
date: toDate(trackedAt),
itemCode,
type, // 'DELIVERY' | 'RETURN'
quantity,
});
}
TypeScript
복사
수집한 이벤트를 날짜·품목·타입별로 정제·집계해서 공장 시스템에 연동했다. 이제 공장은 "오늘 어떤 품목이 얼마나 나가고 얼마나 돌아왔는지"를 숫자로 본다.
트러블슈팅: 이벤트 기반 집계에서 제일 무서운 건 "중복"과 "순서"
큐 기반으로 데이터를 모을 때 실제로 부딪힌 문제들이 있었다.
1) 중복 이벤트 (멱등성)
SQS는 기본적으로 "최소 한 번(at-least-once)" 전달을 보장한다. 즉 같은 트래킹 이벤트가 두 번 들어올 수 있다. 그냥 quantity를 더하기만 하면 재고 숫자가 부풀어 오른다. 그래서 이벤트마다 고유 키를 두고 이미 처리한 이벤트인지 확인한 뒤 집계하도록 멱등성을 확보했다.
tsx
async accumulate(event: AggregateInput) {
// 같은 이벤트가 다시 와도 두 번 반영되지 않게
const isNew = await this.dedup.markIfFirst(event.eventId);
if (!isNew) return;
await this.repo.increment(event);
}
TypeScript
복사
2) 대량 데이터의 집계 부담
매 이벤트마다 DB에 쓰면 하루 3만 건 이상의 쓰기가 몰린다. 그래서 실시간으로 무겁게 쌓기보다, 집계 단위로 누적하고 주기적으로 반영하는 쪽으로 균형을 잡았다. (완전한 실시간 재고가 필요한 도메인이 아니라, 처리량과 정확성이 더 중요한 도메인이었다.)
여기서 배운 것: 이벤트 기반 데이터 파이프라인의 정확성은 "얼마나 빨리 반영하느냐"가 아니라 "중복과 유실을 어떻게 다루느냐"에서 갈린다.
회고
•
Argo TMS 이벤트를 SQS로 실시간 수집·적재하는 파이프라인 구축
•
하루 3만 박스·20여 품목의 배송/회수 데이터 정제·집계 → 공장 연동
•
감에 의존하던 재고 판단을 데이터 기반으로 전환할 토대를 최초로 마련
이 프로젝트에서 가장 뿌듯했던 건 코드가 아니라 문제를 먼저 발견하고 제안한 것, 그리고 "우리가 어디까지 책임질지"의 경계를 잘 그은 것이었다. 다 만들어주는 게 아니라, 현장의 잦은 변화를 우리 시스템에서 분리하는 역할 분담 설계가 오히려 더 오래 가는 구조를 만들었다고 생각한다.