feat: 품목관리 동적 렌더링 시스템 구현
- DynamicItemForm 컴포넌트 구조 생성 - DynamicField: 필드 타입별 렌더링 - DynamicSection: 섹션 단위 렌더링 - DynamicFormRenderer: 페이지 전체 렌더링 - 필드 타입별 컴포넌트 (TextField, NumberField, DropdownField, CheckboxField, DateField, FileField, CustomField) - 커스텀 훅 (useDynamicFormState, useFormStructure, useConditionalFields) - DataTable 공통 컴포넌트 (테이블, 페이지네이션, 검색, 탭필터, 통계카드) - ItemFormWrapper: Feature Flag 기반 폼 선택 - 타입 정의 및 문서화 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -39,7 +39,8 @@ claudedocs/
|
||||
|
||||
| 파일 | 설명 |
|
||||
|------|------|
|
||||
| `[API-REQUEST-2025-11-28] dynamic-page-rendering-api.md` | ⭐ **최신** - 동적 페이지 렌더링 API 요청서 |
|
||||
| `[PLAN-2025-11-28] dynamic-item-form-implementation.md` | ✅ **Phase 1-6 완료** - 품목관리 동적 렌더링 구현 (타입, 훅, 필드, 렌더러, 메인폼, Feature Flag) |
|
||||
| `[API-REQUEST-2025-11-28] dynamic-page-rendering-api.md` | ⭐ **v3.1** - 동적 페이지 렌더링 API 요청서 (ID 기반 통일) |
|
||||
| `[PLAN-2025-11-27] item-form-component-separation.md` | ✅ **완료** - ItemForm 컴포넌트 분리 (1607→415줄, 74% 감소) |
|
||||
| `[IMPL-2025-11-27] realtime-sync-fixes.md` | 실시간 동기화 수정 (BOM, 섹션 복제, 항목 수정, **페이지 삭제 시 섹션 동기화** 2025-11-28) |
|
||||
| `item-master-api-pending-tasks.md` | 진행중인 API 연동 작업 |
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
# 품목관리 페이지 동적 렌더링 구현 계획
|
||||
|
||||
## 작업 일자: 2025-11-28
|
||||
|
||||
## 문서 버전
|
||||
| 버전 | 날짜 | 작성자 | 내용 |
|
||||
|------|------|--------|------|
|
||||
| 1.0 | 2025-11-28 | Claude | 초안 작성 |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 목표
|
||||
|
||||
**현재**: 하드코딩된 품목 유형별 폼 (ProductForm, PartForm, MaterialForm 등)
|
||||
**목표**: API 기반 동적 폼 렌더링 (품목기준관리에서 설정한 구조대로 자동 생성)
|
||||
|
||||
### 기대 효과
|
||||
- 코드 수정 없이 폼 구조 변경 가능
|
||||
- 품목 유형별 필드 추가/삭제가 관리자 UI에서 가능
|
||||
- 새로운 품목 유형 추가 시 개발 공수 대폭 감소
|
||||
- 일관된 폼 렌더링 로직으로 유지보수 용이
|
||||
|
||||
---
|
||||
|
||||
## 📊 현재 구조 분석
|
||||
|
||||
### 현재 파일 구조
|
||||
```
|
||||
src/components/items/ItemForm/
|
||||
├── index.tsx (415줄) ← 메인 컴포넌트
|
||||
├── constants.ts
|
||||
├── types.ts
|
||||
├── ValidationAlert.tsx
|
||||
├── FormHeader.tsx
|
||||
├── BendingDiagramSection.tsx
|
||||
├── BOMSection.tsx
|
||||
├── context/
|
||||
├── hooks/
|
||||
│ └── useItemFormState.ts (364줄) ← 25+ useState 통합
|
||||
└── forms/
|
||||
├── ProductForm.tsx (FG)
|
||||
├── MaterialForm.tsx (RM/SM/CS)
|
||||
├── PartForm.tsx (PT)
|
||||
└── parts/
|
||||
├── AssemblyPartForm.tsx
|
||||
├── BendingPartForm.tsx
|
||||
└── PurchasedPartForm.tsx
|
||||
```
|
||||
|
||||
### 현재 렌더링 방식 (하드코딩)
|
||||
```typescript
|
||||
// index.tsx:234-321
|
||||
{selectedItemType === 'FG' && <ProductForm ... />}
|
||||
{selectedItemType === 'PT' && <PartForm ... />}
|
||||
{(selectedItemType === 'RM' || selectedItemType === 'SM' || selectedItemType === 'CS') &&
|
||||
<MaterialForm ... />}
|
||||
```
|
||||
|
||||
### 문제점
|
||||
1. **품목 유형별 하드코딩**: 새 유형 추가 시 코드 수정 필요
|
||||
2. **필드 변경 시 개발 필요**: 관리자가 직접 변경 불가
|
||||
3. **조건부 렌더링 복잡**: PT의 ASSEMBLY/BENDING/PURCHASED 분기가 복잡
|
||||
4. **상태 관리 복잡**: 25+ useState가 하드코딩된 필드에 종속
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ 동적 렌더링 아키텍처
|
||||
|
||||
### 목표 파일 구조
|
||||
```
|
||||
src/components/items/
|
||||
├── DynamicItemForm/ # 🆕 신규 생성
|
||||
│ ├── index.tsx # 메인 동적 폼
|
||||
│ ├── DynamicFormRenderer.tsx # 섹션/필드 렌더러
|
||||
│ ├── DynamicSection.tsx # 동적 섹션 컴포넌트
|
||||
│ ├── DynamicField.tsx # 동적 필드 컴포넌트
|
||||
│ ├── fields/ # 필드 타입별 컴포넌트
|
||||
│ │ ├── TextField.tsx
|
||||
│ │ ├── DropdownField.tsx
|
||||
│ │ ├── NumberField.tsx
|
||||
│ │ ├── DateField.tsx
|
||||
│ │ ├── FileField.tsx
|
||||
│ │ ├── CheckboxField.tsx
|
||||
│ │ └── CustomField.tsx # 특수 컴포넌트 (BOM, 전개도 등)
|
||||
│ ├── hooks/
|
||||
│ │ ├── useFormStructure.ts # API에서 폼 구조 로드
|
||||
│ │ ├── useDynamicFormState.ts # 동적 상태 관리
|
||||
│ │ └── useConditionalFields.ts # 조건부 필드 로직
|
||||
│ └── types.ts # 타입 정의
|
||||
├── ItemForm/ # 기존 (백업/하이브리드용)
|
||||
└── ItemFormLegacy/ # 마이그레이션 완료 후 이동
|
||||
```
|
||||
|
||||
### 데이터 흐름
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 품목기준관리 │
|
||||
│ (Pages → Sections → Fields 구조 정의) │
|
||||
└───────────────────────────┬─────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ GET /api/v1/item-master/form-structure/{item_type} │
|
||||
│ - page 정보 │
|
||||
│ - sections[] (순서, 접기 가능 여부) │
|
||||
│ - fields[] (타입, 필수 여부, 옵션) │
|
||||
│ - bom_config (BOM 섹션인 경우) │
|
||||
│ - conditional_sections (조건부 렌더링) │
|
||||
└───────────────────────────┬─────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ DynamicItemForm │
|
||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||
│ │ useFormStructure(itemType) │ │
|
||||
│ │ - formStructure: { page, sections, conditionalSections } │ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||
│ │ DynamicFormRenderer │ │
|
||||
│ │ - sections.map(section => <DynamicSection />) │ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||
│ │ DynamicSection │ │
|
||||
│ │ - Card 렌더링 (collapsible, default_open) │ │
|
||||
│ │ - fields.map(field => <DynamicField />) │ │
|
||||
│ │ - BOM 섹션인 경우 → <BOMSection /> │ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||
│ │ DynamicField │ │
|
||||
│ │ - field_type에 따라 적절한 컴포넌트 렌더링 │ │
|
||||
│ │ - textbox → <TextField /> │ │
|
||||
│ │ - dropdown → <DropdownField /> │ │
|
||||
│ │ - file → <FileField /> │ │
|
||||
│ │ - custom → <CustomField /> (전개도, BOM 등) │ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 구현 체크리스트
|
||||
|
||||
### Phase 1: 기반 작업 (API 프록시 + 타입) ✅ 완료 (2025-11-28)
|
||||
- [x] Next.js API 프록시 라우트 → 기존 catch-all 프록시 활용 (`/api/proxy/[...path]/route.ts`)
|
||||
- [x] 동적 폼 구조 타입 정의 → `DynamicItemForm/types.ts`
|
||||
- [x] `FormStructure`, `DynamicSection`, `DynamicField`, `ConditionalSection` 등
|
||||
|
||||
### Phase 2: 폼 구조 로딩 훅 ✅ 완료 (2025-11-28)
|
||||
- [x] `useFormStructure.ts` 구현
|
||||
- [x] API 호출 로직 + Mock 데이터 폴백
|
||||
- [x] 로딩/에러 상태 관리
|
||||
- [x] 캐싱 전략 (5분 TTL)
|
||||
- [x] `useConditionalFields.ts` 구현
|
||||
- [x] 조건부 섹션/필드 표시 로직
|
||||
- [x] 의존성 필드 값 변경 감지
|
||||
|
||||
### Phase 3: 동적 필드 컴포넌트 ✅ 완료 (2025-11-28)
|
||||
- [x] 기본 필드 컴포넌트
|
||||
- [x] `TextField.tsx` (textbox, textarea)
|
||||
- [x] `DropdownField.tsx` (dropdown, searchable-dropdown)
|
||||
- [x] `NumberField.tsx` (number, currency)
|
||||
- [x] `DateField.tsx` (date)
|
||||
- [x] `CheckboxField.tsx` (checkbox, switch)
|
||||
- [x] `FileField.tsx` (file upload)
|
||||
- [x] 특수 필드 컴포넌트
|
||||
- [x] `CustomField.tsx` (플레이스홀더 - 기존 컴포넌트 통합 예정)
|
||||
|
||||
### Phase 4: 동적 폼 렌더러 ✅ 완료 (2025-11-28)
|
||||
- [x] `DynamicField.tsx` 구현 (field_type → 컴포넌트 매핑)
|
||||
- [x] `DynamicSection.tsx` 구현 (Card, Collapsible, 그리드 레이아웃)
|
||||
- [x] `DynamicFormRenderer.tsx` 구현 (섹션 렌더링, 조건부 처리)
|
||||
|
||||
### Phase 5: 메인 폼 컴포넌트 ✅ 완료 (2025-11-28)
|
||||
- [x] `DynamicItemForm/index.tsx` 구현
|
||||
- [x] 커스텀 상태 관리 (react-hook-form 대신 useDynamicFormState)
|
||||
- [x] 동적 유효성 검증
|
||||
- [x] 폼 제출 로직
|
||||
- [x] `useDynamicFormState.ts` 구현
|
||||
- [x] 동적 상태 관리
|
||||
- [x] 필드 값 변경 핸들러
|
||||
- [x] 유효성 검증 로직
|
||||
|
||||
### Phase 6: 기존 폼 통합/마이그레이션 ✅ 완료 (2025-11-28)
|
||||
- [x] 기존 ItemForm과 DynamicItemForm 하이브리드 전환
|
||||
- [x] Feature flag로 전환 가능하게 (`ItemFormWrapper.tsx`)
|
||||
- [x] 품목 유형별 점진적 마이그레이션 (`ENABLED_ITEM_TYPES`)
|
||||
- [x] 개발 모드 토글 UI (localStorage 오버라이드)
|
||||
- [x] 기존 특수 컴포넌트 재사용 (CustomField.tsx에 통합)
|
||||
- [x] DrawingCanvasSimple (파일 업로드 기반)
|
||||
- [x] BOMTable (품목 구성 관리)
|
||||
- [x] BendingDetailTable (전개도 상세 입력 + 자동 계산)
|
||||
- [ ] 페이지 라우트 업데이트 (API 구현 후 진행)
|
||||
- [ ] `/items/create/page.tsx`
|
||||
- [ ] `/items/[id]/edit/page.tsx`
|
||||
|
||||
### Phase 7: 테스트 및 검증
|
||||
- [ ] 품목 유형별 테스트
|
||||
- [ ] FG (제품) 등록/수정 테스트
|
||||
- [ ] PT (부품) 등록/수정 테스트
|
||||
- [ ] RM/SM/CS 등록/수정 테스트
|
||||
- [ ] 조건부 필드 테스트
|
||||
- [ ] PT → ASSEMBLY/BENDING/PURCHASED 분기
|
||||
- [ ] BOM 섹션 조건부 표시
|
||||
- [ ] 에러 처리 테스트
|
||||
- [ ] API 실패 시 폴백
|
||||
- [ ] 유효성 검증 에러 표시
|
||||
|
||||
---
|
||||
|
||||
## 📋 API 응답 구조 (참조용)
|
||||
|
||||
### GET /api/v1/item-master/form-structure/{item_type}
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"page": {
|
||||
"id": 1,
|
||||
"page_name": "제품 등록",
|
||||
"item_type": "FG"
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"id": 101,
|
||||
"title": "기본 정보",
|
||||
"section_type": "BASIC",
|
||||
"order_no": 1,
|
||||
"is_collapsible": false,
|
||||
"is_default_open": true,
|
||||
"fields": [
|
||||
{
|
||||
"id": 1001,
|
||||
"field_name": "품목명",
|
||||
"field_key": "item_name",
|
||||
"field_type": "textbox",
|
||||
"order_no": 1,
|
||||
"is_required": true,
|
||||
"placeholder": "품목명을 입력하세요",
|
||||
"validation_rules": { "maxLength": 100 },
|
||||
"grid_row": 1,
|
||||
"grid_col": 1,
|
||||
"grid_span": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 103,
|
||||
"title": "부품 구성 (BOM)",
|
||||
"section_type": "BOM",
|
||||
"order_no": 3,
|
||||
"bom_config": {
|
||||
"columns": [...],
|
||||
"allow_search": true,
|
||||
"search_endpoint": "/api/v1/items/search"
|
||||
}
|
||||
}
|
||||
],
|
||||
"conditional_sections": [
|
||||
{
|
||||
"condition": {
|
||||
"field_key": "needs_bom",
|
||||
"operator": "equals",
|
||||
"value": true
|
||||
},
|
||||
"show_sections": [103]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 필드 타입 → 컴포넌트 매핑
|
||||
|
||||
| field_type | 컴포넌트 | 설명 |
|
||||
|------------|----------|------|
|
||||
| `textbox` | TextField | 단일 텍스트 입력 |
|
||||
| `textarea` | TextField (multiline) | 여러 줄 텍스트 |
|
||||
| `dropdown` | DropdownField | 선택 목록 |
|
||||
| `searchable-dropdown` | DropdownField (searchable) | 검색 가능 선택 |
|
||||
| `number` | NumberField | 숫자 입력 |
|
||||
| `currency` | NumberField (currency) | 통화 입력 |
|
||||
| `date` | DateField | 날짜 선택 |
|
||||
| `date-range` | DateField (range) | 기간 선택 |
|
||||
| `checkbox` | CheckboxField | 체크박스 |
|
||||
| `switch` | CheckboxField (switch) | 토글 스위치 |
|
||||
| `file` | FileField | 파일 업로드 |
|
||||
| `custom:drawing-canvas` | DrawingCanvas | 전개도 그리기 |
|
||||
| `custom:bending-detail-table` | BendingDetailTable | 전개도 상세 입력 |
|
||||
| `custom:bom-table` | BOMSection | BOM 관리 테이블 |
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 주의사항
|
||||
|
||||
### 1. 기존 기능 보존
|
||||
- 품목코드 자동 생성 로직 유지
|
||||
- 전개도 폭 합계 자동 계산 유지
|
||||
- BOM 검색/추가 기능 유지
|
||||
|
||||
### 2. 점진적 마이그레이션
|
||||
- Feature flag로 신/구 폼 전환 가능하게
|
||||
- 문제 발생 시 즉시 롤백 가능
|
||||
- 품목 유형별로 순차 적용 (FG → PT → RM/SM/CS)
|
||||
|
||||
### 3. 성능 고려
|
||||
- 폼 구조 캐싱 (5분 TTL)
|
||||
- 동적 스키마 생성 최적화
|
||||
- 불필요한 리렌더링 방지
|
||||
|
||||
### 4. 백엔드 의존성
|
||||
- API 미구현 시 Mock 데이터로 개발
|
||||
- API 응답 형식 변경 시 transformer 레이어에서 처리
|
||||
|
||||
---
|
||||
|
||||
## 📅 예상 일정
|
||||
|
||||
| Phase | 작업 | 예상 작업량 |
|
||||
|-------|------|------------|
|
||||
| Phase 1 | API 프록시 + 타입 | 작음 |
|
||||
| Phase 2 | 폼 구조 로딩 훅 | 중간 |
|
||||
| Phase 3 | 동적 필드 컴포넌트 | 중간 |
|
||||
| Phase 4 | 동적 폼 렌더러 | 중간 |
|
||||
| Phase 5 | 메인 폼 컴포넌트 | 큼 |
|
||||
| Phase 6 | 마이그레이션 | 큼 |
|
||||
| Phase 7 | 테스트 | 중간 |
|
||||
|
||||
---
|
||||
|
||||
## 관련 문서
|
||||
|
||||
- `[API-REQUEST-2025-11-28] dynamic-page-rendering-api.md` - API 명세
|
||||
- `[PLAN-2025-11-27] item-form-component-separation.md` - 기존 컴포넌트 분리
|
||||
- `src/contexts/ItemMasterContext.tsx` - 품목기준관리 Context
|
||||
|
||||
---
|
||||
|
||||
## 변경 이력
|
||||
|
||||
| 날짜 | 버전 | 변경 내용 |
|
||||
|------|------|----------|
|
||||
| 2025-11-28 | 1.0 | 초안 작성 |
|
||||
Reference in New Issue
Block a user