DynamicItemForm 개선: - 품목코드 자동생성 기능 추가 - 조건부 표시 로직 개선 - 불필요한 컴포넌트 정리 (DynamicField, DynamicSection 등) - 타입 시스템 단순화 새로운 기능: - Sales 페이지 마이그레이션 (견적관리, 거래처관리) - 공통 컴포넌트 추가 (atoms, molecules, organisms, templates) 문서화: - 구현 문서 및 참조 문서 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
7.4 KiB
7.4 KiB
품목 등록 동적 페이지 재구현
작업 일자: 2025-12-02
문서 버전
| 버전 | 날짜 | 작성자 | 내용 |
|---|---|---|---|
| 1.0 | 2025-12-02 | Claude | 롤백 후 재구현 시작 |
| 1.1 | 2025-12-02 | Claude | Phase 1-8 완료 |
배경
- 이전 작업에서 DynamicItemForm 구현이 디자인 누락으로 롤백됨
- 기존 ItemForm (목업 데이터 기반)의 디자인을 100% 유지하면서 동적 렌더링 구현 필요
핵심 원칙
- 디자인 100% 동일: 기존 ItemForm과 완벽히 동일한 UI/UX
- 밸리데이션 동일: 에러 메시지 위치, 스타일 동일
- 버튼/아이콘 동일: 모든 버튼, 아이콘 위치 및 스타일 동일
- 화면 등장 순서 동일: 섹션 표시 순서, 조건부 렌더링 동일
- 동적 구성: 품목기준관리 API 기반으로 필드/섹션 동적 생성
체크리스트
Phase 1: 현재 상태 분석
- 기존 ItemForm 디자인 완전 분석
- 품목 유형 선택 UI
- 기본 정보 섹션 레이아웃
- BOM 섹션 레이아웃
- 전개도 섹션 레이아웃
- 버튼 위치 및 스타일
- 밸리데이션 에러 표시 방식
- 품목관리 API 응답 구조 확인 (init, pages.getStructure)
Phase 2: DynamicItemForm 폴더 생성
- 기본 폴더 구조 생성
src/components/items/DynamicItemForm/ ├── index.tsx ├── types.ts ├── hooks/ │ ├── index.ts │ ├── useFormStructure.ts │ └── useDynamicFormState.ts └── fields/ ├── index.ts ├── TextField.tsx ├── DropdownField.tsx ├── NumberField.tsx ├── DateField.tsx ├── TextareaField.tsx ├── CheckboxField.tsx └── DynamicFieldRenderer.tsx
Phase 3: 타입 정의
types.ts- 동적 폼 관련 타입 정의- API 응답 타입과 프론트엔드 타입 매핑
- SimpleUnitOption 타입 추가
Phase 4: 훅 구현
useFormStructure.ts- API에서 폼 구조 로드useDynamicFormState.ts- 동적 상태 관리
Phase 5: 필드 컴포넌트 구현 (디자인 100% 동일)
TextField.tsx- 기존 Input 스타일 동일DropdownField.tsx- 기존 Select 스타일 동일NumberField.tsx- 기존 숫자 입력 스타일 동일DateField.tsx- 기존 날짜 선택 스타일 동일TextareaField.tsx- 기존 텍스트영역 스타일 동일CheckboxField.tsx- 기존 체크박스 스타일 동일DynamicFieldRenderer.tsx- 필드 타입별 렌더러
Phase 6: 메인 폼 컴포넌트
index.tsx- 메인 동적 폼 컴포넌트- 기존 ItemForm과 동일한 레이아웃
- 동일한 헤더 스타일 (FormHeader 인라인)
- 동일한 ValidationAlert 스타일
- 동일한 섹션 Card 스타일
- 동일한 버튼 배치
Phase 7: 기존 ItemForm 주석처리 및 전환
/items/create/page.tsx- DynamicItemForm으로 전환- 기존 ItemForm import 주석처리
- 전환 테스트 준비
Phase 8: API 연동
- POST /api/proxy/items 연동 구현
- 품목 등록 후 목록 페이지 이동
- 에러 처리
Phase 9: 테스트
- TypeScript 빌드 검증 완료 (DynamicItemForm 에러 없음)
- 품목 유형별 등록 테스트 (FG, PT, RM, SM, CS)
- 밸리데이션 테스트
- API 연동 테스트
- 품목 목록에 등록된 데이터 표시 확인
테스트 가이드
사전 조건
- 백엔드 서버 실행 (sam-api)
- 프론트엔드 개발 서버 실행 (
npm run dev) - 품목기준관리에서 페이지/섹션/필드 설정 완료
테스트 항목
1. 품목 등록 페이지 접근
URL: http://localhost:3000/items/create
- 페이지 로딩 시 "폼 구조를 불러오는 중..." 표시
- 품목 유형 선택 전 안내 메시지 표시
2. 품목 유형 선택
- FG (완제품) 선택 시 해당 폼 구조 로드
- PT (부품) 선택 시 해당 폼 구조 로드
- RM (원자재) 선택 시 해당 폼 구조 로드
- SM (부자재) 선택 시 해당 폼 구조 로드
- CS (소모품) 선택 시 해당 폼 구조 로드
3. 필드 렌더링
- 텍스트 필드 정상 렌더링
- 숫자 필드 정상 렌더링
- 드롭다운 필드 정상 렌더링 (단위 옵션 포함)
- 체크박스 필드 정상 렌더링
- 날짜 필드 정상 렌더링
- 텍스트영역 필드 정상 렌더링
4. 밸리데이션
- 필수 필드 빈값 시 에러 메시지 표시
- ValidationAlert에 에러 목록 표시
- 에러 필드에 빨간 테두리 표시
5. API 연동
- 저장 버튼 클릭 시 POST /api/proxy/items 호출
- 저장 성공 시 /items 페이지로 이동
- 저장 실패 시 에러 메시지 표시
6. 디자인 검증
- 기존 ItemForm과 레이아웃 동일
- 헤더 스타일 동일 (아이콘, 버튼 위치)
- 섹션 Card 스타일 동일
- 필드 라벨/에러 스타일 동일
생성된 파일
DynamicItemForm 컴포넌트
src/components/items/DynamicItemForm/index.tsx- 메인 폼 (388줄)src/components/items/DynamicItemForm/types.ts- 타입 정의src/components/items/DynamicItemForm/hooks/index.ts- 훅 exportsrc/components/items/DynamicItemForm/hooks/useFormStructure.ts- 폼 구조 로드src/components/items/DynamicItemForm/hooks/useDynamicFormState.ts- 상태 관리src/components/items/DynamicItemForm/fields/index.ts- 필드 exportsrc/components/items/DynamicItemForm/fields/TextField.tsxsrc/components/items/DynamicItemForm/fields/NumberField.tsxsrc/components/items/DynamicItemForm/fields/DropdownField.tsxsrc/components/items/DynamicItemForm/fields/CheckboxField.tsxsrc/components/items/DynamicItemForm/fields/DateField.tsxsrc/components/items/DynamicItemForm/fields/TextareaField.tsxsrc/components/items/DynamicItemForm/fields/DynamicFieldRenderer.tsx
수정된 파일
src/app/[locale]/(protected)/items/create/page.tsx- DynamicItemForm 사용으로 변경
API 흐름
1. useFormStructure(itemType)
└─> itemMasterApi.init()
└─> /api/proxy/item-master/init
└─> pages, sections, fields, unitOptions 반환
2. itemMasterApi.pages.getStructure(pageId)
└─> /api/proxy/item-master/pages/{id}/structure
└─> page, sections, directFields 반환
3. 품목 등록
└─> POST /api/proxy/items
└─> body: DynamicFormData
디자인 매칭 요소
레이아웃
form>space-y-6Card>CardHeader>CardTitle>CardContentgrid grid-cols-1 md:grid-cols-2 gap-4
필드 스타일
- Label with required marker:
<span className="text-red-500"> *</span> - Error state:
className={error ? 'border-red-500' : ''} - Error message:
<p className="text-xs text-red-500 mt-1">{error}</p> - Helper text:
<p className="text-xs text-muted-foreground mt-1">* ...</p>
버튼 스타일
- Cancel:
variant="outline" size="sm" - Submit:
size="sm" disabled={!selectedItemType || isSubmitting} - Icons:
X,Savefrom lucide-react
변경 이력
| 날짜 | 작업 내용 |
|---|---|
| 2025-12-02 | 문서 생성, Phase 1 시작 |
| 2025-12-02 | Phase 1-8 완료: DynamicItemForm 전체 구현 |