탭실 재고관리 ERP

시스템 설계 문서 & 목업 허브

탭실 재고관리 ERP

Apple 기기 (iPad, Pencil, AirPods) 재고 운영 관리 시스템

12

관리 랙 수

6

기기 유형

26

API 엔드포인트

8

DB 테이블

Tech Stack

Hono + TypeScript + Cloudflare Pages + D1 (SQLite)

Production

erp-opr.pages.dev

기기 유형

iPad 9 SG/Silver, iPad 11, Pencil 1/C, AirPods Max

랙 구성 (물리 배치)

ID 랙 이름 유형 저장 기기 층 구성 비고
1iPad 9 SG 랙storage스페이스 그레이1~5층정상 보관
2iPad 9 실버 랙storage실버1~4층정상 보관
3애플펜슬 랙storagePencil 1세대1~2층정상 보관
4검수 테이블inspection혼합-검수 중 임시
5출고 테이블outbound혼합-출고 대기
6이슈재고 랙issueSG/Silver/Pencil-불량/이슈 격리
7보류재고 랙holdSG/Silver/Pencil-보류 중 보관
8iPad 11 랙storageiPad 11세대-정상 보관
9펜슬 USB-C 랙storagePencil USB-C-정상 보관
10에어팟 맥스 랙storageAirPods Max-정상 보관
11SG 예비랙 (창문난간)reserveSG 오버플로우-선입선출 보호
12실버 예비랙 (창문난간)reserveSilver 오버플로우-선입선출 보호

시스템 아키텍처

Frontend (Browser)

Inline HTML — Hono c.html() SSR
Tailwind CSS — CDN 실시간 빌드
Vanilla JS — 프레임워크 없음
Font Awesome — 아이콘

주요 UI 모듈:

  • 대시보드 (정상/이슈/보류 분리)
  • 랙 관리 (층별 상세 + 금일입고)
  • 키오스크 모달 (출고/입고 업무)
  • 데일리 카운팅 (시스템vs실물)
  • 자동배치 설정 관리
  • 이동 이력 조회

Backend (Edge Worker)

Hono v4 — 경량 웹 프레임워크
TypeScript — 타입 안전성
Cloudflare Workers — Edge 런타임
CORS Middleware — API 보호

핵심 로직 모듈:

  • autoPlaceDistribute() — 자동배치 분배
  • buildPlacementStmts() — DB 배치문 생성
  • 트랜잭션 배치 (db.batch) 원자적 처리
  • 이슈해결 / 입고반영 / 벌크이동

Database (D1 / SQLite)

Cloudflare D1 — 글로벌 분산 SQLite
5개 마이그레이션 — 스키마 버전 관리
8개 테이블 — 정규화된 스키마

테이블 구성:

  • racks — 랙 마스터
  • floors — 층 정의
  • inventory — 재고 현황
  • transfer_logs — 이동 이력
  • daily_inbound — 일별 입고
  • daily_counts — 데일리 카운팅
  • rack_auto_config — 자동배치 설정
  • inbound_staging — 입고 스테이징

DB 스키마 (ERD)

racks 랙 마스터
PK id INTEGER
name TEXT UNIQUE
slug TEXT UNIQUE
rack_type TEXT (storage|issue|hold|reserve|...)
has_floors INTEGER
sort_order INTEGER
floors 층 정의
PK id INTEGER
FK rack_id → racks.id
floor_number INTEGER
label TEXT ("1층","2층",...)
UNIQUE(rack_id, floor_number)
inventory 재고 현황 (핵심)
PK id INTEGER
FK rack_id → racks.id
FK floor_id → floors.id (NULL=층없음)
device_type TEXT (ipad_9_sg|...)
quantity INTEGER
transfer_logs 이동 이력
PK id INTEGER
FK from_rack_id, from_floor_id
FK to_rack_id, to_floor_id
device_type TEXT, quantity INTEGER
operator TEXT, memo TEXT
transfer_type TEXT (outbound|inbound|move|inspection|undo|issue_resolve|staging_apply)
rack_auto_config 자동배치 설정
PK id INTEGER
device_type TEXT
FK floor_id → floors.id (NULL=예비랙)
FK rack_id → racks.id
max_capacity INTEGER (0=무제한)
place_order INTEGER (배치 순서)
inbound_staging 입고 스테이징
PK id INTEGER
inbound_date DATE
device_type TEXT
quantity INTEGER
status TEXT (pending|applied)
applied_at DATETIME, applied_by TEXT
UNIQUE(inbound_date, device_type)
daily_inbound 일별 입고 기록
PK id INTEGER
inbound_date DATE
device_type TEXT, quantity INTEGER
operator TEXT, memo TEXT
UNIQUE(inbound_date, device_type)
daily_counts 데일리 카운팅
PK id INTEGER
count_date DATE
FK rack_id, floor_id
device_type TEXT
system_quantity, actual_quantity INTEGER
difference INTEGER

