- DynamicFieldRenderer에 신규 필드 타입 추가 (Currency, File, MultiSelect, Radio, Reference, Toggle, UnitValue, Computed) - DynamicTableSection 및 TableCellRenderer 추가 - 필드 프리셋 및 설정 구조 분리 - 컴포넌트 레지스트리 개발 도구 페이지 추가 - UniversalListPage 개선 - 근태관리 코드 정리 - 즐겨찾기 기능 및 동적 필드 타입 백엔드 스펙 문서 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
12 KiB
동적 필드 타입 확장 — 백엔드 API 스펙
작성일: 2026-02-12 프론트 구현 완료:
[IMPL-2026-02-11] dynamic-field-components.md설계 근거:[DESIGN-2026-02-11] dynamic-field-type-extension.md
요약
프론트엔드에서 14종 필드 타입 + table 섹션 타입 + 조건부 표시 연산자 9종을 렌더링할 준비가 완료되었습니다.
백엔드에서 해당 field_type과 properties JSON을 DB에 저장하고 API로 반환하면, 프론트에서 추가 작업 없이 자동 렌더링됩니다.
1. field_type 확장
현재 (6종)
textbox | number | dropdown | checkbox | date | textarea
추가 필요 (8종)
reference | multi-select | file | currency | unit-value | radio | toggle | computed
전체 (14종)
-- item_fields.field_type 컬럼의 허용값
ENUM('textbox', 'number', 'dropdown', 'checkbox', 'date', 'textarea',
'reference', 'multi-select', 'file',
'currency', 'unit-value', 'radio',
'toggle', 'computed')
영향 범위
item_fields테이블의field_type컬럼item_master_fields테이블의field_type컬럼 (deprecated지만 아직 사용 중이면)- 필드 생성/수정 API의 validation rule
2. 각 field_type별 properties JSON 스펙
모든 설정은 기존 item_fields.properties JSON 컬럼에 저장합니다.
2-1. reference (다른 테이블 검색/선택)
{
"source": "vendors", // 필수. 프리셋: vendors|items|customers|employees|warehouses|processes|equipment|sites|vehicles
"displayField": "name", // 선택. 기본 "name"
"valueField": "id", // 선택. 기본 "id"
"searchFields": ["name"], // 선택. 검색 대상 필드
"searchApiUrl": "/api/...", // source="custom"일 때만 필수
"columns": [ // 선택. 검색 모달 표시 컬럼
{ "key": "code", "label": "코드", "width": "100px" },
{ "key": "name", "label": "이름" }
],
"displayFormat": "{code} - {name}", // 선택. 선택 후 표시 포맷
"returnFields": ["id", "code", "name"] // 선택. 프론트에 반환할 필드
}
저장값: string (선택한 항목의 valueField 값, 기본 id)
프론트 동작: source 프리셋이 있으면 /api/proxy/{source}?search={query} 호출. 없으면 searchApiUrl 사용.
2-2. multi-select (복수 선택 태그)
{
"maxSelections": 5, // 선택. 최대 선택 수 (기본: 무제한)
"allowCustom": false, // 선택. 직접 입력 허용 (기본: false)
"layout": "chips" // 선택. "chips" | "list" (기본: "chips")
}
options: 기존 dropdown과 동일 [{label, value}] — item_fields.options 컬럼 사용
저장값: string[] JSON (예: ["CUT","BEND","WELD"])
2-3. file (파일/이미지 첨부)
{
"accept": ".pdf,.doc,.jpg,.png", // 선택. 허용 확장자 (기본: "*")
"maxSize": 10485760, // 선택. 최대 파일 크기 bytes (기본: 10MB)
"maxFiles": 3, // 선택. 최대 파일 수 (기본: 1)
"preview": true, // 선택. 이미지 미리보기 (기본: true)
"category": "drawing" // 선택. 파일 카테고리 태그
}
저장값: 파일 업로드 후 반환된 URL 또는 file ID
필요 API: POST /v1/files/upload (multipart) — 이미 있으면 그대로 사용
2-4. currency (통화 금액)
{
"currency": "KRW", // 선택. 기본 "KRW". 지원: KRW|USD|EUR|JPY|CNY|GBP
"precision": 0, // 선택. 소수점 자리 (기본: 0)
"showSymbol": true, // 선택. 통화 기호 표시 (기본: true)
"allowNegative": false // 선택. 음수 허용 (기본: false)
}
저장값: number (포맷 없는 원시 숫자)
2-5. unit-value (값 + 단위 조합)
{
"units": [ // 필수. 단위 목록
{ "label": "mm", "value": "mm" },
{ "label": "cm", "value": "cm" },
{ "label": "m", "value": "m" }
],
"defaultUnit": "mm", // 선택. 기본 단위
"precision": 2 // 선택. 소수점 자리
}
저장값: {"value": 100, "unit": "mm"} JSON
2-6. radio (라디오 버튼 그룹)
{
"layout": "horizontal" // 선택. "horizontal" | "vertical" (기본: "vertical")
}
options: 기존 dropdown과 동일 [{label, value}] — item_fields.options 컬럼 사용
저장값: string (선택한 value)
2-7. toggle (On/Off 스위치)
{
"onLabel": "사용", // 선택. 기본 "ON"
"offLabel": "미사용", // 선택. 기본 "OFF"
"onValue": "Y", // 선택. 기본 "true"
"offValue": "N" // 선택. 기본 "false"
}
저장값: string (onValue 또는 offValue)
2-8. computed (계산 필드, 읽기전용)
{
"formula": "unit_price * quantity", // 필수. 수식 (field_key 참조)
"dependsOn": ["unit_price", "quantity"], // 필수. 의존 필드 field_key 목록
"format": "currency", // 선택. "currency" | "number" | "percent"
"precision": 0 // 선택. 소수점 자리
}
저장값: number (프론트에서 자동 계산, 백엔드 저장 시에도 계산 검증 권장)
지원 연산: +, -, *, /, (, ) — field_key를 변수로 사용
3. section type 확장
현재
item_sections.type: ENUM('fields', 'bom')
추가 필요
item_sections.type: ENUM('fields', 'bom', 'table')
table 섹션의 properties JSON
item_sections.properties 컬럼 (신규 또는 기존 description 등과 함께):
{
"tableConfig": {
"columns": [
{
"key": "process_name", // 컬럼 키
"label": "공정명", // 컬럼 헤더
"fieldType": "reference", // 셀 입력 타입 (ItemFieldType)
"width": "150px", // 선택
"isRequired": true, // 선택
"isReadonly": false, // 선택
"options": [...], // dropdown/radio/multi-select용
"properties": { // 해당 필드 타입의 properties
"source": "processes"
}
},
{
"key": "cycle_time",
"label": "사이클타임(초)",
"fieldType": "number",
"width": "100px"
}
],
"minRows": 1, // 선택. 최소 행 수
"maxRows": 30, // 선택. 최대 행 수
"summaryRow": [ // 선택. 합계행
{ "columnKey": "cycle_time", "type": "sum" },
{ "columnKey": "setup_time", "type": "avg" }
]
}
}
summaryRow type: "sum" | "avg" | "count" | "label"
table 섹션 데이터 저장/조회 API
GET /v1/items/{itemId}/section-data/{sectionId}
PUT /v1/items/{itemId}/section-data/{sectionId}
요청/응답 body:
{
"rows": [
{ "process_name": "절단", "cycle_time": 30, "setup_time": 10 },
{ "process_name": "절곡", "cycle_time": 45, "setup_time": 15 }
]
}
이미 유사한 API가 있으면 그대로 사용. 없으면 신규 생성 필요.
4. display_condition 연산자 확장
현재
// item_fields.display_condition
{
"fieldConditions": [
{
"fieldKey": "item_type",
"expectedValue": "FG", // 정확히 일치 (equals만)
"targetFieldIds": ["10", "11"]
}
]
}
확장 (operator 필드 추가)
{
"fieldConditions": [
{
"fieldKey": "item_type",
"operator": "in", // 신규: 연산자
"expectedValue": "FG,PT", // in/not_in은 콤마 구분
"targetFieldIds": ["10", "11"]
},
{
"fieldKey": "quantity",
"operator": "greater_than",
"expectedValue": "100",
"targetSectionIds": ["5"]
}
]
}
지원 연산자 목록
| operator | 설명 | expectedValue 형식 | 예시 |
|---|---|---|---|
equals |
정확히 일치 (기본값) | 단일값 | "FG" |
not_equals |
불일치 | 단일값 | "RM" |
in |
목록 중 하나 일치 | 콤마 구분 | "FG,PT,SM" |
not_in |
목록 중 어느 것에도 불일치 | 콤마 구분 | "RM,CS" |
greater_than |
초과 (숫자) | 숫자 문자열 | "100" |
less_than |
미만 (숫자) | 숫자 문자열 | "0" |
gte |
이상 (숫자) | 숫자 문자열 | "50" |
lte |
이하 (숫자) | 숫자 문자열 | "1000" |
contains |
부분 문자열 포함 | 문자열 | "강판" |
하위호환: operator가 없으면 프론트에서 equals로 처리. 기존 데이터 마이그레이션 불필요.
5. 기존 API 변경 없음 (확인)
다음 API들은 이미 field_type과 properties를 통과시키므로 변경 불필요:
| API | 용도 | 비고 |
|---|---|---|
GET /v1/item-master/pages/{id}/structure |
페이지 구조 조회 | field_type, properties 이미 반환 |
POST /v1/item-master/fields |
필드 생성 | field_type, properties 이미 수신 |
PUT /v1/item-master/fields/{id} |
필드 수정 | 동일 |
GET /v1/item-master/init |
초기화 조회 | field_type 이미 반환 |
변경이 필요한 부분:
field_typevalidation rule에 8종 추가item_sections.type에'table'추가- (선택) table 섹션 데이터 저장/조회 API 신규
6. 작업 우선순위 제안
즉시 (프론트와 바로 연동 가능)
| # | 작업 | 난이도 | 설명 |
|---|---|---|---|
| 1 | field_type 허용값 8종 추가 | 낮음 | DB ENUM 또는 validation 수정만 |
| 2 | display_condition operator 저장 허용 | 낮음 | JSON이므로 스키마 변경 없음 |
다음 단계
| # | 작업 | 난이도 | 설명 |
|---|---|---|---|
| 3 | section type 'table' 추가 | 중간 | DB + API validation |
| 4 | table 섹션 데이터 API | 중간 | 신규 엔드포인트 (CRUD) |
| 5 | reference 소스별 검색 API | 높음 | 각 소스(vendors, items 등)의 검색 API 표준화 |
나중 (필요 시)
| # | 작업 | 난이도 | 설명 |
|---|---|---|---|
| 6 | 파일 업로드 API | 높음 | POST /v1/files/upload (이미 있으면 불필요) |
| 7 | computed 필드 서버사이드 검증 | 중간 | 저장 시 수식 재계산하여 값 검증 |
7. 테스트 방법
백엔드 작업 완료 후 확인 순서:
- 페이지 빌더 (
/dev/page-builder)에서 신규 field_type으로 필드 추가 - 품목 등록 페이지에서 해당 필드가 올바르게 렌더링되는지 확인
- 저장/조회 사이클 테스트 (값이 properties 형식에 맞게 저장/복원되는지)
부록: 프론트 파일 매핑
| field_type | 프론트 컴포넌트 | 파일 |
|---|---|---|
| textbox | TextField | DynamicItemForm/fields/TextField.tsx |
| number | NumberField | DynamicItemForm/fields/NumberField.tsx |
| dropdown | DropdownField | DynamicItemForm/fields/DropdownField.tsx |
| checkbox | CheckboxField | DynamicItemForm/fields/CheckboxField.tsx |
| date | DateField | DynamicItemForm/fields/DateField.tsx |
| textarea | TextareaField | DynamicItemForm/fields/TextareaField.tsx |
| reference | ReferenceField | DynamicItemForm/fields/ReferenceField.tsx |
| multi-select | MultiSelectField | DynamicItemForm/fields/MultiSelectField.tsx |
| file | FileField | DynamicItemForm/fields/FileField.tsx |
| currency | CurrencyField | DynamicItemForm/fields/CurrencyField.tsx |
| unit-value | UnitValueField | DynamicItemForm/fields/UnitValueField.tsx |
| radio | RadioField | DynamicItemForm/fields/RadioField.tsx |
| toggle | ToggleField | DynamicItemForm/fields/ToggleField.tsx |
| computed | ComputedField | DynamicItemForm/fields/ComputedField.tsx |
| section type | 프론트 컴포넌트 | 파일 |
|---|---|---|
| fields | (기존 필드 렌더링) | DynamicItemForm/index.tsx |
| bom | DynamicBOMSection | DynamicItemForm/sections/DynamicBOMSection.tsx |
| table | DynamicTableSection | DynamicItemForm/sections/DynamicTableSection.tsx |