# BOM Tree 시각화 React 구현 요청 **날짜:** 2026-03-18 **요청자:** R&D실 (백엔드) **대상:** React 프론트엔드 **API 상태:** 구현 완료 (tree 엔드포인트 존재) --- ## 변경 요약 품목관리 또는 품목기준관리에서 **BOM(Bill of Materials) 트리를 시각적으로 표시**하는 UI를 추가한다. --- ## 1. 배경 ### 현재 상태 | 시스템 | BOM Tree UI | 비고 | |--------|:----------:|------| | MNG (Laravel) | ✅ 구현됨 | 3-panel: 검색 + 트리 + 상세 | | 서비스 (React) | ❌ 미구현 | API는 있으나 UI 없음 | ### MNG 참고 화면 ``` MNG 품목관리 (item-management) ┌──────────┬──────────────────────────┬──────────────┐ │ 품목 검색 │ BOM 트리 시각화 │ 품목 상세 │ │ │ │ │ │ 🔍 검색 │ ▼ [FG] 제품A │ 코드: P-001 │ │ │ ├─ [PT] 부품B (x2) │ 이름: 제품A │ │ · P-001 │ │ └─ [RM] SUS 철판 (x5)│ 유형: 완제품 │ │ · P-002 │ ├─ [SM] 볼트 M8 (x10) │ │ │ │ └─ [RM] EGI 판재 (x3) │ │ └──────────┴──────────────────────────┴──────────────┘ ``` --- ## 2. 구현 위치 ### 품목관리 > 품목 상세에 BOM 탭 추가 ``` 품목관리 (/master-data/item-management) └─ 품목 상세 (/master-data/item-management/{id}) ├─ 기본정보 탭 (기존) ├─ BOM Tree 탭 ← 신규 추가 └─ 기타 탭 (기존) ``` > 새 메뉴 생성 불필요. 기존 품목 상세 화면에 탭 하나 추가. > BOM 데이터가 없는 품목(원자재 등)은 "BOM 정보가 없습니다" 표시. --- ## 3. BOM Tree UI 명세 ### 3.1 트리 노드 구조 ``` ▼ [FG] P-001 | 제품A x1 ├─ ▼ [PT] PT-001 | 가이드레일 조립품 x2 │ ├─ [RM] 20000 | SUS1.2*1219*2438 x5 │ ├─ [SM] 80067 | 가스켓쫄대(삼각) x10 │ └─ [SM] 80061 | 8인치후렌지 x2 ├─ [RM] 30000 | EGI1.2*1219*2438 x3 └─ [SM] 00043 | 불연지퍼 x20 ``` ### 3.2 노드 요소 | 요소 | 설명 | |------|------| | ▼/▶ | 펼침/접힘 토글 (자식 있을 때만) | | 유형 뱃지 | `[FG]` `[PT]` `[RM]` `[SM]` `[CS]` — 색상 구분 | | 코드 | 품목코드 (monospace) | | 품목명 | 품목 이름 | | 수량 | `x2`, `x5` — 부모 대비 필요 수량 | ### 3.3 유형별 뱃지 색상 | 유형 | 코드 | 색상 | 설명 | |------|------|------|------| | 완제품 | FG | 🔵 blue | 최상위 제품 | | 부품 | PT | 🟢 green | 조립 부품 (자식 가질 수 있음) | | 원자재 | RM | 🟠 orange | 미가공 원자재 (leaf) | | 부자재 | SM | 🟣 purple | 부자재 (leaf) | | 소모품 | CS | ⚪ gray | 소모품 (leaf) | ### 3.4 인터랙션 | 동작 | 설명 | |------|------| | 노드 펼침/접힘 | 화살표 클릭 시 자식 노드 토글 | | 전체 펼침 | 버튼 클릭 시 모든 노드 펼침 | | 전체 접힘 | 버튼 클릭 시 루트만 표시 | | 노드 클릭 | (선택) 해당 품목 상세 정보 표시 또는 품목 상세 페이지 이동 | --- ## 4. API 스펙 (구현 완료) ### 4.1 Items BOM Tree ``` GET /api/v1/items/{id}/bom/tree ``` **응답:** ```json { "success": true, "data": { "id": 100, "code": "P-001", "name": "제품A", "item_type": "FG", "specification": "...", "unit": "EA", "quantity": 1, "depth": 0, "children": [ { "id": 200, "code": "PT-001", "name": "가이드레일 조립품", "item_type": "PT", "quantity": 2, "depth": 1, "children": [ { "id": 300, "code": "20000", "name": "SUS1.2*1219*2438", "item_type": "RM", "quantity": 5, "depth": 2, "children": [] } ] }, { "id": 400, "code": "00043", "name": "불연지퍼", "item_type": "SM", "quantity": 20, "depth": 1, "children": [] } ] } } ``` ### 4.2 Products BOM Tree (제품 기반) ``` GET /api/v1/products/{id}/bom ``` **응답 구조 동일** — `type` 필드가 `PRODUCT` 또는 `MATERIAL`로 구분. ### 4.3 BOM Flat List (참고) ``` GET /api/v1/items/{id}/bom → 1단계 자식만 (flat) GET /api/v1/items/{id}/bom/tree → 전체 계층 (recursive) ``` --- ## 5. TypeScript 타입 ```typescript interface BomTreeNode { id: number; code: string; name: string; item_type: 'FG' | 'PT' | 'RM' | 'SM' | 'CS'; specification?: string; unit?: string; quantity: number; depth: number; children: BomTreeNode[]; } // 유형별 뱃지 색상 매핑 const ITEM_TYPE_COLORS: Record = { FG: 'bg-blue-100 text-blue-800', PT: 'bg-green-100 text-green-800', RM: 'bg-orange-100 text-orange-800', SM: 'bg-purple-100 text-purple-800', CS: 'bg-gray-100 text-gray-800', }; const ITEM_TYPE_LABELS: Record = { FG: '완제품', PT: '부품', RM: '원자재', SM: '부자재', CS: '소모품', }; ``` --- ## 6. 컴포넌트 구조 제안 ``` BomTreeViewer/ ├─ BomTreeViewer.tsx # 메인 컨테이너 (API 호출 + 상태 관리) ├─ BomTreeNode.tsx # 개별 노드 (재귀 렌더링) ├─ BomTreeToolbar.tsx # 전체 펼침/접힘 버튼 └─ types.ts # BomTreeNode 타입 ``` ### 재귀 렌더링 핵심 ```tsx function BomTreeNode({ node, level = 0 }: { node: BomTreeNode; level?: number }) { const [isOpen, setIsOpen] = useState(level < 2); // 2단계까지 기본 펼침 const hasChildren = node.children.length > 0; return (
{/* 펼침/접힘 */} {hasChildren ? ( ) : } {/* 유형 뱃지 */} {node.item_type} {/* 코드 + 이름 */} {node.code} {node.name} {/* 수량 */} x{node.quantity}
{/* 재귀: 자식 노드 */} {isOpen && hasChildren && node.children.map(child => ( ))}
); } ``` --- ## 7. Server Action 예시 ```typescript // actions.ts export async function getBomTree(itemId: number) { return fetchApi(`/items/${itemId}/bom/tree`); } ``` --- ## 작업 체크리스트 - [ ] `BomTreeViewer` 컴포넌트 생성 (재귀 트리 렌더링) - [ ] 유형별 뱃지 색상 적용 (FG/PT/RM/SM/CS) - [ ] 펼침/접힘 토글 구현 - [ ] 전체 펼침/접힘 버튼 - [ ] `GET /api/v1/items/{id}/bom/tree` 연동 - [ ] 품목 상세 또는 독립 페이지에 배치 - [ ] (선택) 노드 클릭 시 품목 상세 이동 --- ## 참고 - MNG BOM Tree 구현: `mng/resources/views/item-management/index.blade.php` (라인 248-316) - API Tree 빌더: `api/app/Services/Products/ProductComponentResolver.php` - Items BOM API: `api/app/Http/Controllers/Api/V1/ItemsBomController.php`