diff --git a/plans/bom-tree-3level-react.md b/plans/bom-tree-3level-react.md index a994345..e1597a2 100644 --- a/plans/bom-tree-3level-react.md +++ b/plans/bom-tree-3level-react.md @@ -1,112 +1,264 @@ -# 서비스 BOM 트리 3단계 구조 개선 +# BOM 트리 3단계 구조 개선 — React 구현 요청 > **작성일**: 2026-03-18 -> **상태**: 계획 +> **요청자**: API/MNG 개발팀 +> **대상**: React 프론트엔드 +> **상태**: MNG 구현 완료, React 구현 대기 --- ## 1. 개요 -### 1.1 목적 +품목관리 > 제품상세 화면의 BOM 트리를 2단계(FG → PT 플랫 리스트)에서 3단계(FG → 카테고리 → PT)로 개선한다. -서비스(React) 품목관리 > 제품상세의 BOM 트리를 MNG와 동일한 3단계 계층 구조로 개선한다. +### 1.1 현재 상태 -### 1.2 현재 상태 +| 항목 | 상태 | 설명 | +|------|------|------| +| **BOM 데이터 category 필드** | ✅ 완료 | `items.bom` JSON에 `category` 필드 포함 | +| **MNG BOM 트리 3단계** | ✅ 완료 | MNG 품목관리에서 3단계 구조 표시 | +| **React BOM 트리** | ❌ 2단계 | 플랫 테이블로 17건 나열 | -| 항목 | MNG (백오피스) | React (서비스) | -|------|-------------|--------------| -| BOM 트리 구조 | ✅ 3단계 (FG → 카테고리 → PT) | ❌ 2단계 (FG → PT) | -| 카테고리 그룹 | 주자재, 모터, 제어기, 절곡품, 부자재, 검사비 | 없음 (플랫 리스트) | -| 접힘/펼침 | ✅ chevron 토글 | ✅ 토글 있음 | +### 1.2 대상 페이지 -### 1.3 목표 구조 +`/production/screen-production/{code}?mode=view&type=FG&id={id}` + +### 1.3 대상 컴포넌트 + +`src/components/items/ItemDetailClient.tsx` — 557~611줄 "부품 구성 (BOM)" 섹션 + +--- + +## 2. 현재 vs 목표 UI + +### 현재 (2단계 — 플랫 테이블) ``` -현재 (2단계): -▼ FG KSS02 스크린 SUS마감 벽면형 - PT 스크린 실리카 x10.65 - PT 모터 150K(S) (220V) x1 - PT 제어기 노출형 x1 - PT 케이스 500*380 x3.22 - ...17건 플랫 리스트 +부품 구성 (BOM) 총 17개 품목 +┌────┬──────────────────────┬──────────────────┬────────┬──────┐ +│ 번호 │ 품목코드 │ 품목명 │ 수량 │ 단위 │ +├────┼──────────────────────┼──────────────────┼────────┼──────┤ +│ 1 │ EST-RAW-슬랫-방화 │ 스크린 실리카 │ +10.65 │ m² │ +│ 2 │ EST-MOTOR-220V-150K │ 모터 150K(S) │ x1 │ EA │ +│ 3 │ EST-CTRL-노출형 │ 제어기 노출형 │ x1 │ EA │ +│ 4 │ BD-케이스-500*380 │ 케이스 500*380 │ +3.22 │ m │ +│ 5 │ BD-케이스용 연기차단...│ 케이스용 연기차단재│ +3.22 │ m │ +│ .. │ ... │ ... │ ... │ ... │ +│ 17 │ EST-INSPECTION │ 검사비 │ x1 │ EA │ +└────┴──────────────────────┴──────────────────┴────────┴──────┘ +``` -개선 후 (3단계): -▼ FG KSS02 스크린 SUS마감 벽면형 - ▼ 주자재 (1건) - PT 스크린 실리카 x10.65 - ▼ 모터 (1건) - PT 모터 150K(S) (220V) x1 - ▼ 제어기 (1건) - PT 제어기 노출형 x1 - ▼ 절곡품 (9건) - PT 케이스 500*380 x3.22 - PT 케이스용 연기차단재 x3.22 - ... - ▼ 부자재 (4건) - PT 감기샤프트 5인치 6m x1 - ... - ▼ 검사비 (1건) - PT 검사비 x1 +### 목표 (3단계 — 카테고리 그룹 접힘/펼침) + +``` +부품 구성 (BOM) 총 17개 품목 (6개 그룹) + +▼ 주자재 (1건) + │ EST-RAW-슬랫-방화 스크린 실리카 x10.65 m² +▼ 모터 (1건) + │ EST-MOTOR-220V-150K 모터 150K(S) (220V) x1 EA +▼ 제어기 (1건) + │ EST-CTRL-노출형 제어기 노출형 x1 EA +▼ 절곡품 (9건) + │ BD-케이스-500*380 케이스 500*380 x3.22 m + │ BD-케이스용 연기차단... 케이스용 연기차단재 x3.22 m + │ BD-마구리-505*385 마구리 505*385 x1 EA + │ ... +▶ 부자재 (4건) ← 접힌 상태 +▼ 검사비 (1건) + │ EST-INSPECTION 검사비 x1 EA ``` --- -## 2. 구현 범위 +## 3. BOM 데이터 구조 (API 변경 없음) -### 2.1 백엔드 (API) +현재 API 응답의 `item.bom` 배열에 이미 `category` 필드가 포함되어 있다. +**API 수정 불필요** — React에서 `category`로 그룹화하면 된다. -BOM 데이터에 이미 `category` 필드가 포함되어 있으므로, API 응답에서 카테고리 그룹 노드를 생성하면 된다. - -**MNG 구현 참고**: `mng/app/Services/ItemManagementService.php`의 `buildBomNode()` 메서드 - -``` -핵심 로직: -1. BOM 항목에 category 필드가 있으면 카테고리별 그룹화 -2. 가상 카테고리 노드(item_type='CAT') 생성 -3. 각 카테고리 노드 하위에 실제 PT 품목 배치 -``` - -**변경 대상**: API의 BOM 트리 응답 엔드포인트 (또는 React에서 프론트엔드 그룹화) - -### 2.2 프론트엔드 (React) - -**대상 페이지**: `/production/screen-production/{code}?mode=view&type=FG&id={id}` - -**변경 방식 (2가지 선택지)**: - -| 방식 | 설명 | 장점 | 단점 | -|------|------|------|------| -| A. API 응답 변경 | API에서 3단계 트리 반환 | React 수정 최소화 | API 하위 호환성 확인 필요 | -| B. React 그룹화 | BOM 데이터의 category 필드로 프론트에서 그룹화 | API 변경 불필요 | React 컴포넌트 수정 필요 | - -**권장: B 방식** — BOM 데이터에 이미 `category`가 있으므로 React에서 그룹화하는 것이 API 영향 없이 안전하다. - ---- - -## 3. BOM 데이터 구조 - -`items.bom` JSON 필드에 저장된 카테고리 정보: +### 3.1 item.bom 배열 예시 ```json [ - {"child_item_id": 15657, "child_item_code": "EST-RAW-슬랫-방화", "quantity": 10.65, "unit": "m²", "category": "주자재"}, - {"child_item_id": 15627, "child_item_code": "EST-MOTOR-220V-150K", "quantity": 1, "unit": "EA", "category": "모터"}, - {"child_item_id": 15578, "child_item_code": "BD-케이스-500*380", "quantity": 3.22, "unit": "m", "category": "절곡품"}, - ... + { + "child_item_id": 15657, + "child_item_code": "EST-RAW-슬랫-방화", + "quantity": 10.65, + "unit": "m²", + "category": "주자재" + }, + { + "child_item_id": 15627, + "child_item_code": "EST-MOTOR-220V-150K", + "quantity": 1, + "unit": "EA", + "category": "모터" + }, + { + "child_item_id": 15578, + "child_item_code": "BD-케이스-500*380", + "quantity": 3.22, + "unit": "m", + "category": "절곡품" + } ] ``` -`category` 필드 값: `주자재`, `모터`, `제어기`, `절곡품`, `부자재`, `검사비` +### 3.2 category 값 목록 (순서대로) + +| category 값 | 설명 | 대표 아이템 | +|-------------|------|------------| +| `주자재` | 주요 원자재 | 스크린 실리카 | +| `모터` | 모터류 | 모터 150K, 400K | +| `제어기` | 제어 장치 | 제어기 노출형/매입형 | +| `절곡품` | 절곡 가공 부품 | 케이스, 가이드레일, L-BAR | +| `부자재` | 보조 자재 | 샤프트, 각파이프, 앵글 | +| `검사비` | 검사 비용 | 검사비 | --- -## 4. 관련 파일 +## 4. 구현 가이드 -| 프로젝트 | 파일 | 역할 | -|---------|------|------| -| MNG | `app/Services/ItemManagementService.php` | 3단계 트리 구현 (참고용) | -| React | BOM 트리 컴포넌트 | 개선 대상 | -| API | BOM 조회 엔드포인트 | 방식 A 선택 시 변경 | +### 4.1 그룹화 로직 (핵심) + +```typescript +// item.bom 배열을 category별로 그룹화 +const groupedBom = useMemo(() => { + if (!item.bom || item.bom.length === 0) return {}; + + const groups: Record = {}; + for (const line of item.bom) { + const cat = line.category || '기타'; + if (!groups[cat]) groups[cat] = []; + groups[cat].push(line); + } + return groups; +}, [item.bom]); + +// 카테고리 순서 보장 (선택) +const categoryOrder = ['주자재', '모터', '제어기', '절곡품', '부자재', '검사비']; +const sortedCategories = Object.keys(groupedBom).sort((a, b) => { + const ia = categoryOrder.indexOf(a); + const ib = categoryOrder.indexOf(b); + return (ia === -1 ? 999 : ia) - (ib === -1 ? 999 : ib); +}); +``` + +### 4.2 접힘/펼침 상태 관리 + +```typescript +// 카테고리별 접힘 상태 (기본: 모두 펼침) +const [collapsedCategories, setCollapsedCategories] = useState>(new Set()); + +const toggleCategory = (category: string) => { + setCollapsedCategories(prev => { + const next = new Set(prev); + if (next.has(category)) next.delete(category); + else next.add(category); + return next; + }); +}; +``` + +### 4.3 렌더링 구조 + +```tsx +{sortedCategories.map(category => { + const items = groupedBom[category]; + const isCollapsed = collapsedCategories.has(category); + + return ( +
+ {/* 카테고리 헤더 (클릭으로 접기/펼치기) */} +
toggleCategory(category)} + > + {isCollapsed ? : } + {category} + {items.length}건 +
+ + {/* 품목 리스트 (접힌 상태면 숨김) */} + {!isCollapsed && ( + + + {items.map((line, index) => ( + + + {line.childItemCode} + + {line.childItemName} + {line.quantity} + {line.unit} + + ))} + +
+ )} +
+ ); +})} +``` + +--- + +## 5. 수정 대상 파일 + +| 파일 | 수정 내용 | +|------|----------| +| `src/components/items/ItemDetailClient.tsx` | 557~611줄 BOM 섹션을 카테고리 그룹 UI로 교체 | + +### 5.1 현재 코드 (교체 대상: 557~611줄) + +```tsx +{/* BOM 정보 - 절곡 부품은 제외 */} +{(item.itemType === 'FG' || (item.itemType === 'PT' && item.partType !== 'BENDING')) && item.bom && item.bom.length > 0 && ( + + +
+ + + 부품 구성 (BOM) + + + 총 {item.bom.length}개 품목 + +
+
+ +
+ + ...플랫 테이블... +
+
+
+
+)} +``` + +--- + +## 6. 참고: MNG 구현 (동작 확인 가능) + +MNG(admin.codebridge-x.com) > 품목관리에서 FG 품목 선택 시 BOM 탭에서 3단계 트리를 확인할 수 있다. + +**MNG 구현 파일:** +- `mng/app/Services/ItemManagementService.php` — `buildBomNode()` 메서드 +- `mng/resources/views/item-management/index.blade.php` — `renderBomTree()` JS 함수 + +**MNG 핵심 로직:** BOM 데이터의 `category` 필드가 있으면 가상 카테고리 노드(`item_type='CAT'`)를 생성하여 중간 계층으로 삽입한다. React에서는 이를 프론트엔드에서 처리하면 된다. + +--- + +## 7. 체크리스트 + +- [ ] `item.bom` 배열의 `category` 필드로 그룹화 +- [ ] 카테고리별 접힘/펼침 토글 구현 +- [ ] 카테고리 헤더에 건수 Badge 표시 +- [ ] `category` 없는 항목은 "기타"로 분류 +- [ ] 절곡품 Badge 표시 유지 (`line.isBending`) +- [ ] 기존 BOM 조건 유지 (FG 또는 PT 비절곡품만 표시) ---