API 엔드포인트

Method Path 설명 카테고리
GET/api/dashboard전체 대시보드 데이터 (정상/이슈/보류 분리)대시보드
GET/api/racks/:id특정 랙 상세 (층별 재고)랙 관리
PUT/api/inventory/:id단일 슬롯 수량 수정재고
POST/api/transfer재고 이동 (핵심 — A→B)이동
POST/api/transfer-bulk대량 이동 (여러 층에서)이동
POST/api/transfer-bulk-sources다중 소스 벌크 이동이동
POST/api/transfer-from-inbound입고 재고 → 특정 랙 이동입고
GET/api/auto-place-config자동배치 설정 조회 (?device_type=)자동배치
POST/api/auto-place-config자동배치 설정 저장 (순서+MAX+예비랙)자동배치
POST/api/transfer-auto-resolve이슈재고 → 자동배치 전환자동배치
GET/api/inbound-staging전일 입고 대기 재고 조회스테이징
POST/api/inbound-staging/apply대기 재고 → 정상재고 자동배치 반영스테이징
GET/api/daily-inbound금일 입고 기기 조회입고
POST/api/daily-inbound금일 입고 수량 등록/수정입고
GET/api/daily-summary데일리 카운팅 집계카운팅
POST/api/daily-count카운팅 결과 저장카운팅
GET/api/daily-counts카운팅 이력 조회카운팅
GET/api/transfers이동 이력 조회이력
POST/api/quick-issue빠른 이슈 등록이슈
POST/api/inspection-workflow검수 워크플로우검수
POST/api/inbound-with-issue입고+이슈 동시 처리입고
POST/api/shipment출고 처리출고
POST/api/inventory/bulk-set재고 일괄 설정재고
POST/api/undo/:id이동 이력 되돌리기이력

출고 업무 로직

1. 출고 업무 선택

키오스크 → 출고모드

2. 기기 선택 + 수량

출고할 기기/수량/담당자

3. 출고 완료

inventory - / transfer_log 기록

출고 서브타입

single — 단일 출고 bulk — 대량 출고 inspection — 검수 출고 inbound_issue — 입고+이슈 quick_issue — 빠른 이슈

입고 업무 로직

1. 정상 입고 등록

daily_inbound에 기기별 수량 기록

2. 이슈재고 입고

정상 수량 + 이슈 수량 동시 처리

3. 검수 워크플로우

검수 테이블 → 정상/이슈 분류

4. 랙 배치

입고 재고 → 정상 랙 이동

금일 입고된 기기

데일리 카운팅 시 daily_inbound 수량이 정상재고에 자동 합산됩니다. 실제 랙 이동은 별도 처리합니다.

이슈재고 해결 로직 (자동배치 D안)

키오스크 2-Step 플로우

1

기기 선택

이슈 랙(rack 6)에 있는 기기 목록 표시, 재고가 있는 기기만 선택 가능

2

자동배치 프리뷰 + 실행

auto-place-config에서 순서대로 배치 미리보기 → 수량/담당자 입력 → 실행

3

완료 & 결과

분할 배치 상세 표시, 예비랙 사용 시 경고

API 호출 체인

POST /api/transfer-auto-resolve
↓ 내부적으로:
autoPlaceDistribute(db, deviceType, qty)

place_order 순서대로 남은 수량 분배

buildPlacementStmts()

INSERT/UPDATE inventory + transfer_logs

db.batch([...stmts])

원자적 트랜잭션 실행

예비랙(창문난간) 동작 원리

  • 예비랙은 rack_auto_config에서 floor_id = NULL, max_capacity = 0(무제한)으로 설정
  • place_order 순서에 따라 정상 랙이 가득 차면 예비랙에 남은 수량 전량 적재
  • 선입선출이 꼬이는 것을 방지하기 위해, 1층(맨 아래)에 넣지 않고 예비랙에 보관
  • 관리자가 자동배치 설정 탭에서 예비랙 순서를 자유롭게 조정 가능

