- 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)
14 KiB
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_idUNIQUE (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)
controlTimercomplete, (2)controlGroupTimercomplete + 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 현재 문제점
- 공정 전체 시간이 다중 경로로 갱신 —
controlTimer.complete,controlGroupTimer.start,saveResult,confirmResult4 경로. 어느 경로가 권위 있는지 불명확. - 항목 단위 시간 컬럼은 사실상 죽은 스키마 —
process_work_result.started_at,duration_minutes는 한 번도 채워진 적 없음. - 컬럼 타입이 모두
character varying(500)— 시간/수치 컬럼이 문자열로 저장됨. 인덱스 효율, 형 변환 비용, 정합성 모두 약화. - phase 타이머 = 공정 타이머에 가까움 — phase별 시간을 SUM해서 공정
actual_work_time을 만든다. 작업자에게는 동일 개념을 두 번 말하는 것처럼 보일 수 있다.
6.2 정리 방향 (안)
A. 공정 전체 타이머를 권위 있는 단일 진입점으로 격상
wop_result.started_at/completed_at은controlTimer만이 갱신하도록 단일화controlGroupTimer.start에서 부모wop_result.started_at을 자동 갱신하던 로직 제거saveResult/confirmResult는 수량·status만 다루고 시각은 건드리지 않음
B. 죽은 항목별 시간 컬럼 제거 또는 명시적 사용
- 사용 의도 없으면
process_work_result.started_at/duration_minutes제거 검토 - 사용한다면 항목 입력 시 자동 채움 로직 추가
C. 컬럼 타입 정리 (장기 과제)
started_at/completed_at을timestamp with time zone으로 마이그레이션*_paused_time/actual_work_time/duration_minutes를integer(초)로- 즉시 적용은 운영 데이터 마이그레이션 부담이 크므로 별도 기획서 필요
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 작업기준 입력 영역] │
└─────────────────────────────────────────────────────────────────────┘
핵심 요소:
- 공정 전체 타이머 표시 — 현재 누락.
wop_result.started_at기준 경과시간을 헤더에 항상 표시. - phase 진행률 한눈 표시 — 모든 phase의 상태(대기/진행/완료)와 누적 시간을 헤더 한 줄에 압축 표시.
- 버튼 라벨 명확화 — phase 영역의 "종료" 버튼은 "단계 완료"로, 공정 전체 종료는 별도 큰 버튼("공정 종료")으로 분리.
- 일시정지 가시화 — paused 상태일 때 화면에 사유 입력(또는 표시) 영역 노출. 누적 정지시간을 부각.
7.2 상태 전이의 명시화
| 작업자 행동 | 화면 변화 |
|---|---|
| 공정 시작 클릭 | 공정 타이머 시작, 첫 phase 자동 활성 |
| phase 입력 시작 | 해당 phase 카드 강조 + 자동 시작(현재 동작 유지) |
| phase 종료 | "단계 완료" 배지 부여, 다음 phase로 포커스 이동 |
| 모든 phase 완료 | 공정 종료 버튼 강조 (그 전에는 disabled로 두어 오작동 방지 가능) |
| 공정 종료 | 종료 시각·실작업시간·일시정지시간을 요약 카드로 표시 |
7.3 색상·타이포 가이드 (현 코드 기준 보강)
- 공정 타이머: 짙은 파랑(brand) — 공정 단위 권위
- phase 타이머: 옅은 회색~파랑 — 보조 정보
- 정지 상태: 호박색(amber) 통일 (현재도 amber 사용 중)
- 완료: 녹색 통일 (현재 유지)
- 모든 시간 표시는
tabular-nums monospace(현재도 사용 중) 유지
8. 권장 작업 순서
- (선결) 결정 필요 — phase 타이머 유지/제거/축소 중 어느 방향인지 확정.
- 단기 (UI만) — 공정 전체 타이머 표시 영역을 헤더에 추가하고
handleTimerAction핸들러를 시작/종료 버튼에 연결. 작업자는 즉시 공정 진행 상태를 인지 가능. - 중기 (백엔드 정리) — 시각 갱신 경로를
controlTimer단일화. saveResult/confirmResult에서 시각 갱신 제거. - 중기 (UI 강화) — phase 진행률 헤더 바, 일시정지 사유 가시화, 단계별 라벨 정비.
- 장기 (스키마) — 컬럼 타입 정리, 죽은 항목 컬럼 정리.
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 단위까지 필요한지)
- "공정 종료" 버튼을 누르는 권한은 누구에게 줄 것인가? (작업자 / 검수자 / 관리자)
- 컬럼 타입 정리는 본서버에도 적용할 것인가? 적용 시 마이그레이션 일정 협의 필요.