이 글은 누구를 위한 것인가
- 주문 처리 속도가 느려 당일 출고 마감에 자주 실패하는 팀
- 오피킹(잘못된 상품 집기) 비율이 높아 CS가 늘어나는 팀
- 창고 WMS를 도입하거나 개선하려는 운영/개발 담당자
들어가며
창고에서 작업자가 주문 1개당 창고를 걸어다니는 거리가 줄수록 처리 속도는 올라간다. 피킹 경로 최적화만으로도 처리 속도를 30-50% 개선할 수 있다.
이 글은 bluefoxdev.kr의 이커머스 물류 운영 가이드 를 참고하여 작성했습니다.
1. 피킹 방식 비교
[피킹 방식 선택 가이드]
1. 싱글 오더 피킹 (Single Order):
방식: 주문 1개씩 처리
적합: 소량 고품질 상품, 맞춤 패키징
단점: 이동 거리 최대
2. 배치 피킹 (Batch Picking):
방식: 여러 주문을 묶어서 한 번에 피킹
적합: 주문량 많고 SKU 수 제한적
장점: 이동 거리 50-70% 감소
단점: 분류 작업 추가
3. 존 피킹 (Zone Picking):
방식: 창고를 구역 분할, 구역별 담당자
적합: 대형 창고, 많은 SKU
장점: 전문화, 빠름
단점: 구역 간 합산 작업
4. 웨이브 피킹 (Wave Picking):
방식: 시간대별 주문을 묶어 일괄 처리
적합: 마감 시간이 있는 운영
장점: 출고 마감 시간 준수
[권장]
일 1000건 미만: 배치 피킹
일 1000건 이상: 존 + 웨이브 조합
2. 피킹 경로 최적화
from typing import NamedTuple
class Location(NamedTuple):
aisle: str # 통로 (A, B, C...)
row: int # 행 번호
shelf: int # 선반 번호
def optimize_picking_route(pick_list: list[dict]) -> list[dict]:
"""
S자형 경로로 피킹 순서 최적화
통로를 순서대로, 짝수 통로는 역방향으로
"""
# 위치 파싱
items_with_location = []
for item in pick_list:
loc = parse_location(item["location"]) # "A-03-2" → Location("A", 3, 2)
items_with_location.append({"item": item, "location": loc})
# 통로 그룹화
aisles = {}
for entry in items_with_location:
aisle = entry["location"].aisle
if aisle not in aisles:
aisles[aisle] = []
aisles[aisle].append(entry)
# S자 경로: 통로 순서대로, 짝/홀에 따라 방향 다름
optimized = []
for i, aisle in enumerate(sorted(aisles.keys())):
items_in_aisle = aisles[aisle]
# 짝수 통로: 낮은 행 → 높은 행
# 홀수 통로: 높은 행 → 낮은 행 (S자)
reverse = (i % 2 == 1)
sorted_items = sorted(
items_in_aisle,
key=lambda x: x["location"].row,
reverse=reverse
)
optimized.extend([entry["item"] for entry in sorted_items])
return optimized
def create_batch_pick_list(orders: list[dict], batch_size: int = 20) -> list[list[dict]]:
"""주문을 배치로 묶어 pick list 생성"""
# 공통 SKU를 가진 주문끼리 묶기
batches = []
current_batch = []
for order in orders:
if len(current_batch) >= batch_size:
batches.append(create_consolidated_pick_list(current_batch))
current_batch = []
current_batch.append(order)
if current_batch:
batches.append(create_consolidated_pick_list(current_batch))
return batches
def create_consolidated_pick_list(orders: list[dict]) -> dict:
"""여러 주문의 아이템을 SKU별로 합산"""
consolidated = {}
for order in orders:
for item in order["items"]:
sku = item["sku"]
if sku not in consolidated:
consolidated[sku] = {
"sku": sku,
"location": item["location"],
"total_qty": 0,
"order_ids": [],
}
consolidated[sku]["total_qty"] += item["quantity"]
consolidated[sku]["order_ids"].append(order["id"])
pick_list = list(consolidated.values())
return {
"orders": [o["id"] for o in orders],
"pick_list": optimize_picking_route(pick_list),
}
3. 오피킹 방지 (바코드 검증)
async def verify_pick(pick_id: str, scanned_barcode: str, picker_id: str) -> dict:
"""피킹 시 바코드 스캔 검증"""
pick_item = await get_pick_item(pick_id)
expected_barcode = pick_item["product_barcode"]
if scanned_barcode == expected_barcode:
# 정상 피킹
await update_pick_status(pick_id, "picked", picker_id)
return {"success": True, "next_location": get_next_pick_location(pick_id)}
else:
# 오피킹 경고
await log_pick_error(pick_id, scanned_barcode, picker_id)
return {
"success": False,
"error": "잘못된 상품입니다",
"expected_sku": pick_item["sku"],
"expected_location": pick_item["location"],
}
마무리
피킹 최적화는 기술보다 프로세스가 먼저다. 배치 피킹과 S자 경로 최적화만으로도 작업 효율이 크게 오른다. 오피킹은 바코드 스캔 검증으로 거의 제거할 수 있다. WMS 도입 전에도 엑셀로 배치 피킹 리스트를 만드는 것부터 시작해볼 수 있다.