8.3 KiB
KBX FE Workflow Truth · Queue Signal · Runtime Hardening v58
1. 냉정한 진단
v57까지 KBX는 T01~T09 Template, Home Workbench, Import Recovery, Reconcile/Operations authoritative outcome, WMS retry/idempotency, Playwright 실행계약까지 상당히 정리되었다. 그러나 실제 FE 코드를 다시 보면 여전히 “문서/정적 Gate는 강한데 Browser Interaction은 틀릴 수 있는” 결함이 남아 있었다.
이번에 발견한 핵심은 네 가지다.
- Selection Contract 불일치 — OMS 클레임은
KbxDataGrid가update:selection을 제공하지 않는데v-model:selection을 사용하고 있었다. 동시에KbxListPage에selectionCount를 전달하지 않아 Command의requiresSelection계약도 실제 선택과 연결되지 않았다. - Workflow UI와 실제 Command 불일치 — WorkflowBar는
start/complete를 노출하지만 Page command handler는approve/hold만 API로 전송했다. 보이는 Action과 실행 가능한 Action이 달랐다. - Queue Signal 왜곡 — WMS 작업 Queue의 기본 검색이 READY였고 예외 카운터를 현재 rows에서 계산해, 실제 BLOCKED 작업이 존재해도 첫 화면에서
확인 필요 0으로 보일 수 있었다. - Vue writable model 위험 —
KbxSearchPanel은 새 객체를update:modelValue로 반환하는데 일부 Page는const search = reactive(...)전체 객체를v-model로 넘겼다. 정적 TypeScript Syntax Gate는 통과하지만 Browser에서 const reactive binding 재할당 위험이 있는 패턴이다.
이 네 가지는 모두 “코드량/컴포넌트 수”가 아니라 실제 업무 실행 Truth 문제다.
2. OMS-CLM-001 — Selection/Workflow Closure
기존 문제
v-model:selection+selection="multiple"가 동시에 존재- 실제
KbxDataGrid는selectionChanged만 emit KbxListPage의 selection count는 0으로 남을 수 있음- WorkflowBar
start/complete가 보이지만 command handler는 실행하지 않음 HOLD는 Command에는 있으나 Workflow state에는 없음- 여러 건 승인/보류 중 일부 실패하면 첫 실패에서 전체 흐름이 끊길 수 있음
v58 변경
@selection-changed="selected=$event"로 실제 Grid 계약 사용:selection-count="selected.length"를 T01 CommandBar까지 전달approve / hold / start / complete전체 Transition을 동일 실행경로로 연결HOLD를 semantichold상태로 명시- Transition별 server result를 기다린 뒤 재조회
- 행별 실패를 수집하고 나머지 행은 계속 처리
요청 / 성공 / 실패를 persistent receipt로 표시- 처리 후 receipt로 Keyboard Focus 이동
중요한 원칙은 성공 Toast를 늘린 것이 아니라, 선택 → Command → Server → 결과 → 재조회 → Focus가 한 업무 흐름으로 닫히게 한 것이다.
3. Demo Claims — Runtime Fixture를 실제 계약으로 복원
기존 Demo Claims는 실제 Backend DTO와 달랐다.
status: '접수'처럼 한글 표시값을 API value로 사용channelName,requestedQty,ownerName누락- 모든 transition이 현재 상태와 무관하게 성공
v58은 Demo를 다음 실제 업무 상태로 정렬했다.
REQUESTED → APPROVED → IN_PROGRESS → COMPLETED
그리고 REQUESTED → HOLD를 별도로 둔다.
허용되지 않은 Transition은 실제 Axios rejection + business-rule Problem으로 처리한다. Demo는 보기 좋은 샘플 데이터가 아니라 Browser Runtime에서 실제 FE 계약을 검증하는 실행 Fixture여야 한다.
4. WMS-WORK-001 — Queue Signal Truth
기존 문제
기본 검색조건이 status='READY'였다. 따라서 화면 진입 후 사용자가 조회하면 BLOCKED/IN_PROGRESS가 기본적으로 제외된다.
더 큰 문제는 예외 카운터를 rows.filter(status==='BLOCKED')로 계산한 것이다. 현재 rows가 READY만 포함하면 실제 예외가 있어도 0으로 보인다.
T06 Work Queue가 사용자의 “지금 처리해야 할 업무”를 보여줘야 한다는 목적과 충돌한다.
v58 변경
- 기본 상태를 전체로 변경
- 화면 진입 시 자동으로 내 작업 조회
WmsWorkSearchSummary도입- 상태 필터 적용 전 summary rows에서
ready / inProgress / blocked계산 - Grid count는 현재 필터 결과, Summary/Exception은 동일 기본조건의 전체 업무 압력을 표시
- BLOCKED 선택 시
작업 시작을 disabled - “예외 작업은 행을 열어 원인을 먼저 확인”이라는 다음 행동 표시
- Double Click은 작업 상세 Context 진입을 유지
즉 상태 필터를 좁혀도 “예외가 존재한다”는 운영 신호는 사라지지 않는다.
5. SearchPanel Whole-object v-model Runtime Contract
이번에 추가한 Gate는 다음 패턴을 전체 apps/web/src에서 금지한다.
const search = reactive(...)
<KbxSearchPanel v-model="search" />
KbxSearchPanel이 update:modelValue로 새 객체를 반환하는 구조에서는 writable ref가 맞다.
v58에서 수정한 화면:
- OMS Claims
- WMS Work Queue
- COMMON UX Metrics
- ERP Item Master
Component Catalog는 이미 ref<Record<string,unknown>>를 사용하고 있어 그대로 유지한다.
이 Gate의 의미는 특정 화면 1건을 고치는 데 있지 않다. 같은 Runtime-class 결함이 다시 추가되는 것을 자동 차단한다는 데 있다.
6. Home — 화면 수의 사실성
Home의 사용 가능 화면과 전체 N개 화면이 props.entries.length를 사용하면 menuVisible=false인 Utility/Hidden entry까지 사용자에게 실행 가능한 화면처럼 계산될 수 있다.
v58은 menuVisible !== false인 launchable entry만 센다.
작은 숫자 차이처럼 보이지만 Home은 업무 탐색의 Source of Truth다. 숫자가 실제 Explorer와 다르면 사용자는 Navigation 자체를 신뢰하지 않게 된다.
7. QA/E2E 보강
추가 E2E:
OMS Claims
- F3 조회
- 실제 Grid row selection
- WorkflowBar 승인
- authoritative receipt Focus
- APPROVED → 처리 시작
- HOLD 상태 명시 / 다음 Action 없음 확인
WMS Work Queue
- 화면 진입만으로 전체 내 작업 조회
- BLOCKED 작업이 초기 결과에서 보임
- 예외 요약이 1건을 표시
- 예외 필터 적용
- BLOCKED 작업에서 시작 Action disabled
Runtime package 설치가 가능한 환경에서는 이 시나리오가 기존 Playwright Desktop/WMS project에 그대로 포함된다.
8. 검증 결과
node scripts/validate-kbx.mjs 전체 PASS.
- Design Tokens: 161
- Screens: 20
- Components: 86
- Core APIs: 34
- TypeScript/Vue script units: 311
- T01~T09: 9/9
- Design Debt: 116 <= 131
- Design-Code Parity: PASS
- Release Governance: major PASS
- v41~v58 FE regression: PASS
v58 전용 Gate도 PASS.
9. Runtime 제한 — 여전히 BLOCKED
현재 container에서 Corepack pnpm@10.33.4 실행을 다시 시도했으나 npm registry DNS가 EAI_AGAIN으로 실패한다.
따라서 다음은 PASS라고 주장하지 않는다.
pnpm install --frozen-lockfile- Vue production typecheck/build
- Vitest runtime
- Playwright Desktop/WMS runtime
System Chromium을 사용한 Static Reference --dump-dom도 다시 시도했으나 DBus/zygote 환경에서 timeout되었다.
정적/계약 검증 PASS와 Browser Runtime PASS는 계속 분리한다.
10. 다음 P0 — Status Grid Rendering
v58 이후 가장 우선순위가 높은 FE Component 부채는 KbxDataGrid의 type:'status'다.
현재 Grid는 status type을 가운데 정렬하는 수준이며 KbxStatusDefinition(value/label/semantic)을 Cell Renderer까지 일관되게 연결하지 않는다. 이 상태에서는 Backend value인 REQUESTED, IN_PROGRESS, BLOCKED가 사용자 화면에 그대로 노출될 가능성이 있다.
다음 단계에서는 새 UI Framework가 아니라:
- Domain status value → KBX Status Definition mapping
- Grid status cell에서 Familiar Korean label + semantic cue
- Filter/Excel export는 canonical value와 표시 label의 역할 분리
- 미정의 상태는 조용히 오표시하지 않고 Unknown/Fallback 정책 적용
- OMS/WMS/ERP 상태 Dictionary 중복 제거
를 우선하는 것이 맞다.
이 작업은 미관 개선이 아니라 Familiar First + Explicit State + Hallucination/오표시 방지를 Grid 핵심 계약에 내리는 작업이다.