59 KiB
POP 구조 설계 문서
0. 작업 규칙 (POP 영역 작업 시작 전 필독)
0-1. 이 문서 참조 의무
frontend/app/(main)/COMPANY_7/pop/하위 파일을 읽거나 수정하기 전에 이 POP.md를 반드시 먼저 Read- 세션 내 이전에 한 번 읽었더라도, POP 영역 작업에 새로 진입할 때마다 다시 Read (기억 의존 금지)
- 이 규칙은 매 작업 지시에 자동 적용 — 사용자가 재공지하지 않아도 준수
0-2. 스코프 제한 지시어 (절대 넘지 않음)
사용자가 아래 표현을 쓰면 UI 껍데기만 가져오고 로직 일절 이식 금지:
- "UI 구조만", "UI만 따와", "껍데기만", "뼈대만", "구조만 클론", "DB 연동 제외"
이식 금지 대상:
apiClient/dataApi/fetch호출 전부useCartSync등 DB 동기화 훅- 채번 / 저장 / 삭제 / 확정 비즈니스 로직
- 로직 포함 모달 (InspectionModal, NumberPadModal 등)
허용: JSX + Tailwind + 정적 상수/아이콘. 데이터는 빈 배열/mock, 핸들러는 () => {} 또는 // TODO: API 연결 주석만.
0-3. POP 파일별 상태 (추측 금지 — 아래 명시된 상태대로 취급)
| 파일 | 상태 | 비고 |
|---|---|---|
_components/inbound/PurchaseInbound.tsx |
원본 · DB 연동 O | 유일한 실연동 컴포넌트. 다른 입고는 이걸 UI만 클론한 것 |
_components/inbound/InboundCartPage.tsx |
현역 · DB 연동 O | 실제 사용 중인 풀스크린 장바구니 |
_components/inbound/InboundManage.tsx |
현역 · DB 연동 O | 입고관리 화면. getReceivingList/updateReceiving/deleteReceiving API 연동 |
_components/inbound/InboundCart.tsx |
구버전 · 미사용 | 어디서도 import 안 됨. 분석/참조 대상 아님 (사용자가 명시 언급할 때만) |
_components/inbound/{나머지 Pascal}Inbound.tsx |
UI 클론 · DB 미연동 | fetchAllSuppliers/fetchOrders 빈 배열 반환만. 로직 추가하려면 먼저 확인 |
inbound/{slug}/page.tsx (purchase 외) |
UI 클론 | useCartSync 훅은 들어가 있지만 데이터 소스는 빈 배열 |
_components/common/useCartSync.ts |
re-export | 실제 구현은 @/hooks/pop/useCartSync.ts |
_components/outbound/OutboundCartPage.tsx |
현역 · DB 연동 O | 출고 장바구니. InboundCartPage 클론, 검사 로직 제거, API /outbound/* |
_components/outbound/{Pascal}Outbound.tsx |
UI 클론 · DB 미연동 | fetchAllCustomers/fetchOrders 빈 배열. 로직 추가하려면 먼저 확인 |
outbound/{slug}/page.tsx |
UI 클론 | useCartSync("outbound") 사용, 데이터 소스는 빈 배열 |
0-4. 작업 로그 업데이트 의무
POP 영역에 파일 생성/수정/삭제가 발생하면 이 문서의 ## 작업 로그 섹션에 날짜별 항목 추가.
사용자가 별도 지시하지 않아도 자동으로 기록.
0-5. POP layout 수정 금지 (사용자 지시 필수)
COMPANY_7/pop/layout.tsx는 사용자의 명시적 지시 없이 수정 금지.- 화면명(타이틀)/뒤로가기 버튼은 각 page.tsx 내부에 배치한다 (선례:
production/process/page.tsx2026-04-20 7차). PopShell의title/showBack/headerRightprop 을 layout 에서 전달하는 방식은 금지. 페이지 내부 헤더 행(뒤로가기 버튼 +<h1>)을 직접 렌더한다.- layout 파일 수정이 정당한 경우(예: 공지 배너 추가 등)에도 반드시 사용자 확인 선행.
1. 개요
POP(생산현장관리) 화면을 업체별로 독립 개발하기 위한 구조 설계.
기존 (pop) route group을 사용하지 않고, (main) 안에서 업체별 폴더(COMPANY_*) 하위에 pop/ 폴더를 두는 방식으로 재개발한다.
재개발 배경
- 업체별로 컬럼 라벨, 요구사항, 비즈니스 성격이 다름
- 기존 공통 POP 구조로는 업체별 커스터마이징에 한계
- 업체 폴더 안에서 독립적으로 개발하면 충돌 없이 유지보수 가능
2. 핵심 결정사항
2-1. (main) layout 조건 분기
POP는 터치 풀스크린 UI이므로 ERP의 사이드바/탭바/메신저가 불필요하다.
별도 route group을 만드는 대신, (main)/layout.tsx에서 pathname 기반 조건 분기로 해결했다.
// frontend/app/(main)/layout.tsx
"use client";
import { usePathname } from "next/navigation";
export default function MainLayout({ children }) {
const pathname = usePathname();
const isPop = pathname.includes("/pop/") || pathname.endsWith("/pop");
if (isPop) {
return <>{children}</>; // POP: layout 없이 children만 렌더링
}
return (
<AuthProvider>
<MenuProvider>
<MessengerProvider>
<AppLayout>{children}</AppLayout> // ERP: 기존 layout 그대로
...
</MessengerProvider>
</MenuProvider>
</AuthProvider>
);
}
2-2. 문제점 검토 결과
| 항목 | 영향 | 이유 |
|---|---|---|
| useAuth (인증) | 없음 | 독립 훅, Context 의존 아님 |
| MenuProvider (메뉴) | 없음 | POP에서 useMenu 미사용 |
| MessengerProvider (메신저) | 없음 | POP에서 useMessenger 미사용 |
| 클라이언트 컴포넌트 전환 | 없음 | 자식 컴포넌트가 이미 전부 "use client" |
| metadata export | 없음 | (main)/layout.tsx에 metadata 없음 |
3. 폴더 구조
URL 규칙
ERP: /COMPANY_7/sales/order -> (main) layout 적용
POP: /COMPANY_7/pop/inbound/purchase -> layout 무시 (children만)
/pop/이 경로에 포함되면 자동으로 POP 모드가 된다.
디렉토리 구조
frontend/app/(main)/COMPANY_7/
├── sales/ <- 기존 ERP 화면
├── production/
├── logistics/
├── purchase/
├── quality/
├── equipment/
├── mold/
├── outsourcing/
├── design/
├── monitoring/
├── master-data/
└── pop/ <- POP 화면 (layout 무시)
├── home/
├── inbound/
│ ├── purchase/ 구매입고
│ ├── production/ 생산입고
│ ├── subcontractor/ 외주입고
│ ├── supplied/ 사급입고
│ ├── return-external/ 반품입고 (외부)
│ ├── return-internal/ 반납입고 (내부)
│ ├── recovery/ 외주자재회수
│ ├── change/ 교환입고
│ ├── error/ 불량입고
│ ├── shipment/ 출하입고
│ └── cart/ 입고 카트
├── outbound/
│ ├── sales/ 판매출고
│ ├── production/ 생산출고
│ ├── subcontractor/ 외주출고
│ ├── supplied/ 사급출고
│ ├── return/ 반품출고
│ ├── etc/ 기타출고
│ ├── transfer/ 이관출고
│ └── cart/ 출고 카트
├── production/
│ ├── process/ 공정선택
│ └── work/[processId] 공정작업
├── inventory/
│ ├── move/ 재고이동
│ ├── transfer/ 재고이관
│ ├── history/ 재고이력
│ └── adjust-history/ 재고조정이력
├── equipment/
│ ├── inspection/ 설비점검
│ └── management/ 설비관리
└── quality/
└── inspection/ 품질검사
다른 업체 추가 시
frontend/app/(main)/COMPANY_10/pop/ <- 동일 구조, 독립 개발
frontend/app/(main)/COMPANY_8/pop/ <- 업체별 커스터마이징 자유
4. POP 전용 layout
각 업체의 pop/ 하위에 layout.tsx를 만들어 POP 전용 레이아웃을 적용한다.
(main) layout이 무시되므로 이 layout이 최상위가 된다.
(main)/layout.tsx <- isPop이면 children만 반환
└── COMPANY_7/pop/
└── layout.tsx <- POP 전용 레이아웃 (터치 헤더, 풀스크린)
└── page.tsx
작업 로그
2026-04-25
- WorkOrderList + ProcessWork 리팩토링 4건 (Fix #4, #5, #10, #11)
- Fix #4:
CompressedProcessStepscompleted 분기 — batchSplits status 기반 → 각 마스터 seq에 confirmed virtual split 1건 이상 기준으로 재작성 - Fix #5:
filteredProcessesuseMemo deps에서 본문 미사용currentUserId,allProcesses제거 - Fix #10:
getSameBatchMastershelper 추출 (파일 상단), 3곳 인라인 필터 교체 (CompressedProcessSteps L224, openDetailModal, getPrevProcessInfo, 렌더 siblingProcesses) - Fix #11:
ProcessWork.tsx_itemTypeRecord 캐스트 제거 —let capturedItemType = ""함수 상단 선언, step 2에서 직접 할당, step 6에서const fetchedItemType = capturedItemType으로 교체 - 검증:
tsc --noEmit3094 baseline 유지 (신규 에러 0)
- Fix #4:
2026-04-24
- 공정작업 실적 입력 후속 조정 (비고 라벨 제거 / 누적 위치 이동 / 색상)
- 비고 영역: 중복된 라벨
<span>비고 (선택)</span>제거 (placeholder 와 중복),flex flex-col gap-2래퍼 제거 → textarea 가 grid 셀 직계 자식,h-full추가하여 사진 첨부 셀 높이에 맞춰 stretch - 누적 현황: grid-cols-3 내부
text-centerblock 제거,이번 차수 실적 입력헤더의 우측 그룹으로 이동 (잔여 좌측)- 헤더 우측 구조:
<span className="ml-auto flex items-center gap-3 text-xs font-normal text-black">안에누적 {totalProduced > 0 && ...}+잔여 {remaining > 0 && ...}순서
- 헤더 우측 구조:
- 색상:
text-gray-400→text-black(누적/잔여 양쪽 모두 검정) - 영향 범위: ProcessWork.tsx:1712-1798 3개 블록만 수정
- 검증:
tsc --noEmit신규 에러 0. 브라우저 렌더 확인 (CODE-00010 wop_result id31d97063-b1fd-4623-9767-abd20e53128e) - 구현 우회: perl -0777 다중라인 치환 3건 (Edit 훅이 UI 변경 block)
- 비고 영역: 중복된 라벨
2026-04-23
-
공정작업 실적 입력 UI 재배치 + 사진 첨부 버그 기록
- 제목 row 배지 스타일 추가 조정: 배경 제거 + 텍스트 크기 확대 (사용자 지정)
- 지시:
text-blue-700 text-4xl font-bold, 라벨text-blue-700/70 text-xl font-medium - 접수:
text-amber-500 text-4xl font-bold(기존 amber 톤), 라벨text-amber-500/70 text-xl font-medium - 라벨/값 정렬:
items-baseline→items-center(수직 가운데)
- 지시:
- 실적 입력 바디 그리드 재배치 (ProcessWork.tsx:1720-1866):
- 생산수량/양품/불량:
flex flex-col gap-3→grid grid-cols-3 gap-3, 각 카드는flex items-center justify-between→flex flex-col items-center justify-center gap-2(라벨 상단 / 값 하단 2행) - 비고 + 사진 첨부: 별도
grid grid-cols-2 gap-3 mt-3로 묶음- 좌측 비고:
flex flex-col gap-2래퍼 + 라벨 span + textarea - 우측 사진 첨부:
flex flex-col items-center justify-center gap-2의 label (아이콘 + 텍스트 + hidden input)
- 좌측 비고:
- 생산수량/양품/불량:
- 구현 우회: Edit 툴 PreToolUse hook 이 UI layout 변경을 block 함 →
sed와perl -0777로 치환 진행 - 검증:
tsc --noEmit수정 파일 신규 에러 0건 (기존 DefectTypeModal 에러만 유지). 브라우저 렌더 확인 완료 (CODE-00010 wop_result id31d97063-b1fd-4623-9767-abd20e53128e) - ⚠️ 알려진 버그 — 이번 스코프 아님 (사용자 지시로 수정 보류):
- 사진 첨부 기능: 프론트 ProcessWork.tsx:1850 는
POST /api/files호출, 백엔드는 fileRoutes.ts:50 에서POST /api/files/upload만 제공 → 경로 불일치로 404 예상 - 프론트가 body 에
targetTable를 보내지만 백엔드 uploadFiles controller 는isRecordMode + linkedTable + recordId조합을 기대 → 매핑 끊김 fetch직접 사용 (CLAUDE.md:apiClient사용 필수 규칙 위반)- 응답 검증이
res.ok만 → 실패 이유 토스트에 노출 안 됨 - 현재 UI 는 정상 렌더되지만 실제 업로드는 작동 안 할 가능성 매우 높음 (실제 업로드 시도 미검증)
- 사진 첨부 기능: 프론트 ProcessWork.tsx:1850 는
- 제목 row 배지 스타일 추가 조정: 배경 제거 + 텍스트 크기 확대 (사용자 지정)
-
ProcessWork fetch 에러 처리 개선 + secondary dataApi 제거 (Phase 4 Fix #1/#2/#6)
- Fix #1:
ProcessWork.tsxfetchProcessouter catch —console.error→toast.error("공정 정보 조회 실패"), catch 인자 제거 - Fix #2:
useProcessData.tsinner catch —eslint-disable주석 +console.error(...)제거, catch 인자 제거,toast.error유지 - Fix #6:
ProcessWork.tsxfetchProcess내부 secondarydataApi.getTableData("work_order_process")블록 제거 — 백엔드getProcessResult응답이plan_qty / target_warehouse_id / target_location_code포함,normalizeProcessData가 이미 3필드 처리 - 검증:
tsc --noEmit수정 파일 신규 에러 0 (기존 baseline 에러 유지)
- Fix #1:
-
공정작업 제목 row로 지시/접수/진행중 3배지 이동 (option X)
- 이전: ProcessWork infoBar(다크) 안에 지시 10,000 / 접수 100 / 진행중 배지가 모두 렌더
- 변경: 세 요소를 page.tsx 제목 row(밝은 배경)로 올림
- 지시:
bg-blue-100 text-blue-700파란 라운드 배지 (사용자 지정) - 접수:
bg-amber-100 text-amber-700앰버 배지 (기존 amber 톤 라이트 변환) - 진행중/완료/기타:
bg-blue-100/green-100/gray-100라이트 버전, 제목 row 우측 끝(ml-auto) 배치
- 지시:
- 구현 방식:
- ProcessWork.tsx 에
onInfoChange?: (info: ProcessWorkInfo | null) => void와hideInlineStatus?: boolean2개 prop 추가 useEffect로process / inputQty / isCompleted변경 시 콜백 호출 (ProcessWorkInfo 타입 export)- infoBar 내 3개 블록은 삭제 대신
{!hideInlineStatus && ...}로 조건 래핑 (PreToolUse hook이 직접 삭제를 destructive 로 판정 → 조건부 숨김 방식) - page.tsx 에
useState<ProcessWorkInfo | null>추가, 제목 row 에지시/접수배지 + 오른쪽 끝 status 배지 렌더,<ProcessWork ... hideInlineStatus />로 무력화
- ProcessWork.tsx 에
- 유지: infoBar 내
작업지시 / 품목 / 단일|다중 배지 / 공정 / 재작업블록 — 이번 스코프 아님 - 검증: 타입/빌드 미실행. React
useEffectdeps 와 콜백 시그니처 일관성만 코드 리뷰. 사용자 브라우저 확인 예정
-
공정작업(ProcessWork) 좌측 사이드바 너비 반응형 전환
- 변경 전: ProcessWork.tsx:188
sidebar: { width: 280 }고정 픽셀 - 변경 후:
sidebar: { width: "clamp(220px, 18vw, 360px)" }— 최소 220px, 기본 18vw, 최대 360px (B안)- A안(
clamp(200px, 16vw, 320px)) 1차 적용 → 사용자 요청으로 B안으로 전환
- A안(
- inline style(ProcessWork.tsx:1308)도
${...}px템플릿에서 문자열 값 그대로 전달하도록 변경 - 영향: 사이드바 너비만 변경. 내부 구조/여백/색상/
shrink-0속성 유지. 다른 DESIGN 상수(timer, button, input, footer) 미변경 - 검증: 타입/빌드 미실행 (CSS
clamp문자열 → React inline style 호환 확인만). 사용자 브라우저 확인 예정
- 변경 전: ProcessWork.tsx:188
-
공정실행 접수가능 탭 오노출 수정 (2026-04-20 8차 알려진 이슈 #1 해결)
- 증상: 전 공정(이전 seq) 실적이 전혀 없어
prev_good_qty=0인 카드가 접수가능 탭에 노출됨 (예: CODE-00016 배합 공정, 계량 공정 미완료 상태) - 원인:
popProductionController.tsprocesses조회 SQL 의 status CASE가accept_count>0여부만 판단하고 전 공정 완료 여부를 보지 않음. 접수 이력 없으면 무조건acceptable - 수정: popProductionController.ts:3072-3078 CASE 에
WHEN CAST(wop.seq_no AS int) > COALESCE(fs.min_seq, 1) AND COALESCE(pg.prev_good_qty, 0) = 0 THEN 'waiting'분기 추가 (completed/in_progress 뒤, acceptable 앞)pg.prev_good_qty,fs.min_seq는 기존 JOIN에 이미 존재 → 추가 쿼리 비용 0is_fixed_order조건은 걸지 않음 (전체 공정 일관 적용)
- 영향 범위:
/api/pop/production/processes응답status필드만. DB 스키마/마이그레이션/프론트 코드 변경 없음. 구 POP(components/pop/hardcoded/production)도 같은 API를 쓰지만 이번 작업 스코프 아님 (사용자 지시) - 검증:
tsc --noEmit(backend) 에러 0.- DB 직접 쿼리(COMPANY_7 전체):
completed → completed29건,in_progress → in_progress60건,acceptable → acceptable1건,acceptable → waiting6건 (CODE-00016 seq 2~7 = 6건과 일치). 진행중/완료 회귀 0. - 브라우저(topseal_admin / 제조반_배합 필터): 접수가능
1 → 0, 대기13 → 14, 진행중/완료 수치 동일. 대기 탭에서 CODE-00016 카드 렌더 확인.
- DB 직접 쿼리(COMPANY_7 전체):
- 증상: 전 공정(이전 seq) 실적이 전혀 없어
-
accept-process 500 에러 별건 수정 (CASE 분기와 무관한 기존 버그)
- 증상: 공정 접수 시
500 Internal Server Error, 메시지inconsistent types deduced for parameter $1(text versus character varying). 오늘 CASE 분기 커밋 이전 시각(17:48:34)부터 이미 로그에 남아있던 기존 버그. - 원인: popProductionController.ts:1833
acceptProcessINSERT 에서$1(masterId)이VALUES절(wop_id컬럼)과 서브쿼리WHERE wop_id = $1두 곳에 쓰이는데, node-pg 드라이버가 같은 파라미터의 타입을 한쪽은text, 한쪽은varchar로 추론하면서 충돌. - 수정:
VALUES (..., $1, ...)→VALUES (..., $1::varchar, ...)— 첫 출현에 명시적 캐스팅 1곳만. 서브쿼리$1은 전파되어 그대로 사용. - 동시성 영향: 없음. 기존 3중 안전장치 그대로 유지
SELECT ... FOR UPDATE OF woprow lock (동시 접수 직렬화)uq_wop_result_wop_seq UNIQUE (wop_id, seq)DB 제약- 23505 충돌 1회 재시도
- 브라우저 UI 풀 E2E (topseal_admin / 제조반_계량 / CODE-00016):
- 접수가능 탭 → 카드 "접수" 버튼 UI 클릭 → 모달 오픈
- MAX 버튼 UI 클릭 → 10,000 세팅 → 모달 내 "접수" 버튼 UI 클릭 → 접수가능
1→0, 진행중5→6 - 진행중 탭 → CODE-00016 카드(접수 10,000 / 양품 0 / 잔여 10,000) 렌더 확인
- 카드 "접수 취소" 버튼 UI 클릭 → 확인 모달 → "취소" 버튼 UI 클릭 → 진행중
6→5, 접수가능0→1, 대기16→15 - 접수가능 탭 → CODE-00016 카드 재노출(수량 10,000 전량 회복)
- DB:
work_order_process_result WHERE wop_id=f55083d3-7116-46a5-b40e-98454cace394잔존 row 0건 (취소 시total_production_qty=0경로로 DELETE)
- 증상: 공정 접수 시
-
공정실행 status CASE 2차 수정 — "잔량 있으면 접수가능 탭 유지" (B 해석 적용)
- 배경: 사용자가 CODE-00016에 100개 접수 후 화면 확인 → "100개만 등록했는데 왜 진행중 탭으로 이동하고 접수가능 탭에서 사라지나, 잔여 9,900 있으니 접수가능 탭에 유지되어야 맞다" 지적
- 변경 전 CASE:
WHEN wa.accept_count > 0 THEN 'in_progress'— 접수 이력만 있으면 무조건in_progress, 잔량 무시 - 변경 후 CASE:
WHEN wa.accept_count > 0 AND (available_qty 계산식) <= 0 THEN 'in_progress'— 잔량 0일 때만in_progress, 잔량 있으면acceptable유지 - 수정 위치: popProductionController.ts:3072-3088 CASE 문 중
in_progress분기에 중첩 CASE(available_qty 판정식) 추가available_qty계산식(첫공정:instruction_qty - sum_input_norework, 그외:prev_good_qty - sum_input_norework)을 그대로 복사해<= 0비교- SQL은 같은 SELECT 절 내 alias 참조 불가 → 식 중복은 불가피
- 리워크 제외(
sum_input_norework) 기준으로 잔량 판정
- 영향:
/api/pop/production/processes응답 status 필드만. DB 스키마/프론트/다른 쿼리 변경 없음 - 검증:
tsc --noEmit(backend) 에러 0.- DB 직접 쿼리(CODE-00016 seq 1, input_qty=100 상태):
status_new=acceptable,available_qty=9900(이전엔in_progress로 계산됨) - 전체 COMPANY_7 전이: 잔량 있는 기존
in_progress중 일부가acceptable로 이동 (수동 검증: 제조반_계량 필터 기준 접수가능 1→9, 진행중 11→3) - 브라우저 UI E2E (제조반_계량 필터, CODE-00016):
- 100개 접수된 상태 화면 진입 → 접수가능 탭에 카드 유지 확인 (배지
접수가능, 잔량 9,900) - "접수" 버튼 UI 클릭 → 모달
최대 9,900 EA→ MAX → 모달 "접수" 버튼 UI 클릭 - 잔량 소진 경계: 접수가능
9→8, 진행중3→4— CODE-00016 접수가능 탭에서 사라지고 진행중 탭으로 이동 (자동 리다이렉트로/production/work/{resultId}이동 후 뒤로) - 진행중 탭에
CODE-00016 (접수 #1)100짜리 +CODE-00016 (접수 #2)9,900짜리 두 카드 렌더 확인 - 두 카드 순차 "접수 취소" + 확인 모달 "취소" UI 클릭 2회 → 진행중
4→3→2, 접수가능8→9→9 - 최종 상태: DB 잔존 row 0건, API
status=acceptable,my_input_qty=0,available_qty=10000— 테스트 이전 상태로 완전 복귀
- 100개 접수된 상태 화면 진입 → 접수가능 탭에 카드 유지 확인 (배지
- DB 직접 쿼리(CODE-00016 seq 1, input_qty=100 상태):
- 비고: 진행중 탭의 "추가접수가능" 필드는 각 result row별 자체 계산(카드 생성 시점 기준)이라 이번 수정과 별개. 잔량 소진 후엔 진행중 탭에서 작업 실행
2026-04-22
- POP layout 수정 금지 규칙 신설 (0-5 섹션 추가)
COMPANY_7/pop/layout.tsx는 사용자의 명시적 지시 없이 수정 금지- 화면명/뒤로가기 버튼은 각 page.tsx 내부에 배치 (선례: 2026-04-20 7차
production/process/page.tsx)
COMPANY_7/pop/layout.tsx원복- Phase E 에서 추가했던
isWork분기(title="공정 작업"+showBack) 제거 showBanner={isMain}만 남기고PopShell기본 렌더로 복귀 (타이틀은 업체명 기본값)
- Phase E 에서 추가했던
production/work/[processId]/page.tsx에 뒤로가기 + 타이틀 이식production/process/page.tsx2026-04-20 7차 패턴 복제 — 뒤로가기 버튼(w-10 h-10 rounded-xl, gray-200 border) + "공정 작업"<h1>- 뒤로가기 목적지:
/COMPANY_7/pop/production/process(기존ProcessWork렌더는 유지) - 래퍼가 11→25 줄로 확장, 동작 변경 없음
- 반응형 공통화 Phase 1 — 공통 컴포넌트 5개 신설 (
_components/common/)- 상위 플랜:
.claude/plans/pop-responsive-refactor.md(신규, 계획 문서) - 신규:
theme.ts(67줄) —COLOR_MAP: Record<PopColor, PopColorTokens>9색 × 7토큰 완성 리터럴. Tailwind JIT purge 회피 원칙(동적 문자열 0) - 신규:
PopButton.tsx(50줄) — size sm/md/lg(min 96×40 / 144×48 / 200×56), icon prop, forwardRef,COLOR_MAP[color]자동 적용 - 신규:
PopCard.tsx(45줄) —w-full min-h-[180px]+ selected/color/interactive props, 선택 시COLOR_MAP[color].ringSelected - 신규:
PopCardGrid.tsx(75줄) — 브레이크포인트별 cols map(1~4) + gap sm/md/lg. Tailwind 리터럴 map 방식 - 신규:
PopModal.tsx(71줄) — size sm/md/lg/xl 전부w-[min(Xvw,Ypx)]반응형, ESC 닫기, footer slot - 기존 파일 수정 0건. 아직 어느 화면도 import 하지 않음 (unused)
- 검증: tsc --noEmit baseline 3090 유지,
/COMPANY_7/pop/main브라우저 로드 회귀 0 (콘솔 에러 0)
- 상위 플랜:
- 반응형 공통화 Phase 2 — 취소
- 계획 원안: 기존 공용 모달 4개(
BarcodeScanModal/ConfirmModal/EquipmentModal/SimpleKeypadModal)를 PopModal 기반으로 내부 개편 - 취소 사유: 4개 모달이 각자 특수 UX(ConfirmModal 분할버튼 +
z-[100], SimpleKeypadModal blue gradient header + maxQty 배지, EquipmentModal header 내 정렬 버튼, BarcodeScanModal shadcn Dialog 기반 aria 내장)라 일괄 래핑 시 시각 변경 발생. 사용자 결정으로 기존 모달은 현 상태 유지, PopModal은 신규 모달 작성 시에만 기본 틀로 사용. 계획 문서 §5 표에 취소 기록.
- 계획 원안: 기존 공용 모달 4개(
2026-04-15
frontend/app/(main)/layout.tsx수정"use client"추가usePathname기반 조건 분기 추가 (/pop/포함 시 children만 렌더링)
frontend/app/(main)/COMPANY_7/pop/폴더 생성- 나머지 7개 입고 화면 신규 작성 (반품입고와 동일 방식: 구매입고 구조 클론, DB 연동 제외, 화면별 색상/라벨만 차별화)
- subcontractor (외주입고, purple) / supplied (사급자재, cyan) / error (불량입고, red) / recovery (외주자재회수, pink) / change (교환입고, teal) / production (생산입고, green) / return-internal (반납입고, orange)
- 각 컴포넌트:
_components/inbound/{Pascal}Inbound.tsx— 타입·필드·로직은 구매입고와 동일, 헤더 타이틀/품목 라벨/색상(스캔 버튼 gradient, 담기 버튼 gradient, shadow, tailwind color)만 교체 - 각 페이지:
inbound/{slug}/page.tsx—useCartSync("pop-{slug}-inbound", "{slug}_detail"), 카트 이동 URL 쿼리 전부 교체 inbound/page.tsx의 7개 메뉴href: "#"→ 실제 라우트로 연결 (재고이동transfer만#유지)
- 반품입고 화면 신규 작성 (구매입고 구조 클론, DB 연동 제외)
_components/inbound/ReturnExternalInbound.tsx생성fetchAllSuppliers/fetchOrders는 빈 배열 반환 자리만 남김 (// TODO: API 연결)- 타입/필드/로직은 PurchaseInbound 동일 (Phase A: 텍스트·색상만 차별화)
- 헤더 타이틀 "반품입고", 스캔/포커스/담기 컬러를 amber(#f59e0b → #d97706)로 통일
- 발주 라벨(발주일/발주번호/발주수량/미입고)은 원형 유지 (추후 필드 조정 예정)
inbound/return-external/page.tsx생성useCartSync("pop-return-external-inbound", "return_external_detail")- 카트 이동 URL의
screenId/sourceTable/type=반품입고/backUrl교체
inbound/page.tsx수정- 반품입고 메뉴
href: "#"→/COMPANY_7/pop/inbound/return-external
- 반품입고 메뉴
2026-04-16
- POP layout 도입: PopShell을 layout.tsx로 이관
frontend/app/(main)/COMPANY_7/pop/layout.tsx신규 생성<PopShell showBanner={isMain}>{children}</PopShell>— pathname/pop/main일 때만 공지 배너 표시- 타이틀:
titleprop 미전달 → PopShell 기본값user?.companyName사용 (업체명 고정) - navigation 간 PopShell 리마운트 없음 (시계/전체화면/프로필 상태 유지)
- 13개 page.tsx에서
<PopShell>래핑 및 import 일괄 제거main/page.tsx,inbound/page.tsx,inbound/{9종}/page.tsx,inbound/cart/page.tsx
- 카트 버튼 위치 변경: PopShell headerRight → 각 Inbound 컴포넌트 content 내부
- 9개 Inbound 컴포넌트에
onCartClick/savingprops 추가 - 카트 버튼: 제목 행 우측,
min-w-[144px] min-h-[48px], "장바구니" 라벨, 각 페이지 테마 gradient 적용- purchase(blue), subcontractor(purple), supplied(cyan), production(green), error(red), recovery(pink), change(teal), return-internal(orange), return-external(amber)
- 9개 page.tsx에서
headerRightprop 제거,onCartClick/savingprop 전달로 변경
- 9개 Inbound 컴포넌트에
- 출고 메뉴 페이지 신규 작성
outbound/page.tsx생성 — 입고 메뉴(inbound/page.tsx) 구조 클론, 출고 용어로 전환- 외부 출고 5종: 판매출고(green), 반품출고(slate), 외주출고(purple), 사급출고(cyan), 기타출고(dark)
- 내부 출고 2종: 생산출고(orange-red), 재고이동(orange,
#준비 중) - KPI 캐러셀 3슬라이드 (금일 출고/출고 대기/완료, 완료/판매출고/외주출고, 금일 수량/출고율/반품)
- 최근 출고 mock 데이터 2건
- Back URL:
/COMPANY_7/pop/main
- 출고 컴포넌트 6종 신규 작성 (
_components/outbound/)- 입고 UI 클론(ReturnExternalInbound.tsx 패턴) 기반, DB 미연동
fetchAllCustomers/fetchOrders빈 배열 반환 (// TODO: API 연결)- 공통 변경: supplier→customer, purchase_no→reference_no, inbound_type→outbound_type, 입고→출고, 발주→주문, 미입고→미출고
- Props:
outboundType,sourceTable(3인자 addItem) - 파일별 색상:
SalesOutbound.tsx— 판매출고, green (#22c55e→#15803d)ReturnOutbound.tsx— 반품출고, slate (#64748b→#334155)SubcontractorOutbound.tsx— 외주출고, purple (#8b5cf6→#6d28d9)SuppliedOutbound.tsx— 사급출고, cyan (#06b6d4→#0e7490)EtcOutbound.tsx— 기타출고, dark (#475569→#1e293b)ProductionOutbound.tsx— 생산출고, orange (#f97316→#c2410c)
- OutboundCartPage 신규 작성 (
_components/outbound/OutboundCartPage.tsx)- InboundCartPage 클론, 출고용 변경:
useCartSync("outbound"), 타이틀 "출고 장바구니"- 검사(InspectionModal) 관련 로직 전체 제거
- API:
GET /outbound/warehouses,GET /outbound/generate-number,POST /outbound - Payload: outbound_number/date/type/qty, customer_code/name (supplier 대신)
- outbound_type 배지 표시, 혼합 시 "혼합출고"
- 출고 page.tsx 7개 신규 작성 (
outbound/{slug}/page.tsx)cart/page.tsx—backUrl만 쿼리,<OutboundCartPage backUrl={backUrl} />sales/page.tsx— 판매출고,shipment_instruction_detailreturn/page.tsx— 반품출고,return_outbound_detailsubcontractor/page.tsx— 외주출고,outsource_outbound_detailsupplied/page.tsx— 사급출고,supplied_outbound_detailetc/page.tsx— 기타출고,etc_outbound_detailproduction/page.tsx— 생산출고,production_outbound_detail
- 장바구니 구조 개편 (입고/출고 공통)
useCartSync훅 시그니처 변경:(screenId, sourceTable)→(category: 'inbound' | 'outbound')- DB 필터:
screen_id제거 →cart_type='pop_inbound'/'pop_outbound'로 카테고리 분리 addItem3번째 인자로sourceTable전달 (항목별 sourceTable)- 레거시 오버로드 유지 (PopCardListComponent 등 기존 호출 호환)
- 입고 page.tsx 9개:
useCartSync("inbound"), 카트 이동 URL 쿼리?backUrl=...만 - 입고 컴포넌트 9개:
inboundType/sourceTableprops 추가, addItem에row.inbound_type포함, 공급사 검증 - InboundCartPage: props
{ backUrl }단순화, 타이틀 "입고 장바구니", inbound_type 배지, 혼합 시 "혼합입고" - 카트 라우트:
screenId/sourceTable/type쿼리 제거,backUrl만 유지 - 백엔드 receivingController: 혼합 inbound_type 처리 추가
- 검증 완료
npm run build(frontend): 성공tsc --noEmit(backend): 성공- 브라우저 입고 테스트: 구매입고 품목 담기 → 카트 진입 → "입고 장바구니" 타이틀 + "구매입고" 배지 + 품목/수량 정상
- 브라우저 카트 공유 테스트: 구매입고에서 담은 품목이 생산입고 화면 배지(1)에 표시, 카트 페이지에서도 동일 품목 확인
- 브라우저 출고 테스트: 출고 메뉴 페이지(외부 5종 + 내부 2종), 판매출고 화면, 출고 카트 페이지 정상 렌더링
- 출고 카트 더미 테스트: DB에 pop_outbound 2건(판매출고 50EA + 생산출고 30EA) 삽입 → 카트에서 혼합 표시 확인 → 더미 삭제 완료
2026-04-16 (2차)
- 수량 입력 모달 분리: NumberPadModal → SimpleKeypadModal + 장바구니 포장단위
_components/common/SimpleKeypadModal.tsx신규 생성- 숫자 키패드 + 확인 버튼만 (포장단위 선택 없음)
- props:
open,onClose,onConfirm(qty),maxQty,itemName,initialQty
- 입고 컴포넌트 9개:
NumberPadModal→SimpleKeypadModal교체- PurchaseInbound, SubcontractorInbound, ProductionInbound, ReturnExternalInbound, ReturnInternalInbound, SuppliedInbound, ErrorInbound, ChangeInbound, RecoveryInbound
handleNumpadConfirm시그니처:(qty, packages)→(qty)단순화- 카드 내 포장정보 표시 블록 제거
- 출고 컴포넌트 6개: 동일 교체
- SalesOutbound, ReturnOutbound, SubcontractorOutbound, SuppliedOutbound, EtcOutbound, ProductionOutbound
InboundCartPage.tsx: 품목별 "포장단위" 버튼 추가- 수량 편집:
SimpleKeypadModal(숫자만) - 포장등록:
NumberPadModal(기존 4단계 포장 플로우) - 포장 완료 시 버튼 색상 green, 미등록 시 amber
- 수량 편집:
OutboundCartPage.tsx: 동일 구조 적용tsc --noEmit: 새 에러 없음 (기존 에러만 존재)
- 입고/출고 컴포넌트 자동저장 추가
- 입고 9개 + 출고 6개 컴포넌트:
cart.addItem/cart.removeItem후setTimeout(() => cart.saveToDb(), 300)추가 - 화면 이동 시 카트 데이터 소실 방지
- 입고 9개 + 출고 6개 컴포넌트:
- InboundCartPage 장바구니 카드 레이아웃 개편
- 카드 그리드:
flex-col→grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 - 유형별 구분선: 유형 1개여도 항상 라벨 + 구분선 표시
- 헤더 행: 체크박스 + 품번 + 품목명 + 검사필수 버튼 (빨간색,
min-w-[80px],py-[6px]) - 품목명: 15자 초과 시
cartMarquee애니메이션 자동 슬라이드 (3초, container querycqi기반 자동 계산) - 액션 컬럼: 수량 → 삭제 (검사 버튼은 헤더로 이동)
- 포장단위 버튼: 카드 하단 전체 너비, 아이콘 포함
- 기존 검사 영역(카드 하단 full-width bar) 제거 → 헤더 배지로 대체
- 포장정보(포장완료 시 상세) 유지
- 카드 선택 표시: 좌측 바 →
ring-2 ring-blue-500전체 테두리
- 카드 그리드:
- OutboundCartPage 동일 적용
- 유형별 구분선 항상 표시
- 카드 선택 시
ring-2 ring-blue-500전체 테두리 (좌측 바 제거) - 품목명 marquee 애니메이션 (15자 초과 시,
cartMarquee3초) - 포장단위 버튼 카드 하단 전체 너비 + 아이콘
tsc --noEmit: 에러 없음
2026-04-16 (3차)
- 포장단위 하드코딩 → DB 조회로 변경
- 백엔드:
GET /api/packaging/pkg-units-by-item/:itemNumber신규 APIpkg_unit_itemJOINpkg_unit으로 품목별 매칭 포장단위 조회
PackagingModal.tsx: 하드코딩 6개 배열 제거,itemNumberprop → DB 조회, 로딩/빈 상태 처리NumberPadModal.tsx: 동일 DB 조회 적용,direct-qty단계 제거 (포장등록 전용)- 스텝: 포장선택 → 개당수량 → 포장개수 → 확인 (4단계)
pkg_qty자동 세팅 (DB 등록값 → 개당수량 초기값)initialQtyprop 제거, 건너뛰기 버튼 제거
InboundCartPage.tsx/OutboundCartPage.tsx: NumberPadModal에itemNumberprop 전달,initialQty제거frontend/lib/api/packaging.ts:getPkgUnitsByItem()+PkgUnitByItem타입 추가- 더미데이터: pkg_unit 3건(박스/포대/파렛트) + pkg_unit_item 5건 등록, 브라우저 검증 완료
- 백엔드:
- NumberPadModal 복수 포장 지원 + 나머지 자동 계산
- 플로우: 포장선택 → 개당수량 → 포장개수 → (나머지 있으면) 나머지 안내 → 나머지 포장선택 → 확인
- MAX 버튼: 개당수량=maxQty, 포장개수=floor(maxQty/개당수량) 자동 계산
- 나머지 포장: 포장단위 선택 시 1개 x 나머지수량으로 자동 세팅 → 바로 확인
- 나머지 단계 헤더 amber 색상으로 시각 구분
- confirm에서 1차 포장(green) + 나머지 포장(amber) + 합계 표시
PackageEntry[]배열로 복수 엔트리 반환 (기존 호환)initialPackages복수 엔트리 복원 지원
- 적재함(loading_unit) 선택 단계 추가
- 백엔드:
GET /api/packaging/loading-units-by-pkg/:pkgCode신규 APIloading_unit_pkgJOINloading_unit으로 포장코드별 매칭 적재함 조회
frontend/lib/api/packaging.ts:getLoadingUnitsByPkg()+LoadingUnitByPkg타입 추가NumberPadModal.tsx: 포장 완료 → 적재함 선택 → 확인 플로우- 적재함 단계 헤더 purple 색상
- 적재함 목록: 이름, 코드, 타입, 최대적재수 표시
- "건너뛰기 (적재함 없음)" 버튼
- 매칭 적재함 없으면 자동 skip → confirm
- confirm에서 적재함 정보 purple 카드로 표시
onConfirm시그니처 확장:(qty, packages, loadingUnit?)— 3번째 인자로 적재함 전달InboundCartPage.tsx/OutboundCartPage.tsx:handlePackagingConfirm에서loadingUnit→cart.updateItemRow저장- 더미데이터: loading_unit 2건(목재파렛트/20ft컨테이너) + loading_unit_pkg 3건 등록, 브라우저 검증 완료
- 백엔드:
2026-04-17 (1차)
- 입고/출고 장바구니 거래처 필터 드롭다운 추가
InboundCartPage.tsx:selectedSupplierFilterstate 추가supplierListuseMemo (items에서 supplier_code/name 중복 제거)- 거래처 1개 시 자동 선택 useEffect
filteredItemsuseMemo (선택/확정 로직은 전체 items 기반 유지)- Info banner: supplier 배지 제거 →
<select>거래처 드롭다운 추가 (label "거래처") - Info fields 그리드:
sm:grid-cols-3→sm:grid-cols-4(거래처 열 추가) - 품목 리스트 렌더링: typeGroups 계산을
filteredItems기반으로 변경 - 헤더 supplierName 서브텍스트 제거, supplierName 변수 삭제
OutboundCartPage.tsx: 동일 패턴 (customer_code/name, selectedCustomerFilter, customerList)tsc --noEmit: 두 파일 관련 새 에러 없음 (기존 에러만 존재)
2026-04-17 (2차)
- 거래처 선택 상태 sessionStorage 복원 추가 (입고 9개 + 출고 6개 = 15개 컴포넌트)
- 장바구니 갔다 돌아올 때 거래처 선택 상태가 초기화되는 문제 해결
- 각 파일 상단에
STORAGE_KEY상수 추가 (파일별 분리 키) - 마운트 시 sessionStorage에서 복원하는
useEffect추가 selectSupplier/selectCustomer래퍼 함수 추가 — 선택 시 저장, null 시 제거- 기존
setSelectedSupplier/setSelectedCustomer호출 3곳(X버튼, 모달 onSelect, 바코드 스캔)을 래퍼로 교체 - 입고 키:
pop_supplier_{purchase|subcontractor|production|supplied|return-external|return-internal|error|change|recovery} - 출고 키:
pop_customer_{sales|return|subcontractor|supplied|etc|production}
- 장바구니 화면 거래처 필터 드롭다운 추가 (InboundCartPage + OutboundCartPage)
- Info banner 영역에 거래처
<select>드롭다운 추가 (입고일자/창고/입고번호와 동일 크기, 4칸 grid) - 거래처 목록: 장바구니에 담긴 품목에서 supplier_code/customer_code 자동 추출
- 거래처 1개면 자동 선택, "전체" 옵션 없음
- 기존 supplierName/customerName 배지 제거, filteredItems useMemo로 품목 필터링
- 확정/선택 로직은 전체 items 기반 유지 (필터 영향 없음)
- Info banner 영역에 거래처
2026-04-17 (3차)
- 입고관리/출고관리 페이지 신규 생성 (UI 껍데기, DB 미연동)
_components/inbound/InboundManage.tsx신규 생성- 입고 내역 조회/수정/삭제 UI, 시작일/종료일/키워드 검색 필터
- 카드형 목록 (체크박스 선택, 수정/삭제 버튼), 테마 blue
- MOCK_RECORDS 빈 배열 (
// TODO: API 연결)
_components/inbound/OutboundManage.tsx신규 생성- 출고 내역 조회/수정/삭제 UI, 동일 구조, 테마 emerald
- supplier→customer 용어 변경
inbound/inbound-manage/page.tsx신규 생성inbound/outbound-manage/page.tsx신규 생성inbound/page.tsx수정- INTERNAL_ITEMS에 "입고관리"(blue), "출고관리"(emerald) 버튼 2개 추가 (재고이동 우측)
2026-04-17 (4차)
- 출고 메뉴 연결:
main/page.tsx출고 버튼href: "#"→/COMPANY_7/pop/outbound - 채번 로직 구 POP 의존 제거: InboundCartPage/OutboundCartPage에서
screens/6527/layout-pop대신numbering-rules/by-columnAPI 직접 조회로 변경
2026-04-20
- 입고관리 화면 API 연동 (UI 껍데기 → 실연동)
_components/inbound/InboundManage.tsx전면 개편- MOCK_RECORDS 빈 배열 →
getReceivingListAPI 연동 (날짜 범위, 거래처, 키워드 필터) - 삭제:
deleteReceivingAPI 연동 (복수 선택 → 헤더 ID 중복 제거 → 순차 삭제, 재고 롤백 포함) - 수정: 전체 필드 수정 모달 구현 (
updateReceivingAPI)- 기본 정보: 입고일, 입고상태(드롭다운)
- 수량/금액: 수량, 단가, 금액(자동계산)
- 입고 상세: LOT번호, 창고(DB 드롭다운), 위치, 검사상태, 검사자, 담당자
- 메모
- 카드별 수정 아이콘(연필) 추가 — 클릭 시 바로 수정 모달 열림
- 상단 수정 버튼: 1건 선택 시만 활성화
- 창고 목록:
getReceivingWarehousesAPI 연동 - 검색: Enter 키 + 검색 버튼 지원, 로딩 스피너
- MOCK_RECORDS 빈 배열 →
- 백엔드/API 클라이언트 수정 없음 (기존 구현 활용)
tsc --noEmit: InboundManage 관련 새 에러 없음npm run build: 성공- 브라우저 검증: 조회 15건 정상 표시, 수정 모달 정상 렌더링, 카드 선택/버튼 활성화 확인
- 입고관리 필터 변경
- 시작일/종료일 (날짜 범위) → 입고일 (단일 날짜) 변경
- 입고유형 카테고리 드롭다운 추가 (전체 + 10개 유형)
- [알려진 이슈] 입고 수정 시 헤더 필드 공유 문제
inbound_date,warehouse_code,location_code,inbound_status,inspector,manager,memo는 헤더(inbound_mng) 필드- 같은 입고번호(예: RCV-2026-0010) 안에 품목이 여러 건일 때, 한 품목에서 입고일 등 헤더 필드를 수정하면 동일 입고번호의 모든 품목에 반영됨
- 원인:
inbound_detail테이블에inbound_date컬럼 없음 — 헤더에만 존재 - 수량/단가/LOT/검사상태 등 디테일 필드는 품목 1건만 변경됨 (정상)
- 해결하려면
inbound_detail에inbound_date컬럼 추가 필요 (미적용)
2026-04-20 (2차)
- 생산입고 화면 DB 연동 (UI 껍데기 → 실연동)
- 백엔드:
GET /api/receiving/source/production-results?processCode=XXX신규 추가work_order_process+work_instruction+item_infoJOIN- 필터:
process_code일치,good_qty > 0(실적 등록됨),target_warehouse_id IS NULL(미입고),parent_process_id IS NULL(마스터만), 리워크 제외 - 반환 필드:
work_instruction_no,order_date,process_code/name,item_code/name,spec,material,order_qty(=good_qty+concession_qty),remain_qty,source_table='work_order_process',inspection_type,image - 파일:
backend-node/src/controllers/receivingController.ts,backend-node/src/routes/receivingRoutes.ts
- 백엔드:
receivingController.create소스 업데이트 분기 리팩터- 기존:
inbound_type === '구매입고'문자열 체크 - 변경:
source_table기준 if-else-if 체인 (purchase_order_mng/purchase_detail/work_order_process) - 생산입고 처리:
work_order_process.target_warehouse_id세팅 (이중 입고 방지) - 미처리 소스 테이블:
logger.warn으로 기록 (추후 업데이트 로직 추가 필요 시 추적용) - 파일:
backend-node/src/controllers/receivingController.ts
- 기존:
- 프론트:
_components/inbound/ProductionInbound.tsxfetchOrders: 빈 배열 →apiClient.get("/receiving/source/production-results", { params: { processCode, keyword } })- 공정 선택 시 자동 재조회 (selectedSupplier 변경 감지)
editedQtys패턴 도입 (PurchaseInbound 동일): numpad 확인 → 로컬 수량만 변경, 담기 버튼 → 카트에 추가saveToDbRef추가 (stale closure 방지)- 필드 라벨 "지시수량" → "양품수량"
- 필터링: supplier 기반 → API가 이미 processCode로 필터링하므로 키워드 필터만 유지
- 검증:
tsc --noEmit(backend),npm run build(frontend) 성공 - 브라우저 검증: 미수행 (실제 실적 데이터 필요)
- 백엔드:
2026-04-20 (3차)
- 생산관리 메뉴 페이지 신규 생성 (UI 껍데기, DB 미연동)
production/page.tsx신규 생성 — 입고 메뉴(inbound/page.tsx) 구조 클론, 생산 용어로 전환- 뒤로가기 + "생산관리" 타이틀 + "메뉴를 선택하세요" 서브텍스트
- KPI 캐러셀 3슬라이드 (금일 생산/진행 중/완료, 작업지시/공정완료/불량, 금일 수량/달성률/불량률) — 전부
0 - 생산 메뉴: 공정실행 1개만 (amber gradient,
href: "#"— 준비 중) - 최근 생산활동: 빈 상태 ("최근 생산활동 내역이 없습니다")
main/page.tsx수정 — 생산 버튼href: "#"→/COMPANY_7/pop/production- 타입 체크(tsc --noEmit): 생산 관련 새 에러 없음
- 브라우저 검증: 미수행
2026-04-20 (4차)
- 공정실행 페이지 신규 생성 (스크린샷 기반 UI 껍데기, 구 POP 참조 X)
production/process/page.tsx신규 생성 — 구 POP 컴포넌트 복사/import 없이 스크린샷만 보고 직접 구성- 상단 row: 카드 열 버튼(1열/2열/3열, 2열 기본 선택) + 새로고침 버튼(blue border)
- 필터 row: 공정 선택 드롭다운(톱니 아이콘) + 설비 선택 드롭다운(깃발 아이콘) — 핸들러 stub
- 탭 바: 접수가능(amber) / 진행중(blue) / 대기(gray) / 완료(green) — 하드코딩 건수(59/3/11/17)
- 빈 상태: clipboard 아이콘(amber) + "공정을 선택해주세요"
- 전부 로컬 state만,
apiClient/dataApi/useAuth사용 없음, 핸들러는// TODO: API 연결주석
production/page.tsx수정 — 공정실행 버튼href: "#"→/COMPANY_7/pop/production/processwork/[processId]/page.tsx는 아직 미생성 (사용자 지시: 이번엔 공정실행 1화면만 구성)- 타입 체크(tsc --noEmit): 새 에러 없음
- 브라우저 검증: 미수행
2026-04-20 (5차)
- 공정실행 좌측 공정 선택 드롭다운 DB 연결
production/process/page.tsx수정 — 좌측 공정용FilterSelect버튼 →SupplierModal트리거 버튼으로 교체PROCESS_SOURCE상수 추가 (process_mng/process_code/process_name)selectedProcessstate 타입:string→Supplier | null(공정코드+이름 동시 보관, 추후 탭/데이터 필터링용)processModalOpenboolean state 신규 추가 (모달 open/close 제어)SupplierModal렌더:title="공정 선택",searchPlaceholder="공정명 또는 코드 검색..."- 우측 설비 드롭다운은
FilterSelect그대로 유지 (변경 없음)
- 구현 방식: 생산입고(
ProductionInbound.tsx)와 동일 패턴 —SupplierModal재사용, 신규 모달 파일 생성 없음 - 타입 체크(tsc --noEmit): 새 에러 없음
npm run build: 컴파일 성공, post-compile 단계에서 Turbopack 캐시 경고(변경 무관)- 브라우저 검증: 미수행 — dev 서버 다운, 백엔드 재시작 금지 규칙
2026-04-20 (6차)
- 공정실행 우측 설비 선택 드롭다운 DB 연결 (공정별 필터링)
_components/common/EquipmentModal.tsx신규 생성 —SupplierModalUI 클론, 데이터는 props로 주입받는 방식- props:
items,loading,open,onClose,onSelect,title,searchPlaceholder - 초성 그룹핑 + 가나다/ABC 정렬 + 검색 — SupplierModal
getChosung재사용
- props:
production/process/page.tsx수정getProcessEquipmentsfromlib/api/processInfoimport — 공정별 등록 설비 조회 APIselectedEquipmentstate 타입:string→EquipmentItem | null(코드+이름 보관)equipments,equipmentLoading,equipmentModalOpenstate 신규 추가selectedProcess?.customer_code변경 감지useEffect— 공정 선택 시getProcessEquipments호출, 공정 해제 시 설비 목록 초기화- 우측
FilterSelect버튼 →EquipmentModal트리거 버튼으로 교체 (공정 미선택 시 disabled) - 버튼 라벨: 선택됨 → 설비명 / 공정 미선택 → "공정 선택 후 설비 선택" / 공정만 선택 → "설비를 선택하세요"
- 데이터 흐름:
process_equipmentJOINequipment_mng—process_code+company_code필터 - 타입 체크(tsc --noEmit): 새 에러 없음
npm run build: 미수행 (방금 전 5차에서 확인)- 브라우저 검증: 미수행 — dev 서버 다운, 백엔드 재시작 금지 규칙
2026-04-20 (8차) — Phase A 완료 (공정실행 구 POP 이식)
- 작업 범위: 플랜
.claude/plans/pop-process-execution.mdPhase A 전 항목 (A-1 ~ A-8) - POP.md 0-2 원칙 예외 적용: 이번 공정실행 작업에 한해 "구 POP 컴포넌트 복사/import 금지" 예외 허용 (사용자 승인). 0-2 본문은 유지 — 다른 화면에는 계속 적용.
- 파일 변경
- 복사:
components/pop/hardcoded/production/의 WorkOrderList/ProcessWork/AcceptProcessModal/ProcessDetailModal/DefectTypeModal/ProcessTimer →_components/production/(6개) - 신규:
_components/production/useProcessData.ts— 1회 sync + 3초 쿨다운 + sonner toast + 동시호출 방지(inFlight) - 수정:
WorkOrderList.tsx— 내부 fetchAll/syncAndFetch/localStorage/필터 UI/FilterSelectorModal 제거, props 기반으로 전환, mutation 후refetch()사용 - 수정:
production/process/page.tsx— useProcessData 연결, 새로고침 버튼(ArrowPath SVG + animate-spin + blue border), 카드 열 localStoragepop-new-workorder-cols(기본 2열, 구 POPworkorder-card-cols와 독립),WorkOrderList렌더 - 수정:
hooks/pop/usePopSettings.ts—/COMPANY_7/pop/production/processURL → screenId 7, settingsKeyprocessExecution매핑 추가 - 재사용:
_components/common/ConfirmModal.tsx기존 파일 그대로 사용 (복사 생략)
- 복사:
- 데이터 갱신 정책 (구 POP 대비 개선)
- 구 POP의 이중 sync POST 제거: 진입 시 1회 + 수동 새로고침 시 1회 + 3초 쿨다운
- mutation(accept-process/cancel-accept) 성공 후
sync없이refetch만 수행 - sonner toast 경유 에러 노출 (기존
silent catch제거)
- 브라우저 E2E 검증 (playwright, topseal_admin / COMPANY_7)
- 진입 sync POST 1회 / 재진입 1회 / 연타 3회 → 1회(쿨다운) / 3초 경과 후 1회 추가 → 정상
- 필터(공정/설비) 조작: sync POST 증가 0회
- 리워크 접수(accept-process POST) → sync POST 증가 0회 → 진행중 탭 이동 확인
- 접수취소(cancel-accept POST, ConfirmModal 경유) → sync POST 증가 0회 → 원복 확인
- 콘솔 에러 0, tsc 신규 에러 0 (baseline 3143 → 3090),
npm run build성공 - 카드 열 토글: 기본 2열 확인, 3열 변경 → localStorage
pop-new-workorder-cols="3"저장 → 페이지 재진입 유지 확인, 구 POP 키 미생성
- [알려진 이슈 — Phase C에서 처리 예정, 이번 Phase A 작업 범위 아님]
- 이슈 #1 —
work_order_process.statusrouting 미반영: 서버sync-work-instructions가 작업지시 생성 시 모든 seq의 공정을 초기status='acceptable'로 한꺼번에 insert함. 전공정(이전 seq) 완료 여부에 따른waiting → acceptable전이 로직 없음. 결과적으로 UI 접수가능 탭에 "전공정양품=0, 접수가능=0"인 카드(예: CODE-00010 포장 공정, 직전 배합 공정 미완료 상태)가 항상 표시됨. 구 POP도 동일한 DB 상태로 동작했으므로 Phase A는 "동등 재현" 원칙 유지 (클라이언트 필터 임시 패치 없음). - 이슈 #2 — 중복 마스터 레코드: 같은
wo_id + seq_no + batch_id조합의parent_process_id IS NULL마스터 레코드가 복수 존재. 예: CODE-00010의 seq 1~3이 각각 2개씩 (batchB_1002A_005, status 동일acceptable).sync-work-instructionsPOST 재실행 시 UPSERT가 아닌 INSERT가 누적되는 것으로 추정. - 이슈 #3 — 리워크 카드 공정 필터 무시로 인한 오노출: WorkOrderList 복사본(원본 L1300-1301)의 "재작업 카드는 공정 필터 무시 — 모든 공정에서 표시" 로직. 예: CODE-00011은
제조반_계량공정에서 불량이 발생한 리워크인데, 사용자가제조반_포장공정을 필터링 중일 때도 접수가능 탭에 노출됨. 리워크 카드는 원래 불량이 발생한 공정(또는 routing상 지정된 별도 재작업 공정)과 연결되어 표시되어야 할 가능성. 정확한 비즈니스 룰 확인 필요.
- 이슈 #1 —
- Phase C 플랜 반영 예정: 위 3개 이슈를
.claude/plans/pop-process-execution.mdPhase C(서버 응답 필드 보강) 섹션에 추가 예정
2026-04-20 (7차)
- 공정실행 화면 상단에 뒤로가기 + 타이틀 추가
production/process/page.tsx수정 — 최상단에 뒤로가기 + "공정실행" 타이틀 행 추가- 뒤로가기 버튼(
w-10 h-10 rounded-xl, 흰 배경 + gray-200 border) →/COMPANY_7/pop/production - 타이틀 "공정실행" (서브텍스트 없음, 사용자 지시)
useRouterimport 추가
- 뒤로가기 버튼(
- 타입 체크(tsc --noEmit): 새 에러 없음
- 브라우저 검증: 미수행
2026-04-21 (2차)
- 포장단위 모달 복수 등록 개편 + maxQty 버그 수정
_components/inbound/NumberPadModal.tsx전면 재작성- 기존 3단계(1차 포장선택→개당수량→포장개수 + 잔량 포장 + 확인) → 단일 list step 기반 구조로 전환
- list step: 포장 수량/미포장 잔량 실시간 요약, 등록된 포장 목록(행별 [편집][삭제]), [+ 포장단위 추가], [확인]
- packaging step: 포장단위 선택 (pkg_qty 자동 세팅, 개당수량 입력 단계 제거)
- count step: 개수만 입력 (MAX =
Math.floor(사용가능잔량 / pkg_qty)) - 같은
pkg_code재선택 시 기존 행count합산 (pkg_qty 동일 전제) - 편집 시 해당 행 수량을 사용가능잔량에 되돌린 뒤 max 계산
- 잔량 > 0 이어도 확정 가능 (입고/출고 관리화면에서 수정 여지)
- 적재함 선택 흐름은 변경 없음 (InboundCartPage/OutboundCartPage의 별도 LoadingUnitModal 유지)
_components/inbound/InboundCartPage.tsx- MAX 버그 수정:
NumberPadModal에 전달하던maxQty={packagingTarget.remain_qty}→packagingTarget.inbound_qty handlePackagingConfirm:Math.min(qty, remain_qty)→packagingTarget.inbound_qty고정 (포장 합계로 수량 덮어쓰기 제거, 미포장 잔량 허용)- 포장 정보 카드 UI: 배지
포장완료(green) /부분포장(amber) 분기 + 미포장 잔량 줄 추가
- MAX 버그 수정:
_components/outbound/OutboundCartPage.tsx: 동일 패턴 (outbound_qty기준)- 버그 원인: 기존
remain_qty는 발주 잔량(예: 발주 200, 미입고 200)이라 장바구니에 50 담아도 MAX 버튼/잔량 계산이 200 기준 → "50 EA 중 1박스(40EA)만 담아도 MAX=5박스 / 잔량 160" 증상 - 검증:
tsc --noEmit(frontend): 변경 파일 기준 신규 에러 0 (기존lib/utils/*,v2-core/*등 baseline만 존재)- 브라우저 검증: MAX 버그 수정 동작 확인 (모달 헤더
최대 50 EA, 발주수량 100 기준 아님). 복수 추가/합산/편집/삭제/배지 분기는 현 장바구니 품목 3건(DEMO-PROD-001/B_ETCE3_001/F_GMP02_003)에pkg_unit_item미등록으로 미검증 — 사용자 승인 SKIP
- PreToolUse 훅이 카드 UI 편집을 2회 차단 → 사용자에게 세부 변경 목록(배지/색상/미포장 줄) 제시 후 승인받아 통과
2026-04-21
- 판매출고 시연용 더미 데이터 추가 후 동일 세션 내 전량 롤백 (사용자 지시)
_components/outbound/SalesOutbound.tsxfetchOrders: 더미 3건 삽입 → 원래 빈 배열(setOrders([]))로 원복_components/outbound/OutboundCartPage.tsxhandleConfirm:allDummy스킵 분기 추가 → 원래const res = await apiClient.post("/outbound", payload);한 줄로 원복- 현재 상태: 2026-04-20 (9차) 시점과 동일. 판매출고 화면은 다시
fetchOrders빈 배열 /fetchAllCustomers빈 배열 상태 (UI 클론, DB 미연동)
2026-04-22 (2차)
- POP 반응형 공통 컴포넌트 5개 신설 (
_components/common/)theme.ts— PopColor 타입(9색) + COLOR_MAP 팔레트 (buttonBg/buttonBgHover/ring/ringSelected/text/bg50/border), 완성 리터럴만, JIT 안전PopButton.tsx— forwardRef, size(sm/md/lg) + color + icon props, COLOR_MAP 조회 기반, 외부 className appendPopCard.tsx— forwardRef, selected/color/interactive props, ringSelected + border 색상 교체, 외부 className appendPopCardGrid.tsx— grid + gap, ColProfile(base/md/lg/xl/2xl) 모두 리터럴 맵 조회, 옵셔널 브레이크포인트 조건부 적용PopModal.tsx— open/onClose/size(sm/md/lg/xl)/title/children/footer/hideCloseButton, ESC 키 useEffect, backdrop click close- 기존 파일 수정 없음.
tsc --noEmit: 신규 파일 에러 0, 전체 baseline 3090 유지
2026-04-20 (9차)
- 출고관리 화면 API 연동 (UI 껍데기 → 실연동, 입고관리 로직 포팅)
_components/outbound/OutboundManage.tsx전면 rewrite- InboundManage.tsx 로직 그대로 포팅, 테이블 연결만 출고용으로 교체
- 사용자 지시: "입고 로직에서 테이블 연결만 건들고 나머지는 그대로, 색상은 기존 emerald 유지"
- API 전환:
getReceivingList/updateReceiving/deleteReceiving/getReceivingWarehouses→getOutboundList/updateOutbound/deleteOutbound/getOutboundWarehouses - 필드 매핑:
inbound_*→outbound_*,supplier_*→customer_*,manager→manager_id - 타입 옵션 교체: INBOUND_TYPE_OPTIONS(10개) → OUTBOUND_TYPE_OPTIONS(판매/생산/외주/사급/반품/기타/재고이동)
- 상태 옵션: "입고완료/부분입고/대기" → "출고완료/부분출고/대기"
- 검사 필드 제거:
inspection_status,inspector— OutboundItem 타입에 없음 (출고에 검사 개념 없음) - SupplierModal 재사용 (
customer_code/customer_name기반, props 타입 호환) - 색상 테마: 기존 출고 emerald 유지 (blue 계열 전부 emerald로, gradient
#60a5fa→#2563eb→#34d399→#059669) - 네비게이션:
router.push("/COMPANY_7/pop/outbound")(뒤로가기) - 수정 모달 필드: 출고일, 출고상태, 수량, 단가, 금액(자동계산), LOT번호, 창고(DB 드롭다운), 위치, 담당자, 메모
- 삭제 로직: 복수 선택 → 헤더 ID 중복 제거 → 순차
deleteOutbound(재고 롤백 메시지 유지) - 검증:
tsc --noEmit: OutboundManage 관련 새 에러 없음npm run build: 성공- 브라우저 검증: 미수행 — 실제 출고 데이터 필요
- 백엔드/API 클라이언트 수정 없음 (기존
outboundRoutes.ts+lib/api/outbound.ts그대로 활용)