DynamicItemForm 개선: - 품목코드 자동생성 기능 추가 - 조건부 표시 로직 개선 - 불필요한 컴포넌트 정리 (DynamicField, DynamicSection 등) - 타입 시스템 단순화 새로운 기능: - Sales 페이지 마이그레이션 (견적관리, 거래처관리) - 공통 컴포넌트 추가 (atoms, molecules, organisms, templates) 문서화: - 구현 문서 및 참조 문서 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
15 KiB
15 KiB
품목관리 페이지 동적 렌더링 구현 계획
작업 일자: 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
현재 렌더링 방식 (하드코딩)
// index.tsx:234-321
{selectedItemType === 'FG' && <ProductForm ... />}
{selectedItemType === 'PT' && <PartForm ... />}
{(selectedItemType === 'RM' || selectedItemType === 'SM' || selectedItemType === 'CS') &&
<MaterialForm ... />}
문제점
- 품목 유형별 하드코딩: 새 유형 추가 시 코드 수정 필요
- 필드 변경 시 개발 필요: 관리자가 직접 변경 불가
- 조건부 렌더링 복잡: PT의 ASSEMBLY/BENDING/PURCHASED 분기가 복잡
- 상태 관리 복잡: 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)
- Next.js API 프록시 라우트 → 기존 catch-all 프록시 활용 (
/api/proxy/[...path]/route.ts) - 동적 폼 구조 타입 정의 →
DynamicItemForm/types.tsFormStructure,DynamicSection,DynamicField,ConditionalSection등
Phase 2: 폼 구조 로딩 훅 ✅ 완료 (2025-11-28)
useFormStructure.ts구현- API 호출 로직 + Mock 데이터 폴백
- 로딩/에러 상태 관리
- 캐싱 전략 (5분 TTL)
useConditionalFields.ts구현- 조건부 섹션/필드 표시 로직
- 의존성 필드 값 변경 감지
Phase 3: 동적 필드 컴포넌트 ✅ 완료 (2025-11-28)
- 기본 필드 컴포넌트
TextField.tsx(textbox, textarea)DropdownField.tsx(dropdown, searchable-dropdown)NumberField.tsx(number, currency)DateField.tsx(date)CheckboxField.tsx(checkbox, switch)FileField.tsx(file upload)
- 특수 필드 컴포넌트
CustomField.tsx(플레이스홀더 - 기존 컴포넌트 통합 예정)
Phase 4: 동적 폼 렌더러 ✅ 완료 (2025-11-28)
DynamicField.tsx구현 (field_type → 컴포넌트 매핑)DynamicSection.tsx구현 (Card, Collapsible, 그리드 레이아웃)DynamicFormRenderer.tsx구현 (섹션 렌더링, 조건부 처리)
Phase 5: 메인 폼 컴포넌트 ✅ 완료 (2025-11-28)
DynamicItemForm/index.tsx구현- 커스텀 상태 관리 (react-hook-form 대신 useDynamicFormState)
- 동적 유효성 검증
- 폼 제출 로직
useDynamicFormState.ts구현- 동적 상태 관리
- 필드 값 변경 핸들러
- 유효성 검증 로직
Phase 6: 기존 폼 통합/마이그레이션 ✅ 완료 (2025-11-28)
- 기존 ItemForm과 DynamicItemForm 하이브리드 전환
- Feature flag로 전환 가능하게 (
ItemFormWrapper.tsx) - 품목 유형별 점진적 마이그레이션 (
ENABLED_ITEM_TYPES) - 개발 모드 토글 UI (localStorage 오버라이드)
- Feature flag로 전환 가능하게 (
- 기존 특수 컴포넌트 재사용 (CustomField.tsx에 통합)
- DrawingCanvasSimple (파일 업로드 기반)
- BOMTable (품목 구성 관리)
- BendingDetailTable (전개도 상세 입력 + 자동 계산)
- 페이지 라우트 업데이트 ✅ 완료 (2025-12-01)
/items/create/page.tsx→ DynamicItemForm 연동/items/[id]/edit/page.tsx→ DynamicItemForm + 실제 API 연동
Phase 7: 테스트 및 검증
- 품목 유형별 테스트
- FG (제품) 등록/수정 테스트
- PT (부품) 등록/수정 테스트
- RM/SM/CS 등록/수정 테스트
- 조건부 필드 테스트
- PT → ASSEMBLY/BENDING/PURCHASED 분기
- BOM 섹션 조건부 표시
- 에러 처리 테스트
- API 실패 시 폴백
- 유효성 검증 에러 표시
📋 API 응답 구조 (참조용)
GET /api/v1/item-master/form-structure/{item_type}
{
"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 | 초안 작성 |
| 2025-12-01 | 1.1 | Phase 6 페이지 라우트 업데이트 완료 (API 연동) |