입고 스테이징 반영 로직

STEP 1 — D일 업무중

금일 입고 등록

daily_inbound에 수량 기록

STEP 2 — D일 퇴근 전

내 자리에 임시 보관

정상 여부 확인 대기

STEP 3 — D+1일 (자동)

스테이징 이동

daily_inbound → inbound_staging

STEP 4 — D+1일 (수동)

"반영하기" 클릭

자동배치 → 정상재고 분배

데이터 흐름

daily_inbound inbound_staging inventory

GET /api/inbound-staging 호출 시:

  1. inbound_staging에서 status='pending' 조회
  2. daily_inbound에서 오늘 이전 + qty > 0 조회
  3. staging에 없는 항목 자동 INSERT
  4. 기기별 합산 summary 반환

반영 실행 (POST /api/inbound-staging/apply)

  1. pending 상태 staging 항목 로드
  2. 기기별 수량 합산
  3. 각 기기에 autoPlaceDistribute() 실행
  4. buildPlacementStmts()로 DB문 생성
  5. staging status → 'applied' 업데이트
  6. daily_inbound quantity → 0 리셋
  7. db.batch() 원자적 실행

프론트엔드 UI

  • 대시보드 배너: 대기 재고가 있으면 주황색 알림 배너 표시 (기기별 수량 + "반영하기" 버튼)
  • 반영 모달: 키오스크 형태 — 자동배치 시뮬레이션 프리뷰(게이지바) + 담당자 선택 + 실행
  • 완료 화면: 기기별 분할배치 상세, 예비랙 사용 경고

자동배치 엔진 (autoPlaceDistribute)

분배 알고리즘 (의사코드)

function autoPlaceDistribute(deviceType, quantity):
  // 1. 설정 로드 (place_order 순서)
  configs = DB.query(rack_auto_config WHERE device_type ORDER BY place_order)
  
  remaining = quantity
  placements = []

  // 2. 순서대로 분배
  for each config in configs:
    if remaining <= 0: break
    
    if config.is_reserve:
      // 예비랙: 남은 수량 전부 적재 (무제한)
      placements.add(config.rack, remaining)
      remaining = 0
    else:
      // 정상 랙: MAX - 현재수량 = 여유공간만큼
      available = config.max_capacity - config.current_qty
      if available <= 0: skip
      place = min(remaining, available)
      placements.add(config.rack, config.floor, place)
      remaining -= place

  // 3. 결과
  if remaining > 0: return ERROR "모든 랙 가득참"
  return { placements, usedReserve }

현재 자동배치 설정 (운영중)

iPad 9 SG
순서위치MAX
12층70
2예비랙무제한
33층70
44층60
55층70
61층100
iPad 9 Silver
순서위치MAX
13층70
22층70
34층70
41층100
Apple Pencil 1세대
순서위치MAX
11층550
22층550

관리자 설정 UI (자동배치 설정 탭)

  • 기기별 각 층의 MAX 용량 직접 수정 가능
  • 순서 변경: ↑↓ 화살표로 place_order 조정
  • 예비랙 추가: "+예비랙을 순서에 추가" 버튼으로 원하는 위치에 삽입
  • 예비랙 제거: X 버튼으로 순서에서 제거
  • 저장 시 기존 설정 DELETE 후 새로 INSERT (원자적)

UI 목업 갤러리

개발 과정에서 만든 모든 목업 페이지를 모아서 확인

MOCKUP 1

이슈재고 해결 — 초기 개선안

이슈재고를 정상재고로 전환하는 3가지 초기 방안(A/B 비교). 수동으로 층을 선택하는 방식.

/static/mockup.html
MOCKUP 2

이슈재고 해결 — C안 (일괄 자동전환)

버튼 하나로 모든 이슈재고를 일괄 자동 전환하는 간소화 방안.

/static/mockup-c.html
MOCKUP 3 — 채택

자동배치 D안 — MAX 기반 순환 배치 + 예비랙

최종 채택안. 각 층의 MAX 용량 설정 기반으로 자동 순환 배치. 가득 차면 예비랙(창문난간)으로 오버플로우.

/static/mockup-auto.html
MOCKUP 4 — 구현완료

입고 자동반영 (Staging → 정상재고)

입고 후 다음날 출근 시 "반영하기" 버튼으로 정상재고에 자동 분배. 4단계 플로우 다이어그램 + 대시보드 배너 UI.

/static/mockup-inbound-staging.html