Files
vexplor_dev/docs/kjs/POP_PROCESS_TIMER_ANALYSIS.md
kjs b4f6139151 Implement Subcontractor Stock Management Features
- Added new routes and controller for subcontractor stock management, including endpoints for vendor summaries, stock details, history, adjustments, and scrap management.
- Integrated the subcontractor stock service to handle business logic related to inventory adjustments and history tracking.
- Developed frontend components for managing subcontractor stock, including modals for adjustments and history viewing, enhancing user interaction and data management.

(TASK: ERP-026)
2026-05-08 16:17:06 +09:00

14 KiB

POP 공정 타이머 구조 분석 및 개선안

작성일: 2026-05-08 대상: POP ProcessWork 화면 — 공정 시간 추적 구조


1. 개요

POP에서 작업자가 누르는 "시작/정지/재개/종료" 버튼이 어떤 테이블의 어떤 컬럼을 갱신하는지, 현재 구조에서 작업자가 무엇을 인지하기 어려운지, 어떻게 정리해야 하는지를 한 문서에 정리한다.

핵심 결론 요약:

  • 시간은 두 테이블에 나뉘어 저장된다.
    • work_order_process_result — 공정 전체(1 공정 = 1 행)
    • process_work_result — 공정작업기준(공정 1개 안에 N 행, work_phase로 PRE/IN/POST 그룹화)
  • 백엔드는 두 종류의 타이머 엔드포인트를 제공한다 (/timer, /group-timer).
  • 그러나 POP 화면에는 phase 타이머만 노출되어 있고, 공정 전체 타이머 핸들러(handleTimerAction)는 정의만 되어 있고 어디에서도 호출되지 않는다.
  • 결과적으로 공정 전체의 started_at/completed_at은 phase 타이머·결과 저장 로직의 부산물로만 채워진다. 일시정지/실작업시간 컬럼은 운영 데이터상 거의 비어 있다.

2. 데이터 모델

2.1 work_order_process_result — 공정 전체

  • wop_id UNIQUE (1 공정 = 1 행)
  • 주요 컬럼
컬럼 의미 운영 상태
started_at 공정 시작 시각 사용 중
completed_at 공정 완료 시각 사용 중
paused_at 일시정지 시각 ⚠️ 빈 값
total_paused_time 누적 일시정지 시간(초) ⚠️ 빈 값
actual_work_time 실작업시간(초) ⚠️ 빈 값
accepted_at / accepted_by 작업 수락 별도 흐름
input_qty / good_qty / defect_qty / concession_qty 수량 saveResult에서 갱신
status waiting / in_progress / completed

2.2 process_work_result — 공정작업기준

  • 공정 1개 안에 N 행 (작업 항목별)
  • work_phase 컬럼으로 PRE / IN / POST 3 단계로 그룹화
  • 시간 컬럼은 항목별이 아니라 phase 그룹 단위로 동작 (phase 내 모든 row가 동일 시각 공유)
컬럼 의미 운영 상태
recorded_at 항목 결과 기록 시각 사용 중
started_at 항목 개별 시작 ⚠️ 빈 값 (스키마만 존재)
duration_minutes 항목 소요 시간 ⚠️ 빈 값
group_started_at phase 그룹 시작 ⚠️ 빈 값
group_completed_at phase 그룹 완료 ⚠️ 빈 값
group_paused_at / group_total_paused_time phase 일시정지 추적 ⚠️ 빈 값
result_value / is_passed 측정값 / 판정

2.3 시간 추적 단위 시각화

work_order_process_result (1행)
└─ started_at / completed_at / paused_at / total_paused_time / actual_work_time
   │
   └── process_work_result (N행, work_phase 그룹화)
       ├─ phase=PRE  ─┬─ 항목1
       │              ├─ 항목2     ← 같은 group_started_at / group_completed_at 공유
       │              └─ 항목3
       ├─ phase=IN   ─┬─ 항목4
       │              └─ 항목5     ← 별개의 group_* 공유
       └─ phase=POST ─┬─ 항목6
                       └─ 항목7

3. 백엔드 시간 저장 흐름

3.1 POST /pop/production/timer — 공정 전체 타이머 (controlTimer)

backend-node/src/controllers/popProductionController.ts:885

action 동작
start wop_result.started_at = NOW() (NULL일 때만), status = 'in_progress'
pause wop_result.paused_at = NOW()
resume total_paused_time += (NOW - paused_at), paused_at = NULL
complete completed_at = NOW(), actual_work_time = phase 시간 합산, status = 'completed', 수량 갱신

3.2 POST /pop/production/group-timer — Phase 그룹 타이머 (controlGroupTimer)

backend-node/src/controllers/popProductionController.ts:1026

