# 품목기준관리 동적 필드 타입 확장 설계

> 작성일: 2026-02-11
> 목적: 멀티테넌시 품목기준관리의 필드 타입 확장 및 범용 테이블 섹션 설계
> 범위: 프론트엔드 컴포넌트 설계 + 백엔드 API 계약 + 산업별 확장 구조

---

## 목차

1. [배경 및 목표](#1-배경-및-목표)
2. [현재 시스템 분석](#2-현재-시스템-분석)
3. [확장 필드 타입 레지스트리](#3-확장-필드-타입-레지스트리)
4. [범용 테이블 섹션 설계](#4-범용-테이블-섹션-설계)
5. [섹션 템플릿 라이브러리](#5-섹션-템플릿-라이브러리)
6. [산업별 확장 구조](#6-산업별-확장-구조)
7. [백엔드 API 계약](#7-백엔드-api-계약)
8. [프론트엔드 컴포넌트 아키텍처](#8-프론트엔드-컴포넌트-아키텍처)
9. [조건부 표시 확장](#9-조건부-표시-확장)
10. [검증 프레임워크](#10-검증-프레임워크)
11. [구현 로드맵](#11-구현-로드맵)

---

## 1. 배경 및 목표

### 1.1 현재 문제

품목기준관리(`/master-data/item-master-data-management`)는 **동적 폼 구성 시스템**이 이미 존재하지만, 필드 타입이 6가지로 제한되어 제조 ERP의 다양한 요구를 충족하지 못함.

| 현재 있는 것 | 없어서 부족한 것 |
|-------------|-----------------|
| textbox | 다른 테이블 참조/검색 선택 (거래처, 품목, 고객) |
| number | 복수 선택 (태그형) |
| dropdown | 파일/이미지 업로드 |
| checkbox | 통화/금액 (단위 포함) |
| date | 값+단위 조합 (100mm, 50kg) |
| textarea | 범용 테이블/그리드 (BOM 외) |

또한 BOM이 유일한 "테이블형 섹션"이지만, 제조 ERP에서는 **공정, 품질검사, 구매처, 단가이력** 등도 테이블 구조가 필요함.

### 1.2 설계 목표

```
핵심 원칙: "항목을 미리 정의"하지 않고 "필드 타입과 config 조합"을 확장한다.
```

1. **필드 타입 확장**: 6종 → 14종으로 확장 (입력 원자 단위)
2. **범용 테이블 섹션**: BOM 전용 → config 기반 범용 테이블로 일반화
3. **섹션 템플릿 라이브러리**: 산업별 표준 섹션 프리셋 제공
4. **백엔드 key + type + config 체계**: 백엔드가 스키마만 정의하면 프론트가 자동 렌더링
5. **멀티테넌시 확장성**: 테넌트마다 다른 항목 구성 가능
6. **산업 불문 확장**: 제조/공사/유통/물류 전방위 커버

### 1.3 설계 원칙

- **하위 호환**: 기존 6가지 필드 타입은 그대로 유지, 기존 코드 변경 없음
- **점진적 확장**: 새 필드 타입 추가 = 새 컴포넌트 1개 추가 + DynamicFieldRenderer switch 1줄 추가
- **config 기반**: 필드의 동작은 `field_type` + `properties` 조합으로 결정
- **백엔드 독립**: 프론트 컴포넌트는 미리 만들고, 백엔드는 나중에 key-config 매핑만 추가

---

## 2. 현재 시스템 분석

### 2.1 아키텍처

```
품목기준관리 (Admin)                    품목 등록/수정 (User)
ItemMasterDataManagement.tsx           DynamicItemForm/index.tsx
  ↓ 구조 정의                            ↓ 구조 기반 렌더링
  Pages → Sections → Fields            GET /pages/{id}/structure
                  → BOM Items            ↓
                                       DynamicFieldRenderer (switch)
                                         → TextField
                                         → NumberField
                                         → DropdownField
                                         → CheckboxField
                                         → DateField
                                         → TextareaField
```

### 2.2 핵심 파일

| 파일 | 줄 수 | 역할 |
|------|-------|------|
| `DynamicItemForm/index.tsx` | 1,048 | 메인 폼 컴포넌트 |
| `DynamicItemForm/types.ts` | 261 | 타입 정의 |
| `DynamicItemForm/fields/DynamicFieldRenderer.tsx` | 44 | 필드 타입 라우터 |
| `DynamicItemForm/sections/DynamicBOMSection.tsx` | 515 | BOM 테이블 섹션 |
| `DynamicItemForm/hooks/` | 7개 훅 | 상태/검증/조건부 표시 |
| `types/item-master-api.ts` | 745 | API 타입 정의 |
| `ItemMasterDataManagement.tsx` | 1,005 | Admin 관리 페이지 |

### 2.3 현재 필드 응답 구조 (ItemFieldResponse)

```typescript
{
  id: number,
  field_name: string,           // "품목명"
  field_key: string | null,     // "98_item_name"
  field_type: 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea',
  is_required: boolean,
  placeholder: string | null,
  default_value: string | null,
  options: [{label, value}] | null,           // dropdown 옵션
  properties: Record<string, any> | null,     // 추가 메타데이터
  validation_rules: Record<string, any> | null,
  display_condition: Record<string, any> | null,
}
```

### 2.4 현재 섹션 타입

```typescript
section.type: 'fields' | 'bom'
// 'fields' → DynamicFieldRenderer로 각 필드 렌더링
// 'bom'    → DynamicBOMSection (하드코딩된 BOM 전용 UI)
```

---

## 3. 확장 필드 타입 레지스트리

### 3.1 전체 필드 타입 목록

#### 기존 유지 (6종)

| field_type | 컴포넌트 | 설명 |
|-----------|---------|------|
| `textbox` | TextField | 단일 행 텍스트 |
| `number` | NumberField | 숫자 입력 |
| `dropdown` | DropdownField | 단일 선택 |
| `checkbox` | CheckboxField | 체크박스 |
| `date` | DateField | 날짜 선택 |
| `textarea` | TextareaField | 여러 행 텍스트 |

#### 신규 추가 (8종)

| field_type | 컴포넌트 | 설명 | 우선순위 |
|-----------|---------|------|---------|
| `reference` | ReferenceField | 다른 테이블 참조 검색/선택 | 🔴 Phase 1 |
| `multi-select` | MultiSelectField | 복수 선택 (태그형) | 🔴 Phase 1 |
| `file` | FileField | 파일/이미지 업로드 | 🔴 Phase 1 |
| `currency` | CurrencyField | 통화 금액 (포맷 + 단위) | 🟡 Phase 2 |
| `unit-value` | UnitValueField | 값 + 단위 조합 | 🟡 Phase 2 |
| `radio` | RadioField | 라디오 버튼 그룹 | 🟡 Phase 2 |
| `toggle` | ToggleField | On/Off 토글 스위치 | 🟢 Phase 3 |
| `computed` | ComputedField | 읽기 전용 계산 필드 | 🟢 Phase 3 |

### 3.2 각 필드 타입별 상세 스펙

---

#### 3.2.1 `reference` — 참조 룩업 필드

**용도**: 다른 테이블의 데이터를 검색하여 선택 (거래처, 품목, 고객, 공정, 현장, 차량 등)

**UI**: 검색 입력 + 드롭다운 팝업 (SearchableSelectionModal 기반)

**properties 스키마**:
```jsonc
{
  "source": "vendors",           // 참조할 데이터 소스 (필수) — 프리셋 또는 "custom"
  "displayField": "name",        // 선택 후 표시할 필드 (기본: "name")
  "valueField": "id",            // 저장할 값 필드 (기본: "id")
  "searchFields": ["name", "code"], // 검색 대상 필드 (기본: ["name"])
  "searchApiUrl": "/api/proxy/vendors", // 검색 API URL ("custom" source일 때 필수)
  "minSearchLength": 1,          // 최소 검색 글자 수 (기본: 1)
  "modalTitle": "거래처 선택",     // 모달 제목 (선택, 없으면 field_name + " 선택")
  "columns": [                   // 검색 결과 표시 컬럼 (선택)
    { "key": "code", "label": "코드", "width": "120px" },
    { "key": "name", "label": "이름" },
    { "key": "contact", "label": "연락처", "width": "150px" }
  ],
  "displayFormat": "{code} - {name}", // 선택 후 표시 포맷 (선택)
  "returnFields": ["id", "code", "name"] // 선택 시 폼에 저장할 필드들 (선택)
}
```

**저장되는 값**:
```jsonc
// 단일 값: valueField 기준
{ "vendor_id": "123" }

// returnFields 설정 시: 여러 필드 동시 저장
{ "vendor_id": "123", "vendor_code": "V-001", "vendor_name": "삼성전자" }
```

**소스 프리셋** (프론트에서 미리 정의, 산업별 확장 가능):

| source | 산업 | API | displayField |
|--------|------|-----|--------------|
| `vendors` | 공통 | `/api/proxy/vendors` | name |
| `items` | 공통 | `/api/proxy/items` | name |
| `customers` | 공통 | `/api/proxy/customers` | company_name |
| `employees` | 공통 | `/api/proxy/employees` | name |
| `processes` | 제조 | `/api/proxy/processes` | process_name |
| `warehouses` | 공통 | `/api/proxy/warehouses` | name |
| `materials` | 제조 | `/api/proxy/item-master/materials` | material_name |
| `surface_treatments` | 제조 | `/api/proxy/item-master/surface-treatments` | treatment_name |
| `sites` | 공사 | `/api/proxy/sites` | site_name |
| `vehicles` | 물류 | `/api/proxy/vehicles` | plate_number |
| `routes` | 물류 | `/api/proxy/routes` | route_name |
| `stores` | 유통 | `/api/proxy/stores` | store_name |
| `custom` | — | properties.searchApiUrl | properties.displayField |

> `custom` source를 사용하면 백엔드에 새 API만 추가하면 프론트 코드 수정 없이 어떤 참조든 연결 가능.

---

#### 3.2.2 `multi-select` — 복수 선택 필드

**용도**: 여러 항목을 동시에 선택 (태그/칩 형태)

**UI**: Combobox + 태그 칩

**properties 스키마**:
```jsonc
{
  "maxSelections": 5,            // 최대 선택 수 (선택, 기본: 무제한)
  "allowCustom": false,          // 사용자 직접 입력 허용 여부 (기본: false)
  "layout": "chips"              // "chips" | "list" (기본: "chips")
}
```

**options 사용**: 기존 dropdown과 동일한 `[{label, value}]` 형태

**저장되는 값**:
```jsonc
{ "applicable_processes": ["CUT", "BEND", "WELD", "PAINT"] }
```

---

#### 3.2.3 `file` — 파일/이미지 업로드 필드

**용도**: 문서, 이미지, 도면 첨부

**UI**: 파일 선택 버튼 + 미리보기 (이미지일 경우)

**properties 스키마**:
```jsonc
{
  "accept": ".pdf,.doc,.docx",   // 허용 파일 타입 (기본: "*")
  "maxSize": 10485760,           // 최대 파일 크기 bytes (기본: 10MB)
  "maxFiles": 5,                 // 최대 파일 수 (기본: 1)
  "preview": true,               // 미리보기 표시 여부 (기본: true, 이미지 파일만)
  "uploadApiUrl": "/api/proxy/files/upload",  // 업로드 API (선택)
  "category": "drawing"          // 파일 카테고리 태그 (선택)
}
```

**저장되는 값**:
```jsonc
// 단일 파일
{ "drawing_file": { "fileId": "uuid-123", "fileName": "도면_v2.pdf", "fileUrl": "/files/uuid-123" } }

// 복수 파일
{ "attachments": [
    { "fileId": "uuid-123", "fileName": "도면.pdf", "fileUrl": "/files/uuid-123" },
    { "fileId": "uuid-456", "fileName": "사진.jpg", "fileUrl": "/files/uuid-456" }
  ]
}
```

---

#### 3.2.4 `currency` — 통화/금액 필드

**용도**: 단가, 총액 등 금액 입력 (천 단위 콤마 + 통화 기호)

**UI**: 숫자 입력 + 통화 기호 + 천단위 포맷

**properties 스키마**:
```jsonc
{
  "currency": "KRW",            // 통화 코드 (기본: "KRW")
  "precision": 0,               // 소수점 자릿수 (기본: KRW=0, USD=2)
  "showSymbol": true,           // 통화 기호 표시 (기본: true)
  "allowNegative": false        // 음수 허용 (기본: false)
}
```

**저장되는 값**: 숫자 (`{ "unit_price": 15000 }`)

---

#### 3.2.5 `unit-value` — 값+단위 조합 필드

**용도**: 치수, 무게, 용량, 거리 등 (숫자 + 단위 동시 입력)

**UI**: 숫자 입력 + 단위 선택 드롭다운 (inline)

**properties 스키마**:
```jsonc
{
  "units": ["mm", "cm", "m", "inch"],  // 선택 가능 단위 목록 (필수)
  "defaultUnit": "mm",                 // 기본 단위 (선택)
  "precision": 1,                      // 소수점 자릿수 (기본: 0)
  "showConversion": false              // 단위 변환 표시 (기본: false)
}
```

**저장되는 값**: `{ "thickness": { "value": 2.5, "unit": "mm" } }`

---

#### 3.2.6 `radio` — 라디오 버튼 그룹

**용도**: 상호 배타적 선택 (3~5개 이내 옵션에 적합)

**UI**: 라디오 버튼 그룹 (수평/수직)

**properties 스키마**:
```jsonc
{
  "layout": "horizontal"         // "horizontal" | "vertical" (기본: "horizontal")
}
```

**options 사용**: dropdown과 동일한 `[{label, value}]`

---

#### 3.2.7 `toggle` — 토글 스위치

**용도**: On/Off 상태 전환

**UI**: Switch 컴포넌트

**properties 스키마**:
```jsonc
{
  "onLabel": "활성",             // On 상태 라벨 (선택)
  "offLabel": "비활성",          // Off 상태 라벨 (선택)
  "onValue": "active",          // On 상태 저장값 (기본: "true")
  "offValue": "inactive"        // Off 상태 저장값 (기본: "false")
}
```

---

#### 3.2.8 `computed` — 읽기 전용 계산 필드

**용도**: 다른 필드 값을 기반으로 자동 계산되는 필드 (표시 전용)

**UI**: 읽기 전용 표시 (배경색 구분)

**properties 스키마**:
```jsonc
{
  "formula": "{quantity} * {unit_price}",  // 계산식 (필수)
  "dependsOn": ["quantity", "unit_price"], // 의존 필드 키 목록 (필수)
  "format": "currency",                    // 표시 포맷: "number" | "currency" | "percent" (기본: "number")
  "precision": 0                           // 소수점 자릿수 (기본: 0)
}
```

---

### 3.3 field_type 확장 타입 정의 (TypeScript)

```typescript
// 기존
type FieldType = 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea';

// 확장
type ExtendedFieldType = FieldType
  | 'reference'      // Phase 1
  | 'multi-select'   // Phase 1
  | 'file'           // Phase 1
  | 'currency'       // Phase 2
  | 'unit-value'     // Phase 2
  | 'radio'          // Phase 2
  | 'toggle'         // Phase 3
  | 'computed';      // Phase 3
```

---

## 4. 범용 테이블 섹션 설계

### 4.1 현재 문제

```
현재: section.type === 'bom' → DynamicBOMSection (515줄, BOM 전용 하드코딩)
필요: 공정, 품질검사, 구매처, 단가이력 등도 테이블 필요
```

### 4.2 설계: section.type 확장

```typescript
// 기존
section.type: 'fields' | 'bom'

// 확장
section.type: 'fields' | 'bom' | 'table'
//                               ↑ 신규: 범용 테이블
```

### 4.3 범용 테이블 섹션 config

`section.properties`에 테이블 설정을 담음:

```jsonc
{
  "table_config": {
    // 컬럼 정의 (핵심)
    "columns": [
      {
        "key": "process_code",          // 컬럼 키 (데이터 저장 키)
        "label": "공정코드",             // 컬럼 헤더
        "type": "reference",            // 셀 입력 타입 (필드 타입과 동일한 14종)
        "width": "150px",               // 컬럼 너비 (선택)
        "required": true,               // 필수 여부
        "config": {                     // 타입별 추가 설정 (properties와 동일 구조)
          "source": "processes",
          "displayField": "process_name",
          "searchFields": ["process_name", "process_code"]
        }
      },
      {
        "key": "process_name",
        "label": "공정명",
        "type": "textbox",
        "width": "200px",
        "readOnly": true,               // 참조 필드에서 자동 채움
        "autoFillFrom": "process_code.process_name"  // 자동 채움 소스
      },
      {
        "key": "quantity",
        "label": "수량",
        "type": "number",
        "width": "100px",
        "config": { "min": 0, "precision": 2 }
      },
      {
        "key": "unit",
        "label": "단위",
        "type": "dropdown",
        "width": "100px",
        "config": { "source": "unitOptions" }
      },
      {
        "key": "start_date",
        "label": "시작일",
        "type": "date",
        "width": "140px"
      },
      {
        "key": "note",
        "label": "비고",
        "type": "textbox"                // width 미지정 = 나머지 공간
      }
    ],

    // 행 동작
    "addable": true,                     // 행 추가 가능 (기본: true)
    "deletable": true,                   // 행 삭제 가능 (기본: true)
    "reorderable": true,                 // 행 순서 변경 가능 (기본: false)
    "maxRows": 100,                      // 최대 행 수 (선택, 기본: 무제한)
    "minRows": 0,                        // 최소 행 수 (선택, 기본: 0)

    // 표시
    "showRowNumber": true,               // 행 번호 표시 (기본: true)
    "showCheckbox": false,               // 행 선택 체크박스 (기본: false)
    "emptyMessage": "데이터가 없습니다.",  // 빈 상태 메시지

    // 요약행 (선택)
    "summaryRow": {
      "enabled": true,
      "columns": {
        "quantity": { "type": "sum", "label": "합계" },
        "amount": { "type": "sum", "format": "currency" }
      }
    },

    // 데이터 소스 (기존 데이터 로드용, 선택)
    "dataApiUrl": "/api/proxy/items/{itemId}/routings",
    "saveApiUrl": "/api/proxy/items/{itemId}/routings"
  }
}
```

### 4.4 기존 BOM과의 관계

```
DynamicBOMSection (기존) → 유지 (하위 호환)
DynamicTableSection (신규) → 범용 테이블

section.type === 'bom'   → DynamicBOMSection (기존 그대로)
section.type === 'table' → DynamicTableSection (신규)
```

**점진적 마이그레이션**: BOM도 나중에 `type: 'table'`로 전환 가능하지만, 당장은 불필요.

### 4.5 테이블 셀 = 필드 컴포넌트 재사용

테이블 각 셀의 입력은 **DynamicFieldRenderer와 동일한 컴포넌트**를 사용:

```
table column.type === "reference"  → ReferenceField (인라인 축소판)
table column.type === "number"     → NumberField
table column.type === "dropdown"   → DropdownField
table column.type === "date"       → DateField
table column.type === "currency"   → CurrencyField
... (14종 모두 사용 가능)
```

즉, **필드 타입 컴포넌트 1개 = 폼 필드에서도, 테이블 셀에서도 동일하게 사용**.

### 4.6 테이블 섹션 저장 데이터 구조

```jsonc
{
  "table_section_123": [
    {
      "_rowId": "uuid-1",
      "process_code": "CUT-001",
      "process_name": "절단",
      "quantity": 10,
      "unit": "EA",
      "start_date": "2026-03-01",
      "note": "레이저 절단"
    }
  ]
}
```

---

## 5. 섹션 템플릿 라이브러리

### 5.1 전체 프리셋 목록 (산업 공통 + 산업별)

#### 공통 프리셋

| 프리셋 ID | 이름 | type | 설명 |
|----------|------|------|------|
| `basic-info` | 기본정보 | fields | 코드, 이름, 유형, 상태, 비고 |
| `drawing` | 도면/문서 | fields | 파일 업로드 + 버전 관리 |
| `custom` | 사용자 정의 | fields/table | 빈 섹션 (직접 구성) |

#### 제조 프리셋

| 프리셋 ID | 이름 | type | 설명 |
|----------|------|------|------|
| `specifications` | 규격/치수 | fields | 두께, 너비, 높이, 무게, 공차 |
| `bom` | BOM (자재명세서) | bom | 기존 BOM 구조 유지 |
| `routing` | 공정/라우팅 | table | 공정코드, 작업시간, 작업장 |
| `quality-spec` | 품질검사 항목 | table | 검사항목, 규격, 허용치, 검사방법 |
| `procurement` | 구매정보 | table | 공급업체, 단가, 리드타임, MOQ |
| `cost-breakdown` | 원가 구성 | table | 원가항목, 금액, 비율 |
| `inventory` | 재고 정보 | fields | 창고, 안전재고, 발주점 |

#### 공사 프리셋

| 프리셋 ID | 이름 | type | 설명 |
|----------|------|------|------|
| `work-schedule` | 공정표 | table | 공종, 수량, 단가, 착수일, 완료일, 진행률 |
| `material-plan` | 자재투입계획 | table | 자재, 수량, 단위, 투입시기, 발주여부 |
| `labor-plan` | 인력투입계획 | table | 직종, 인원, 기간, 일단가, 금액 |
| `equipment-plan` | 장비투입계획 | table | 장비명, 규격, 수량, 기간, 단가 |
| `safety-checklist` | 안전점검 항목 | table | 점검항목, 점검주기, 담당자, 결과 |
| `site-info` | 현장정보 | fields | 현장명, 주소, 발주처, 감리사, 공사기간 |

#### 유통 프리셋

| 프리셋 ID | 이름 | type | 설명 |
|----------|------|------|------|
| `pricing` | 가격정보 | table | 거래처유형, 단가, 할인율, 적용기간 |
| `packaging` | 포장정보 | fields | 포장단위, 박스수량, 바코드, 중량 |
| `store-allocation` | 매장배분 | table | 매장, 배분수량, 배분일, 상태 |
| `promotion` | 프로모션 | table | 프로모션명, 할인율, 시작일, 종료일, 조건 |

#### 물류 프리셋

| 프리셋 ID | 이름 | type | 설명 |
|----------|------|------|------|
| `transport-spec` | 운송규격 | fields | 중량, 부피, 위험물등급, 보관온도, 적재방법 |
| `route-plan` | 배차/경로 | table | 출발지, 도착지, 거리, 소요시간, 운임 |
| `loading-plan` | 적재계획 | table | 품목, 수량, 중량, 적재순서, 위치 |
| `tracking` | 추적정보 | table | 일시, 위치, 상태, 온도, 비고 |

### 5.2 프리셋 상세 예시

#### `work-schedule` (공사 — 공정표)

```jsonc
{
  "preset_id": "work-schedule",
  "type": "table",
  "table_config": {
    "columns": [
      { "key": "work_type", "label": "공종", "type": "reference", "width": "180px",
        "config": { "source": "custom", "searchApiUrl": "/api/proxy/work-types",
                    "displayField": "name" }, "required": true },
      { "key": "quantity", "label": "수량", "type": "number", "width": "100px",
        "config": { "min": 0, "precision": 2 } },
      { "key": "unit", "label": "단위", "type": "dropdown", "width": "80px",
        "config": { "options": [
          {"label":"m²","value":"m2"}, {"label":"m³","value":"m3"},
          {"label":"m","value":"m"}, {"label":"EA","value":"EA"},
          {"label":"TON","value":"TON"}, {"label":"식","value":"SET"}
        ]}},
      { "key": "unit_price", "label": "단가", "type": "currency", "width": "130px",
        "config": { "currency": "KRW" } },
      { "key": "amount", "label": "금액", "type": "computed", "width": "140px",
        "config": { "formula": "{quantity} * {unit_price}", "format": "currency" } },
      { "key": "start_date", "label": "착수일", "type": "date", "width": "130px" },
      { "key": "end_date", "label": "완료일", "type": "date", "width": "130px" },
      { "key": "progress", "label": "진행률(%)", "type": "number", "width": "100px",
        "config": { "min": 0, "max": 100 } },
      { "key": "note", "label": "비고", "type": "textbox" }
    ],
    "addable": true,
    "deletable": true,
    "reorderable": true,
    "summaryRow": {
      "enabled": true,
      "columns": { "amount": { "type": "sum", "format": "currency" } }
    },
    "emptyMessage": "공정 항목을 추가하세요."
  }
}
```

#### `route-plan` (물류 — 배차/경로)

```jsonc
{
  "preset_id": "route-plan",
  "type": "table",
  "table_config": {
    "columns": [
      { "key": "seq", "label": "순번", "type": "number", "width": "70px" },
      { "key": "origin", "label": "출발지", "type": "reference", "width": "180px",
        "config": { "source": "custom", "searchApiUrl": "/api/proxy/locations",
                    "displayField": "name" }, "required": true },
      { "key": "destination", "label": "도착지", "type": "reference", "width": "180px",
        "config": { "source": "custom", "searchApiUrl": "/api/proxy/locations",
                    "displayField": "name" }, "required": true },
      { "key": "distance", "label": "거리", "type": "unit-value", "width": "120px",
        "config": { "units": ["km", "m"], "defaultUnit": "km", "precision": 1 } },
      { "key": "duration", "label": "소요시간(분)", "type": "number", "width": "110px" },
      { "key": "vehicle", "label": "차량", "type": "reference", "width": "150px",
        "config": { "source": "vehicles", "displayField": "plate_number" } },
      { "key": "freight", "label": "운임", "type": "currency", "width": "130px",
        "config": { "currency": "KRW" } },
      { "key": "note", "label": "비고", "type": "textbox" }
    ],
    "addable": true,
    "deletable": true,
    "reorderable": true,
    "emptyMessage": "경로를 추가하세요."
  }
}
```

#### `pricing` (유통 — 가격정보)

```jsonc
{
  "preset_id": "pricing",
  "type": "table",
  "table_config": {
    "columns": [
      { "key": "customer_type", "label": "거래처유형", "type": "dropdown", "width": "140px",
        "config": { "options": [
          {"label":"도매","value":"WHOLESALE"}, {"label":"소매","value":"RETAIL"},
          {"label":"온라인","value":"ONLINE"}, {"label":"특판","value":"SPECIAL"}
        ]}, "required": true },
      { "key": "unit_price", "label": "단가", "type": "currency", "width": "130px",
        "config": { "currency": "KRW" }, "required": true },
      { "key": "discount_rate", "label": "할인율(%)", "type": "number", "width": "100px",
        "config": { "min": 0, "max": 100, "precision": 1 } },
      { "key": "final_price", "label": "최종가", "type": "computed", "width": "130px",
        "config": { "formula": "{unit_price} * (1 - {discount_rate}/100)", "format": "currency" } },
      { "key": "valid_from", "label": "적용시작", "type": "date", "width": "130px" },
      { "key": "valid_to", "label": "적용종료", "type": "date", "width": "130px" },
      { "key": "note", "label": "비고", "type": "textbox" }
    ],
    "addable": true,
    "deletable": true,
    "emptyMessage": "가격 정보를 추가하세요."
  }
}
```

---

## 6. 산업별 확장 구조

### 6.1 핵심 개념: 4-Level 아키텍처

```
┌──────────────────────────────────────────────────────────────┐
│ Level 1: 필드 타입 컴포넌트 (14종)                             │
│ ─────────────────────────────────────────────────────────── │
│ 코드 레벨. 거의 안 바뀜.                                       │
│ textbox | number | dropdown | checkbox | date | textarea     │
│ reference | multi-select | file | currency | unit-value      │
│ radio | toggle | computed                                    │
│                                                              │
│ → UI 입력의 "원자(atom)" 단위. 모든 산업의 입력 형태를 커버.     │
│ → 새 컴포넌트 추가 = 완전히 새로운 입력 패러다임이 등장할 때만.    │
└──────────────────────────────────────────────────────────────┘
                            ▼
┌──────────────────────────────────────────────────────────────┐
│ Level 2: properties config (JSON)                            │
│ ─────────────────────────────────────────────────────────── │
│ 설정 레벨. 필드 타입의 동작을 결정. 코드 변경 없음.               │
│                                                              │
│ 같은 "reference" 타입이지만:                                   │
│   제조: { "source": "processes" }       → 공정 선택            │
│   공사: { "source": "custom",                                │
│           "searchApiUrl": "/api/proxy/work-types" } → 공종선택 │
│   물류: { "source": "vehicles" }        → 차량 선택            │
│   유통: { "source": "stores" }          → 매장 선택            │
│                                                              │
│ 같은 "unit-value" 타입이지만:                                  │
│   제조: { "units": ["mm","cm","m","inch"] }  → 치수           │
│   물류: { "units": ["km","m"] }              → 거리           │
│   유통: { "units": ["g","kg","ton"] }        → 중량           │
└──────────────────────────────────────────────────────────────┘
                            ▼
┌──────────────────────────────────────────────────────────────┐
│ Level 3: 섹션 프리셋 (JSON)                                   │
│ ─────────────────────────────────────────────────────────── │
│ 템플릿 레벨. 산업별 표준 섹션 구성. 코드 변경 없음.               │
│                                                              │
│ 제조: basic-info + specifications + bom + routing + quality   │
│ 공사: basic-info + site-info + work-schedule + material-plan  │
│ 유통: basic-info + packaging + pricing + store-allocation     │
│ 물류: basic-info + transport-spec + route-plan + loading-plan │
│                                                              │
│ → 관리자가 "섹션 추가" → 프리셋 선택 → 자동 구성               │
│ → 새 프리셋 = JSON 추가만, 컴포넌트 수정 없음                   │
└──────────────────────────────────────────────────────────────┘
                            ▼
┌──────────────────────────────────────────────────────────────┐
│ Level 4: reference sources (API URL)                         │
│ ─────────────────────────────────────────────────────────── │
│ 연결 레벨. 새 데이터 소스를 reference 필드에 연결. 코드 변경 없음. │
│                                                              │
│ 새 산업/도메인 추가 시:                                        │
│   1. 백엔드에 API 추가 (예: /api/proxy/work-types)            │
│   2. reference 필드에 source: "custom" + searchApiUrl 설정    │
│   3. 끝. 프론트 코드 수정 없음.                                │
│                                                              │
│ 자주 사용되는 source는 프리셋으로 등록:                          │
│   reference-sources.ts에 추가 → source: "work_types"로 단축   │
└──────────────────────────────────────────────────────────────┘
```

### 6.2 산업별 변경 범위 매트릭스

| 변경 항목 | 코드 변경 | DB 변경 | config 변경 |
|----------|----------|---------|------------|
| 새 필드 타입 추가 | ✅ 컴포넌트 1개 | ✅ field_type 값 추가 | — |
| 새 reference 소스 추가 | ❌ | ✅ API 엔드포인트 | ✅ source config |
| 새 테이블 섹션 구성 | ❌ | ✅ section + properties | ✅ table_config JSON |
| 새 섹션 프리셋 추가 | ❌ (또는 프리셋 JSON 1건) | ❌ | ✅ 프리셋 JSON |
| 새 산업 진출 | ❌ | ✅ API들 | ✅ 프리셋 + source |
| 테넌트별 커스터마이징 | ❌ | ✅ 테넌트 config | ❌ |

> **핵심**: "새 산업 진출" 시에도 프론트엔드 코드 변경 = 0줄.
> 백엔드 API + config JSON만 추가.

### 6.3 산업별 구성 예시

#### 제조업 테넌트 (금속 가공)

```
페이지: 제품(FG) 등록
├── 기본정보 (fields)
│   ├── 품목코드 [textbox, required]
│   ├── 품목명 [textbox, required]
│   ├── 품목유형 [dropdown: FG/PT/SM/RM/CS]
│   ├── 단위 [dropdown: EA/SET/M]
│   └── 상태 [toggle: 활성/비활성]
├── 규격/치수 (fields)
│   ├── 두께 [unit-value: mm/cm/inch]
│   ├── 너비 [unit-value: mm/cm/m]
│   ├── 높이 [unit-value: mm/cm/m]
│   ├── 무게 [unit-value: g/kg/ton]
│   ├── 재질 [reference → materials]
│   └── 표면처리 [reference → surface_treatments]
├── BOM (bom) — 기존 유지
├── 공정/라우팅 (table)
│   └── [공정, 작업장, 셋업시간, 사이클타임, 비고]
├── 품질검사 (table)
│   └── [검사항목, 규격, 상한, 하한, 검사방법, 측정장비]
└── 도면 (fields)
    ├── 도면파일 [file: .pdf/.dwg/.dxf]
    ├── 도면번호 [textbox]
    └── 도면버전 [textbox]
```

#### 공사관리 테넌트 (건설)

```
페이지: 공사 항목 등록
├── 기본정보 (fields)
│   ├── 항목코드 [textbox, required]
│   ├── 항목명 [textbox, required]
│   ├── 공사구분 [dropdown: 토목/건축/설비/전기]
│   └── 상태 [toggle]
├── 현장정보 (fields)
│   ├── 현장 [reference → sites]
│   ├── 발주처 [reference → customers]
│   ├── 감리사 [reference → vendors]
│   ├── 착공일 [date]
│   ├── 준공예정일 [date]
│   └── 공사금액 [currency: KRW]
├── 공정표 (table)
│   └── [공종, 수량, 단위, 단가, 금액(computed), 착수일, 완료일, 진행률]
├── 자재투입계획 (table)
│   └── [자재(reference→items), 수량, 단위, 투입시기, 발주여부(checkbox)]
├── 인력투입계획 (table)
│   └── [직종, 인원, 기간(일), 일단가(currency), 금액(computed)]
└── 안전점검 (table)
    └── [점검항목, 점검주기(dropdown), 담당자(reference→employees), 최근점검일(date)]
```

#### 유통업 테넌트 (도소매)

```
페이지: 상품 등록
├── 기본정보 (fields)
│   ├── 상품코드 [textbox, required]
│   ├── 상품명 [textbox, required]
│   ├── 카테고리 [reference → categories]
│   ├── 브랜드 [reference → brands]
│   └── 상태 [toggle]
├── 포장정보 (fields)
│   ├── 포장단위 [dropdown: 낱개/박스/팔레트]
│   ├── 입수량 [number]
│   ├── 바코드 [textbox]
│   ├── 중량 [unit-value: g/kg]
│   └── 상품이미지 [file: .jpg/.png, maxFiles:5]
├── 가격정보 (table)
│   └── [거래처유형, 단가, 할인율, 최종가(computed), 적용기간]
├── 매장배분 (table)
│   └── [매장(reference→stores), 배분수량, 배분일, 상태(dropdown)]
└── 프로모션 (table)
    └── [프로모션명, 할인율, 시작일, 종료일, 적용조건]
```

#### 물류업 테넌트 (운송)

```
페이지: 화물 등록
├── 기본정보 (fields)
│   ├── 화물코드 [textbox, required]
│   ├── 화물명 [textbox, required]
│   ├── 화물유형 [dropdown: 일반/냉장/냉동/위험물]
│   └── 상태 [toggle]
├── 운송규격 (fields)
│   ├── 총중량 [unit-value: kg/ton]
│   ├── 부피 [unit-value: m³/CBM]
│   ├── 위험물등급 [dropdown: 1~9등급/해당없음]
│   ├── 보관온도 [unit-value: ℃]
│   ├── 적재방법 [radio: 팔레트/산적/컨테이너]
│   └── 특수요구사항 [textarea]
├── 배차/경로 (table)
│   └── [출발지, 도착지, 거리, 소요시간, 차량(reference), 운임(currency)]
├── 적재계획 (table)
│   └── [품목(reference→items), 수량, 중량, 적재순서, 위치]
└── 추적정보 (table)
    └── [일시(date), 위치, 상태(dropdown), 온도(number), 비고]
```

### 6.4 코드 변경이 필요한 예외 케이스

14종 필드 타입으로 커버 불가능한 **완전히 새로운 입력 패러다임**:

| 새 입력 패러다임 | 필요한 field_type | 작업량 |
|----------------|-----------------|-------|
| 지도 위치 선택 (GPS 좌표) | `map-picker` | 컴포넌트 1개 + switch 1줄 |
| 간트차트 편집 | `gantt` (섹션 타입) | 섹션 컴포넌트 1개 |
| 전자서명/도장 | `signature` | 컴포넌트 1개 + switch 1줄 |
| 바코드/QR 스캔 | `barcode-scan` | 컴포넌트 1개 + switch 1줄 |
| 조직도 선택 | `org-chart-picker` | 컴포넌트 1개 + switch 1줄 |
| 색상 선택 (컬러피커) | `color-picker` | 컴포넌트 1개 + switch 1줄 |

> 이런 경우에도 **컴포넌트 1개 파일 추가 + switch문 1줄 추가**로 끝.
> 기존 코드 수정 없음. 다른 필드 타입에 영향 없음.

### 6.5 확장 가능성 요약

```
Q: 새 산업(예: 의료)을 추가하려면?
A: 프론트 코드 변경 0줄.
   1. 백엔드에 의료 도메인 API 추가 (환자, 의약품, 의료기기 등)
   2. reference source config 추가 (JSON)
   3. 의료 섹션 프리셋 추가 (JSON)
   4. 테넌트 생성 시 의료 프리셋 자동 적용

Q: 새 필드 타입(예: 지도)이 필요하면?
A: MapPickerField.tsx 1개 생성 + switch 1줄 추가.
   기존 14종 컴포넌트/기존 config/기존 프리셋 전부 영향 없음.

Q: 기존 테넌트가 산업을 추가하면? (제조 + 물류 겸업)
A: 해당 테넌트의 페이지에 물류 프리셋 섹션만 추가.
   코드 변경 없음. Admin UI에서 클릭으로 완료.
```

---

## 7. 백엔드 API 계약

### 7.1 field_type 확장 — DB 변경

```sql
-- item_fields 테이블의 field_type 컬럼 확장
-- 기존: ENUM('textbox','number','dropdown','checkbox','date','textarea')
-- 변경: VARCHAR(30) 또는 ENUM 확장

ALTER TABLE item_fields
MODIFY COLUMN field_type VARCHAR(30) NOT NULL DEFAULT 'textbox';

-- 허용 값: textbox, number, dropdown, checkbox, date, textarea,
--          reference, multi-select, file, currency, unit-value, radio, toggle, computed
-- 향후 추가 가능: map-picker, signature, barcode-scan 등
```

### 7.2 section type 확장

```sql
ALTER TABLE item_sections
MODIFY COLUMN type VARCHAR(20) NOT NULL DEFAULT 'fields';

-- 허용 값: fields, bom, table
-- 향후 추가 가능: gantt, calendar 등
```

### 7.3 properties 필드 활용

**기존 `properties` 컬럼** (`JSON` 타입)이 이미 `item_fields`와 `item_sections` 테이블에 존재함.
신규 필드 타입의 config는 이 컬럼에 저장.

```sql
-- reference 타입 필드
UPDATE item_fields SET
  field_type = 'reference',
  properties = '{"source":"vendors","displayField":"name","searchFields":["name","code"]}'
WHERE id = 100;

-- table 타입 섹션
UPDATE item_sections SET
  type = 'table',
  properties = '{"table_config":{"columns":[...],"addable":true}}'
WHERE id = 50;
```

### 7.4 Init API / Page Structure API — 변경 없음

기존 응답 구조 그대로. `field_type`과 `properties`에 새로운 값이 들어올 뿐.

```jsonc
// GET /v1/item-master/pages/{id}/structure — 응답 구조 동일
{
  "page": { ... },
  "sections": [
    {
      "section": { "type": "fields", ... },
      "fields": [
        { "field": { "field_type": "reference", "properties": { "source": "vendors" } } }
      ]
    },
    {
      "section": {
        "type": "table",
        "properties": { "table_config": { "columns": [...] } }
      },
      "fields": [],
      "bom_items": []
    }
  ]
}
```

### 7.5 테이블 섹션 데이터 CRUD API (신규)

```
GET  /v1/items/{itemId}/section-data/{sectionId}
→ { "data": [{ "process": "CUT-001", "cycle_time": 5.0, ... }, ...] }

PUT  /v1/items/{itemId}/section-data/{sectionId}
← { "rows": [{ "process": "CUT-001", "cycle_time": 5.0, ... }, ...] }

POST /v1/items/{itemId}/section-data/{sectionId}
← { "process": "CUT-001", "cycle_time": 5.0, ... }

DELETE /v1/items/{itemId}/section-data/{sectionId}/{rowId}
```

### 7.6 Reference 필드 검색 API

기존 API 활용 + custom source:

| source | API | 비고 |
|--------|-----|------|
| vendors | `GET /v1/vendors?search={q}&size=20` | 기존 |
| items | `GET /v1/items?search={q}&size=20` | 기존 |
| customers | `GET /v1/customers?search={q}&size=20` | 기존 |
| employees | `GET /v1/employees?search={q}&size=20` | 기존 |
| processes | `GET /v1/processes?search={q}&size=20` | 기존 |
| warehouses | `GET /v1/warehouses?search={q}&size=20` | 기존 |
| materials | `GET /v1/item-master/materials?search={q}` | 기존 |
| custom | `properties.searchApiUrl?search={q}&size=20` | **신규 산업별 API** |

> 새 산업 추가 시: 해당 도메인 API 생성 → source: "custom" + searchApiUrl 설정

### 7.7 파일 업로드 API

```
POST   /v1/files/upload     ← multipart/form-data
GET    /v1/files/{fileId}   → binary
DELETE /v1/files/{fileId}
```

---

## 8. 프론트엔드 컴포넌트 아키텍처

### 8.1 파일 구조 (신규 추가분)

```
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
├── presets/
│   ├── index.ts                     # ★ 신규: 프리셋 레지스트리
│   └── section-presets.ts           # ★ 신규: 전 산업 프리셋 정의
└── config/
    └── reference-sources.ts         # ★ 신규: 참조 소스 프리셋
```

### 8.2 DynamicFieldRenderer 확장

```typescript
export function DynamicFieldRenderer(props: DynamicFieldRendererProps) {
  switch (props.field.field_type) {
    // 기존 6종 (변경 없음)
    case 'textbox':     return <TextField {...props} />;
    case 'number':      return <NumberField {...props} />;
    case 'dropdown':    return <DropdownField {...props} />;
    case 'checkbox':    return <CheckboxField {...props} />;
    case 'date':        return <DateField {...props} />;
    case 'textarea':    return <TextareaField {...props} />;
    // Phase 1
    case 'reference':     return <ReferenceField {...props} />;
    case 'multi-select':  return <MultiSelectField {...props} />;
    case 'file':          return <FileField {...props} />;
    // Phase 2
    case 'currency':      return <CurrencyField {...props} />;
    case 'unit-value':    return <UnitValueField {...props} />;
    case 'radio':         return <RadioField {...props} />;
    // Phase 3
    case 'toggle':        return <ToggleField {...props} />;
    case 'computed':      return <ComputedField {...props} />;
    default:
      return <TextField {...props} />;
  }
}
```

### 8.3 테이블 셀 = 필드 컴포넌트 재사용

```typescript
// sections/TableCellRenderer.tsx
// DynamicFieldRenderer를 테이블 셀용으로 래핑 (축소 UI)
export function TableCellRenderer({ column, value, onChange }: TableCellProps) {
  // column config → ItemFieldResponse 호환 객체로 변환
  const fieldLike: ItemFieldResponse = {
    field_type: column.type,
    field_name: column.label,
    properties: column.config,
    options: column.config?.options,
    is_required: column.required || false,
    // ... 최소 필수 필드
  };

  return (
    <DynamicFieldRenderer
      field={fieldLike}
      value={value}
      onChange={onChange}
      compact={true}  // 테이블 셀용 축소 모드
    />
  );
}
```

### 8.4 참조 소스 프리셋

```typescript
// config/reference-sources.ts
export const REFERENCE_SOURCES: Record<string, ReferenceSourceConfig> = {
  // 공통
  vendors:     { apiUrl: '/api/proxy/vendors', displayField: 'name', ... },
  items:       { apiUrl: '/api/proxy/items', displayField: 'name', ... },
  customers:   { apiUrl: '/api/proxy/customers', displayField: 'company_name', ... },
  employees:   { apiUrl: '/api/proxy/employees', displayField: 'name', ... },
  warehouses:  { apiUrl: '/api/proxy/warehouses', displayField: 'name', ... },
  // 제조
  processes:   { apiUrl: '/api/proxy/processes', displayField: 'process_name', ... },
  materials:   { apiUrl: '/api/proxy/item-master/materials', displayField: 'material_name', ... },
  surface_treatments: { apiUrl: '/api/proxy/item-master/surface-treatments', ... },
  // 공사
  sites:       { apiUrl: '/api/proxy/sites', displayField: 'site_name', ... },
  // 물류
  vehicles:    { apiUrl: '/api/proxy/vehicles', displayField: 'plate_number', ... },
  routes:      { apiUrl: '/api/proxy/routes', displayField: 'route_name', ... },
  // 유통
  stores:      { apiUrl: '/api/proxy/stores', displayField: 'store_name', ... },
};
// "custom" source → properties.searchApiUrl 직접 사용
```

---

## 9. 조건부 표시 확장

### 9.1 현재 (유지)

```jsonc
{ "fieldKey": "item_type", "expectedValue": "FG", "targetFieldIds": ["150"] }
```

### 9.2 확장: 비교 연산자 지원

```jsonc
{ "fieldKey": "item_type", "operator": "in", "expectedValue": ["FG","PT"], "targetFieldIds": ["150"] }
```

| operator | 설명 | 하위 호환 |
|----------|------|----------|
| `equals` | 같음 (기본) | ✅ operator 없으면 equals |
| `not_equals` | 다름 | |
| `in` | 배열 포함 | |
| `not_in` | 배열 미포함 | |
| `is_empty` | 비어있음 | |
| `is_not_empty` | 비어있지 않음 | |
| `greater_than` | 초과 | |
| `less_than` | 미만 | |

---

## 10. 검증 프레임워크

### 10.1 필드 타입별 추가 검증

| field_type | 추가 검증 |
|-----------|----------|
| `reference` | 선택 항목 존재 여부 |
| `multi-select` | maxSelections 초과 |
| `file` | maxSize, accept 타입, maxFiles |
| `currency` | allowNegative, precision |
| `unit-value` | 값이 숫자, 단위가 유효 |
| `computed` | 검증 없음 (자동 계산) |

### 10.2 테이블 섹션 검증

```typescript
function validateTableRows(rows, columns): string[] {
  const errors = [];
  rows.forEach((row, idx) => {
    columns.forEach(col => {
      if (col.required && !row[col.key]) {
        errors.push(`${idx + 1}행: ${col.label}은(는) 필수입니다.`);
      }
    });
  });
  return errors;
}
```

---

## 11. 구현 로드맵

### Phase 1: 핵심 확장 (🔴)

| 작업 | 예상 줄 수 |
|------|-----------|
| ReferenceField | ~200 |
| MultiSelectField | ~120 |
| FileField | ~180 |
| DynamicTableSection + TableCellRenderer | ~450 |
| reference-sources.ts | ~120 |
| 타입 정의 확장 | +50 |
| DynamicFieldRenderer switch 확장 | +10 |
| **총** | **~1,130줄 신규, ~30줄 수정** |

### Phase 2: 편의 필드 (🟡)

| 작업 | 예상 줄 수 |
|------|-----------|
| CurrencyField | ~80 |
| UnitValueField | ~100 |
| RadioField | ~60 |
| 섹션 프리셋 라이브러리 (전 산업) | ~400 |
| 프리셋 선택 UI | +100 |
| **총** | **~740줄 신규** |

### Phase 3: 고급 필드 (🟢)

| 작업 | 예상 줄 수 |
|------|-----------|
| ToggleField | ~50 |
| ComputedField | ~120 |
| 조건부 표시 연산자 확장 | +40 |
| 테이블 검증 강화 | +60 |
| **총** | **~270줄 신규** |

### 백엔드 작업 (프론트와 병렬)

| 작업 | 설명 |
|------|------|
| field_type 컬럼 확장 | VARCHAR(30) |
| section type 확장 | 'table' 추가 |
| 테이블 데이터 API | section-data CRUD |
| 산업별 도메인 API | 해당 산업 진출 시 추가 |
| 프리셋 시딩 | 테넌트 생성 시 산업별 프리셋 자동 적용 |

---

## 부록

### A. 기존 코드 영향 분석

| 기존 파일 | 변경 | 내용 |
|----------|------|------|
| `DynamicFieldRenderer.tsx` | switch 추가 | +8 case문 |
| `DynamicItemForm/index.tsx` | 섹션 렌더링 | +10줄 (table case) |
| `types.ts` | 타입 확장 | field_type union + 신규 인터페이스 |
| `item-master-api.ts` | field_type 확장 | union 값 추가 |
| **기존 필드 컴포넌트 6개** | **변경 없음** | |
| **DynamicBOMSection** | **변경 없음** | |
| **hooks 7개** | **변경 없음** | |

### B. 전체 아키텍처 다이어그램

```
┌───────────────────────────────────────────────────────────┐
│                    Admin (품목기준관리)                       │
│  → 산업 선택 → 프리셋 선택 → 필드 config 설정                │
└────────────────────┬──────────────────────────────────────┘
                     │ 저장 (field_type + properties JSON)
                     ▼
┌───────────────────────────────────────────────────────────┐
│                      백엔드 DB                              │
│  item_pages → item_sections → item_fields                  │
│              type: fields/bom/table                        │
│              properties: { table_config / field config }    │
│                                                            │
│  field_type (14종): 모든 산업의 입력 원자 단위                 │
│  properties (JSON): 산업/테넌트별 동작 결정                   │
└────────────────────┬──────────────────────────────────────┘
                     │ 조회 (기존 API 구조 그대로)
                     ▼
┌───────────────────────────────────────────────────────────┐
│                User (품목 등록/수정)                          │
│  DynamicItemForm                                           │
│  ├─ DynamicFieldRenderer (14종 switch)                     │
│  │   └─ 각 컴포넌트가 properties를 읽어 동작 결정              │
│  ├─ DynamicBOMSection (기존 유지)                            │
│  └─ DynamicTableSection (columns config 기반 렌더링)         │
│      └─ TableCellRenderer → DynamicFieldRenderer 재사용     │
└───────────────────────────────────────────────────────────┘
```

---

**문서 버전**: 1.1 (산업별 확장 구조 추가)
**마지막 업데이트**: 2026-02-11
**다음 단계**: 백엔드 검토 → Phase 1 구현 착수
