Files
sam-react-prod/claudedocs/architecture/[IMPL-2026-02-11] dynamic-field-components.md
유병철 020d74f36c feat(WEB): DynamicItemForm 필드 타입 확장 및 컴포넌트 레지스트리 추가
- DynamicFieldRenderer에 신규 필드 타입 추가 (Currency, File, MultiSelect, Radio, Reference, Toggle, UnitValue, Computed)
- DynamicTableSection 및 TableCellRenderer 추가
- 필드 프리셋 및 설정 구조 분리
- 컴포넌트 레지스트리 개발 도구 페이지 추가
- UniversalListPage 개선
- 근태관리 코드 정리
- 즐겨찾기 기능 및 동적 필드 타입 백엔드 스펙 문서 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 11:17:57 +09:00

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 + 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에서 읽는 값:

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