라이넨스
경력근거: 사용률, 생산성, 오류율, 납기 준수율 등 KPI를 모니터링합니다.
공고는 이 자리의 첫 업무로 '직영 세탁공장의 생산, 품질, 수배송, 설비 운영 프로세스를 직접 관찰합니다' 와 '공장장과 작업자가 경험적으로 수행하는 판단과 업무 방식을 구조화합니다' 를 적었습니다. 배송차 출발 전에 모자란 수량을 재고로 채울지 재세탁을 기다릴지 정하는 일은 수배송 현장에서 출고 담당자가 경험으로 내리는 판단이고, 렌탈과 세탁 두 계약이 섞인 라이넨스 공장이라 같은 부족도 거래처마다 답이 다릅니다. 판정을 하루 단위로 모으면 공고의 '사용률, 생산성, 오류율, 납기 준수율 등 KPI를 모니터링합니다' 가운데 납기 준수율이 거래처마다 나오고, 회차별 판정 화면은 '현장 문제를 해결하기 위한 웹 도구, 대시보드, 자동화 워크플로 및 AI Agent를 직접 설계하고 구현합니다' 의 웹 도구 한 벌입니다. 이 포트폴리오는 여러 시트를 시각 순서로 배정 계산해 판정 화면으로 내는 기술을 보여줍니다. 산업세탁과 린넨 물류 전문성은 주장하지 않으며, 재고를 회차에 나누는 순서는 공장장과 정할 기준으로 둡니다.
주요업무가 '직영 세탁공장의 생산, 품질, 수배송, 설비 운영 프로세스를 직접 관찰합니다' 로 시작하고 '공장장과 작업자가 경험적으로 수행하는 판단과 업무 방식을 구조화합니다' 가 이어집니다. 출고 담당자가 출발 직전에 머릿속으로 하던 재고·재세탁 배분을 출발 순서 배정 규칙으로 옮긴 것이 이 두 줄을 받습니다.
자격요건에 'Python, TypeScript, JavaScript, SQL 등 하나 이상의 기술 스택을 실무에서 활용할 수 있는 분' 이, 주요업무에 '현장 문제를 해결하기 위한 웹 도구, 대시보드, 자동화 워크플로 및 AI Agent를 직접 설계하고 구현합니다' 가 있습니다. Python 판정 엔진, Next.js 관리자 페이지, 3주차 n8n 출발 전 알림 흐름이 이 구성을 따릅니다.
'사용률, 생산성, 오류율, 납기 준수율 등 KPI를 모니터링합니다' 와 '한 공장에서 해결한 문제를 다른 공장에도 적용할 수 있도록 표준화합니다' 가 공고에 있습니다. 회차 판정을 쌓아 거래처별 납기 준수율을 내는 2주차 화면과, 공장마다 재고를 따로 쓰는지를 설정으로 뺀 3주차 엔진이 여기에 닿습니다.
라이넨스는 숙박업체에 두 가지 계약으로 린넨을 보냅니다. 렌탈 거래처에는 라이넨스 소유의 표준 규격 시트·타월을 빌려주므로 모자라면 공장 렌탈 재고에서 같은 품목을 꺼내 채울 수 있고, 세탁 거래처에는 그 호텔이 소유한 린넨을 빨아 돌려주므로 재고로는 채울 수 없고 그 호텔 몫의 재세탁분만 기다릴 수 있습니다. 이 관리자 페이지는 김포 세탁공장의 하루 치 주문표·출고 검수 수량·배송 회차표·거래처 계약·재세탁 대기표·렌탈 재고표 여섯 시트를 받아, 배송 회차·거래처·품목마다 다섯 판정 중 하나를 냅니다. 모자란 줄에는 출발 시각이 이른 회차부터 렌탈 재고를 배정하고 남은 재고를 다음 회차로 넘기며, 재세탁분은 출발 전에 끝나는 묶음만 세고 같은 거래처의 앞 회차가 쓴 수량을 뺍니다. 모자란 채 나가는 줄에는 클로드가 쓴 호텔 안내 문안 초안이 붙고, 전담 매니저가 읽고 고쳐 직접 보냅니다.
김포 세탁공장에서 배송 회차마다 카트를 차에 싣기 전 마지막으로 수량을 확인하는 출고 담당자와, 호텔·모텔 거래처를 맡아 모자란 날 미리 전화하는 전담 매니저 — 지금은 주문표와 출고 검수 수량을 눈으로 맞대다 출발 직전에야 모자란 걸 알고, 재고로 채울지 재세탁을 기다릴지를 그 자리에서 정한다
출고 담당자가 출발 직전에야 모자란 걸 아는 까닭은 정보가 없어서가 아니라, 주문표·검수 수량·재세탁 대기표·렌탈 재고가 서로 다른 시트에 있고 렌탈 재고가 회차를 거치며 줄어드는 계산을 머릿속으로 이어 가기 때문입니다. 06:30 회차에 킹 시트 24장을 재고에서 꺼내면 14:00 회차가 쓸 수 있는 재고는 그만큼 줄고, 세탁 거래처는 재고가 넉넉해도 채울 수 없으며, 같은 호텔의 재세탁분은 오전 회차가 먼저 쓰면 저녁 회차 몫이 남지 않습니다. 하루 회차를 출발 순서대로 한 번에 계산해 두면 출고 담당자는 판정을 보고 카트를 채우고, 전담 매니저는 모자란 채 나갈 호텔에만 미리 전화하면 됩니다.
출고 담당자가 그날 주문표·출고 검수·배송 회차표·거래처 계약·재세탁 대기·렌탈 재고 여섯 시트가 든 엑셀 한 파일을 올리면, 배송 회차·거래처·품목마다 다섯 판정과 렌탈 재고 배정 수량·출발 전 끝나는 재세탁 수량·남는 부족 수량이 판정별 표로 나오는 관리자 페이지입니다. 모자란 채 나가는 줄에는 클로드가 쓴 호텔 안내 문안 초안이 붙습니다. 판정 엔진은 Python FastAPI, 화면은 Next.js 로 만들어 Vercel 에 올립니다. 검수 수량은 출고 담당자가 엑셀로 옮긴 값을 그대로 받는다고 두고, 검수 라인에서 직접 받는 연동은 3주차로 미룹니다.
출고 담당자가 손으로 판정해 둔 합성 사흘 치 150줄과 화면 판정을 줄마다 견주는 대조 화면을 더합니다. 업로드한 시트와 판정을 PostgreSQL 에 쌓아 전담 매니저가 거래처별로 주문대로 채워 나간 줄의 비율(납기 준수율)을 날짜별로 보고, 회차가 떠난 뒤 실제로 실린 수량을 받아 출발 전 판정과 다른 줄을 따로 셉니다.
검수 라인 집계를 흉내 낸 모의 API 에서 카트 검수 수량이 들어올 때마다 그 회차부터 뒤 회차까지 배정을 다시 돌리고, 출발 30분 전에 모자란 채 나갈 줄과 안내 문안 묶음을 전담 매니저의 Slack 채널로 넘기는 워크플로를 n8n 으로 만듭니다. 공장마다 렌탈 재고를 따로 쓰는지 함께 쓰는지를 설정으로 빼서 다른 공장 시트에도 같은 엔진을 씁니다.
출고 담당자가 2026-10-02 하루 치 엑셀을 올리면 06:30 호텔 마곡나루의 킹 시트 24장 부족이 렌탈 재고로 채울 수 있음에 서고 재고가 60장에서 36장으로 줄며, 14:00 청라베이 호텔 킹 시트는 남은 재고가 없어 13:20 재세탁분 8장을 세고도 2장이 모자란 채 출발 칸에 섭니다. 전담 매니저는 그 줄 옆 안내 문안 초안을 고쳐 호텔에 보냅니다.
17:00 호텔 등촌 바스타월은 재세탁 대기 50장이 있지만 10:30 회차가 30장을 먼저 써 20장만 남아 5장이 모자란 채 출발로 나옵니다. 회차마다 재세탁 수량을 따로 더하는 계산이었다면 이 줄은 재세탁분으로 채울 수 있다고 잘못 나왔을 줄입니다.
출고 담당자 손판정 형식으로 작성자가 지은 사흘 치 150줄을 화면 판정과 줄마다 맞대어, 같은 시각 회차의 재고 순서와 출발 정각에 끝나는 재세탁분에서 둘이 갈리는지 셉니다. 사흘 모두 합성이라 이 일치 수는 공장 성과가 아닙니다.
경계 줄 다섯을 pytest 고정 입력으로 둡니다: 부족과 남은 재고가 같은 송정 레지던스, 출발 정각에 끝나는 청라베이 호텔 재세탁 5장, 완료 시각이 빈 발산 라임모텔 베개커버 10장, 카트 두 대로 나뉜 호텔 등촌 바스타월, 주문 없이 실린 송정 레지던스 핸드타월. 같은 시각 회차를 run_id 순으로 바꾸는 커밋이 들어오면 청라베이 호텔이 재세탁분으로 채움으로, 송정 레지던스가 모자란 채 출발로 뒤집혀 테스트가 실패합니다.
진짜 성능 검증은 여기서 멈춥니다. 위 수치는 합성 엑셀로 잰 값이라, 라이넨스 배송이 모자란 채 나가는 일이 줄어드는지는 실제 주문표와 검수 기록, 몇 주 치 호텔 문의 기록을 봐야 압니다. 실제 시트가 오면 여섯 시트 양식에 열만 맞춰 올리고, 손판정과 갈린 줄부터 펼쳐 봅니다.
150줄 일치 수는 출고 담당자의 실제 판정이 아니라 제가 지은 엑셀에서 나온 숫자라, 이 화면이 모자란 채 나가는 배송을 줄인다고 아직 말할 수 없습니다. 지금 손에 있는 것은 14:00 에 함께 떠나는 청라베이 호텔과 송정 레지던스가 남은 킹 시트 6장을 어떻게 나누는지 계산으로 보여주는 화면과, 실제 시트를 받았을 때 손판정과 갈린 줄을 펼칠 대조 화면입니다.
경계 줄은 데이터를 짓기 전에 먼저 골랐습니다. 규칙과 데이터를 함께 지으면 수량과 시각이 정확히 맞닿는 줄이 저절로는 생기지 않기 때문입니다. 넣은 줄은 부족 6장과 남은 재고 6장이 같은 송정 레지던스, 출발 14:00 과 같은 시각에 끝나는 청라베이 호텔 재세탁 5장, 완료 시각이 빈 발산 라임모텔 베개커버 10장, 앞 회차가 재세탁분을 먼저 쓴 호텔 등촌, 주문 없이 실린 송정 레지던스 핸드타월 20장입니다.
같은 시각 회차에서 부족이 적은 줄부터 재고를 주는 순서와, 렌탈 거래처에 재고를 먼저 쓰고 재세탁분을 나중에 쓰는 순서는 제가 정한 규칙입니다. 라온모텔 강서점처럼 자기 재세탁분이 출발 전에 끝나는 렌탈 거래처가 재고를 먼저 가져가면 뒤 회차의 다른 렌탈 거래처가 모자랄 수 있어, 실제 도입에서는 어느 순서를 쓸지 공장장과 정해야 합니다.
렌탈 재고표는 아침에 한 번 센 값입니다. 낮 동안 회수해 다시 빤 렌탈 린넨이 재고로 돌아오는 흐름은 1주차 계산에 없고, 실제 공장에서는 그 보충 때문에 오후 회차 판정이 달라질 수 있습니다.
출고 검수 수량은 사람이 세어 엑셀에 옮긴 값이라 검수 오차가 있으면 판정도 따라 틀립니다. 검수 라인 집계와 잇는 것은 3주차 모의 API 까지이고, 실제 검수 시스템의 데이터 모양은 라이넨스에서 받아야 압니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
거래처가 회차별로 받기로 한 품목과 수량을 그날 아침 주문 관리 엑셀에서 내려받은 주문표입니다. 줄마다 주문 줄 번호, 배송 회차, 거래처, 품목, 수량을 둡니다. 한 거래처가 오전과 저녁 두 회차로 받는 날도 있습니다.
| order_no | run_id | client_id | item_code | order_qty |
|---|---|---|---|---|
| OD-1002-01 | 1002-1A | CL-0141 | KS-SHEET | 120 |
| OD-1002-02 | 1002-1A | CL-0141 | BT-TOWEL | 240 |
| OD-1002-03 | 1002-1B | CL-0152 | BM-MAT | 40 |
| OD-1002-04 | 1002-1C | CL-0163 | BT-TOWEL | 300 |
| OD-1002-05 | 1002-2A | CL-0174 | PC-COVER | 80 |
아래 JSON Schema 를 그대로 만족하는 하루 치 주문표를 60줄 더 만들어 주세요. 거래처 열 곳이 회차 열 개에 나뉘어 받고, 큰 호텔 두세 곳은 하루 두 회차로 받습니다. order_qty 는 시트·커버는 20~150장, 타월은 100~300장 사이로 씁니다. 쉬운 데이터만 만들지 말고 같은 시각에 떠나는 두 회차가 같은 품목을 주문한 줄을 섞으세요. 판정 결과나 부족 수량은 넣지 마세요. 형식 깨뜨리기 시도로 order_qty 를 '120장'·'1,200' 처럼 글자로 적은 줄 3건을 따로 만듭니다.
{"type":"object","properties":{"order_no":{"type":"string","description":"주문 줄 번호입니다. 하루 안에서 줄마다 다릅니다"},"run_id":{"type":"string","description":"이 주문을 싣는 배송 회차입니다"},"client_id":{"type":"string"},"item_code":{"type":"string","description":"린넨 품목 코드입니다. rental_stock.item_code 와 같은 체계입니다"},"order_qty":{"type":"integer","minimum":0,"description":"거래처가 이 회차에 받기로 한 수량(장)입니다"}},"required":["order_no","run_id","client_id","item_code","order_qty"]}{"type":"object","properties":{"order_no":{"type":"string","description":"주문 줄 번호입니다. 하루 안에서 줄마다 다릅니다"},"run_id":{"type":"string","description":"이 주문을 싣는 배송 회차입니다"},"client_id":{"type":"string"},"item_code":{"type":"string","description":"린넨 품목 코드입니다. rental_stock.item_code 와 같은 체계입니다"},"order_qty":{"type":"integer","minimum":0,"description":"거래처가 이 회차에 받기로 한 수량(장)입니다"}},"required":["order_no","run_id","client_id","item_code","order_qty"]}출고 담당자가 카트를 차에 싣기 전 마지막으로 센 출고 검수 수량입니다. 카트 한 대에 한 거래처의 한 품목을 싣고, 수량이 많으면 같은 품목을 카트 두 대에 나눠 싣습니다. 주문표에 없는 품목이 카트에 올라가는 일도 있습니다.
| cart_no | run_id | client_id | item_code | packed_qty |
|---|---|---|---|---|
| CT-1A-01 | 1002-1A | CL-0141 | KS-SHEET | 96 |
| CT-1A-02 | 1002-1A | CL-0141 | BT-TOWEL | 240 |
| CT-1B-01 | 1002-1B | CL-0152 | BM-MAT | 52 |
| CT-1C-01 | 1002-1C | CL-0163 | BT-TOWEL | 262 |
| CT-2A-01 | 1002-2A | CL-0174 | PC-COVER | 55 |
아래 JSON Schema 를 그대로 만족하는 출고 검수 수량을 70줄 더 만들어 주세요. 주문표의 줄마다 카트 한 대를 두되 타월 200장 이상은 카트 두 대로 나눕니다. 대부분은 주문 수량과 같게 두고, 다음을 섞으세요: 주문보다 5~40장 적은 카트, 주문보다 많이 실린 카트, 주문표에 없는 품목을 실은 카트 2건, 남은 렌탈 재고와 부족 수량이 꼭 같아지는 카트. 판정 결과는 넣지 마세요.
{"type":"object","properties":{"cart_no":{"type":"string","description":"출고 검수를 마친 카트 번호입니다"},"run_id":{"type":"string"},"client_id":{"type":"string"},"item_code":{"type":"string"},"packed_qty":{"type":"integer","minimum":0,"description":"출고 검수에서 센 수량(장)입니다. 한 품목이 카트 여러 대에 나뉘면 카트마다 한 줄입니다"}},"required":["cart_no","run_id","client_id","item_code","packed_qty"]}{"type":"object","properties":{"cart_no":{"type":"string","description":"출고 검수를 마친 카트 번호입니다"},"run_id":{"type":"string"},"client_id":{"type":"string"},"item_code":{"type":"string"},"packed_qty":{"type":"integer","minimum":0,"description":"출고 검수에서 센 수량(장)입니다. 한 품목이 카트 여러 대에 나뉘면 카트마다 한 줄입니다"}},"required":["cart_no","run_id","client_id","item_code","packed_qty"]}그날 배송 회차표입니다. 회차마다 배송차, 권역, 공장 출발 시각을 둡니다. 같은 시각에 두 대가 함께 떠나는 회차가 있습니다.
| run_id | departs_at | vehicle | route_name |
|---|---|---|---|
| 1002-1A | 2026-10-02 06:30 | 1호차 | 마곡·발산 |
| 1002-1B | 2026-10-02 06:45 | 2호차 | 김포 시내 |
| 1002-1C | 2026-10-02 07:00 | 3호차 | 화곡·강서 |
| 1002-2A | 2026-10-02 10:00 | 1호차 | 발산·화곡 |
| 1002-2B | 2026-10-02 10:20 | 2호차 | 김포공항 |
아래 JSON Schema 를 그대로 만족하는 다른 날의 배송 회차표를 사흘 치 30줄 더 만들어 주세요. 하루 회차는 06:00~18:00 사이 네 번이고 회차마다 배송차 두세 대가 서로 다른 권역으로 10~30분 간격을 두고 떠납니다. 쉬운 데이터만 만들지 말고 하루에 한 번은 두 대가 같은 시각에 떠나게 하세요. 판정 결과는 넣지 마세요.
{"type":"object","properties":{"run_id":{"type":"string","description":"날짜(MMDD)-회차·호차입니다. 1002-3B 는 10월 2일 3회차 2호차입니다"},"departs_at":{"type":"string","description":"배송차가 공장을 떠나는 시각, YYYY-MM-DD HH:MM"},"vehicle":{"type":"string"},"route_name":{"type":"string"}},"required":["run_id","departs_at","vehicle","route_name"]}{"type":"object","properties":{"run_id":{"type":"string","description":"날짜(MMDD)-회차·호차입니다. 1002-3B 는 10월 2일 3회차 2호차입니다"},"departs_at":{"type":"string","description":"배송차가 공장을 떠나는 시각, YYYY-MM-DD HH:MM"},"vehicle":{"type":"string"},"route_name":{"type":"string"}},"required":["run_id","departs_at","vehicle","route_name"]}거래처 대장에서 계약 방식만 뽑은 표입니다. 렌탈 거래처는 라이넨스 소유 린넨을 빌려 쓰므로 공장 렌탈 재고로 채울 수 있고, 세탁 거래처는 자기 소유 린넨을 맡기므로 재고로 채울 수 없습니다. 전담 매니저 이름을 함께 둡니다.
| client_id | client_name | client_type | service_type | account_manager |
|---|---|---|---|---|
| CL-0141 | 호텔 마곡나루 | 호텔 | 렌탈 | 김서윤 |
| CL-0152 | 김포 루체호텔 | 호텔 | 렌탈 | 김서윤 |
| CL-0163 | 라온모텔 강서점 | 모텔 | 렌탈 | 박도현 |
| CL-0174 | 발산 라임모텔 | 모텔 | 세탁 | 박도현 |
| CL-0185 | 스테이 김포공항 | 호텔 | 렌탈 | 김서윤 |
아래 JSON Schema 를 그대로 만족하는 거래처 대장을 30줄 더 만들어 주세요. 호텔·모텔이 대부분이고 레지던스·피트니스를 몇 곳 섞으며, 렌탈과 세탁을 6:4 정도로 둡니다. 전담 매니저 세 명이 거래처를 나눠 맡습니다. 판정 결과는 넣지 마세요.
{"type":"object","properties":{"client_id":{"type":"string"},"client_name":{"type":"string"},"client_type":{"type":"string","enum":["호텔","모텔","레지던스","피트니스"]},"service_type":{"type":"string","enum":["렌탈","세탁"],"description":"렌탈은 라이넨스 소유 린넨을 빌려주는 계약, 세탁은 거래처 소유 린넨을 빨아 돌려주는 계약입니다"},"account_manager":{"type":"string","description":"그 거래처를 맡은 전담 매니저입니다"}},"required":["client_id","client_name","client_type","service_type","account_manager"]}{"type":"object","properties":{"client_id":{"type":"string"},"client_name":{"type":"string"},"client_type":{"type":"string","enum":["호텔","모텔","레지던스","피트니스"]},"service_type":{"type":"string","enum":["렌탈","세탁"],"description":"렌탈은 라이넨스 소유 린넨을 빌려주는 계약, 세탁은 거래처 소유 린넨을 빨아 돌려주는 계약입니다"},"account_manager":{"type":"string","description":"그 거래처를 맡은 전담 매니저입니다"}},"required":["client_id","client_name","client_type","service_type","account_manager"]}검수에서 얼룩·이염·탈수 불량으로 걸려 다시 빨고 있는 묶음 목록입니다. 거래처·품목별로 완료 예정 시각을 두고, 세탁 라인이 아직 시각을 정하지 못한 묶음은 시각이 비어 있습니다.
| rewash_no | client_id | item_code | rewash_qty | ready_at | rewash_reason |
|---|---|---|---|---|---|
| RW-1002-01 | CL-0163 | BT-TOWEL | 10 | 2026-10-02 06:10 | 얼룩 |
| RW-1002-02 | CL-0174 | PC-COVER | 25 | 2026-10-02 15:40 | 얼룩 |
| RW-1002-03 | CL-0174 | PC-COVER | 10 | — | 탈수 불량 |
| RW-1002-04 | CL-0185 | BT-TOWEL | 12 | 2026-10-02 11:00 | 이염 |
| RW-1002-05 | CL-0196 | BT-TOWEL | 30 | 2026-10-02 09:50 | 얼룩 |
아래 JSON Schema 를 그대로 만족하는 재세탁 대기 묶음을 40줄 더 만들어 주세요. 묶음은 5~40장이고 완료 예정 시각은 05:30~18:00 사이입니다. 쉬운 데이터만 만들지 말고 다음을 섞으세요: 그 거래처 회차 출발 시각과 꼭 같은 시각에 끝나는 묶음, ready_at 이 null 인 묶음, 같은 거래처·품목에 오전과 오후로 나뉜 두 묶음, 그날 주문이 없는 거래처의 묶음. 판정 결과는 넣지 마세요.
{"type":"object","properties":{"rewash_no":{"type":"string","description":"재세탁 묶음 번호입니다"},"client_id":{"type":"string"},"item_code":{"type":"string"},"rewash_qty":{"type":"integer","minimum":1,"description":"검수에서 걸려 다시 빠는 수량(장)입니다"},"ready_at":{"type":["string","null"],"description":"재세탁 완료 예정 시각, YYYY-MM-DD HH:MM. 세탁 라인이 아직 시각을 정하지 않았으면 null 입니다"},"rewash_reason":{"type":"string","enum":["얼룩","이염","탈수 불량"]}},"required":["rewash_no","client_id","item_code","rewash_qty","ready_at","rewash_reason"]}{"type":"object","properties":{"rewash_no":{"type":"string","description":"재세탁 묶음 번호입니다"},"client_id":{"type":"string"},"item_code":{"type":"string"},"rewash_qty":{"type":"integer","minimum":1,"description":"검수에서 걸려 다시 빠는 수량(장)입니다"},"ready_at":{"type":["string","null"],"description":"재세탁 완료 예정 시각, YYYY-MM-DD HH:MM. 세탁 라인이 아직 시각을 정하지 않았으면 null 입니다"},"rewash_reason":{"type":"string","enum":["얼룩","이염","탈수 불량"]}},"required":["rewash_no","client_id","item_code","rewash_qty","ready_at","rewash_reason"]}그날 첫 회차가 떠나기 전 공장 창고에서 센 렌탈 린넨 재고입니다. 품목마다 한 줄이고, 낮 동안 회차가 떠날 때마다 이 수량에서 빠져나갑니다.
| item_code | item_name | stock_qty |
|---|---|---|
| KS-SHEET | 킹 시트 | 60 |
| QS-SHEET | 퀸 시트 | 140 |
| SS-SHEET | 싱글 시트 | 90 |
| DK-COVER | 이불커버 킹 | 40 |
| DQ-COVER | 이불커버 퀸 | 75 |
아래 JSON Schema 를 그대로 만족하는 렌탈 재고표를 다른 날 몫으로 20줄 더 만들어 주세요. 시트·이불커버·베개커버·타월·발매트 품목을 두고 stock_qty 는 0~300장 사이로 씁니다. 쉬운 데이터만 만들지 말고 그날 부족 수량 합보다 재고가 적은 품목을 몇 개 섞으세요. 판정 결과는 넣지 마세요.
{"type":"object","properties":{"item_code":{"type":"string"},"item_name":{"type":"string"},"stock_qty":{"type":"integer","minimum":0,"description":"그날 첫 회차 출발 전에 센 렌탈 린넨 재고(장)입니다"}},"required":["item_code","item_name","stock_qty"]}{"type":"object","properties":{"item_code":{"type":"string"},"item_name":{"type":"string"},"stock_qty":{"type":"integer","minimum":0,"description":"그날 첫 회차 출발 전에 센 렌탈 린넨 재고(장)입니다"}},"required":["item_code","item_name","stock_qty"]}| Python | 여섯 시트를 pandas 로 읽고, 출발 시각 순서로 렌탈 재고와 재세탁분을 배정하는 판정 엔진을 순수 함수로 짜 FastAPI 로 엽니다. |
| TypeScript | Next.js 관리자 페이지에서 판정별 표와 줄 펼침(재고가 회차를 거치며 줄어든 흐름, 센 재세탁 묶음)을 타입이 있는 응답으로 그립니다. |
| JavaScript | 브라우저에서 SheetJS 로 시트 이름과 머리글 위치를 읽어 여섯 시트가 다 있는지 업로드 전에 알려 줍니다. |
| SQL | 2주차에 PostgreSQL 에 날짜별 판정과 출발 뒤 실제 실린 수량을 쌓고, 거래처별 납기 준수율과 출발 전 판정이 빗나간 줄을 집계합니다. |
| API·데이터베이스 | 판정 API 하나를 출고 화면과 전담 매니저 화면이 함께 부르고, 3주차에는 모의 검수 API 가 보낸 카트 수량을 받아 다시 계산합니다. |
| 클라우드 환경 | 1주차부터 화면과 판정 API 를 Vercel 에 올려 공장 태블릿 브라우저에서 링크로 엽니다. |
| LLM | 모자란 채 나가는 줄의 거래처·품목·부족 수량·재세탁 완료 예정 시각을 받아 클로드가 호텔 안내 문안 초안을 씁니다. |
| Workflow Automation | 3주차에 카트 검수 수량이 들어오면 재판정하고 출발 30분 전에 Slack 으로 안내 문안 묶음을 넘기는 흐름을 n8n 으로 만듭니다. |
| MES·ERP·WMS 연동 | 3주차 모의 검수 API 를 검수 라인 집계 시스템 자리에 두고, 실제 시스템과는 시트 열 매핑만 바꿔 잇도록 입력층을 나눕니다. |
| 물류 최적화 | 같은 시각에 떠나는 회차끼리는 부족 수량이 적은 줄부터 재고를 줘, 다 채워 나가는 거래처 수를 늘리는 배정 순서를 규칙으로 둡니다. |
✗ fail → 앞 회차 배정이 뒤 회차로 넘어가지 않으면 정렬 기준과 재세탁 사용분 차감부터 고치고 다시 돌립니다.
✓ pass → 2주차 손판정 대조와 거래처별 납기 준수율 화면으로 넘어갑니다.
✗ fail → 차이가 재고 순서에서 나오면 연동보다 같은 시각 회차의 배정 기준을 공장장과 다시 정합니다.
✓ pass → 3주차 모의 검수 API 연동과 출발 전 Slack 알림으로 넘어갑니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
verdict사유 라벨 · run_id + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
주문 수량대로 실림주문대로모자람 — 렌탈 재고로 채울 수 있음재고로 채움모자람 — 재세탁분이 출발 전에 끝남재세탁분으로 채움모자란 채 출발 — 호텔에 미리 알림미리 알릴 부족주문보다 많이 실림더 실림하루 배송 회차의 회차·거래처·품목 줄마다 내는 출발 전 판정입니다. 주문 수량과 출고 검수 수량, 이 줄 차례에 남아 있던 렌탈 재고와 쓴 수량, 출발 전에 끝나 쓸 수 있는 재세탁 수량과 쓴 수량, 남는 부족 수량, 판정, 호텔 안내 문안 초안을 실어 출고 화면과 전담 매니저 화면이 같은 줄을 읽습니다.
{"type":"object","properties":{"run_id":{"type":"string","description":"한 줄은 run_id·client_id·item_code 세 값으로 정해집니다"},"departs_at":{"type":"string"},"client_id":{"type":"string"},"client_name":{"type":"string"},"service_type":{"type":"string","enum":["렌탈","세탁"]},"item_code":{"type":"string"},"item_name":{"type":"string"},"order_no":{"type":["string","null"],"description":"주문 없이 실린 카트의 줄이면 null 입니다"},"order_qty":{"type":"integer","minimum":0,"description":"주문 없이 실린 카트의 줄이면 0 입니다"},"packed_qty":{"type":"integer","minimum":0,"description":"정적 계산값입니다. 같은 run_id·client_id·item_code 의 packed_carts.packed_qty 합입니다"},"short_qty":{"type":"integer","minimum":0,"description":"정적 계산값입니다. order_qty − packed_qty 이고 0 미만이면 0 입니다"},"over_qty":{"type":"integer","minimum":0,"description":"정적 계산값입니다. packed_qty − order_qty 이고 0 미만이면 0 입니다"},"rental_stock_before":{"type":["integer","null"],"minimum":0,"description":"정적 계산값입니다. 이 줄 차례에 남아 있던 렌탈 재고로, rental_stock.stock_qty 에서 배정 순서상 앞선 같은 품목 줄들의 rental_used_qty 합을 뺀 값입니다. 세탁 거래처이거나 모자람이 없으면 null 입니다"},"rental_used_qty":{"type":["integer","null"],"minimum":0,"description":"정적 계산값입니다. min(short_qty, rental_stock_before) 입니다"},"rewash_ready_qty":{"type":["integer","null"],"minimum":0,"description":"정적 계산값입니다. 같은 client_id·item_code 의 rewash_queue 중 ready_at 이 이 줄의 departs_at 보다 이른 rewash_qty 합에서, 같은 거래처·품목의 앞선 줄들이 쓴 rewash_used_qty 합을 뺀 값입니다. 렌탈 재고로 다 채웠거나 모자람이 없으면 null 입니다"},"rewash_used_qty":{"type":["integer","null"],"minimum":0,"description":"정적 계산값입니다. min(렌탈 재고를 쓰고 남은 모자람, rewash_ready_qty) 입니다"},"left_short_qty":{"type":"integer","minimum":0,"description":"정적 계산값입니다. short_qty − rental_used_qty − rewash_used_qty 이고 null 은 0 으로 셉니다"},"verdict":{"type":"string","enum":["주문 수량대로 실림","모자람 — 렌탈 재고로 채울 수 있음","모자람 — 재세탁분이 출발 전에 끝남","모자란 채 출발 — 호텔에 미리 알림","주문보다 많이 실림"]},"evidence":{"type":"array","items":{"type":"object","properties":{"table":{"type":"string","enum":["client_orders","packed_carts","delivery_runs","client_contracts","rewash_queue","rental_stock"]},"field":{"type":"string"},"agg":{"type":["string","null"],"enum":["count","sum","avg","min","max",null]},"value":{}},"required":["table","field","value"]}},"summary":{"type":"string"},"notice_draft":{"type":["string","null"],"description":"클로드가 판정 줄을 받아 쓴 호텔 안내 문안 초안입니다. 모자란 채 출발 줄에만 있고 나머지는 null 입니다. 전담 매니저가 읽고 고쳐 직접 보냅니다"}},"required":["run_id","departs_at","client_id","client_name","service_type","item_code","item_name","order_no","order_qty","packed_qty","short_qty","over_qty","rental_stock_before","rental_used_qty","rewash_ready_qty","rewash_used_qty","left_short_qty","verdict","evidence","summary","notice_draft"]}[
{
"run_id": "1002-1A",
"departs_at": "2026-10-02 06:30",
"client_id": "CL-0141",
"client_name": "호텔 마곡나루",
"service_type": "렌탈",
"item_code": "BT-TOWEL",
"item_name": "바스타월",
"order_no": "OD-1002-02",
"order_qty": 240,
"packed_qty": 240,
"short_qty": 0,
"over_qty": 0,
"rental_stock_before": null,
"rental_used_qty": null,
"rewash_ready_qty": null,
"rewash_used_qty": null,
"left_short_qty": 0,
"verdict": "주문 수량대로 실림",
"evidence": [
{
"table": "client_orders",
"field": "order_qty",
"agg": null,
"value": 240
},
{
"table": "packed_carts",
"field": "packed_qty",
"agg": null,
"value": 240
}
],
"summary": "주문 240장과 출고 검수 240장이 같습니다.",
"notice_draft": null
},
{
"run_id": "1002-1A",
"departs_at": "2026-10-02 06:30",
"client_id": "CL-0141",
"client_name": "호텔 마곡나루",
"service_type": "렌탈",
"item_code": "KS-SHEET",
"item_name": "킹 시트",
"order_no": "OD-1002-01",
"order_qty": 120,
"packed_qty": 96,
"short_qty": 24,
"over_qty": 0,
"rental_stock_before": 60,
"rental_used_qty": 24,
"rewash_ready_qty": null,
"rewash_used_qty": null,
"left_short_qty": 0,
"verdict": "모자람 — 렌탈 재고로 채울 수 있음",
"evidence": [
{
"table": "client_orders",
"field": "order_qty",
"agg": null,
"value": 120
},
{
"table": "packed_carts",
"field": "packed_qty",
"agg": null,
"value": 96
},
{
"table": "client_contracts",
"field": "service_type",
"agg": null,
"value": "렌탈"
},
{
"table": "delivery_runs",
"field": "departs_at",
"agg": null,
"value": "2026-10-02 06:30"
},
{
"table": "rental_stock",
"field": "stock_qty",
"agg": null,
"value": 60
}
],
"summary": "하루 첫 회차라 아침 킹 시트 렌탈 재고 60장에서 모자란 24장을 꺼내 채우고, 뒤 회차로 넘어가는 재고는 36장입니다.",
"notice_draft": null
},
{
"run_id": "1002-1B",
"departs_at": "2026-10-02 06:45",
"client_id": "CL-0152",
"client_name": "김포 루체호텔",
"service_type": "렌탈",
"item_code": "BM-MAT",
"item_name": "발매트",
"order_no": "OD-1002-03",
"order_qty": 40,
"packed_qty": 52,
"short_qty": 0,
"over_qty": 12,
"rental_stock_before": null,
"rental_used_qty": null,
"rewash_ready_qty": null,
"rewash_used_qty": null,
"left_short_qty": 0,
"verdict": "주문보다 많이 실림",
"evidence": [
{
"table": "client_orders",
"field": "order_qty",
"agg": null,
"value": 40
},
{
"table": "packed_carts",
"field": "packed_qty",
"agg": null,
"value": 52
}
],
"summary": "주문 40장보다 12장 많은 52장이 실렸습니다. 렌탈 발매트 12장이 주문 없이 나가므로 다음 수거 때 회수할 수량으로 적습니다.",
"notice_draft": null
},
{
"run_id": "1002-1C",
"departs_at": "2026-10-02 07:00",
"client_id": "CL-0163",
"client_name": "라온모텔 강서점",
"service_type": "렌탈",
"item_code": "BT-TOWEL",
"item_name": "바스타월",
"order_no": "OD-1002-04",
"order_qty": 300,
"packed_qty": 262,
"short_qty": 38,
"over_qty": 0,
"rental_stock_before": 30,
"rental_used_qty": 30,
"rewash_ready_qty": 10,
"rewash_used_qty": 8,
"left_short_qty": 0,
"verdict": "모자람 — 재세탁분이 출발 전에 끝남",
"evidence": [
{
"table": "client_orders",
"field": "order_qty",
"agg": null,
"value": 300
},
{
"table": "packed_carts",
"field": "packed_qty",
"agg": null,
"value": 262
},
{
"table": "client_contracts",
"field": "service_type",
"agg": null,
"value": "렌탈"
},
{
"table": "delivery_runs",
"field": "departs_at",
"agg": null,
"value": "2026-10-02 07:00"
},
{
"table": "rental_stock",
"field": "stock_qty",
"agg": null,
"value": 30
},
{
"table": "rewash_queue",
"field": "ready_at",
"agg": null,
"value": "2026-10-02 06:10"
},
{
"table": "rewash_queue",
"field": "rewash_qty",
"agg": null,
"value": 10
}
],
"summary": "모자란 38장 가운데 아침 바스타월 재고 30장을 모두 쓰고, 남은 8장은 06:10 에 끝난 재세탁 10장에서 채웁니다. 이 줄 뒤로 바스타월 렌탈 재고는 0장입니다.",
"notice_draft": null
},
{
"run_id": "1002-2A",
"departs_at": "2026-10-02 10:00",
"client_id": "CL-0174",
"client_name": "발산 라임모텔",
"service_type": "세탁",
"item_code": "PC-COVER",
"item_name": "베개커버",
"order_no": "OD-1002-05",
"order_qty": 80,
"packed_qty": 55,
"short_qty": 25,
"over_qty": 0,
"rental_stock_before": null,
"rental_used_qty": null,
"rewash_ready_qty": 0,
"rewash_used_qty": 0,
"left_short_qty": 25,
"verdict": "모자란 채 출발 — 호텔에 미리 알림",
"evidence": [
{
"table": "client_orders",
"field": "order_qty",
"agg": null,
"value": 80
},
{
"table": "packed_carts",
"field": "packed_qty",
"agg": null,
"value": 55
},
{
"table": "client_contracts",
"field": "service_type",
"agg": null,
"value": "세탁"
},
{
"table": "delivery_runs",
"field": "departs_at",
"agg": null,
"value": "2026-10-02 10:00"
},
{
"table": "rewash_queue",
"field": "ready_at",
"agg": null,
"value": "2026-10-02 15:40"
},
{
"table": "rewash_queue",
"field": "ready_at",
"agg": null,
"value": null
},
{
"table": "rewash_queue",
"field": "rewash_qty",
"agg": "sum",
"value": 35
}
],
"summary": "고객 소유 린넨을 빠는 세탁 거래처라 베개커버 렌탈 재고 200장을 쓸 수 없습니다. 재세탁 25장은 15:40 완료 예정이고 10장은 완료 시각이 비어 있어 10:00 출발 전 수량으로 세지 않으므로, 25장이 모자란 채 나갑니다.",
"notice_draft": "발산 라임모텔 담당자님, 오늘 오전 10시 배송분 베개커버가 주문 80장 중 55장만 실려 25장이 부족합니다. 다시 빨고 있는 베개커버는 오후 3시 40분쯤 세탁이 끝날 예정이라 객실 준비에 참고 부탁드립니다."
},
{
"run_id": "1002-2A",
"departs_at": "2026-10-02 10:00",
"client_id": "CL-0229",
"client_name": "화곡 비즈호텔",
"service_type": "세탁",
"item_code": "FT-TOWEL",
"item_name": "페이스타월",
"order_no": "OD-1002-06",
"order_qty": 150,
"packed_qty": 150,
"short_qty": 0,
"over_qty": 0,
"rental_stock_before": null,
"rental_used_qty": null,
"rewash_ready_qty": null,
"rewash_used_qty": null,
"left_short_qty": 0,
"verdict": "주문 수량대로 실림",
"evidence": [
{
"table": "client_orders",
"field": "order_qty",
"agg": null,
"value": 150
},
{
"table": "packed_carts",
"field": "packed_qty",
"agg": null,
"value": 150
}
],
"summary": "주문 150장과 출고 검수 150장이 같습니다.",
"notice_draft": null
},
{
"run_id": "1002-2B",
"departs_at": "2026-10-02 10:20",
"client_id": "CL-0185",
"client_name": "스테이 김포공항",
"service_type": "렌탈",
"item_code": "BT-TOWEL",
"item_name": "바스타월",
"order_no": "OD-1002-08",
"order_qty": 120,
"packed_qty": 110,
"short_qty": 10,
"over_qty": 0,
"rental_stock_before": 0,
"rental_used_qty": 0,
"rewash_ready_qty": 0,
"rewash_used_qty": 0,
"left_short_qty": 10,
"verdict": "모자란 채 출발 — 호텔에 미리 알림",
"evidence": [
{
"table": "client_orders",
"field": "order_qty",
"agg": null,
"value": 120
},
{
"table": "packed_carts",
"field": "packed_qty",
"agg": null,
"value": 110
},
{
"table": "client_contracts",
"field": "service_type",
"agg": null,
"value": "렌탈"
},
{
"table": "delivery_runs",
"field": "departs_at",
"agg": null,
"value": "2026-10-02 10:20"
},
{
"table": "rental_stock",
"field": "stock_qty",
"agg": null,
"value": 30
},
{
"table": "rewash_queue",
"field": "ready_at",
"agg": null,
"value": "2026-10-02 11:00"
},
{
"table": "rewash_queue",
"field": "rewash_qty",
"agg": null,
"value": 12
}
],
"summary": "렌탈 거래처지만 07:00 에 떠난 3호차가 바스타월 재고 30장을 모두 써 남은 재고가 없습니다. 재세탁 12장은 11:00 완료 예정이라 10:20 출발에 맞추지 못해 10장이 모자란 채 나갑니다.",
"notice_draft": "스테이 김포공항 담당자님, 오늘 오전 10시 20분 배송분 바스타월이 주문 120장 중 110장만 실려 10장이 부족합니다. 체크인 전 객실 배정에 참고 부탁드리고, 보충 일정은 정해지는 대로 다시 연락드리겠습니다."
},
{
"run_id": "1002-2B",
"departs_at": "2026-10-02 10:20",
"client_id": "CL-0185",
"client_name": "스테이 김포공항",
"service_type": "렌탈",
"item_code": "KS-SHEET",
"item_name": "킹 시트",
"order_no": "OD-1002-07",
"order_qty": 80,
"packed_qty": 50,
"short_qty": 30,
"over_qty": 0,
"rental_stock_before": 36,
"rental_used_qty": 30,
"rewash_ready_qty": null,
"rewash_used_qty": null,
"left_short_qty": 0,
"verdict": "모자람 — 렌탈 재고로 채울 수 있음",
"evidence": [
{
"table": "client_orders",
"field": "order_qty",
"agg": null,
"value": 80
},
{
"table": "packed_carts",
"field": "packed_qty",
"agg": null,
"value": 50
},
{
"table": "client_contracts",
"field": "service_type",
"agg": null,
"value": "렌탈"
},
{
"table": "delivery_runs",
"field": "departs_at",
"agg": null,
"value": "2026-10-02 10:20"
},
{
"table": "rental_stock",
"field": "stock_qty",
"agg": null,
"value": 60
}
],
"summary": "06:30 회차가 24장을 먼저 써 남은 킹 시트 재고 36장에서 모자란 30장을 채웁니다. 14:00 회차로 넘어가는 재고는 6장입니다.",
"notice_draft": null
},
{
"run_id": "1002-2C",
"departs_at": "2026-10-02 10:30",
"client_id": "CL-0196",
"client_name": "호텔 등촌",
"service_type": "세탁",
"item_code": "BT-TOWEL",
"item_name": "바스타월",
"order_no": "OD-1002-09",
"order_qty": 150,
"packed_qty": 120,
"short_qty": 30,
"over_qty": 0,
"rental_stock_before": null,
"rental_used_qty": null,
"rewash_ready_qty": 30,
"rewash_used_qty": 30,
"left_short_qty": 0,
"verdict": "모자람 — 재세탁분이 출발 전에 끝남",
"evidence": [
{
"table": "client_orders",
"field": "order_qty",
"agg": null,
"value": 150
},
{
"table": "packed_carts",
"field": "packed_qty",
"agg": null,
"value": 120
},
{
"table": "client_contracts",
"field": "service_type",
"agg": null,
"value": "세탁"
},
{
"table": "delivery_runs",
"field": "departs_at",
"agg": null,
"value": "2026-10-02 10:30"
},
{
"table": "rewash_queue",
"field": "ready_at",
"agg": null,
"value": "2026-10-02 09:50"
},
{
"table": "rewash_queue",
"field": "ready_at",
"agg": null,
"value": "2026-10-02 15:00"
},
{
"table": "rewash_queue",
"field": "rewash_qty",
"agg": "sum",
"value": 50
}
],
"summary": "세탁 거래처라 렌탈 재고는 쓰지 않습니다. 09:50 에 끝나는 재세탁 30장이 모자란 30장과 같아 출발 전에 모두 채웁니다.",
"notice_draft": null
},
{
"run_id": "1002-3A",
"departs_at": "2026-10-02 13:30",
"client_id": "CL-0141",
"client_name": "호텔 마곡나루",
"service_type": "렌탈",
"item_code": "FT-TOWEL",
"item_name": "페이스타월",
"order_no": "OD-1002-10",
"order_qty": 200,
"packed_qty": 200,
"short_qty": 0,
"over_qty": 0,
"rental_stock_before": null,
"rental_used_qty": null,
"rewash_ready_qty": null,
"rewash_used_qty": null,
"left_short_qty": 0,
"verdict": "주문 수량대로 실림",
"evidence": [
{
"table": "client_orders",
"field": "order_qty",
"agg": null,
"value": 200
},
{
"table": "packed_carts",
"field": "packed_qty",
"agg": null,
"value": 200
}
],
"summary": "주문 200장과 출고 검수 200장이 같습니다.",
"notice_draft": null
},
{
"run_id": "1002-3B",
"departs_at": "2026-10-02 14:00",
"client_id": "CL-0207",
"client_name": "청라베이 호텔",
"service_type": "렌탈",
"item_code": "KS-SHEET",
"item_name": "킹 시트",
"order_no": "OD-1002-11",
"order_qty": 100,
"packed_qty": 90,
"short_qty": 10,
"over_qty": 0,
"rental_stock_before": 0,
"rental_used_qty": 0,
"rewash_ready_qty": 8,
"rewash_used_qty": 8,
"left_short_qty": 2,
"verdict": "모자란 채 출발 — 호텔에 미리 알림",
"evidence": [
{
"table": "client_orders",
"field": "order_qty",
"agg": null,
"value": 100
},
{
"table": "packed_carts",
"field": "packed_qty",
"agg": null,
"value": 90
},
{
"table": "client_contracts",
"field": "service_type",
"agg": null,
"value": "렌탈"
},
{
"table": "delivery_runs",
"field": "departs_at",
"agg": null,
"value": "2026-10-02 14:00"
},
{
"table": "rental_stock",
"field": "stock_qty",
"agg": null,
"value": 60
},
{
"table": "rewash_queue",
"field": "ready_at",
"agg": null,
"value": "2026-10-02 13:20"
},
{
"table": "rewash_queue",
"field": "ready_at",
"agg": null,
"value": "2026-10-02 14:00"
},
{
"table": "rewash_queue",
"field": "rewash_qty",
"agg": "sum",
"value": 13
}
],
"summary": "같은 14:00 에 떠나는 송정 레지던스가 모자람이 더 적어 남은 킹 시트 재고 6장을 먼저 받았습니다. 재세탁분은 13:20 에 끝나는 8장만 세고 14:00 정각에 끝나는 5장은 빼므로 2장이 모자란 채 나갑니다.",
"notice_draft": "청라베이 호텔 담당자님, 오늘 오후 2시 배송분 킹 시트가 주문 100장 중 98장만 실려 2장이 부족합니다. 킹 객실 준비 순서에 참고 부탁드립니다."
},
{
"run_id": "1002-3C",
"departs_at": "2026-10-02 14:00",
"client_id": "CL-0218",
"client_name": "송정 레지던스",
"service_type": "렌탈",
"item_code": "HT-TOWEL",
"item_name": "핸드타월",
"order_no": null,
"order_qty": 0,
"packed_qty": 20,
"short_qty": 0,
"over_qty": 20,
"rental_stock_before": null,
"rental_used_qty": null,
"rewash_ready_qty": null,
"rewash_used_qty": null,
"left_short_qty": 0,
"verdict": "주문보다 많이 실림",
"evidence": [
{
"table": "client_orders",
"field": "order_no",
"agg": "count",
"value": 0
},
{
"table": "packed_carts",
"field": "packed_qty",
"agg": null,
"value": 20
}
],
"summary": "주문표에 송정 레지던스 핸드타월 줄이 없는데 카트 CT-3C-02 에 20장이 실렸습니다. 주문 0장으로 보고 20장을 회수할 수량으로 적습니다.",
"notice_draft": null
},
{
"run_id": "1002-3C",
"departs_at": "2026-10-02 14:00",
"client_id": "CL-0218",
"client_name": "송정 레지던스",
"service_type": "렌탈",
"item_code": "KS-SHEET",
"item_name": "킹 시트",
"order_no": "OD-1002-12",
"order_qty": 40,
"packed_qty": 34,
"short_qty": 6,
"over_qty": 0,
"rental_stock_before": 6,
"rental_used_qty": 6,
"rewash_ready_qty": null,
"rewash_used_qty": null,
"left_short_qty": 0,
"verdict": "모자람 — 렌탈 재고로 채울 수 있음",
"evidence": [
{
"table": "client_orders",
"field": "order_qty",
"agg": null,
"value": 40
},
{
"table": "packed_carts",
"field": "packed_qty",
"agg": null,
"value": 34
},
{
"table": "client_contracts",
"field": "service_type",
"agg": null,
"value": "렌탈"
},
{
"table": "delivery_runs",
"field": "departs_at",
"agg": null,
"value": "2026-10-02 14:00"
},
{
"table": "rental_stock",
"field": "stock_qty",
"agg": null,
"value": 60
}
],
"summary": "모자란 6장이 14:00 에 남은 킹 시트 재고 6장과 같아 재고로 채웁니다. 같은 시각 청라베이 호텔보다 모자람이 적어 먼저 배정했습니다.",
"notice_draft": null
},
{
"run_id": "1002-4A",
"departs_at": "2026-10-02 17:00",
"client_id": "CL-0196",
"client_name": "호텔 등촌",
"service_type": "세탁",
"item_code": "BT-TOWEL",
"item_name": "바스타월",
"order_no": "OD-1002-13",
"order_qty": 150,
"packed_qty": 125,
"short_qty": 25,
"over_qty": 0,
"rental_stock_before": null,
"rental_used_qty": null,
"rewash_ready_qty": 20,
"rewash_used_qty": 20,
"left_short_qty": 5,
"verdict": "모자란 채 출발 — 호텔에 미리 알림",
"evidence": [
{
"table": "client_orders",
"field": "order_qty",
"agg": null,
"value": 150
},
{
"table": "packed_carts",
"field": "packed_qty",
"agg": "sum",
"value": 125
},
{
"table": "client_contracts",
"field": "service_type",
"agg": null,
"value": "세탁"
},
{
"table": "delivery_runs",
"field": "departs_at",
"agg": null,
"value": "2026-10-02 17:00"
},
{
"table": "rewash_queue",
"field": "ready_at",
"agg": null,
"value": "2026-10-02 09:50"
},
{
"table": "rewash_queue",
"field": "ready_at",
"agg": null,
"value": "2026-10-02 15:00"
},
{
"table": "rewash_queue",
"field": "rewash_qty",
"agg": "sum",
"value": 50
}
],
"summary": "재세탁 대기 50장 가운데 30장은 10:30 회차가 이미 썼고, 17:00 전에 끝나는 나머지는 15:00 의 20장입니다. 카트 두 대에 실린 125장에 20장을 더해도 5장이 모자란 채 나갑니다.",
"notice_draft": "호텔 등촌 담당자님, 오늘 오후 5시 배송분 바스타월이 주문 150장 중 145장만 실려 5장이 부족합니다. 객실 정비 순서에 참고 부탁드립니다."
},
{
"run_id": "1002-4A",
"departs_at": "2026-10-02 17:00",
"client_id": "CL-0230",
"client_name": "마곡 핏라운지",
"service_type": "세탁",
"item_code": "FT-TOWEL",
"item_name": "페이스타월",
"order_no": "OD-1002-14",
"order_qty": 300,
"packed_qty": 300,
"short_qty": 0,
"over_qty": 0,
"rental_stock_before": null,
"rental_used_qty": null,
"rewash_ready_qty": null,
"rewash_used_qty": null,
"left_short_qty": 0,
"verdict": "주문 수량대로 실림",
"evidence": [
{
"table": "client_orders",
"field": "order_qty",
"agg": null,
"value": 300
},
{
"table": "packed_carts",
"field": "packed_qty",
"agg": null,
"value": 300
}
],
"summary": "주문 300장과 출고 검수 300장이 같습니다.",
"notice_draft": null
}
][자동 검증 · 코드가 판정합니다] 1. verdict 가 주문 수량대로 실림·모자람 — 렌탈 재고로 채울 수 있음·모자람 — 재세탁분이 출발 전에 끝남·모자란 채 출발 — 호텔에 미리 알림·주문보다 많이 실림 다섯 값 중 하나인지 확인합니다. 2. 필수 필드 run_id·departs_at·client_id·client_name·service_type·item_code·item_name·order_no·order_qty·packed_qty·short_qty·over_qty·rental_stock_before·rental_used_qty·rewash_ready_qty·rewash_used_qty·left_short_qty·verdict·evidence·summary·notice_draft 가 모두 존재하는지 확인합니다. 3. packed_qty 가 같은 run_id·client_id·item_code 의 packed_carts.packed_qty 합과 같은지 다시 계산합니다. 호텔 등촌 1002-4A 바스타월은 카트 두 대 80+45 로 125 입니다. 주문표에 없는 카트의 줄은 order_qty 가 0 입니다(CT-3C-02). 4. short_qty 가 order_qty − packed_qty(0 미만이면 0), over_qty 가 packed_qty − order_qty(0 미만이면 0)와 같은지 두 근거 값의 차이로 다시 계산합니다. 5. 배정 순서는 short_qty 가 0 보다 큰 줄을 delivery_runs.departs_at 이 이른 순으로, 같은 시각이면 short_qty 가 작은 순으로, 그래도 같으면 client_id 순으로 세웁니다. rental_stock_before 가 그 item_code 의 rental_stock.stock_qty 에서 이 순서로 앞선 줄들의 rental_used_qty 합을 뺀 값인지 다시 계산합니다. 킹 시트는 60 → 36 → 6 → 0 이고, 14:00 에 함께 떠나는 송정 레지던스(6장)가 청라베이 호텔(10장)보다 먼저 받습니다. 6. client_contracts.service_type 이 세탁이면 rental_stock_before·rental_used_qty 는 null 입니다. 발산 라임모텔 베개커버는 재고가 200장이어도 재고를 쓰지 않습니다. 7. service_type 이 렌탈이고 short_qty 가 rental_stock_before 이하이면 verdict 는 모자람 — 렌탈 재고로 채울 수 있음이고 rental_used_qty 는 short_qty 입니다. 같은 값이면 채운 것으로 둡니다(송정 레지던스 6장 = 남은 재고 6장). 8. service_type 이 렌탈이고 short_qty 가 rental_stock_before 보다 크면 rental_used_qty 는 rental_stock_before 이고 남은 모자람을 재세탁분으로 넘깁니다(라온모텔 강서점 38장 중 재고 30장). 9. rewash_ready_qty 가 같은 client_id·item_code 의 rewash_queue 중 ready_at 이 그 줄의 departs_at 보다 이른 rewash_qty 합에서 같은 거래처·품목의 앞선 줄들이 쓴 rewash_used_qty 합을 뺀 값인지 다시 계산합니다. ready_at 이 null 이거나 departs_at 과 같은 시각이면 세지 않습니다(RW-1002-03, RW-1002-08). 호텔 등촌 1002-4A 는 30+20=50 에서 1002-2C 가 쓴 30 을 빼 20 입니다. 10. 렌탈 재고를 쓰고 남은 모자람(세탁 거래처는 short_qty 전부)이 rewash_ready_qty 이하이면 verdict 는 모자람 — 재세탁분이 출발 전에 끝남이고 rewash_used_qty 는 그 남은 모자람입니다(호텔 등촌 1002-2C 는 30 = 30). 11. 남은 모자람이 rewash_ready_qty 보다 크면 verdict 는 모자란 채 출발 — 호텔에 미리 알림이고 rewash_used_qty 는 rewash_ready_qty, left_short_qty 는 short_qty − rental_used_qty − rewash_used_qty 입니다(청라베이 호텔 10 − 0 − 8 = 2). 이 판정이면 notice_draft 가 있고, 다른 판정이면 notice_draft 는 null 입니다. 12. over_qty 가 0 보다 크면 verdict 는 주문보다 많이 실림이고 재고·재세탁 배정에 들어가지 않습니다. short_qty 와 over_qty 가 모두 0 이면 verdict 는 주문 수량대로 실림입니다. 13. 수량 필드가 채워졌으면 0 이상인지 범위를 봅니다. 14. evidence 의 table·field 가 client_orders·packed_carts·delivery_runs·client_contracts·rewash_queue·rental_stock 에 실재하는 컬럼인지 대조합니다. 15. agg 가 없는 evidence 의 value 가 그 컬럼에 수록된 행의 값 원문과 같은지 확인합니다. 16. summary 와 notice_draft 가 존댓말로 끝나는지 확인합니다. [수동 검증 · 사람이 봅니다] 1. 모자란 채 나간 수량을 다음 회차에 보충할지, 언제 보낼지는 전담 매니저가 호텔과 통화해 정합니다. 2. notice_draft 가 summary 에 없는 수량이나 추가 배송 약속을 말하지 않는지 전담 매니저가 읽고 고칩니다. [평가 러너 · 2주차] 1. 출고 담당자가 손으로 판정해 둔 합성 사흘 치 150줄과 화면 판정이 일치하는 줄 수를 셉니다. 2. 어긋난 줄은 같은 시각 회차의 재고 순서에서 갈렸는지, 재세탁 완료 시각을 출발 정각까지 셌는지에서 갈렸는지 줄마다 남깁니다.
근거: 사용률, 생산성, 오류율, 납기 준수율 등 KPI를 모니터링합니다.