298 lines
8.4 KiB
Markdown
298 lines
8.4 KiB
Markdown
# BOM 트리 3단계 구조 — React 구현 요청
|
|
|
|
> **작성일**: 2026-03-18
|
|
> **요청자**: API/MNG 개발팀
|
|
> **대상**: React 프론트엔드
|
|
> **상태**: API 구현 완료, React 구현 대기
|
|
|
|
---
|
|
|
|
## 1. 개요
|
|
|
|
품목관리 > 제품상세 화면의 BOM 표시를 플랫 테이블에서 **3단계 트리**(FG → 카테고리 → PT)로 개선한다.
|
|
**API가 이미 3단계 구조를 반환**하므로, React는 렌더링만 변경하면 된다.
|
|
|
|
### 1.1 현재 상태
|
|
|
|
| 항목 | 상태 |
|
|
|------|------|
|
|
| **API BOM 트리 3단계** | ✅ 완료 (`GET /api/v1/items/{id}/bom/tree`) |
|
|
| **API BOM 목록 category 필드** | ✅ 완료 (`GET /api/v1/items/{id}/bom`) |
|
|
| **MNG BOM 트리 3단계** | ✅ 완료 (참고용) |
|
|
| **React BOM 표시** | ❌ 플랫 테이블 (개선 필요) |
|
|
|
|
### 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
|
|
|
|
### 현재 (플랫 테이블)
|
|
|
|
```
|
|
부품 구성 (BOM) 총 17개 품목
|
|
┌────┬──────────────────────┬──────────────────┬────────┬──────┐
|
|
│ 번호 │ 품목코드 │ 품목명 │ 수량 │ 단위 │
|
|
├────┼──────────────────────┼──────────────────┼────────┼──────┤
|
|
│ 1 │ EST-RAW-슬랫-방화 │ 스크린 실리카 │ +10.65 │ m² │
|
|
│ 2 │ EST-MOTOR-220V-150K │ 모터 150K(S) │ x1 │ EA │
|
|
│ .. │ ...17건 나열... │ │ │ │
|
|
└────┴──────────────────────┴──────────────────┴────────┴──────┘
|
|
```
|
|
|
|
### 목표 (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
|
|
│ ...
|
|
▶ 부자재 (4건) ← 접힌 상태
|
|
▼ 검사비 (1건)
|
|
│ EST-INSPECTION 검사비 x1 EA
|
|
```
|
|
|
|
---
|
|
|
|
## 3. API 응답 구조 (구현 완료)
|
|
|
|
### 3.1 BOM 트리 엔드포인트
|
|
|
|
```
|
|
GET /api/v1/items/{id}/bom/tree
|
|
```
|
|
|
|
**응답 예시** (`GET /api/v1/items/15531/bom/tree`):
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"message": "...",
|
|
"data": {
|
|
"id": 15531,
|
|
"code": "FG-KSS02-측면형-SUS",
|
|
"name": "KSS02 스크린 SUS마감 측면형",
|
|
"item_type": "FG",
|
|
"unit": "EA",
|
|
"depth": 1,
|
|
"children": [
|
|
{
|
|
"id": 0,
|
|
"code": "",
|
|
"name": "주자재",
|
|
"item_type": "CAT",
|
|
"unit": "",
|
|
"depth": 2,
|
|
"count": 1,
|
|
"children": [
|
|
{
|
|
"id": 15657,
|
|
"code": "EST-RAW-슬랫-방화",
|
|
"name": "스크린 실리카",
|
|
"item_type": "PT",
|
|
"unit": "EA",
|
|
"depth": 3,
|
|
"quantity": 10.65,
|
|
"children": []
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": 0,
|
|
"code": "",
|
|
"name": "모터",
|
|
"item_type": "CAT",
|
|
"unit": "",
|
|
"depth": 2,
|
|
"count": 1,
|
|
"children": [
|
|
{
|
|
"id": 15627,
|
|
"code": "EST-MOTOR-220V-150K",
|
|
"name": "모터 150K(S) (220V)",
|
|
"item_type": "PT",
|
|
"unit": "EA",
|
|
"depth": 3,
|
|
"quantity": 1,
|
|
"children": []
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": 0,
|
|
"name": "절곡품",
|
|
"item_type": "CAT",
|
|
"count": 9,
|
|
"children": [ "...9개 PT 품목..." ]
|
|
},
|
|
{
|
|
"id": 0,
|
|
"name": "부자재",
|
|
"item_type": "CAT",
|
|
"count": 4,
|
|
"children": [ "...4개 PT 품목..." ]
|
|
},
|
|
{
|
|
"id": 0,
|
|
"name": "검사비",
|
|
"item_type": "CAT",
|
|
"count": 1,
|
|
"children": [ "...1개 PT 품목..." ]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3.2 핵심 구조
|
|
|
|
| 필드 | 설명 |
|
|
|------|------|
|
|
| `item_type: "CAT"` | 카테고리 그룹 노드 (가상 노드, id=0) |
|
|
| `item_type: "FG"/"PT"` | 실제 품목 노드 |
|
|
| `count` | CAT 노드에만 존재 — 하위 품목 건수 |
|
|
| `quantity` | 품목 노드에만 존재 — 소요 수량 |
|
|
| `children` | 하위 노드 배열 (재귀) |
|
|
|
|
### 3.3 BOM 목록 엔드포인트 (category 필드 포함)
|
|
|
|
```
|
|
GET /api/v1/items/{id}/bom
|
|
```
|
|
|
|
각 항목에 `category` 필드가 추가되었다:
|
|
|
|
```json
|
|
[
|
|
{
|
|
"child_item_id": 15657,
|
|
"child_item_code": "EST-RAW-슬랫-방화",
|
|
"child_item_name": "스크린 실리카",
|
|
"child_item_type": "PT",
|
|
"unit": "m²",
|
|
"quantity": 10.65,
|
|
"category": "주자재"
|
|
}
|
|
]
|
|
```
|
|
|
|
---
|
|
|
|
## 4. React 구현 가이드
|
|
|
|
### 4.1 방법 A: BOM 트리 API 활용 (권장)
|
|
|
|
`/api/v1/items/{id}/bom/tree`를 호출하여 3단계 구조를 그대로 렌더링한다.
|
|
|
|
```typescript
|
|
// 1. BOM 트리 데이터 로드
|
|
const [bomTree, setBomTree] = useState(null);
|
|
|
|
useEffect(() => {
|
|
if (item?.id) {
|
|
fetch(`/api/proxy/items/${item.id}/bom/tree`)
|
|
.then(res => res.json())
|
|
.then(data => setBomTree(data.data));
|
|
}
|
|
}, [item?.id]);
|
|
|
|
// 2. 재귀 렌더링
|
|
function BomTreeNode({ node }) {
|
|
const [isOpen, setIsOpen] = useState(true);
|
|
const isCategory = node.item_type === 'CAT';
|
|
|
|
return (
|
|
<div className={isCategory ? '' : 'ml-4'}>
|
|
<div
|
|
className={`flex items-center gap-2 py-1.5 px-2 rounded ${isCategory ? 'bg-gray-50 cursor-pointer hover:bg-gray-100' : 'hover:bg-gray-50'}`}
|
|
onClick={() => isCategory && setIsOpen(!isOpen)}
|
|
>
|
|
{node.children?.length > 0 && (
|
|
isOpen ? <ChevronDown className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />
|
|
)}
|
|
{isCategory ? (
|
|
<>
|
|
<span className="text-sm font-semibold">{node.name}</span>
|
|
<Badge variant="outline">{node.count}건</Badge>
|
|
</>
|
|
) : (
|
|
<>
|
|
<Badge>{node.item_type}</Badge>
|
|
<span className="text-sm">{node.name}</span>
|
|
<code className="text-xs text-gray-400">{node.code}</code>
|
|
{node.quantity && <span className="ml-auto text-blue-600">x{node.quantity}</span>}
|
|
</>
|
|
)}
|
|
</div>
|
|
{isOpen && node.children?.map((child, i) => (
|
|
<BomTreeNode key={child.id || i} node={child} />
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
### 4.2 방법 B: 기존 item.bom의 category로 그룹화
|
|
|
|
API 추가 호출 없이 기존 `item.bom` 데이터의 `category` 필드로 프론트에서 그룹화한다.
|
|
|
|
```typescript
|
|
const groupedBom = useMemo(() => {
|
|
if (!item.bom) return {};
|
|
const groups: Record<string, typeof item.bom> = {};
|
|
for (const line of item.bom) {
|
|
const cat = line.category || '기타';
|
|
if (!groups[cat]) groups[cat] = [];
|
|
groups[cat].push(line);
|
|
}
|
|
return groups;
|
|
}, [item.bom]);
|
|
```
|
|
|
|
> 방법 A가 권장 — API가 구조를 보장하므로 프론트 로직이 단순해진다.
|
|
|
|
---
|
|
|
|
## 5. 수정 대상
|
|
|
|
| 파일 | 수정 내용 |
|
|
|------|----------|
|
|
| `src/components/items/ItemDetailClient.tsx` | 557~611줄 BOM Card 섹션을 트리 UI로 교체 |
|
|
| `src/types/item.ts` (선택) | BOMLine 타입에 `category?: string` 추가 |
|
|
|
|
---
|
|
|
|
## 6. 체크리스트
|
|
|
|
- [ ] BOM 트리 API 호출 (`/api/v1/items/{id}/bom/tree`) 또는 기존 `item.bom`의 category 그룹화
|
|
- [ ] `item_type === 'CAT'` 노드는 카테고리 헤더로 렌더링 (접힘/펼침)
|
|
- [ ] 카테고리 헤더에 건수(`count`) Badge 표시
|
|
- [ ] 품목 노드에 코드, 품목명, 수량, 단위 표시
|
|
- [ ] 기존 BOM 조건 유지 (FG 또는 PT 비절곡품만 표시)
|
|
- [ ] MNG 화면(admin.codebridge-x.com > 품목관리)에서 동작 확인 가능
|
|
|
|
---
|
|
|
|
## 관련 문서
|
|
|
|
- `changes/20260318_item-management-bom-tree.md` — MNG BOM 트리 3단계 구현 완료
|
|
- `rules/item-policy.md` — 품목 정책
|
|
|
|
---
|
|
|
|
**최종 업데이트**: 2026-03-18
|