action 동작
start process_work_result.group_started_at = NOW() (phase 내 모든 row), 동시에 부모 wop_result.started_at도 NULL이면 NOW로
pause group_paused_at = NOW()
resume group_total_paused_time += (NOW - group_paused_at)
complete group_completed_at = NOW(), paused 정산, wop_result.actual_work_time을 phase별 MAX 시간 SUM으로 재계산

→ phase 타이머가 공정 전체 타이머에도 영향을 주는 의존성이 있다.

3.3 결과 저장 / 확정 (saveResult, confirmResult)

  • 결과 저장/확정 시 wop_result.completed_at, status, 수량 등을 추가 갱신.
  • 즉 공정 전체 종료 시각은 (1) controlTimer complete, (2) controlGroupTimer complete + saveResult 흐름, (3) confirmResult 흐름 등 여러 경로에서 갱신될 수 있다.

4. 프론트(POP) UI 현황

4.1 ProcessWork.tsx 함수 정의

함수 위치 UI 노출
handleTimerAction (공정 전체) line 909 호출하는 UI 없음
handlePhaseTimerAction (phase) line 965 Group Summary Bar에 노출 (line 1745~)

4.2 작업자가 보는 화면

  • 우측 상단 Group Summary Bar에 선택된 phase의 타이머만 표시
    • 상태별 버튼: 시작 / 정지 / 종료 / 재개 / 완료 배지
    • 경과 시간(elapsedSec)을 모노스페이스 폰트로 표시
  • 공정 전체의 시작/종료/실작업시간은 화면에 직접 표시되지 않음

4.3 작업자 인지의 한계

항목 작업자 시점
"공정 자체가 언제 시작됐나" 알 수 없음 (UI 없음)
"공정 전체 진행 상태가 어디까지 왔나" phase별 타이머만으로 추정해야 함
일시정지 / 실작업시간 표시되지 않음, 운영상 채워지지도 않음
종료 버튼이 phase 종료인지 공정 종료인지 "종료" 버튼이 phase 종료를 의미하지만 라벨상 혼동 가능

5. 운영 데이터 실태 (개발 DB)

테이블/컬럼 채움 상태
wop_result.started_at / completed_at 채워짐 (단, controlTimer가 아니라 controlGroupTimer/saveResult 부산물)
wop_result.paused_at, total_paused_time, actual_work_time 거의 빈 값
process_work_result.group_started_at, group_completed_at, group_* 빈 값 — phase 타이머 사용 흔적 없음
process_work_result.recorded_at 결과 입력 시 기록

시사점: 현장 작업자는 phase 타이머 버튼을 거의 쓰지 않거나, 자동 시작 트리거(입력 영역 상호작용)만 발동시키고 명시적으로 누르지 않는 것으로 추정.


6. 데이터 모델 정리안

6.1 현재 문제점

  1. 공정 전체 시간이 다중 경로로 갱신controlTimer.complete, controlGroupTimer.start, saveResult, confirmResult 4 경로. 어느 경로가 권위 있는지 불명확.
  2. 항목 단위 시간 컬럼은 사실상 죽은 스키마process_work_result.started_at, duration_minutes는 한 번도 채워진 적 없음.
  3. 컬럼 타입이 모두 character varying(500) — 시간/수치 컬럼이 문자열로 저장됨. 인덱스 효율, 형 변환 비용, 정합성 모두 약화.
  4. phase 타이머 = 공정 타이머에 가까움 — phase별 시간을 SUM해서 공정 actual_work_time을 만든다. 작업자에게는 동일 개념을 두 번 말하는 것처럼 보일 수 있다.

6.2 정리 방향 (안)

A. 공정 전체 타이머를 권위 있는 단일 진입점으로 격상

  • wop_result.started_at/completed_atcontrolTimer만이 갱신하도록 단일화
  • controlGroupTimer.start에서 부모 wop_result.started_at을 자동 갱신하던 로직 제거
  • saveResult/confirmResult는 수량·status만 다루고 시각은 건드리지 않음

B. 죽은 항목별 시간 컬럼 제거 또는 명시적 사용

  • 사용 의도 없으면 process_work_result.started_at / duration_minutes 제거 검토
  • 사용한다면 항목 입력 시 자동 채움 로직 추가

C. 컬럼 타입 정리 (장기 과제)

  • started_at/completed_attimestamp with time zone으로 마이그레이션
  • *_paused_time / actual_work_time / duration_minutesinteger(초)로
  • 즉시 적용은 운영 데이터 마이그레이션 부담이 크므로 별도 기획서 필요

D. phase 타이머의 책임 축소

  • phase 타이머는 "각 단계의 소요 시간 분석용 지표"로 한정
  • 공정 전체 진행/완료 판단은 wop_result만 본다

6.3 정리 후 구조 (목표)

[공정 시작 버튼] ──────────────► controlTimer(start)
                                 └─ wop_result.started_at = NOW

