- DynamicFieldRenderer에 신규 필드 타입 추가 (Currency, File, MultiSelect, Radio, Reference, Toggle, UnitValue, Computed) - DynamicTableSection 및 TableCellRenderer 추가 - 필드 프리셋 및 설정 구조 분리 - 컴포넌트 레지스트리 개발 도구 페이지 추가 - UniversalListPage 개선 - 근태관리 코드 정리 - 즐겨찾기 기능 및 동적 필드 타입 백엔드 스펙 문서 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
11 KiB
동적 필드 타입 컴포넌트 — 프론트엔드 구현 기획서
작성일: 2026-02-11 설계 근거:
[DESIGN-2026-02-11] dynamic-field-type-extension.md상태: ✅ 프론트 구현 완료 — 백엔드 작업 대기 백엔드 스펙:item-master/[API-REQUEST-2026-02-12] dynamic-field-type-backend-spec.md
목적
현재 DynamicItemForm의 필드 타입이 6종(textbox, number, dropdown, checkbox, date, textarea)으로 제한되어 있어 제조/공사/유통/물류 등 다양한 산업의 품목관리 요구를 충족하지 못함.
이 작업의 목표:
- 8종의 신규 필드 컴포넌트를 미리 만들어둔다
- 범용 테이블 섹션(DynamicTableSection)을 만든다
- 백엔드가
field_type+propertiesconfig를 보내면 자동 렌더링되는 구조를 완성한다 - 백엔드 작업 전에도 mock props로 독립 테스트 가능하게 한다
API 연동 시 동작 흐름:
백엔드 DB: field_type = "reference", properties = { "source": "vendors" }
↓
API 응답: GET /v1/item-master/pages/{id}/structure
↓
프론트: DynamicFieldRenderer → switch("reference") → <ReferenceField />
ReferenceField가 properties.source 읽어서 /api/proxy/vendors 검색 API 호출
컴포넌트 구현 목록
Phase 1: 핵심 컴포넌트
| # | 컴포넌트 | field_type | 상태 | 비고 |
|---|---|---|---|---|
| 1-1 | ReferenceField | reference |
✅ 완료 | 다른 테이블 검색/선택 |
| 1-2 | MultiSelectField | multi-select |
✅ 완료 | 복수 선택 태그 |
| 1-3 | FileField | file |
✅ 완료 | 파일/이미지 업로드 |
| 1-4 | DynamicTableSection | (섹션) | ✅ 완료 | 범용 테이블 섹션 |
| 1-5 | TableCellRenderer | (내부) | ✅ 완료 | 테이블 셀 렌더러 |
| 1-6 | reference-sources.ts | (config) | ✅ 완료 | 참조 소스 프리셋 정의 |
| 1-7 | DynamicFieldRenderer 확장 | (수정) | ✅ 완료 | switch문 + 신규 import |
| 1-8 | 타입 정의 확장 | (수정) | ✅ 완료 | ItemFieldType 통합 + config 인터페이스 |
Phase 2: 편의 컴포넌트
| # | 컴포넌트 | field_type | 상태 | 비고 |
|---|---|---|---|---|
| 2-1 | CurrencyField | currency |
✅ 완료 | 통화 금액 (천단위 포맷) |
| 2-2 | UnitValueField | unit-value |
✅ 완료 | 값+단위 조합 (100mm) |
| 2-3 | RadioField | radio |
✅ 완료 | 라디오 버튼 그룹 |
| 2-4 | section-presets.ts | (config) | ✅ 완료 | 산업별 섹션 프리셋 JSON |
Phase 3: 고급 컴포넌트
| # | 컴포넌트 | field_type | 상태 | 비고 |
|---|---|---|---|---|
| 3-1 | ToggleField | toggle |
✅ 완료 | On/Off 스위치 |
| 3-2 | ComputedField | computed |
✅ 완료 | 계산 필드 (읽기전용) |
| 3-3 | 조건부 표시 연산자 확장 | (수정) | ✅ 완료 | in/not_in/greater_than 등 9종 |
각 컴포넌트 스펙
1-1. ReferenceField
파일: DynamicItemForm/fields/ReferenceField.tsx
역할: 다른 테이블의 데이터를 검색하여 선택 (거래처, 품목, 고객, 현장, 차량 등)
UI 구성:
- 읽기전용 Input + 검색 버튼(돋보기 아이콘)
- 클릭 시 SearchableSelectionModal 열림
- 선택 후 displayField 값 표시
- X 버튼으로 선택 해제
properties에서 읽는 값:
interface ReferenceConfig {
source: string; // "vendors" | "items" | "custom" 등
displayField?: string; // 기본 "name"
valueField?: string; // 기본 "id"
searchFields?: string[]; // 기본 ["name"]
searchApiUrl?: string; // source="custom"일 때 필수
columns?: Array<{ key: string; label: string; width?: string }>;
displayFormat?: string; // "{code} - {name}"
returnFields?: string[]; // ["id", "code", "name"]
}
API 연동 시:
REFERENCE_SOURCES[source]에서 apiUrl 조회GET {apiUrl}?search={query}&size=20호출- 결과를 SearchableSelectionModal에 표시
API 연동 전 (mock):
- props로 전달된 options 사용 또는
- 빈 상태에서 UI/UX만 확인
1-2. MultiSelectField
파일: DynamicItemForm/fields/MultiSelectField.tsx
역할: 여러 항목을 동시에 선택 (태그 칩 형태로 표시)
UI 구성:
- Combobox (검색 가능한 드롭다운)
- 선택된 항목은 칩(Chip/Badge)으로 표시
- 칩의 X 버튼으로 개별 해제
properties에서 읽는 값:
interface MultiSelectConfig {
maxSelections?: number; // 최대 선택 수 (기본: 무제한)
allowCustom?: boolean; // 직접 입력 허용 (기본: false)
layout?: 'chips' | 'list'; // 기본: "chips"
}
options: 기존 dropdown과 동일 [{label, value}]
저장값: string[] (예: ["CUT", "BEND", "WELD"])
1-3. FileField
파일: DynamicItemForm/fields/FileField.tsx
역할: 파일/이미지 첨부
UI 구성:
- 파일 선택 버튼 ("파일 선택" 또는 드래그 앤 드롭 영역)
- 선택된 파일 목록 표시 (이름, 크기, 삭제 버튼)
- 이미지 파일일 경우 미리보기 썸네일
properties에서 읽는 값:
interface FileConfig {
accept?: string; // ".pdf,.doc" (기본: "*")
maxSize?: number; // bytes (기본: 10MB = 10485760)
maxFiles?: number; // 기본: 1
preview?: boolean; // 이미지 미리보기 (기본: true)
category?: string; // 파일 카테고리 태그
}
API 연동 시: POST /v1/files/upload (multipart)
API 연동 전: File 객체를 로컬 상태로 관리, URL.createObjectURL로 미리보기
1-4. DynamicTableSection
파일: DynamicItemForm/sections/DynamicTableSection.tsx
역할: config 기반 범용 테이블 (공정, 품질검사, 구매처, 공정표, 배차 등)
UI 구성:
- 테이블 헤더 (columns config 기반)
- 행 추가/삭제 버튼
- 각 셀은 TableCellRenderer (= DynamicFieldRenderer 재사용)
- 요약행 (선택, summaryRow config)
- 빈 상태 메시지
props:
interface DynamicTableSectionProps {
section: ItemSectionResponse;
tableConfig: TableConfig;
rows: Record<string, any>[];
onRowsChange: (rows: Record<string, any>[]) => void;
disabled?: boolean;
}
tableConfig 구조: 설계서 섹션 4.3 참조
API 연동 시: GET/PUT /v1/items/{itemId}/section-data/{sectionId}
API 연동 전: rows를 폼 상태(formData)에 로컬 관리
1-5. TableCellRenderer
파일: DynamicItemForm/sections/TableCellRenderer.tsx
역할: 테이블 셀 = DynamicFieldRenderer를 테이블 셀용 축소 모드로 래핑
핵심: column config → ItemFieldResponse 호환 객체로 변환 → DynamicFieldRenderer 호출
function TableCellRenderer({ column, value, onChange, compact }) {
const fieldLike = columnToFieldResponse(column);
return <DynamicFieldRenderer field={fieldLike} value={value} onChange={onChange} />;
}
1-6. reference-sources.ts
파일: DynamicItemForm/config/reference-sources.ts
역할: reference 필드의 소스별 기본 설정 (API URL, 표시 필드, 검색 컬럼)
내용: 공통(vendors, items, customers, employees, warehouses) + 산업별(processes, sites, vehicles, stores 등)
확장 방법: 새 소스 추가 = 이 파일에 객체 1개 추가
2-1. CurrencyField
파일: DynamicItemForm/fields/CurrencyField.tsx
역할: 통화 금액 입력 (천단위 콤마, 통화 기호)
UI: Input + 통화기호(₩) prefix + 천단위 포맷
- 입력 중: 숫자만
- 포커스 아웃: "₩15,000" 포맷
properties: { currency, precision, showSymbol, allowNegative }
저장값: number (포맷 없이)
2-2. UnitValueField
파일: DynamicItemForm/fields/UnitValueField.tsx
역할: 값 + 단위 조합 입력 (100mm, 50kg)
UI: Input(숫자) + Select(단위) 가로 배치
properties: { units, defaultUnit, precision }
저장값: { value: number, unit: string }
2-3. RadioField
파일: DynamicItemForm/fields/RadioField.tsx
역할: 라디오 버튼 그룹
UI: RadioGroup (수평/수직)
properties: { layout: "horizontal" | "vertical" }
options: [{label, value}]
3-1. ToggleField
파일: DynamicItemForm/fields/ToggleField.tsx
역할: On/Off 토글 스위치
UI: Switch + 라벨
properties: { onLabel, offLabel, onValue, offValue }
3-2. ComputedField
파일: DynamicItemForm/fields/ComputedField.tsx
역할: 다른 필드 기반 자동 계산 (읽기 전용)
UI: 읽기전용 표시 (배경색 구분, muted)
properties: { formula, dependsOn, format, precision }
동작: dependsOn 필드 값이 변경될 때마다 formula 재계산
파일 구조
DynamicItemForm/
├── fields/
│ ├── DynamicFieldRenderer.tsx ← switch 확장
│ ├── TextField.tsx (기존)
│ ├── NumberField.tsx (기존)
│ ├── DropdownField.tsx (기존)
│ ├── CheckboxField.tsx (기존)
│ ├── DateField.tsx (기존)
│ ├── TextareaField.tsx (기존)
│ ├── ReferenceField.tsx ★ Phase 1
│ ├── MultiSelectField.tsx ★ Phase 1
│ ├── FileField.tsx ★ Phase 1
│ ├── CurrencyField.tsx ★ Phase 2
│ ├── UnitValueField.tsx ★ Phase 2
│ ├── RadioField.tsx ★ Phase 2
│ ├── ToggleField.tsx ★ Phase 3
│ └── ComputedField.tsx ★ Phase 3
├── sections/
│ ├── DynamicBOMSection.tsx (기존)
│ ├── DynamicTableSection.tsx ★ Phase 1
│ └── TableCellRenderer.tsx ★ Phase 1
├── config/
│ └── reference-sources.ts ★ Phase 1
├── presets/
│ └── section-presets.ts ★ Phase 2
├── hooks/ (기존, 변경 없음)
├── types.ts ← 타입 확장
└── index.tsx ← table 섹션 렌더링 추가
기존 코드 수정 범위
| 파일 | 수정 내용 | 줄 수 |
|---|---|---|
DynamicFieldRenderer.tsx |
switch case 8개 추가 + import | +20줄 |
types.ts |
ExtendedFieldType union + config 인터페이스 | +80줄 |
index.tsx |
섹션 렌더링에 case 'table' 추가 |
+15줄 |
item-master-api.ts |
field_type union 확장 | +3줄 |
기존 컴포넌트 6개 + BOM + hooks 7개 = 변경 없음
상태 범례
- ⬜ 대기
- 🔄 진행 중
- ✅ 완료
- ⏸️ 보류
마지막 업데이트: 2026-02-12 — Phase 1+2+3 전체 완료 (15/15 항목)