# BOM 트리 3단계 구조 개선 — React 구현 요청 > **작성일**: 2026-03-18 > **요청자**: API/MNG 개발팀 > **대상**: React 프론트엔드 > **상태**: MNG 구현 완료, React 구현 대기 --- ## 1. 개요 품목관리 > 제품상세 화면의 BOM 트리를 2단계(FG → PT 플랫 리스트)에서 3단계(FG → 카테고리 → PT)로 개선한다. ### 1.1 현재 상태 | 항목 | 상태 | 설명 | |------|------|------| | **BOM 데이터 category 필드** | ✅ 완료 | `items.bom` JSON에 `category` 필드 포함 | | **MNG BOM 트리 3단계** | ✅ 완료 | MNG 품목관리에서 3단계 구조 표시 | | **React BOM 트리** | ❌ 2단계 | 플랫 테이블로 17건 나열 | ### 1.2 대상 페이지 `/production/screen-production/{code}?mode=view&type=FG&id={id}` ### 1.3 대상 컴포넌트 `src/components/items/ItemDetailClient.tsx` — 557~611줄 "부품 구성 (BOM)" 섹션 --- ## 2. 현재 vs 목표 UI ### 현재 (2단계 — 플랫 테이블) ``` 부품 구성 (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단계 — 카테고리 그룹 접힘/펼침) ``` 부품 구성 (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 ``` --- ## 3. BOM 데이터 구조 (API 변경 없음) 현재 API 응답의 `item.bom` 배열에 이미 `category` 필드가 포함되어 있다. **API 수정 불필요** — React에서 `category`로 그룹화하면 된다. ### 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": "절곡품" } ] ``` ### 3.2 category 값 목록 (순서대로) | category 값 | 설명 | 대표 아이템 | |-------------|------|------------| | `주자재` | 주요 원자재 | 스크린 실리카 | | `모터` | 모터류 | 모터 150K, 400K | | `제어기` | 제어 장치 | 제어기 노출형/매입형 | | `절곡품` | 절곡 가공 부품 | 케이스, 가이드레일, L-BAR | | `부자재` | 보조 자재 | 샤프트, 각파이프, 앵글 | | `검사비` | 검사 비용 | 검사비 | --- ## 4. 구현 가이드 ### 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 비절곡품만 표시) --- ## 관련 문서 - `changes/20260318_item-management-bom-tree.md` — MNG BOM 트리 3단계 구현 완료 내역 - `rules/item-policy.md` — 품목 정책 --- **최종 업데이트**: 2026-03-18