[작업자 항목별 입력]    ──────► saveResult (수량/판정만, 시각은 wop_result 관여 없음)

[phase별 타이머(선택)] ────────► controlGroupTimer
                                 └─ process_work_result.group_* 만 갱신

[공정 종료 버튼] ──────────────► controlTimer(complete)
                                 ├─ wop_result.completed_at = NOW
                                 ├─ actual_work_time = NOW - started_at - total_paused_time
                                 └─ status = 'completed'

7. POP 작업자 인지 개선안 (UI)

7.1 화면 정보 위계 (제안)

상단 헤더에 3 단계 정보를 분명히 노출:

┌─────────────────────────────────────────────────────────────────────┐
│ [공정명: ABC-001]  [수량: 100/120]  [공정 타이머: 01:23:45 ▶]      │  ← 공정 전체
│ ───────────────────────────────────────────────────────────────────  │
│  PRE (00:05:12 ✓완료)   IN (01:18:33 ▶진행)   POST (--:--:-- 대기) │  ← phase 진행률 한눈에
│ ───────────────────────────────────────────────────────────────────  │
│ [선택된 phase 작업기준 입력 영역]                                    │
└─────────────────────────────────────────────────────────────────────┘

핵심 요소:

  1. 공정 전체 타이머 표시 — 현재 누락. wop_result.started_at 기준 경과시간을 헤더에 항상 표시.
  2. phase 진행률 한눈 표시 — 모든 phase의 상태(대기/진행/완료)와 누적 시간을 헤더 한 줄에 압축 표시.
  3. 버튼 라벨 명확화 — phase 영역의 "종료" 버튼은 "단계 완료"로, 공정 전체 종료는 별도 큰 버튼("공정 종료")으로 분리.
  4. 일시정지 가시화 — paused 상태일 때 화면에 사유 입력(또는 표시) 영역 노출. 누적 정지시간을 부각.

7.2 상태 전이의 명시화

작업자 행동 화면 변화
공정 시작 클릭 공정 타이머 시작, 첫 phase 자동 활성
phase 입력 시작 해당 phase 카드 강조 + 자동 시작(현재 동작 유지)
phase 종료 "단계 완료" 배지 부여, 다음 phase로 포커스 이동
모든 phase 완료 공정 종료 버튼 강조 (그 전에는 disabled로 두어 오작동 방지 가능)
공정 종료 종료 시각·실작업시간·일시정지시간을 요약 카드로 표시

7.3 색상·타이포 가이드 (현 코드 기준 보강)

  • 공정 타이머: 짙은 파랑(brand) — 공정 단위 권위
  • phase 타이머: 옅은 회색~파랑 — 보조 정보
  • 정지 상태: 호박색(amber) 통일 (현재도 amber 사용 중)
  • 완료: 녹색 통일 (현재 유지)
  • 모든 시간 표시는 tabular-nums monospace (현재도 사용 중) 유지

8. 권장 작업 순서

  1. (선결) 결정 필요 — phase 타이머 유지/제거/축소 중 어느 방향인지 확정.
  2. 단기 (UI만) — 공정 전체 타이머 표시 영역을 헤더에 추가하고 handleTimerAction 핸들러를 시작/종료 버튼에 연결. 작업자는 즉시 공정 진행 상태를 인지 가능.
  3. 중기 (백엔드 정리) — 시각 갱신 경로를 controlTimer 단일화. saveResult/confirmResult에서 시각 갱신 제거.
  4. 중기 (UI 강화) — phase 진행률 헤더 바, 일시정지 사유 가시화, 단계별 라벨 정비.
  5. 장기 (스키마) — 컬럼 타입 정리, 죽은 항목 컬럼 정리.

9. 영향 범위

  • 공통 백엔드: popProductionController.ts (모든 회사 자동 반영)
  • 회사별 프론트: frontend/app/(main)/COMPANY_*/pop/_components/production/ProcessWork.tsx (7개 + COMPANY_30, 8개 회사)
    • 회사별 작업 시 명시적 지시 후 개별 Edit 진행 (CLAUDE.md 회사별 작업 범위 규칙 준수)
  • DB: 단기 작업은 스키마 변경 없음. 장기 작업 시 마이그레이션 별도 기획서 필요.

10. 미결 질문 (작업 착수 전 확인 사항)

  • phase(PRE/IN/POST) 타이머는 운영상 의미 있는 데이터인가? (현재 빈 값) — 의미 있다면 입력을 강제, 없다면 제거.
  • 일시정지 추적은 어느 단위에서 필요한가? (공정 단위만 충분한지, phase 단위까지 필요한지)
  • "공정 종료" 버튼을 누르는 권한은 누구에게 줄 것인가? (작업자 / 검수자 / 관리자)
  • 컬럼 타입 정리는 본서버에도 적용할 것인가? 적용 시 마이그레이션 일정 협의 필요.