위밋모빌리티
경력근거: SAP·ERP·메시징 등 외부 시스템과의 API·웹훅 수신/발신 연동을 설계
위밋모빌리티 FDE 는 SAP·ERP·메시징 등 외부 시스템과의 API·웹훅 수신/발신 연동을 설계하고 재시도·멱등성·정합성·관측성까지 프로덕션 수준으로 완성하며, 장애를 증상이 아니라 근본 원인까지 파고들고 현장 요구를 구조화해 코어 팀에 전달합니다. 고객사 전송 원장과 수신 로그를 매일 대조해 어디서 몇 건이 어떤 원인으로 새는지 문서로 남기는 이 도구는 그 일의 증거를 만드는 자리에 정확히 놓입니다. 산출물이 비개발 이해관계자인 배차 담당자가 그대로 읽는 문서라, 고객·비개발 이해관계자와 소통하며 요구를 기술로 번역하는 축에도 함께 닿습니다. 이 포트폴리오가 보여주는 것은 연동 경계 기록 대조·멱등성·원인 분류 기술이며, 물류·SCM 최적화나 LIO·ROOUTY 경로최적화 도메인 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
SAP·ERP·메시징 API·웹훅 수신/발신 연동을 재시도·멱등성·정합성·관측성까지 프로덕션 수준으로 완성합니다. 두 기록을 매일 대조해 그 경계가 어디서 새는지 남기는 도구가 이 축과 직접 정렬됩니다.
증상이 아니라 근본 원인까지 파고들어 재발 방지로 잇습니다. 발견마다 조치 주체와 원본 값을 남기는 대사 리포트가 그 판단의 재료가 됩니다.
고객사 비개발 담당자와 직접 소통하며 요구를 기술로 번역하고 코어 팀에 구조화해 전달합니다. 배차 담당자가 그대로 읽는 리포트 문서가 이 축의 산출물입니다.
고객사 물류 IT 담당자가 ERP 에서 내려받아 건네주는 일일 전송 원장 CSV 와, 위밋모빌리티 수신 서버가 남긴 처리 로그 JSONL 을 주문번호·멱등키로 대조합니다. 두 기록이 어긋난 지점을 누락·값 불일치·중복 재전송·처리 실패·지연으로 분류하고, 발견마다 양쪽 원본 값을 인용해 마감 대사 리포트 Markdown 한 장과 고객사에 건넬 재전송 요청 CSV 를 만듭니다. 분류 정답은 대조 규칙(키 매칭·차집합·필드 비교·멱등키 중복·상태값·시각 차 임계)이 결정하고, LLM 은 그 결과를 담당자가 읽을 문단으로 풀고 재전송 요청 문안 초안을 쓰는 보조만 맡습니다. LIO·ROOUTY 경로최적화 엔진이 아니라 그 앞단 연동이 새는 곳을 매일 아침 짚는 도구입니다.
대형 화주 고객사 관제실에 파견 나간 위밋모빌리티 FDE. 매일 아침 배차팀이 'ERP 에서는 분명 보냈다'고 말한 주문과 수신 서버에 실제로 들어온 주문을 엑셀 두 개 띄워 눈으로 맞춰보던 사람입니다. 그 대조를 명령 한 줄로 끝내고, 결과를 배차팀에 그대로 건넬 문서로 받고 싶어 합니다.
전송 원장과 수신 로그를 같은 키로 묶으면, (a) 누락·값 불일치·중복 재전송·처리 실패·지연을 규칙으로 분류하고, (b) 발견마다 양쪽 원본 값을 인용해 고객사가 조치할 일과 우리가 조치할 일을 가르며, (c) 배차 담당자가 그대로 읽는 리포트 한 장으로 아침 확인 통화를 대신합니다.
고객사 전송 원장 CSV 와 수신 서버 처리 로그 JSONL 을 받아 주문번호·멱등키로 대조하고, 누락·값 불일치·중복 재전송·처리 실패·지연을 원인별로 가려 마감 대사 리포트 Markdown 한 장과 재전송 요청 CSV 를 내놓는 동작하는 CLI입니다. 원장·로그는 일반 물류 더미로 시작하고 고객사별 컬럼 매핑은 YAML 한 장으로 둡니다.
원인 분류를 심은 정답 라벨·운영자 2명 라벨과 비교해 원인별 정확도와 일치도를 터미널 표로 내놓고, 이때부터 대사 도구를 컨테이너로 싸 AWS 에서 매일 아침 정기 실행합니다.
일자별 발견을 누적해 원인·고객사별 발생 추이 절을 리포트 말미에 붙이고, 두 번째 고객사 원장 포맷을 컬럼 매핑만 바꿔 그대로 돌립니다.
정상·누락·값 불일치·중복 재전송·처리 실패·지연이 섞인 하루치 원장 120행과 수신 로그를 넣으면, 명령 한 줄로 마감 대사 리포트 Markdown 과 재전송 요청 CSV 가 몇 초 안에 떨어지는 걸 시연합니다
머리글이 두 줄이거나 소계 행·빈 열·중량 단위 표기가 섞인 망가진 원장 30종을 넣어도 파싱이 멈추지 않고, 읽지 못한 행은 리포트 맨 뒤 절에 원본 그대로 남습니다
더미에 심은 정답 원인 60건 중 분류가 일치한 게 55건(약 92%)입니다. 단, 원장·로그·정답을 모두 작성자가 만든 합성 기준이라 '성능 검증'이 아니라 '데모 조건에서의 동작'임을 명시합니다
진짜 성능 검증은 여기서 멈춥니다. 위 수치는 전부 작성자가 만든 더미데이터 자가채점이며, 실제 정확도는 위밋모빌리티 고객사의 실 전송 원장과 실 수신 로그, 실 운영자가 있어야 측정 가능합니다. 이 도구는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 화주명 표기 흔들림·중량 자릿수 오류·희망 시각 결측·같은 멱등키 재전송·지연 경계값 같은 케이스를 더미에 일부러 섞어, 쉬운 행만 통과시키는 게 아님을 보여줍니다
값이 어긋났을 때 어느 쪽 기록이 맞는지는 대조만으로 정해지지 않습니다. 도구는 옳고 그름을 단정하지 않고 양쪽 원본 값을 나란히 인용한 뒤 조치 주체만 가르며, 판단 근거가 부족하면 hedge 문장을 답니다
고객사마다 원장 컬럼명과 파일 형식이 제각각입니다. 매핑 YAML 로 흡수하고 매핑이 없는 컬럼은 추측하지 말고 '읽지 못한 행'으로 남깁니다. 실제 도입 시 고객사 실 양식이 더미와 다르므로 매핑을 다시 맞춰야 합니다(도입 단계 과제로 분리)
이 포트폴리오가 보여주는 것은 연동 경계 기록 대조·멱등성·원인 분류·리포트 산출 기술입니다. 물류·SCM 최적화나 LIO·ROOUTY 경로최적화 도메인 전문성은 포트폴리오로 증명되지 않으므로 주장하지 않으며, 경로최적화 엔진 자체는 만들지 않습니다. 그 깊이는 면접에서 모르면 모른다고 말하는 편이 낫습니다
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
고객사 물류 IT 담당자가 ERP 에서 내려받아 건네주는 일일 전송 원장입니다. 대조의 한쪽 기준이 되며, 화주명 표기 흔들림·희망 시각 결측·중량 자릿수 오류를 일부러 섞고 expected_finding 으로 정답을 심어 자가채점합니다.
| ledger_row | order_no | shipper_name_raw | sent_at | dest_code | weight_kg | want_deliver_ts | idem_key | expected_finding |
|---|---|---|---|---|---|---|---|---|
| L001 | ord_2101 | 씨제이대한통운 | 2026-03-04T08:02 | D-1100 | 12.5 | 2026-03-04T15:00 | u-2101 | matched |
| L002 | ord_2102 | 씨제이대한통운 | 2026-03-04T08:05 | D-1104 | 8 | 2026-03-04T16:00 | u-2102 | missing_inbound |
| L003 | ord_2103 | 씨제이대한통운 | 2026-03-04T08:07 | D-1120 | 20 | 2026-03-04T17:30 | u-2103 | field_mismatch |
| L004 | ord_2104 | CJ 대한통운 | 2026-03-04T08:11 | D-1131 | 5 | 2026-03-04T14:00 | u-2104 | duplicate_retry |
| L005 | ord_2105 | 씨제이대한통운 | 2026-03-04T08:15 | D-1142 | 9.5 | 2026-03-04T18:00 | u-2105 | processing_failed |
아래 Zod 스키마에 맞는 고객사 전송 원장 80행을 JSON 배열로 생성합니다. 하루치 한 고객사 기준이며 sent_at 은 08:00~11:00 사이로 오름차순입니다. expected_finding 은 짝이 되는 수신 로그와의 관계를 미리 정해 둔 정답이고, 우선순위는 누락 > 처리 실패 > 중복 재전송 > 값 불일치 > 지연 > 일치입니다.
{"type":"object","properties":{"ledger_row":{"type":"string"},"order_no":{"type":"string"},"shipper_name_raw":{"type":"string"},"sent_at":{"type":"string"},"dest_code":{"type":"string"},"weight_kg":{"type":"number"},"want_deliver_ts":{"type":["string","null"]},"idem_key":{"type":"string"},"expected_finding":{"type":"string","enum":["matched","missing_inbound","field_mismatch","duplicate_retry","processing_failed","delayed"]}},"required":["ledger_row","order_no","shipper_name_raw","sent_at","dest_code","weight_kg","want_deliver_ts","idem_key","expected_finding"]}
쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라(쉬운 행만 통과시키는 함정 회피): (1) 같은 화주의 표기 별칭('씨제이대한통운'/'CJ 대한통운'/'CJ대한통운'/'씨제이 대한통운') 5건 이상, (2) want_deliver_ts=null 결측 3건, (3) 중량 경계값(0.5 미만·자릿수가 한 자리 어긋난 값) 4건, (4) expected_finding 이 missing_inbound 인 행 6건 이상, (5) duplicate_retry 3건과 processing_failed 3건, (6) delayed 4건. 정답은 위 우선순위대로 정직하게 넣어라. JSON 배열만.{"type":"object","properties":{"ledger_row":{"type":"string"},"order_no":{"type":"string"},"shipper_name_raw":{"type":"string"},"sent_at":{"type":"string"},"dest_code":{"type":"string"},"weight_kg":{"type":"number"},"want_deliver_ts":{"type":["string","null"]},"idem_key":{"type":"string"},"expected_finding":{"type":"string","enum":["matched","missing_inbound","field_mismatch","duplicate_retry","processing_failed","delayed"]}},"required":["ledger_row","order_no","shipper_name_raw","sent_at","dest_code","weight_kg","want_deliver_ts","idem_key","expected_finding"]}위밋모빌리티 수신 서버가 남긴 처리 로그입니다. 대조의 다른 한쪽 기준이며, 원장에 없는 주문(unexpected_inbound)과 DLQ 격리·재시도 회차를 담습니다. 정답 라벨은 원장 쪽에만 심고 이 로그는 원본 기록 그대로 둡니다.
| event_id | order_no | shipper_id | received_at | dest_code | weight_kg | want_deliver_ts | idem_key | status | retry_no |
|---|---|---|---|---|---|---|---|---|---|
| ev_5001 | ord_2101 | cj | 2026-03-04T08:03 | D-1100 | 12.5 | 2026-03-04T15:00 | u-2101 | processed | 0 |
| ev_5002 | ord_2103 | cj | 2026-03-04T08:08 | D-1120 | 2 | 2026-03-04T17:30 | u-2103 | processed | 0 |
| ev_5003 | ord_2104 | cj | 2026-03-04T08:12 | D-1131 | 5 | 2026-03-04T14:00 | u-2104 | processed | 0 |
| ev_5004 | ord_2104 | cj | 2026-03-04T08:14 | D-1131 | 5 | 2026-03-04T14:00 | u-2104 | processed | 1 |
| ev_5005 | ord_2105 | cj | 2026-03-04T08:16 | D-1142 | 9.5 | 2026-03-04T18:00 | u-2105 | dlq | 2 |
아래 Zod 스키마에 맞는 수신 서버 처리 로그 85행을 JSON 배열로 생성합니다. 같은 하루치 전송 원장과 짝을 이루며, 원장의 expected_finding 과 앞뒤가 맞아야 합니다.
{"type":"object","properties":{"event_id":{"type":"string"},"order_no":{"type":"string"},"shipper_id":{"type":"string"},"received_at":{"type":"string"},"dest_code":{"type":"string"},"weight_kg":{"type":"number"},"want_deliver_ts":{"type":["string","null"]},"idem_key":{"type":"string"},"status":{"type":"string","enum":["processed","dlq"]},"retry_no":{"type":"number"}},"required":["event_id","order_no","shipper_id","received_at","dest_code","weight_kg","want_deliver_ts","idem_key","status","retry_no"]}
짝 맞추기 규칙: missing_inbound 인 원장 행은 이 로그에 아예 넣지 않는다. processing_failed 인 행은 status=dlq 로 넣는다. duplicate_retry 인 행은 같은 idem_key 로 retry_no 0 과 1 두 건을 2~3분 간격으로 넣는다. field_mismatch 인 행은 dest_code·weight_kg·want_deliver_ts 중 하나만 원장과 다르게 넣는다(중량 자릿수 어긋남 또는 결측 대 값). delayed 인 행은 received_at 을 sent_at 보다 20~50분 뒤로 넣는다. matched 인 행은 모든 비교 필드를 원장과 같게 하고 received_at 을 1~3분 뒤로 넣는다. 여기에 더해 원장에 없는 주문번호(unexpected_inbound) 4건을 섞어라. JSON 배열만.{"type":"object","properties":{"event_id":{"type":"string"},"order_no":{"type":"string"},"shipper_id":{"type":"string"},"received_at":{"type":"string"},"dest_code":{"type":"string"},"weight_kg":{"type":"number"},"want_deliver_ts":{"type":["string","null"]},"idem_key":{"type":"string"},"status":{"type":"string","enum":["processed","dlq"]},"retry_no":{"type":"number"}},"required":["event_id","order_no","shipper_id","received_at","dest_code","weight_kg","want_deliver_ts","idem_key","status","retry_no"]}| Node.js | 대조 엔진·리포트 렌더러를 구동하는 CLI 런타임입니다 |
| TypeScript | 로더·대조 규칙·출력 스키마의 구현 언어입니다 |
| API·웹훅 연동 로그 | 수신 경계가 남긴 처리 기록이 대조의 한쪽 입력입니다 |
| 멱등성 키 | 재전송 중복을 가려내는 대조 키입니다 |
| 메시지 큐(DLQ) | 처리 실패로 격리된 주문이 리포트의 처리 실패 항목으로 올라옵니다 |
| 관계형 DB | 일자별 대사 결과를 적재하고 누적 조회하며 인덱스·트랜잭션을 씁니다 |
| 이벤트 기반 아키텍처 | 수신 로그가 이벤트 단위 기록이라 재시도 회차까지 대조에 들어옵니다 |
| 관측성(APM·로그·트레이스) | 전송·수신 타임스탬프 차이로 지연을 판정하고 재시도 구간을 되짚습니다 |
| AWS | 2주차부터 대사 도구를 컨테이너로 매일 아침 정기 실행합니다 |
| 입력검증·시큐어코딩 | 원장 파일 파싱에 스키마 검증을 걸고 고객사별 데이터를 분리 보관합니다 |
| LLM | 대조 결과를 배차 담당자가 읽을 문단으로 풀고 재전송 요청 문안 초안을 씁니다 |
| Claude | 요약 문단·요청 문안 초안을 쓰는 LLM 입니다 |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 2주차(분류가 맞았는지 확인)로 진행합니다.
✓ pass → 여기서 멈춰도 완결입니다. 더 키우면 3주차(일자 누적 추이)로 진행합니다.
대조 발견 1건. 원인·조치 주체·양쪽 원본 값을 한 스키마로 담아 마감 대사 리포트 본문 한 줄과 재전송 요청 CSV 한 행으로 렌더됩니다. 경로최적화 판정이 아니라 연동 경계 기록 대조 결과입니다.
{"type":"object","properties":{"order_no":{"type":"string"},"finding":{"type":"string","enum":["matched","missing_inbound","unexpected_inbound","field_mismatch","duplicate_retry","processing_failed","delayed"]},"owner":{"type":"string","enum":["shipper","weemeet"]},"ledger_value":{"type":["string","null"]},"log_value":{"type":["string","null"]},"reason":{"type":"string","minLength":20},"action":{"type":"string","minLength":10},"hedge":{"type":["string","null"]}},"required":["order_no","finding","owner","ledger_value","log_value","reason","action","hedge"]}[
{
"input_ref": "ord_2103 (값 불일치)",
"output": {
"order_no": "ord_2103",
"finding": "field_mismatch",
"owner": "shipper",
"ledger_value": "weight_kg=20.0",
"log_value": "weight_kg=2.0",
"reason": "전송 원장은 중량 20.0kg 인데 수신 로그에는 2.0kg 로 들어와 자릿수가 한 자리 어긋났습니다. 이대로 두면 차량 적재량 계산이 틀어집니다.",
"action": "재전송 요청 CSV 에 담아 고객사 배차팀에 원장 기준 중량으로 재전송을 요청합니다.",
"hedge": "어느 쪽 값이 맞는지는 원장 작성 시점 기록을 받아야 갈립니다"
}
},
{
"input_ref": "ord_2102 (수신 누락)",
"output": {
"order_no": "ord_2102",
"finding": "missing_inbound",
"owner": "shipper",
"ledger_value": "sent_at=2026-03-04T08:05, dest_code=D-1104",
"log_value": null,
"reason": "고객사 원장에는 08:05 에 보낸 것으로 남아 있는데 수신 로그에 같은 주문번호가 한 건도 없습니다. 오늘 배차 대상에서 통째로 빠집니다.",
"action": "재전송 요청 CSV 맨 위에 올려 고객사 배차팀에 즉시 재전송을 요청합니다.",
"hedge": "발신 실패인지 수신 유실인지는 고객사 전송 로그를 받아야 갈립니다"
}
},
{
"input_ref": "ord_2104 (중복 재전송)",
"output": {
"order_no": "ord_2104",
"finding": "duplicate_retry",
"owner": "weemeet",
"ledger_value": "idem_key=u-2104 (원장 1건)",
"log_value": "idem_key=u-2104 (로그 2건, retry_no=0·1)",
"reason": "같은 멱등키로 2분 간격에 두 번 수신됐습니다. 멱등 처리가 걸려 배차는 한 번만 잡혔지만 재시도가 난 이유는 남습니다.",
"action": "수신 워커의 응답 지연 구간을 트레이스로 되짚고 재발 방지 항목으로 올립니다.",
"hedge": null
}
}
][자동 검증] 1. 스키마 parse 로 출력 형식(필드·enum)이 스키마를 통과하는지 확인합니다. 2. finding 이 대조 규칙 우선순위(누락 > 처리 실패 > 중복 재전송 > 값 불일치 > 지연 > 일치)와 일치하는지 봅니다. 3. ledger_value·log_value 가 입력 두 파일의 실제 필드값 인용인지 토큰 매칭으로 확인하고, 한쪽에만 있는 발견은 없는 쪽을 null 로 강제합니다. 4. finding 을 원장에 심은 expected_finding 과 비교해 원인별 정확도를 계산하고, 원장에 없는 수신 건이 전부 unexpected_inbound 로 잡히는지 확인합니다. [수동 검증] 1. 운영자 2명이 비동기로 원인을 라벨링해 정확도와 bootstrap Cohen's κ(두 사람 일치가 우연 이상인 정도, 0~1) 하한 0.6 이상을 봅니다. 2. 전체의 5% 를 사람이 직접 점검합니다.
근거: SAP·ERP·메시징 등 외부 시스템과의 API·웹훅 수신/발신 연동을 설계