# 동적 필드 타입 컴포넌트 — 프론트엔드 구현 기획서

> 작성일: 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` + `properties` config를 보내면 자동 렌더링되는 구조를 완성한다
- 백엔드 작업 전에도 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에서 읽는 값**:
```typescript
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에서 읽는 값**:
```typescript
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에서 읽는 값**:
```typescript
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**:
```typescript
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 호출

```typescript
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 항목)
