Merge branch 'feature/v2-unified-renewal' of http://39.117.244.52:3000/kjs/ERP-node into feature/v2-renewal
; Please enter a commit message to explain why this merge is necessary, ; especially if it merges an updated upstream into a topic branch. ; ; Lines starting with ';' will be ignored, and an empty message aborts ; the commit.
This commit is contained in:
commit
42ad8cddb3
|
|
@ -12,19 +12,20 @@ alwaysApply: false
|
|||
## 목차
|
||||
|
||||
1. [V2 컴포넌트 규칙 (최우선)](#1-v2-컴포넌트-규칙-최우선)
|
||||
2. [표준 Props 인터페이스](#2-표준-props-인터페이스)
|
||||
3. [멀티테넌시 (company_code)](#3-멀티테넌시-company_code)
|
||||
4. [디자인 모드 vs 인터랙티브 모드](#4-디자인-모드-vs-인터랙티브-모드)
|
||||
5. [로딩 및 에러 처리](#5-로딩-및-에러-처리)
|
||||
6. [테이블 컬럼 기반 입력 위젯](#6-테이블-컬럼-기반-입력-위젯)
|
||||
7. [컴포넌트별 테이블 설정](#7-컴포넌트별-테이블-설정)
|
||||
8. [엔티티 조인 컬럼 활용](#8-엔티티-조인-컬럼-활용)
|
||||
9. [폼 데이터 관리](#9-폼-데이터-관리)
|
||||
10. [다국어 지원](#10-다국어-지원)
|
||||
11. [저장 버튼 및 플로우 연동](#11-저장-버튼-및-플로우-연동)
|
||||
12. [표준 코드 스타일 가이드](#12-표준-코드-스타일-가이드)
|
||||
13. [성능 최적화](#13-성능-최적화)
|
||||
14. [체크리스트](#14-체크리스트)
|
||||
2. [V2 + Zod 레이아웃 저장/로드 시스템 (핵심)](#2-v2--zod-레이아웃-저장로드-시스템-핵심)
|
||||
3. [표준 Props 인터페이스](#3-표준-props-인터페이스)
|
||||
4. [멀티테넌시 (company_code)](#4-멀티테넌시-company_code)
|
||||
5. [디자인 모드 vs 인터랙티브 모드](#5-디자인-모드-vs-인터랙티브-모드)
|
||||
6. [로딩 및 에러 처리](#6-로딩-및-에러-처리)
|
||||
7. [테이블 컬럼 기반 입력 위젯](#7-테이블-컬럼-기반-입력-위젯)
|
||||
8. [컴포넌트별 테이블 설정](#8-컴포넌트별-테이블-설정)
|
||||
9. [엔티티 조인 컬럼 활용](#9-엔티티-조인-컬럼-활용)
|
||||
10. [폼 데이터 관리](#10-폼-데이터-관리)
|
||||
11. [다국어 지원](#11-다국어-지원)
|
||||
12. [저장 버튼 및 플로우 연동](#12-저장-버튼-및-플로우-연동)
|
||||
13. [표준 코드 스타일 가이드](#13-표준-코드-스타일-가이드)
|
||||
14. [성능 최적화](#14-성능-최적화)
|
||||
15. [체크리스트](#15-체크리스트)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -50,7 +51,7 @@ alwaysApply: false
|
|||
| `v2-split-panel-layout` | 분할 패널 | 좌우/상하 분할 |
|
||||
| `v2-numbering-rule` | 채번 규칙 | 자동 채번 생성 |
|
||||
| `v2-tabs-widget` | 탭 위젯 | 탭 레이아웃 |
|
||||
| `v2-unified-repeater` | 통합 리피터 | 행 단위 입력/저장 |
|
||||
| `v2-repeater` | 통합 리피터 | 행 단위 입력/저장 |
|
||||
| `v2-rack-structure` | 렉 구조 | 창고 렉 위치 생성 |
|
||||
| `v2-section-paper` | 섹션 페이퍼 | 섹션 컨테이너 |
|
||||
| `v2-section-card` | 섹션 카드 | 카드 컨테이너 |
|
||||
|
|
@ -90,7 +91,212 @@ export const V2TableListDefinition = createComponentDefinition({
|
|||
|
||||
---
|
||||
|
||||
## 2. 표준 Props 인터페이스
|
||||
## 2. V2 + Zod 레이아웃 저장/로드 시스템 (핵심)
|
||||
|
||||
### 핵심 원칙
|
||||
|
||||
**컴포넌트 코드 수정 시 모든 화면에 자동 반영되도록 V2 + Zod 기반 저장/로드 방식을 사용합니다.**
|
||||
|
||||
```
|
||||
저장: component_url + overrides (차이값만)
|
||||
로드: Zod 기본값 + overrides 병합
|
||||
```
|
||||
|
||||
### 기존 방식 vs V2 방식
|
||||
|
||||
| 항목 | 기존 (문제점) | V2 (해결) |
|
||||
|-----|-------------|----------|
|
||||
| 저장 | 전체 설정 "박제" | url + overrides (차이값만) |
|
||||
| 코드 수정 반영 | 안 됨 | 자동 반영 |
|
||||
| 테이블 | `screen_layouts` (다중 레코드) | `screen_layouts_v2` (1 레코드) |
|
||||
|
||||
### 저장 구조 (screen_layouts_v2)
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "2.0",
|
||||
"components": [
|
||||
{
|
||||
"id": "comp_xxx",
|
||||
"url": "@/lib/registry/components/v2-select",
|
||||
"position": { "x": 100, "y": 50 },
|
||||
"size": { "width": 180, "height": 30 },
|
||||
"displayOrder": 0,
|
||||
"overrides": {
|
||||
"tableName": "warehouse_info",
|
||||
"columnName": "warehouse_code",
|
||||
"label": "창고코드",
|
||||
"webType": "select"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 핵심 필드: overrides에 반드시 포함되어야 하는 속성
|
||||
|
||||
컴포넌트가 테이블 컬럼에서 드래그되어 생성된 경우, 다음 속성들이 `overrides`에 저장됩니다:
|
||||
|
||||
| 속성 | 설명 | 예시 |
|
||||
|-----|------|-----|
|
||||
| `tableName` | 연결된 테이블명 | `"warehouse_info"` |
|
||||
| `columnName` | 연결된 컬럼명 | `"warehouse_code"` |
|
||||
| `label` | 표시 라벨 | `"창고코드"` |
|
||||
| `required` | 필수 입력 여부 | `true` / `false` |
|
||||
| `readonly` | 읽기 전용 여부 | `true` / `false` |
|
||||
| `inputType` | 입력 타입 | `"text"`, `"select"`, `"date"` 등 |
|
||||
| `webType` | 웹 타입 | `"text"`, `"select"`, `"date"` 등 |
|
||||
| `codeCategory` | 코드 카테고리 (코드 타입인 경우) | `"WAREHOUSE_TYPE"` |
|
||||
|
||||
### 저장 로직 (convertLegacyToV2)
|
||||
|
||||
```typescript
|
||||
// frontend/lib/utils/layoutV2Converter.ts
|
||||
|
||||
export function convertLegacyToV2(legacyLayout: LegacyLayoutData): LayoutV2 {
|
||||
const components = legacyLayout.components.map((comp, index) => {
|
||||
const componentType = comp.componentType || comp.widgetType || comp.type;
|
||||
const url = getComponentUrl(componentType);
|
||||
const defaults = getDefaultsByUrl(url);
|
||||
|
||||
// 상위 레벨 속성들도 overrides에 포함 (중요!)
|
||||
const topLevelProps: Record<string, any> = {};
|
||||
if (comp.tableName) topLevelProps.tableName = comp.tableName;
|
||||
if (comp.columnName) topLevelProps.columnName = comp.columnName;
|
||||
if (comp.label) topLevelProps.label = comp.label;
|
||||
if (comp.required !== undefined) topLevelProps.required = comp.required;
|
||||
if (comp.readonly !== undefined) topLevelProps.readonly = comp.readonly;
|
||||
|
||||
// componentConfig에서 차이값만 추출
|
||||
const configOverrides = extractCustomConfig(comp.componentConfig || {}, defaults);
|
||||
|
||||
// 병합
|
||||
const overrides = { ...topLevelProps, ...configOverrides };
|
||||
|
||||
return {
|
||||
id: comp.id,
|
||||
url: url,
|
||||
position: comp.position,
|
||||
size: comp.size,
|
||||
displayOrder: index,
|
||||
overrides: overrides,
|
||||
};
|
||||
});
|
||||
|
||||
return { version: "2.0", components };
|
||||
}
|
||||
```
|
||||
|
||||
### 로드 로직 (convertV2ToLegacy)
|
||||
|
||||
```typescript
|
||||
// frontend/lib/utils/layoutV2Converter.ts
|
||||
|
||||
export function convertV2ToLegacy(v2Layout: LayoutV2): LegacyLayoutData {
|
||||
const components = v2Layout.components.map((comp) => {
|
||||
const componentType = getComponentTypeFromUrl(comp.url);
|
||||
const defaults = getDefaultsByUrl(comp.url);
|
||||
const mergedConfig = mergeComponentConfig(defaults, comp.overrides);
|
||||
|
||||
// overrides에서 상위 레벨 속성들 복원
|
||||
const overrides = comp.overrides || {};
|
||||
|
||||
return {
|
||||
id: comp.id,
|
||||
componentType: componentType,
|
||||
position: comp.position,
|
||||
size: comp.size,
|
||||
componentConfig: mergedConfig,
|
||||
// 상위 레벨 속성 복원 (중요!)
|
||||
tableName: overrides.tableName,
|
||||
columnName: overrides.columnName,
|
||||
label: overrides.label || "",
|
||||
required: overrides.required,
|
||||
readonly: overrides.readonly,
|
||||
};
|
||||
});
|
||||
|
||||
return { components };
|
||||
}
|
||||
```
|
||||
|
||||
### Zod 스키마 구조
|
||||
|
||||
```typescript
|
||||
// frontend/lib/schemas/componentConfig.ts
|
||||
|
||||
// 컴포넌트별 overrides 스키마
|
||||
export const v2SelectOverridesSchema = z.object({
|
||||
mode: z.enum(["dropdown", "combobox", "radio", "checkbox"]).default("dropdown"),
|
||||
source: z.enum(["static", "code", "entity", "db", "distinct"]).default("distinct"),
|
||||
multiple: z.boolean().default(false),
|
||||
searchable: z.boolean().default(true),
|
||||
placeholder: z.string().default("선택하세요"),
|
||||
}).passthrough(); // 정의되지 않은 필드도 통과 (tableName, columnName 등)
|
||||
|
||||
// 스키마 레지스트리
|
||||
export const componentOverridesSchemaRegistry: Record<string, z.ZodType<any>> = {
|
||||
"v2-select": v2SelectOverridesSchema,
|
||||
"v2-input": v2InputOverridesSchema,
|
||||
"v2-table-list": v2TableListOverridesSchema,
|
||||
// ...
|
||||
};
|
||||
|
||||
// 기본값 레지스트리
|
||||
export const componentDefaultsRegistry: Record<string, any> = {
|
||||
"v2-select": {
|
||||
mode: "dropdown",
|
||||
source: "distinct", // 기본: 테이블 컬럼에서 자동 로드
|
||||
multiple: false,
|
||||
searchable: true,
|
||||
},
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
### v2-select 자동 옵션 로드
|
||||
|
||||
`webType`이 `"select"`인 컬럼을 드래그하면:
|
||||
|
||||
1. **저장 시**: `tableName`, `columnName`이 `overrides`에 저장됨
|
||||
2. **로드 시**: `source`가 `"distinct"`이면 자동으로 `/entity/{tableName}/distinct/{columnName}` API 호출
|
||||
3. **결과**: 해당 컬럼의 고유 값들이 옵션으로 표시됨
|
||||
|
||||
```typescript
|
||||
// DynamicComponentRenderer.tsx
|
||||
|
||||
case "v2-select":
|
||||
return (
|
||||
<V2Select
|
||||
{...commonProps}
|
||||
config={{
|
||||
mode: config.mode || "dropdown",
|
||||
source: config.source || "distinct", // 기본: 테이블에서 자동 로드
|
||||
// ...
|
||||
}}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
### 관련 파일
|
||||
|
||||
| 파일 | 역할 |
|
||||
|------|------|
|
||||
| `frontend/lib/schemas/componentConfig.ts` | Zod 스키마 및 기본값 레지스트리 |
|
||||
| `frontend/lib/utils/layoutV2Converter.ts` | V2 ↔ Legacy 변환 유틸리티 |
|
||||
| `frontend/lib/api/screen.ts` | `getLayoutV2`, `saveLayoutV2` API |
|
||||
| `backend-node/src/services/screenManagementService.ts` | 백엔드 저장/로드 로직 |
|
||||
|
||||
### 새 컴포넌트 추가 시 체크리스트
|
||||
|
||||
1. [ ] `componentConfig.ts`에 Zod 스키마 추가 (`.passthrough()` 필수)
|
||||
2. [ ] `componentOverridesSchemaRegistry`에 등록
|
||||
3. [ ] `componentDefaultsRegistry`에 기본값 등록
|
||||
4. [ ] 테이블 컬럼 드래그 시 `tableName`, `columnName` 저장 확인
|
||||
|
||||
---
|
||||
|
||||
## 3. 표준 Props 인터페이스
|
||||
|
||||
### 컴포넌트가 받아야 하는 표준 Props
|
||||
|
||||
|
|
@ -150,7 +356,7 @@ export const MyComponent: React.FC<StandardComponentProps> = ({
|
|||
|
||||
---
|
||||
|
||||
## 3. 멀티테넌시 (company_code)
|
||||
## 4. 멀티테넌시 (company_code)
|
||||
|
||||
### 핵심 원칙
|
||||
|
||||
|
|
@ -199,7 +405,7 @@ const response = await apiClient.post(`/table-management/tables/${tableName}/add
|
|||
|
||||
---
|
||||
|
||||
## 4. 디자인 모드 vs 인터랙티브 모드
|
||||
## 5. 디자인 모드 vs 인터랙티브 모드
|
||||
|
||||
### 모드 구분
|
||||
|
||||
|
|
@ -257,7 +463,7 @@ const handleClick = useCallback(() => {
|
|||
|
||||
---
|
||||
|
||||
## 5. 로딩 및 에러 처리
|
||||
## 6. 로딩 및 에러 처리
|
||||
|
||||
### 로딩 상태 관리
|
||||
|
||||
|
|
@ -378,7 +584,7 @@ if (!silentActions.includes(actionType)) {
|
|||
|
||||
---
|
||||
|
||||
## 6. 테이블 컬럼 기반 입력 위젯
|
||||
## 7. 테이블 컬럼 기반 입력 위젯
|
||||
|
||||
### 드래그 방식으로 입력 폼 생성
|
||||
|
||||
|
|
@ -398,26 +604,26 @@ if (!silentActions.includes(actionType)) {
|
|||
|
||||
| inputType | 생성 위젯 | 설명 |
|
||||
|-----------|----------|------|
|
||||
| `text`, `textarea` | UnifiedInput | 텍스트 입력 |
|
||||
| `number` | UnifiedInput | 숫자 입력 |
|
||||
| `date`, `datetime` | UnifiedDate | 날짜/시간 선택 |
|
||||
| `code`, `category`, `entity` | UnifiedSelect | 선택박스 |
|
||||
| `text`, `textarea` | V2Input | 텍스트 입력 |
|
||||
| `number` | V2Input | 숫자 입력 |
|
||||
| `date`, `datetime` | V2Date | 날짜/시간 선택 |
|
||||
| `code`, `category`, `entity` | V2Select | 선택박스 |
|
||||
| `checkbox`, `radio` | 체크박스/라디오 | 선택 |
|
||||
| `image`, `file` | UnifiedMedia | 파일 업로드 |
|
||||
| `image`, `file` | V2Media | 파일 업로드 |
|
||||
|
||||
### 핵심 Unified 컴포넌트 (3개)
|
||||
### 핵심 V2 컴포넌트 (3개)
|
||||
|
||||
| 컴포넌트 | 담당 inputType | 파일 경로 |
|
||||
|----------|---------------|-----------|
|
||||
| `UnifiedInput` | text, textarea, number, password | `components/unified/UnifiedInput.tsx` |
|
||||
| `UnifiedSelect` | code, category, entity, select | `components/unified/UnifiedSelect.tsx` |
|
||||
| `UnifiedDate` | date, datetime, time, daterange | `components/unified/UnifiedDate.tsx` |
|
||||
| `V2Input` | text, textarea, number, password | `components/v2/V2Input.tsx` |
|
||||
| `V2Select` | code, category, entity, select | `components/v2/V2Select.tsx` |
|
||||
| `V2Date` | date, datetime, time, daterange | `components/v2/V2Date.tsx` |
|
||||
|
||||
### 컴포넌트 패널에서 직접 드래그 가능한 컴포넌트
|
||||
|
||||
| 컴포넌트 ID | 이름 | 설명 |
|
||||
|-------------|------|------|
|
||||
| `v2-unified-repeater` | 리피터 그리드 | 행 단위 데이터 추가/수정/삭제 |
|
||||
| `v2-repeater` | 리피터 그리드 | 행 단위 데이터 추가/수정/삭제 |
|
||||
| `v2-table-list` | 테이블 리스트 | 데이터 목록 조회/필터/정렬 |
|
||||
| `v2-table-search-widget` | 검색 필터 | 테이블 검색 조건 입력 |
|
||||
| `v2-button-primary` | 버튼 | 저장, 삭제, 조회 등 액션 |
|
||||
|
|
@ -492,7 +698,7 @@ const handleSave = async (context: ButtonActionContext) => {
|
|||
|
||||
---
|
||||
|
||||
## 7. 컴포넌트별 테이블 설정
|
||||
## 8. 컴포넌트별 테이블 설정
|
||||
|
||||
### 핵심 원칙
|
||||
|
||||
|
|
@ -519,7 +725,7 @@ interface TableListConfig {
|
|||
#### 저장용 (리피터)
|
||||
|
||||
```typescript
|
||||
interface UnifiedRepeaterConfig {
|
||||
interface V2RepeaterConfig {
|
||||
mainTableName?: string; // 저장할 테이블명
|
||||
useCustomTable?: boolean; // true: mainTableName 사용
|
||||
foreignKeyColumn?: string; // FK 컬럼 (예: receiving_id)
|
||||
|
|
@ -606,7 +812,7 @@ const response = await apiClient.get(
|
|||
|
||||
---
|
||||
|
||||
## 8. 엔티티 조인 컬럼 활용
|
||||
## 9. 엔티티 조인 컬럼 활용
|
||||
|
||||
### 핵심 원칙
|
||||
|
||||
|
|
@ -712,9 +918,9 @@ const getEntityJoinValue = (item: any, columnName: string): any => {
|
|||
|
||||
---
|
||||
|
||||
## 9. 폼 데이터 관리
|
||||
## 10. 폼 데이터 관리
|
||||
|
||||
### 통합 폼 시스템 (UnifiedFormContext)
|
||||
### 통합 폼 시스템 (V2FormContext)
|
||||
|
||||
```typescript
|
||||
import { useFormCompatibility } from "@/hooks/useFormCompatibility";
|
||||
|
|
@ -743,20 +949,20 @@ const MyComponent = ({ onFormDataChange, formData }) => {
|
|||
|
||||
```typescript
|
||||
const handleChange = useCallback((value: any) => {
|
||||
// 1. UnifiedFormContext
|
||||
unifiedContext?.setValue(fieldName, value);
|
||||
// 1. V2FormContext
|
||||
v2Context?.setValue(fieldName, value);
|
||||
|
||||
// 2. ScreenContext
|
||||
screenContext?.updateFormData?.(fieldName, value);
|
||||
|
||||
// 3. 레거시 콜백
|
||||
onFormDataChange?.(fieldName, value);
|
||||
}, [fieldName, unifiedContext, screenContext, onFormDataChange]);
|
||||
}, [fieldName, v2Context, screenContext, onFormDataChange]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 다국어 지원
|
||||
## 11. 다국어 지원
|
||||
|
||||
### 타입 정의 시 다국어 필드 추가
|
||||
|
||||
|
|
@ -824,7 +1030,7 @@ const MyComponent = ({ component }) => {
|
|||
|
||||
---
|
||||
|
||||
## 11. 저장 버튼 및 플로우 연동
|
||||
## 12. 저장 버튼 및 플로우 연동
|
||||
|
||||
### beforeFormSave 이벤트 처리 (필수)
|
||||
|
||||
|
|
@ -912,9 +1118,9 @@ const MyFormComponent = ({ formData, onFormDataChange }) => {
|
|||
|
||||
---
|
||||
|
||||
## 12. 표준 코드 스타일 가이드
|
||||
## 13. 표준 코드 스타일 가이드
|
||||
|
||||
**`v2-unified-repeater`** 컴포넌트를 표준으로 삼아 동일한 구조로 작성합니다.
|
||||
**`v2-repeater`** 컴포넌트를 표준으로 삼아 동일한 구조로 작성합니다.
|
||||
|
||||
### 핵심 원칙: 느슨한 결합도 (Loose Coupling)
|
||||
|
||||
|
|
@ -1276,7 +1482,7 @@ const tableName = config?.dataSource?.tableName;
|
|||
|
||||
---
|
||||
|
||||
## 13. 성능 최적화
|
||||
## 14. 성능 최적화
|
||||
|
||||
### useMemo로 계산 비용 줄이기
|
||||
|
||||
|
|
@ -1452,7 +1658,7 @@ const derivedValue = useMemo(() => data.map(x => x.value), [data]);
|
|||
|
||||
---
|
||||
|
||||
## 14. 체크리스트
|
||||
## 15. 체크리스트
|
||||
|
||||
### V2 컴포넌트 규칙
|
||||
|
||||
|
|
@ -1461,6 +1667,16 @@ const derivedValue = useMemo(() => data.map(x => x.value), [data]);
|
|||
- [ ] Definition 이름에 `V2` 접두사 사용
|
||||
- [ ] 원본 폴더 수정하지 않음
|
||||
|
||||
### V2 + Zod 레이아웃 시스템
|
||||
|
||||
- [ ] `componentConfig.ts`에 Zod 스키마 추가 (`.passthrough()` 필수)
|
||||
- [ ] `componentOverridesSchemaRegistry`에 컴포넌트 등록
|
||||
- [ ] `componentDefaultsRegistry`에 기본값 등록
|
||||
- [ ] 테이블 컬럼 드래그 시 `tableName`, `columnName` 저장 확인
|
||||
- [ ] `convertLegacyToV2`에서 상위 레벨 속성 포함 확인
|
||||
- [ ] `convertV2ToLegacy`에서 상위 레벨 속성 복원 확인
|
||||
- [ ] v2-select는 `source: "distinct"` 기본값 확인
|
||||
|
||||
### 표준 Props
|
||||
|
||||
- [ ] `component`, `isDesignMode` props 지원
|
||||
|
|
@ -1533,7 +1749,7 @@ const derivedValue = useMemo(() => data.map(x => x.value), [data]);
|
|||
|
||||
### 코드 스타일
|
||||
|
||||
- [ ] `v2-unified-repeater` 구조 참고
|
||||
- [ ] `v2-repeater` 구조 참고
|
||||
- [ ] 느슨한 결합도 유지 (이벤트 기반 통신)
|
||||
|
||||
### 성능 최적화
|
||||
|
|
@ -1553,9 +1769,9 @@ const derivedValue = useMemo(() => data.map(x => x.value), [data]);
|
|||
|
||||
| 파일 | 역할 |
|
||||
|------|------|
|
||||
| `components/unified/UnifiedInput.tsx` | text, number 입력 |
|
||||
| `components/unified/UnifiedSelect.tsx` | code, entity 선택 |
|
||||
| `components/unified/UnifiedDate.tsx` | date, datetime 선택 |
|
||||
| `components/v2/V2Input.tsx` | text, number 입력 |
|
||||
| `components/v2/V2Select.tsx` | code, entity 선택 |
|
||||
| `components/v2/V2Date.tsx` | date, datetime 선택 |
|
||||
| `lib/registry/components/v2-*/` | V2 컴포넌트 폴더 |
|
||||
| `lib/api/entityJoin.ts` | 엔티티 조인 API |
|
||||
| `hooks/useFormCompatibility.ts` | 폼 호환성 브릿지 |
|
||||
|
|
@ -1567,6 +1783,6 @@ const derivedValue = useMemo(() => data.map(x => x.value), [data]);
|
|||
|
||||
| 컴포넌트 | 경로 | 참고 사항 |
|
||||
|----------|------|-----------|
|
||||
| `v2-unified-repeater` | `lib/registry/components/v2-unified-repeater/` | **표준 참조 컴포넌트** |
|
||||
| `v2-repeater` | `lib/registry/components/v2-repeater/` | **표준 참조 컴포넌트** |
|
||||
| `v2-table-list` | `lib/registry/components/v2-table-list/` | 조회 컴포넌트 참조 |
|
||||
| `v2-table-search-widget` | `lib/registry/components/v2-table-search-widget/` | 검색 필터 참조 |
|
||||
|
|
|
|||
20
PLAN.MD
20
PLAN.MD
|
|
@ -1,12 +1,12 @@
|
|||
# 프로젝트: V2/Unified 컴포넌트 설정 스키마 정비
|
||||
# 프로젝트: V2/V2 컴포넌트 설정 스키마 정비
|
||||
|
||||
## 개요
|
||||
레거시 컴포넌트를 제거하고, V2/Unified 컴포넌트 전용 Zod 스키마와 기본값 레지스트리를 한 곳에서 관리한다.
|
||||
레거시 컴포넌트를 제거하고, V2/V2 컴포넌트 전용 Zod 스키마와 기본값 레지스트리를 한 곳에서 관리한다.
|
||||
|
||||
## 핵심 기능
|
||||
1. [x] 레거시 컴포넌트 스키마 제거
|
||||
2. [x] V2 컴포넌트 overrides 스키마 정의 (16개)
|
||||
3. [x] Unified 컴포넌트 overrides 스키마 정의 (9개)
|
||||
3. [x] V2 컴포넌트 overrides 스키마 정의 (9개)
|
||||
4. [x] componentConfig.ts 한 파일에서 통합 관리
|
||||
|
||||
## 정의된 V2 컴포넌트 (18개)
|
||||
|
|
@ -16,17 +16,17 @@
|
|||
- v2-numbering-rule, v2-category-manager, v2-pivot-grid
|
||||
- v2-location-swap-selector, v2-aggregation-widget
|
||||
- v2-card-display, v2-table-search-widget, v2-tabs-widget
|
||||
- v2-unified-repeater
|
||||
- v2-v2-repeater
|
||||
|
||||
## 정의된 Unified 컴포넌트 (9개)
|
||||
- unified-input, unified-select, unified-date
|
||||
- unified-list, unified-layout, unified-group
|
||||
- unified-media, unified-biz, unified-hierarchy
|
||||
## 정의된 V2 컴포넌트 (9개)
|
||||
- v2-input, v2-select, v2-date
|
||||
- v2-list, v2-layout, v2-group
|
||||
- v2-media, v2-biz, v2-hierarchy
|
||||
|
||||
## 테스트 계획
|
||||
### 1단계: 기본 기능
|
||||
- [x] V2 레이아웃 저장 시 컴포넌트별 overrides 스키마 검증 통과
|
||||
- [x] Unified 컴포넌트 기본값과 스키마가 매칭됨
|
||||
- [x] V2 컴포넌트 기본값과 스키마가 매칭됨
|
||||
|
||||
### 2단계: 에러 케이스
|
||||
- [x] 잘못된 overrides 입력 시 Zod 검증 실패 처리 (safeParse + console.warn + graceful fallback)
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
## 진행 상태
|
||||
- [x] 레거시 컴포넌트 제거 완료
|
||||
- [x] V2/Unified 스키마 정의 완료
|
||||
- [x] V2/V2 스키마 정의 완료
|
||||
- [x] 한 파일 통합 관리 완료
|
||||
# 프로젝트: 화면 복제 기능 개선 (DB 구조 개편 후)
|
||||
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ app.use("/api/table-categories", tableCategoryValueRoutes); // 카테고리 값
|
|||
app.use("/api/code-merge", codeMergeRoutes); // 코드 병합
|
||||
app.use("/api/numbering-rules", numberingRuleRoutes); // 채번 규칙 관리
|
||||
app.use("/api/entity-search", entitySearchRoutes); // 엔티티 검색
|
||||
app.use("/api/entity", entityOptionsRouter); // 엔티티 옵션 (UnifiedSelect용)
|
||||
app.use("/api/entity", entityOptionsRouter); // 엔티티 옵션 (V2Select용)
|
||||
app.use("/api/driver", driverRoutes); // 공차중계 운전자 관리
|
||||
app.use("/api/tax-invoice", taxInvoiceRoutes); // 세금계산서 관리
|
||||
app.use("/api/cascading-relations", cascadingRelationRoutes); // 연쇄 드롭다운 관계 관리
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ export const getUserList = async (req: AuthenticatedRequest, res: Response) => {
|
|||
// 검색 조건 처리
|
||||
if (search && typeof search === "string" && search.trim()) {
|
||||
// 통합 검색
|
||||
searchType = "unified";
|
||||
searchType = "v2";
|
||||
const searchTerm = search.trim();
|
||||
|
||||
whereConditions.push(`(
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ export async function getDistinctColumnValues(req: AuthenticatedRequest, res: Re
|
|||
}
|
||||
|
||||
/**
|
||||
* 엔티티 옵션 조회 API (UnifiedSelect용)
|
||||
* 엔티티 옵션 조회 API (V2Select용)
|
||||
* GET /api/entity/:tableName/options
|
||||
*
|
||||
* Query Params:
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ router.get("/:tableName", authenticateToken, searchEntity);
|
|||
|
||||
export default router;
|
||||
|
||||
// 엔티티 옵션 라우터 (UnifiedSelect용)
|
||||
// 엔티티 옵션 라우터 (V2Select용)
|
||||
export const entityOptionsRouter = Router();
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
isValidWebType,
|
||||
WEB_TYPE_TO_POSTGRES_CONVERTER,
|
||||
WEB_TYPE_VALIDATION_PATTERNS,
|
||||
} from "../types/unified-web-types";
|
||||
} from "../types/v2-web-types";
|
||||
import { DataflowControlService } from "./dataflowControlService";
|
||||
|
||||
// 테이블 컬럼 정보
|
||||
|
|
|
|||
|
|
@ -987,7 +987,7 @@ class NumberingRuleService {
|
|||
}
|
||||
|
||||
// 카테고리 매핑에서 해당 값에 대한 형식 찾기
|
||||
// selectedValue는 valueCode일 수 있음 (UnifiedSelect에서 valueCode를 value로 사용)
|
||||
// selectedValue는 valueCode일 수 있음 (V2Select에서 valueCode를 value로 사용)
|
||||
const selectedValueStr = String(selectedValue);
|
||||
const mapping = categoryMappings.find(
|
||||
(m: any) => {
|
||||
|
|
|
|||
|
|
@ -1406,7 +1406,7 @@ export class ScreenManagementService {
|
|||
*/
|
||||
private inferWebType(dataType: string): WebType {
|
||||
// 통합 타입 매핑에서 import
|
||||
const { DB_TYPE_TO_WEB_TYPE } = require("../types/unified-web-types");
|
||||
const { DB_TYPE_TO_WEB_TYPE } = require("../types/v2-web-types");
|
||||
|
||||
const lowerType = dataType.toLowerCase();
|
||||
|
||||
|
|
@ -1741,15 +1741,15 @@ export class ScreenManagementService {
|
|||
? inputTypeMap.get(`${tableName}.${columnName}`)
|
||||
: null;
|
||||
|
||||
// 🆕 Unified 컴포넌트는 덮어쓰지 않음 (새로운 컴포넌트 시스템 보호)
|
||||
// 🆕 V2 컴포넌트는 덮어쓰지 않음 (새로운 컴포넌트 시스템 보호)
|
||||
const savedComponentType = properties?.componentType;
|
||||
const isUnifiedComponent = savedComponentType?.startsWith("unified-");
|
||||
const isV2Component = savedComponentType?.startsWith("v2-");
|
||||
|
||||
const component = {
|
||||
id: layout.component_id,
|
||||
// 🔥 최신 componentType이 있으면 type 덮어쓰기 (단, Unified 컴포넌트는 제외)
|
||||
type: isUnifiedComponent
|
||||
? layout.component_type as any // Unified는 저장된 값 유지
|
||||
// 🔥 최신 componentType이 있으면 type 덮어쓰기 (단, V2 컴포넌트는 제외)
|
||||
type: isV2Component
|
||||
? layout.component_type as any // V2는 저장된 값 유지
|
||||
: (latestTypeInfo?.componentType || layout.component_type as any),
|
||||
position: {
|
||||
x: layout.position_x,
|
||||
|
|
@ -1759,8 +1759,8 @@ export class ScreenManagementService {
|
|||
size: { width: layout.width, height: layout.height },
|
||||
parentId: layout.parent_id,
|
||||
...properties,
|
||||
// 🔥 최신 inputType이 있으면 widgetType, componentType 덮어쓰기 (단, Unified 컴포넌트는 제외)
|
||||
...(!isUnifiedComponent && latestTypeInfo && {
|
||||
// 🔥 최신 inputType이 있으면 widgetType, componentType 덮어쓰기 (단, V2 컴포넌트는 제외)
|
||||
...(!isV2Component && latestTypeInfo && {
|
||||
widgetType: latestTypeInfo.inputType,
|
||||
inputType: latestTypeInfo.inputType,
|
||||
componentType: latestTypeInfo.componentType,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
EntityJoinResponse,
|
||||
EntityJoinConfig,
|
||||
} from "../types/tableManagement";
|
||||
import { WebType } from "../types/unified-web-types";
|
||||
import { WebType } from "../types/v2-web-types";
|
||||
import { entityJoinService } from "./entityJoinService";
|
||||
import { referenceCacheService } from "./referenceCacheService";
|
||||
|
||||
|
|
@ -4301,7 +4301,7 @@ export class TableManagementService {
|
|||
*/
|
||||
private inferWebType(dataType: string): WebType {
|
||||
// 통합 타입 매핑에서 import
|
||||
const { DB_TYPE_TO_WEB_TYPE } = require("../types/unified-web-types");
|
||||
const { DB_TYPE_TO_WEB_TYPE } = require("../types/v2-web-types");
|
||||
|
||||
const lowerType = dataType.toLowerCase();
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export type ComponentType = "container" | "row" | "column" | "widget" | "group";
|
|||
|
||||
// 웹 타입 정의
|
||||
// WebType은 통합 타입에서 import (중복 정의 제거)
|
||||
import { WebType } from "./unified-web-types";
|
||||
import { WebType } from "./v2-web-types";
|
||||
export { WebType };
|
||||
|
||||
// 위치 정보
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ export const WEB_TYPE_VALIDATION_PATTERNS: Record<WebType, RegExp | null> = {
|
|||
};
|
||||
|
||||
// 업데이트된 웹 타입 옵션 (기존 WEB_TYPE_OPTIONS 대체)
|
||||
export const UNIFIED_WEB_TYPE_OPTIONS = [
|
||||
export const V2_WEB_TYPE_OPTIONS = [
|
||||
{
|
||||
value: "text",
|
||||
label: "text",
|
||||
|
|
@ -134,7 +134,7 @@ export const componentDefaults: Record<string, any> = {
|
|||
"flow-widget": { type: "flow-widget", webType: "text", displayMode: "horizontal", allowDataMove: false, showStepCount: true },
|
||||
"entity-search-input": { type: "entity-search-input", webType: "entity" },
|
||||
"autocomplete-search-input": { type: "autocomplete-search-input", webType: "entity" },
|
||||
"unified-list": { type: "unified-list", webType: "table" },
|
||||
"v2-list": { type: "v2-list", webType: "table" },
|
||||
"modal-repeater-table": { type: "modal-repeater-table", webType: "table", columns: [], multiSelect: true },
|
||||
"category-manager": { type: "category-manager", webType: "custom" },
|
||||
"numbering-rule": { type: "numbering-rule", webType: "text" },
|
||||
|
|
@ -159,10 +159,10 @@ export const componentDefaults: Record<string, any> = {
|
|||
"repeat-screen-modal": { type: "repeat-screen-modal", webType: "custom" },
|
||||
"related-data-buttons": { type: "related-data-buttons", webType: "custom" },
|
||||
"split-panel-layout2": { type: "split-panel-layout2", webType: "custom" },
|
||||
"unified-input": { type: "unified-input", webType: "text" },
|
||||
"unified-select": { type: "unified-select", webType: "select" },
|
||||
"unified-date": { type: "unified-date", webType: "date" },
|
||||
"unified-repeater": { type: "unified-repeater", webType: "custom" },
|
||||
"v2-input": { type: "v2-input", webType: "text" },
|
||||
"v2-select": { type: "v2-select", webType: "select" },
|
||||
"v2-date": { type: "v2-date", webType: "date" },
|
||||
"v2-repeater": { type: "v2-repeater", webType: "custom" },
|
||||
"v2-repeat-container": { type: "v2-repeat-container", webType: "custom" },
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -527,7 +527,7 @@ flowchart TB
|
|||
|
||||
subgraph Usage["사용처"]
|
||||
U1[NumberingRuleDesigner.tsx]
|
||||
U2[UnifiedSelect.tsx]
|
||||
U2[V2Select.tsx]
|
||||
U3[screenManagementService.ts]
|
||||
end
|
||||
|
||||
|
|
@ -185,7 +185,7 @@ POST /api/screen-management/screens/:screenId/layout-v2
|
|||
| `@/lib/registry/components/flow-widget` | 플로우 위젯 |
|
||||
| `@/lib/registry/components/category-management` | 카테고리 관리 |
|
||||
| `@/lib/registry/components/pivot-table` | 피벗 테이블 |
|
||||
| `@/lib/registry/components/unified-grid` | 통합 그리드 |
|
||||
| `@/lib/registry/components/v2-grid` | 통합 그리드 |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -192,7 +192,7 @@ async function verifyRenderingEquality(layoutId: number) {
|
|||
| 6 | select-basic | 129 | 100% | 낮음 |
|
||||
| 7 | split-panel-layout | 129 | 100% | 높음 |
|
||||
| 8 | date-input | 116 | 100% | 낮음 |
|
||||
| 9 | unified-list | 97 | 100% | 높음 |
|
||||
| 9 | v2-list | 97 | 100% | 높음 |
|
||||
| 10 | number-input | 87 | 100% | 낮음 |
|
||||
|
||||
### 4.2 발견된 문제점
|
||||
|
|
@ -433,7 +433,7 @@ DROP TABLE screen_layouts_v2;
|
|||
- [ ] select-basic
|
||||
- [ ] split-panel-layout
|
||||
- [ ] date-input
|
||||
- [ ] unified-list
|
||||
- [ ] v2-list
|
||||
- [ ] number-input
|
||||
|
||||
### Step 3: 마이그레이션 스크립트
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
# V2 Components 구현 완료 보고서
|
||||
|
||||
## 구현 일시
|
||||
|
||||
2024-12-19
|
||||
|
||||
## 구현된 컴포넌트 목록 (10개)
|
||||
|
||||
### Phase 1: 핵심 입력 컴포넌트
|
||||
|
||||
| 컴포넌트 | 파일 | 모드/타입 | 설명 |
|
||||
| :---------------- | :------------------ | :-------------------------------------------- | :---------------------- |
|
||||
| **V2Input** | `V2Input.tsx` | text, number, password, slider, color, button | 통합 입력 컴포넌트 |
|
||||
| **V2Select** | `V2Select.tsx` | dropdown, radio, check, tag, toggle, swap | 통합 선택 컴포넌트 |
|
||||
| **V2Date** | `V2Date.tsx` | date, time, datetime + range | 통합 날짜/시간 컴포넌트 |
|
||||
|
||||
### Phase 2: 레이아웃 및 그룹 컴포넌트
|
||||
|
||||
| 컴포넌트 | 파일 | 모드/타입 | 설명 |
|
||||
| :---------------- | :------------------ | :-------------------------------------------------------- | :--------------------- |
|
||||
| **V2List** | `V2List.tsx` | table, card, kanban, list | 통합 리스트 컴포넌트 |
|
||||
| **V2Layout** | `V2Layout.tsx` | grid, split, flex, divider, screen-embed | 통합 레이아웃 컴포넌트 |
|
||||
| **V2Group** | `V2Group.tsx` | tabs, accordion, section, card-section, modal, form-modal | 통합 그룹 컴포넌트 |
|
||||
|
||||
### Phase 3: 미디어 및 비즈니스 컴포넌트
|
||||
|
||||
| 컴포넌트 | 파일 | 모드/타입 | 설명 |
|
||||
| :------------------- | :--------------------- | :------------------------------------------------------------- | :---------------------- |
|
||||
| **V2Media** | `V2Media.tsx` | file, image, video, audio | 통합 미디어 컴포넌트 |
|
||||
| **V2Biz** | `V2Biz.tsx` | flow, rack, map, numbering, category, mapping, related-buttons | 통합 비즈니스 컴포넌트 |
|
||||
| **V2Hierarchy** | `V2Hierarchy.tsx` | tree, org, bom, cascading | 통합 계층 구조 컴포넌트 |
|
||||
|
||||
---
|
||||
|
||||
## 공통 인프라
|
||||
|
||||
### 설정 패널
|
||||
|
||||
- **DynamicConfigPanel**: JSON Schema 기반 동적 설정 UI 생성
|
||||
|
||||
### 렌더러
|
||||
|
||||
- **V2ComponentRenderer**: v2Type에 따른 동적 컴포넌트 렌더링
|
||||
|
||||
---
|
||||
|
||||
## 파일 구조
|
||||
|
||||
```
|
||||
frontend/components/v2/
|
||||
├── index.ts # 모듈 인덱스
|
||||
├── V2ComponentRenderer.tsx # 동적 렌더러
|
||||
├── DynamicConfigPanel.tsx # JSON Schema 설정 패널
|
||||
├── V2Input.tsx # 통합 입력
|
||||
├── V2Select.tsx # 통합 선택
|
||||
├── V2Date.tsx # 통합 날짜
|
||||
├── V2List.tsx # 통합 리스트
|
||||
├── V2Layout.tsx # 통합 레이아웃
|
||||
├── V2Group.tsx # 통합 그룹
|
||||
├── V2Media.tsx # 통합 미디어
|
||||
├── V2Biz.tsx # 통합 비즈니스
|
||||
└── V2Hierarchy.tsx # 통합 계층
|
||||
|
||||
frontend/types/
|
||||
└── v2-components.ts # 타입 정의
|
||||
|
||||
db/migrations/
|
||||
└── v2_component_schema.sql # DB 스키마 (미실행)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 사용 예시
|
||||
|
||||
### 기본 사용법
|
||||
|
||||
```tsx
|
||||
import {
|
||||
V2Input,
|
||||
V2Select,
|
||||
V2Date,
|
||||
V2List,
|
||||
V2ComponentRenderer
|
||||
} from "@/components/v2";
|
||||
|
||||
// V2Input 사용
|
||||
<V2Input
|
||||
id="name"
|
||||
label="이름"
|
||||
required
|
||||
config={{ type: "text", placeholder: "이름을 입력하세요" }}
|
||||
value={name}
|
||||
onChange={setName}
|
||||
/>
|
||||
|
||||
// V2Select 사용
|
||||
<V2Select
|
||||
id="status"
|
||||
label="상태"
|
||||
config={{
|
||||
mode: "dropdown",
|
||||
source: "code",
|
||||
codeGroup: "ORDER_STATUS",
|
||||
searchable: true
|
||||
}}
|
||||
value={status}
|
||||
onChange={setStatus}
|
||||
/>
|
||||
|
||||
// V2Date 사용
|
||||
<V2Date
|
||||
id="orderDate"
|
||||
label="주문일"
|
||||
config={{ type: "date", format: "YYYY-MM-DD" }}
|
||||
value={orderDate}
|
||||
onChange={setOrderDate}
|
||||
/>
|
||||
|
||||
// V2List 사용
|
||||
<V2List
|
||||
id="orderList"
|
||||
label="주문 목록"
|
||||
config={{
|
||||
viewMode: "table",
|
||||
searchable: true,
|
||||
pageable: true,
|
||||
pageSize: 10,
|
||||
columns: [
|
||||
{ field: "orderId", header: "주문번호", sortable: true },
|
||||
{ field: "customerName", header: "고객명" },
|
||||
{ field: "orderDate", header: "주문일", format: "date" },
|
||||
]
|
||||
}}
|
||||
data={orders}
|
||||
onRowClick={handleRowClick}
|
||||
/>
|
||||
```
|
||||
|
||||
### 동적 렌더링
|
||||
|
||||
```tsx
|
||||
import { V2ComponentRenderer } from "@/components/v2";
|
||||
|
||||
// v2Type에 따라 자동으로 적절한 컴포넌트 렌더링
|
||||
<V2ComponentRenderer
|
||||
props={{
|
||||
v2Type: "V2Input",
|
||||
id: "dynamicField",
|
||||
label: "동적 필드",
|
||||
config: { type: "text" },
|
||||
value: fieldValue,
|
||||
onChange: setFieldValue,
|
||||
}}
|
||||
/>;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 주의사항
|
||||
|
||||
### 기존 컴포넌트와의 공존
|
||||
|
||||
1. **기존 컴포넌트는 그대로 유지**: 모든 레거시 컴포넌트는 정상 동작
|
||||
2. **신규 화면에서만 V2 컴포넌트 사용**: 기존 화면에 영향 없음
|
||||
3. **마이그레이션 없음**: 자동 마이그레이션 진행하지 않음
|
||||
|
||||
### 데이터베이스 마이그레이션
|
||||
|
||||
`db/migrations/v2_component_schema.sql` 파일은 아직 실행되지 않았습니다.
|
||||
필요시 수동으로 실행해야 합니다:
|
||||
|
||||
```bash
|
||||
psql -h localhost -U postgres -d plm_db -f db/migrations/v2_component_schema.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 다음 단계 (선택)
|
||||
|
||||
1. **화면 관리 에디터 통합**: V2 컴포넌트를 화면 에디터의 컴포넌트 팔레트에 추가
|
||||
2. **기존 비즈니스 컴포넌트 연동**: V2Biz의 플레이스홀더를 실제 구현으로 교체
|
||||
3. **테스트 페이지 작성**: 모든 V2 컴포넌트 데모 페이지
|
||||
4. **문서화**: 각 컴포넌트별 상세 사용 가이드
|
||||
|
||||
---
|
||||
|
||||
## 관련 문서
|
||||
|
||||
- `PLAN_RENEWAL.md`: 리뉴얼 계획서
|
||||
- `docs/phase0-component-usage-analysis.md`: 컴포넌트 사용 현황 분석
|
||||
- `docs/phase0-migration-strategy.md`: 마이그레이션 전략 (참고용)
|
||||
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
|
||||
| 파일 | 참조 횟수 | 영향도 | 용도 |
|
||||
|------|----------|--------|------|
|
||||
| `UnifiedRepeater.tsx` | 3회 | 🟢 낮음 | 타입 주석 |
|
||||
| `V2Repeater.tsx` | 3회 | 🟢 낮음 | 타입 주석 |
|
||||
| `ScreenDesigner.tsx` | 2회 | 🟢 낮음 | 타입 주석 |
|
||||
| `ButtonConfigPanel.tsx` | 2회 | 🟢 낮음 | 타입 주석 |
|
||||
| `ScreenRelationFlow.tsx` | 2회 | 🟢 낮음 | 타입 주석 |
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## 1. 개요
|
||||
|
||||
현재 **68개 이상**으로 파편화된 화면 관리 컴포넌트들을 **9개의 핵심 통합 컴포넌트(Unified Components)**로 재편합니다.
|
||||
현재 **68개 이상**으로 파편화된 화면 관리 컴포넌트들을 **9개의 핵심 통합 컴포넌트(V2 Components)**로 재편합니다.
|
||||
각 컴포넌트는 **속성(Config)** 설정을 통해 다양한 형태(View Mode)와 기능(Behavior)을 수행하도록 설계되어, 유지보수성과 확장성을 극대화합니다.
|
||||
|
||||
### 현재 컴포넌트 현황 (AS-IS)
|
||||
|
|
@ -24,11 +24,11 @@
|
|||
|
||||
| 통합 컴포넌트 (TO-BE) | 포함되는 기존 컴포넌트 (AS-IS) | 핵심 속성 (Configuration) |
|
||||
| :-------------------- | :------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **1. Unified Select** | Select, Radio, Checkbox, Boolean, Code, Entity, Combobox, Toggle | **`mode`**: "dropdown" / "radio" / "check" / "tag"<br>**`source`**: "static" / "code" / "db" / "api"<br>**`dependency`**: { parentField: "..." } |
|
||||
| **2. Unified Input** | Text, Number, Email, Tel, Password, Color, Search, Integer, Decimal | **`type`**: "text" / "number" / "password"<br>**`format`**: "email", "currency", "biz_no"<br>**`mask`**: "000-0000-0000" |
|
||||
| **3. Unified Date** | Date, Time, DateTime, DateRange, Month, Year | **`type`**: "date" / "time" / "datetime"<br>**`range`**: true/false |
|
||||
| **4. Unified Text** | Textarea, RichEditor, Markdown, HTML | **`mode`**: "simple" / "rich" / "code"<br>**`rows`**: number |
|
||||
| **5. Unified Media** | File, Image, Video, Audio, Attachment | **`type`**: "file" / "image"<br>**`multiple`**: true/false<br>**`preview`**: true/false |
|
||||
| **1. V2 Select** | Select, Radio, Checkbox, Boolean, Code, Entity, Combobox, Toggle | **`mode`**: "dropdown" / "radio" / "check" / "tag"<br>**`source`**: "static" / "code" / "db" / "api"<br>**`dependency`**: { parentField: "..." } |
|
||||
| **2. V2 Input** | Text, Number, Email, Tel, Password, Color, Search, Integer, Decimal | **`type`**: "text" / "number" / "password"<br>**`format`**: "email", "currency", "biz_no"<br>**`mask`**: "000-0000-0000" |
|
||||
| **3. V2 Date** | Date, Time, DateTime, DateRange, Month, Year | **`type`**: "date" / "time" / "datetime"<br>**`range`**: true/false |
|
||||
| **4. V2 Text** | Textarea, RichEditor, Markdown, HTML | **`mode`**: "simple" / "rich" / "code"<br>**`rows`**: number |
|
||||
| **5. V2 Media** | File, Image, Video, Audio, Attachment | **`type`**: "file" / "image"<br>**`multiple`**: true/false<br>**`preview`**: true/false |
|
||||
|
||||
### B. 구조/데이터 위젯 (Structure & Data Widgets) - 4종
|
||||
|
||||
|
|
@ -36,10 +36,10 @@
|
|||
|
||||
| 통합 컴포넌트 (TO-BE) | 포함되는 기존 컴포넌트 (AS-IS) | 핵심 속성 (Configuration) | 활용 예시 |
|
||||
| :-------------------- | :-------------------------------------------------- | :------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------- |
|
||||
| **6. Unified List** | **Table, Card List, Repeater, DataGrid, List View** | **`viewMode`**: "table" / "card" / "kanban"<br>**`editable`**: true/false | - `viewMode='table'`: 엑셀형 리스트<br>- `viewMode='card'`: **카드 디스플레이**<br>- `editable=true`: **반복 필드 그룹** |
|
||||
| **7. Unified Layout** | **Row, Col, Split Panel, Grid, Spacer** | **`type`**: "grid" / "split" / "flex"<br>**`columns`**: number | - `type='split'`: **화면 분할 패널**<br>- `type='grid'`: 격자 레이아웃 |
|
||||
| **8. Unified Group** | Tab, Accordion, FieldSet, Modal, Section | **`type`**: "tab" / "accordion" / "modal" | - 탭이나 아코디언으로 내용 그룹화 |
|
||||
| **9. Unified Biz** | **Rack Structure**, Calendar, Gantt | **`type`**: "rack" / "calendar" / "gantt" | - `type='rack'`: **랙 구조 설정**<br>- 특수 비즈니스 로직 플러그인 탑재 |
|
||||
| **6. V2 List** | **Table, Card List, Repeater, DataGrid, List View** | **`viewMode`**: "table" / "card" / "kanban"<br>**`editable`**: true/false | - `viewMode='table'`: 엑셀형 리스트<br>- `viewMode='card'`: **카드 디스플레이**<br>- `editable=true`: **반복 필드 그룹** |
|
||||
| **7. V2 Layout** | **Row, Col, Split Panel, Grid, Spacer** | **`type`**: "grid" / "split" / "flex"<br>**`columns`**: number | - `type='split'`: **화면 분할 패널**<br>- `type='grid'`: 격자 레이아웃 |
|
||||
| **8. V2 Group** | Tab, Accordion, FieldSet, Modal, Section | **`type`**: "tab" / "accordion" / "modal" | - 탭이나 아코디언으로 내용 그룹화 |
|
||||
| **9. V2 Biz** | **Rack Structure**, Calendar, Gantt | **`type`**: "rack" / "calendar" / "gantt" | - `type='rack'`: **랙 구조 설정**<br>- 특수 비즈니스 로직 플러그인 탑재 |
|
||||
|
||||
### C. Config Panel 통합 전략 (핵심)
|
||||
|
||||
|
|
@ -60,16 +60,16 @@
|
|||
### Case 1: "테이블을 카드 리스트로 변경"
|
||||
|
||||
- **AS-IS**: `DataTable` 컴포넌트를 삭제하고 `CardList` 컴포넌트를 새로 추가해야 함.
|
||||
- **TO-BE**: `UnifiedList`의 속성창에서 **[View Mode]**를 `Table` → `Card`로 변경하면 즉시 반영.
|
||||
- **TO-BE**: `V2List`의 속성창에서 **[View Mode]**를 `Table` → `Card`로 변경하면 즉시 반영.
|
||||
|
||||
### Case 2: "단일 선택을 라디오 버튼으로 변경"
|
||||
|
||||
- **AS-IS**: `SelectWidget`을 삭제하고 `RadioWidget` 추가.
|
||||
- **TO-BE**: `UnifiedSelect` 속성창에서 **[Display Mode]**를 `Dropdown` → `Radio`로 변경.
|
||||
- **TO-BE**: `V2Select` 속성창에서 **[Display Mode]**를 `Dropdown` → `Radio`로 변경.
|
||||
|
||||
### Case 3: "입력 폼에 반복 필드(Repeater) 추가"
|
||||
|
||||
- **TO-BE**: `UnifiedList` 컴포넌트 배치 후 `editable: true`, `viewMode: "table"` 설정.
|
||||
- **TO-BE**: `V2List` 컴포넌트 배치 후 `editable: true`, `viewMode: "table"` 설정.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
통합 작업 전 필수 분석 및 설계를 진행합니다.
|
||||
|
||||
- [ ] 기존 컴포넌트 사용 현황 분석 (화면별 위젯 사용 빈도 조사)
|
||||
- [ ] 데이터 마이그레이션 전략 설계 (`widgetType` → `UnifiedWidget.type` 매핑 정의)
|
||||
- [ ] 데이터 마이그레이션 전략 설계 (`widgetType` → `V2Widget.type` 매핑 정의)
|
||||
- [ ] `sys_input_type` 테이블 JSON Schema 설계
|
||||
- [ ] DynamicConfigPanel 프로토타입 설계
|
||||
|
||||
|
|
@ -88,9 +88,9 @@
|
|||
|
||||
가장 중복이 많고 효과가 즉각적인 입력 필드부터 통합합니다.
|
||||
|
||||
- [ ] **UnifiedInput 구현**: Text, Number, Email, Tel, Password 통합
|
||||
- [ ] **UnifiedSelect 구현**: Select, Radio, Checkbox, Boolean 통합
|
||||
- [ ] **UnifiedDate 구현**: Date, DateTime, Time 통합
|
||||
- [ ] **V2Input 구현**: Text, Number, Email, Tel, Password 통합
|
||||
- [ ] **V2Select 구현**: Select, Radio, Checkbox, Boolean 통합
|
||||
- [ ] **V2Date 구현**: Date, DateTime, Time 통합
|
||||
- [ ] 기존 위젯과 **병행 운영** (deprecated 마킹, 삭제하지 않음)
|
||||
|
||||
### Phase 2: Config Panel 통합 (2주)
|
||||
|
|
@ -105,15 +105,15 @@
|
|||
|
||||
프로젝트의 데이터를 보여주는 핵심 뷰를 통합합니다.
|
||||
|
||||
- [ ] **UnifiedList 구현**: Table, Card, Repeater 통합 렌더러 개발
|
||||
- [ ] **UnifiedLayout 구현**: Split Panel, Grid, Flex 통합
|
||||
- [ ] **UnifiedGroup 구현**: Tab, Accordion, Modal 통합
|
||||
- [ ] **V2List 구현**: Table, Card, Repeater 통합 렌더러 개발
|
||||
- [ ] **V2Layout 구현**: Split Panel, Grid, Flex 통합
|
||||
- [ ] **V2Group 구현**: Tab, Accordion, Modal 통합
|
||||
|
||||
### Phase 4: 안정화 및 마이그레이션 (2주)
|
||||
|
||||
신규 컴포넌트 안정화 후 점진적 전환을 진행합니다.
|
||||
|
||||
- [ ] 신규 화면은 Unified 컴포넌트만 사용하도록 가이드
|
||||
- [ ] 신규 화면은 V2 컴포넌트만 사용하도록 가이드
|
||||
- [ ] 기존 화면 데이터 마이그레이션 스크립트 개발
|
||||
- [ ] 마이그레이션 테스트 (스테이징 환경)
|
||||
- [ ] 문서화 및 개발 가이드 작성
|
||||
|
|
@ -122,7 +122,7 @@
|
|||
|
||||
충분한 안정화 기간 후 레거시 컴포넌트 정리를 검토합니다.
|
||||
|
||||
- [ ] 사용 현황 재분석 (Unified 전환율 확인)
|
||||
- [ ] 사용 현황 재분석 (V2 전환율 확인)
|
||||
- [ ] 미전환 화면 목록 정리
|
||||
- [ ] 레거시 컴포넌트 삭제 여부 결정 (별도 회의)
|
||||
|
||||
|
|
@ -132,27 +132,27 @@
|
|||
|
||||
### 5.1 위젯 타입 매핑 테이블
|
||||
|
||||
기존 `widgetType`을 신규 Unified 컴포넌트로 매핑합니다.
|
||||
기존 `widgetType`을 신규 V2 컴포넌트로 매핑합니다.
|
||||
|
||||
| 기존 widgetType | 신규 컴포넌트 | 속성 설정 |
|
||||
| :-------------- | :------------ | :------------------------------ |
|
||||
| `text` | UnifiedInput | `type: "text"` |
|
||||
| `number` | UnifiedInput | `type: "number"` |
|
||||
| `email` | UnifiedInput | `type: "text", format: "email"` |
|
||||
| `tel` | UnifiedInput | `type: "text", format: "tel"` |
|
||||
| `select` | UnifiedSelect | `mode: "dropdown"` |
|
||||
| `radio` | UnifiedSelect | `mode: "radio"` |
|
||||
| `checkbox` | UnifiedSelect | `mode: "check"` |
|
||||
| `date` | UnifiedDate | `type: "date"` |
|
||||
| `datetime` | UnifiedDate | `type: "datetime"` |
|
||||
| `textarea` | UnifiedText | `mode: "simple"` |
|
||||
| `file` | UnifiedMedia | `type: "file"` |
|
||||
| `image` | UnifiedMedia | `type: "image"` |
|
||||
| `text` | V2Input | `type: "text"` |
|
||||
| `number` | V2Input | `type: "number"` |
|
||||
| `email` | V2Input | `type: "text", format: "email"` |
|
||||
| `tel` | V2Input | `type: "text", format: "tel"` |
|
||||
| `select` | V2Select | `mode: "dropdown"` |
|
||||
| `radio` | V2Select | `mode: "radio"` |
|
||||
| `checkbox` | V2Select | `mode: "check"` |
|
||||
| `date` | V2Date | `type: "date"` |
|
||||
| `datetime` | V2Date | `type: "datetime"` |
|
||||
| `textarea` | V2Text | `mode: "simple"` |
|
||||
| `file` | V2Media | `type: "file"` |
|
||||
| `image` | V2Media | `type: "image"` |
|
||||
|
||||
### 5.2 마이그레이션 원칙
|
||||
|
||||
1. **비파괴적 전환**: 기존 데이터 구조 유지, 신규 필드 추가 방식
|
||||
2. **하위 호환성**: 기존 `widgetType` 필드는 유지, `unifiedType` 필드 추가
|
||||
2. **하위 호환성**: 기존 `widgetType` 필드는 유지, `v2Type` 필드 추가
|
||||
3. **점진적 전환**: 화면 수정 시점에 자동 또는 수동 전환
|
||||
|
||||
---
|
||||
|
|
@ -183,7 +183,7 @@
|
|||
|
||||
현재 `frontend/lib/registry/components/`에 등록된 모든 컴포넌트의 통합 가능 여부를 분석했습니다.
|
||||
|
||||
#### UnifiedInput으로 통합 (4개)
|
||||
#### V2Input으로 통합 (4개)
|
||||
|
||||
| 현재 컴포넌트 | 매핑 속성 | 비고 |
|
||||
| :------------- | :--------------- | :------------- |
|
||||
|
|
@ -192,7 +192,7 @@
|
|||
| slider-basic | `type: "slider"` | 속성 추가 필요 |
|
||||
| button-primary | `type: "button"` | 별도 검토 |
|
||||
|
||||
#### UnifiedSelect로 통합 (8개)
|
||||
#### V2Select로 통합 (8개)
|
||||
|
||||
| 현재 컴포넌트 | 매핑 속성 | 비고 |
|
||||
| :------------------------ | :----------------------------------- | :------------- |
|
||||
|
|
@ -205,19 +205,19 @@
|
|||
| mail-recipient-selector | `mode: "multi", type: "email"` | 복합 컴포넌트 |
|
||||
| location-swap-selector | `mode: "swap"` | 특수 UI |
|
||||
|
||||
#### UnifiedDate로 통합 (1개)
|
||||
#### V2Date로 통합 (1개)
|
||||
|
||||
| 현재 컴포넌트 | 매핑 속성 | 비고 |
|
||||
| :------------ | :------------- | :--- |
|
||||
| date-input | `type: "date"` | |
|
||||
|
||||
#### UnifiedText로 통합 (1개)
|
||||
#### V2Text로 통합 (1개)
|
||||
|
||||
| 현재 컴포넌트 | 매핑 속성 | 비고 |
|
||||
| :------------- | :--------------- | :--- |
|
||||
| textarea-basic | `mode: "simple"` | |
|
||||
|
||||
#### UnifiedMedia로 통합 (3개)
|
||||
#### V2Media로 통합 (3개)
|
||||
|
||||
| 현재 컴포넌트 | 매핑 속성 | 비고 |
|
||||
| :------------ | :------------------------------ | :--- |
|
||||
|
|
@ -225,7 +225,7 @@
|
|||
| image-widget | `type: "image"` | |
|
||||
| image-display | `type: "image", readonly: true` | |
|
||||
|
||||
#### UnifiedList로 통합 (8개)
|
||||
#### V2List로 통합 (8개)
|
||||
|
||||
| 현재 컴포넌트 | 매핑 속성 | 비고 |
|
||||
| :-------------------- | :------------------------------------ | :------------ |
|
||||
|
|
@ -238,7 +238,7 @@
|
|||
| table-search-widget | `viewMode: "table", searchable: true` | |
|
||||
| tax-invoice-list | `viewMode: "table", bizType: "tax"` | 특수 비즈니스 |
|
||||
|
||||
#### UnifiedLayout으로 통합 (4개)
|
||||
#### V2Layout으로 통합 (4개)
|
||||
|
||||
| 현재 컴포넌트 | 매핑 속성 | 비고 |
|
||||
| :------------------ | :-------------------------- | :------------- |
|
||||
|
|
@ -247,7 +247,7 @@
|
|||
| divider-line | `type: "divider"` | 속성 추가 필요 |
|
||||
| screen-split-panel | `type: "screen-embed"` | 화면 임베딩 |
|
||||
|
||||
#### UnifiedGroup으로 통합 (5개)
|
||||
#### V2Group으로 통합 (5개)
|
||||
|
||||
| 현재 컴포넌트 | 매핑 속성 | 비고 |
|
||||
| :------------------- | :--------------------- | :------------ |
|
||||
|
|
@ -257,7 +257,7 @@
|
|||
| section-card | `type: "card-section"` | |
|
||||
| universal-form-modal | `type: "form-modal"` | 복합 컴포넌트 |
|
||||
|
||||
#### UnifiedBiz로 통합 (7개)
|
||||
#### V2Biz로 통합 (7개)
|
||||
|
||||
| 현재 컴포넌트 | 매핑 속성 | 비고 |
|
||||
| :-------------------- | :------------------------ | :--------------- |
|
||||
|
|
@ -274,8 +274,8 @@
|
|||
| 현재 컴포넌트 | 문제점 | 제안 |
|
||||
| :-------------------------- | :------------------- | :------------------------------ |
|
||||
| conditional-container | 조건부 렌더링 로직 | 공통 속성으로 분리 |
|
||||
| selected-items-detail-input | 복합 (선택+상세입력) | UnifiedList + UnifiedGroup 조합 |
|
||||
| text-display | 읽기 전용 텍스트 | UnifiedInput (readonly: true) |
|
||||
| selected-items-detail-input | 복합 (선택+상세입력) | V2List + V2Group 조합 |
|
||||
| text-display | 읽기 전용 텍스트 | V2Input (readonly: true) |
|
||||
|
||||
### 8.2 매핑 분석 결과
|
||||
|
||||
|
|
@ -291,7 +291,7 @@
|
|||
|
||||
### 8.3 속성 확장 필요 사항
|
||||
|
||||
#### UnifiedInput 속성 확장
|
||||
#### V2Input 속성 확장
|
||||
|
||||
```typescript
|
||||
// 기존
|
||||
|
|
@ -301,7 +301,7 @@ type: "text" | "number" | "password";
|
|||
type: "text" | "number" | "password" | "slider" | "color" | "button";
|
||||
```
|
||||
|
||||
#### UnifiedSelect 속성 확장
|
||||
#### V2Select 속성 확장
|
||||
|
||||
```typescript
|
||||
// 기존
|
||||
|
|
@ -311,7 +311,7 @@ mode: "dropdown" | "radio" | "check" | "tag";
|
|||
mode: "dropdown" | "radio" | "check" | "tag" | "toggle" | "swap";
|
||||
```
|
||||
|
||||
#### UnifiedLayout 속성 확장
|
||||
#### V2Layout 속성 확장
|
||||
|
||||
```typescript
|
||||
// 기존
|
||||
|
|
@ -326,8 +326,8 @@ type: "grid" | "split" | "flex" | "divider" | "screen-embed";
|
|||
`conditional-container`의 기능을 모든 컴포넌트에서 사용 가능한 공통 속성으로 분리합니다.
|
||||
|
||||
```typescript
|
||||
// 모든 Unified 컴포넌트에 적용 가능한 공통 속성
|
||||
interface BaseUnifiedProps {
|
||||
// 모든 V2 컴포넌트에 적용 가능한 공통 속성
|
||||
interface BaseV2Props {
|
||||
// ... 기존 속성
|
||||
|
||||
/** 조건부 렌더링 설정 */
|
||||
|
|
@ -356,12 +356,12 @@ DB 테이블 `cascading_hierarchy_group`에서 4가지 계층 타입을 지원
|
|||
| **BOM** | 자재명세서 구조 | 부품 > 하위부품 |
|
||||
| **TREE** | 일반 트리 | 카테고리 |
|
||||
|
||||
### 9.2 통합 방안: UnifiedHierarchy 신설 (10번째 컴포넌트)
|
||||
### 9.2 통합 방안: V2Hierarchy 신설 (10번째 컴포넌트)
|
||||
|
||||
계층 구조는 일반 입력/표시 위젯과 성격이 다르므로 **별도 컴포넌트로 분리**합니다.
|
||||
|
||||
```typescript
|
||||
interface UnifiedHierarchyProps {
|
||||
interface V2HierarchyProps {
|
||||
/** 계층 유형 */
|
||||
type: "tree" | "org" | "bom" | "cascading";
|
||||
|
||||
|
|
@ -400,16 +400,16 @@ interface UnifiedHierarchyProps {
|
|||
|
||||
| # | 컴포넌트 | 역할 | 커버 범위 |
|
||||
| :-: | :------------------- | :------------- | :----------------------------------- |
|
||||
| 1 | **UnifiedInput** | 단일 값 입력 | text, number, slider, button 등 |
|
||||
| 2 | **UnifiedSelect** | 선택 입력 | dropdown, radio, checkbox, toggle 등 |
|
||||
| 3 | **UnifiedDate** | 날짜/시간 입력 | date, datetime, time, range |
|
||||
| 4 | **UnifiedText** | 다중 행 텍스트 | textarea, rich editor, markdown |
|
||||
| 5 | **UnifiedMedia** | 파일/미디어 | file, image, video, audio |
|
||||
| 6 | **UnifiedList** | 데이터 목록 | table, card, repeater, kanban |
|
||||
| 7 | **UnifiedLayout** | 레이아웃 배치 | grid, split, flex, divider |
|
||||
| 8 | **UnifiedGroup** | 콘텐츠 그룹화 | tabs, accordion, section, modal |
|
||||
| 9 | **UnifiedBiz** | 비즈니스 특화 | flow, rack, map, numbering 등 |
|
||||
| 10 | **UnifiedHierarchy** | 계층 구조 | tree, org, bom, cascading |
|
||||
| 1 | **V2Input** | 단일 값 입력 | text, number, slider, button 등 |
|
||||
| 2 | **V2Select** | 선택 입력 | dropdown, radio, checkbox, toggle 등 |
|
||||
| 3 | **V2Date** | 날짜/시간 입력 | date, datetime, time, range |
|
||||
| 4 | **V2Text** | 다중 행 텍스트 | textarea, rich editor, markdown |
|
||||
| 5 | **V2Media** | 파일/미디어 | file, image, video, audio |
|
||||
| 6 | **V2List** | 데이터 목록 | table, card, repeater, kanban |
|
||||
| 7 | **V2Layout** | 레이아웃 배치 | grid, split, flex, divider |
|
||||
| 8 | **V2Group** | 콘텐츠 그룹화 | tabs, accordion, section, modal |
|
||||
| 9 | **V2Biz** | 비즈니스 특화 | flow, rack, map, numbering 등 |
|
||||
| 10 | **V2Hierarchy** | 계층 구조 | tree, org, bom, cascading |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -443,14 +443,14 @@ interface UnifiedHierarchyProps {
|
|||
|
||||
### 11.3 속성 통합 설계
|
||||
|
||||
#### 2단계 연쇄 → UnifiedSelect 속성
|
||||
#### 2단계 연쇄 → V2Select 속성
|
||||
|
||||
```typescript
|
||||
// AS-IS: 별도 관리 메뉴에서 정의 후 참조
|
||||
<SelectWidget cascadingRelation="WAREHOUSE_LOCATION" />
|
||||
|
||||
// TO-BE: 컴포넌트 속성에서 직접 정의
|
||||
<UnifiedSelect
|
||||
<V2Select
|
||||
source="db"
|
||||
table="warehouse_location"
|
||||
valueColumn="location_code"
|
||||
|
|
@ -470,7 +470,7 @@ interface UnifiedHierarchyProps {
|
|||
// cascading_condition 테이블에 저장
|
||||
|
||||
// TO-BE: 모든 컴포넌트에 공통 속성으로 적용
|
||||
<UnifiedInput
|
||||
<V2Input
|
||||
conditional={{
|
||||
enabled: true,
|
||||
field: "order_type", // 참조할 필드
|
||||
|
|
@ -487,7 +487,7 @@ interface UnifiedHierarchyProps {
|
|||
// AS-IS: cascading_auto_fill_group 테이블에 정의
|
||||
|
||||
// TO-BE: 컴포넌트 속성에서 직접 정의
|
||||
<UnifiedInput
|
||||
<V2Input
|
||||
autoFill={{
|
||||
enabled: true,
|
||||
sourceTable: "company_mng", // 조회할 테이블
|
||||
|
|
@ -504,7 +504,7 @@ interface UnifiedHierarchyProps {
|
|||
// AS-IS: cascading_mutual_exclusion 테이블에 정의
|
||||
|
||||
// TO-BE: 컴포넌트 속성에서 직접 정의
|
||||
<UnifiedSelect
|
||||
<V2Select
|
||||
mutualExclusion={{
|
||||
enabled: true,
|
||||
targetField: "sub_category", // 상호 배제 대상 필드
|
||||
|
|
@ -518,7 +518,7 @@ interface UnifiedHierarchyProps {
|
|||
| 현재 메뉴 | TO-BE | 비고 |
|
||||
| :-------------------------- | :----------------------- | :-------------------- |
|
||||
| **연쇄 드롭다운 통합 관리** | **삭제** | 6개 탭 전체 제거 |
|
||||
| ├─ 2단계 연쇄관계 | UnifiedSelect 속성 | inline 정의 |
|
||||
| ├─ 2단계 연쇄관계 | V2Select 속성 | inline 정의 |
|
||||
| ├─ 다단계 계층 | **테이블관리로 이동** | 복잡한 구조 유지 필요 |
|
||||
| ├─ 조건부 필터 | 공통 conditional 속성 | 모든 컴포넌트에 적용 |
|
||||
| ├─ 자동 입력 | autoFill 속성 | 컴포넌트별 정의 |
|
||||
|
|
@ -557,21 +557,21 @@ interface UnifiedHierarchyProps {
|
|||
|
||||
| # | 컴포넌트 | 역할 |
|
||||
| :-: | :------------------- | :--------------------------------------- |
|
||||
| 1 | **UnifiedInput** | 단일 값 입력 (text, number, slider 등) |
|
||||
| 2 | **UnifiedSelect** | 선택 입력 (dropdown, radio, checkbox 등) |
|
||||
| 3 | **UnifiedDate** | 날짜/시간 입력 |
|
||||
| 4 | **UnifiedText** | 다중 행 텍스트 (textarea, rich editor) |
|
||||
| 5 | **UnifiedMedia** | 파일/미디어 (file, image) |
|
||||
| 6 | **UnifiedList** | 데이터 목록 (table, card, repeater) |
|
||||
| 7 | **UnifiedLayout** | 레이아웃 배치 (grid, split, flex) |
|
||||
| 8 | **UnifiedGroup** | 콘텐츠 그룹화 (tabs, accordion, section) |
|
||||
| 9 | **UnifiedBiz** | 비즈니스 특화 (flow, rack, map 등) |
|
||||
| 10 | **UnifiedHierarchy** | 계층 구조 (tree, org, bom, cascading) |
|
||||
| 1 | **V2Input** | 단일 값 입력 (text, number, slider 등) |
|
||||
| 2 | **V2Select** | 선택 입력 (dropdown, radio, checkbox 등) |
|
||||
| 3 | **V2Date** | 날짜/시간 입력 |
|
||||
| 4 | **V2Text** | 다중 행 텍스트 (textarea, rich editor) |
|
||||
| 5 | **V2Media** | 파일/미디어 (file, image) |
|
||||
| 6 | **V2List** | 데이터 목록 (table, card, repeater) |
|
||||
| 7 | **V2Layout** | 레이아웃 배치 (grid, split, flex) |
|
||||
| 8 | **V2Group** | 콘텐츠 그룹화 (tabs, accordion, section) |
|
||||
| 9 | **V2Biz** | 비즈니스 특화 (flow, rack, map 등) |
|
||||
| 10 | **V2Hierarchy** | 계층 구조 (tree, org, bom, cascading) |
|
||||
|
||||
### 12.2 공통 속성 (모든 컴포넌트에 적용)
|
||||
|
||||
```typescript
|
||||
interface BaseUnifiedProps {
|
||||
interface BaseV2Props {
|
||||
// 기본 속성
|
||||
id: string;
|
||||
label?: string;
|
||||
|
|
@ -614,10 +614,10 @@ interface BaseUnifiedProps {
|
|||
}
|
||||
```
|
||||
|
||||
### 12.3 UnifiedSelect 전용 속성
|
||||
### 12.3 V2Select 전용 속성
|
||||
|
||||
```typescript
|
||||
interface UnifiedSelectProps extends BaseUnifiedProps {
|
||||
interface V2SelectProps extends BaseV2Props {
|
||||
// 표시 모드
|
||||
mode: "dropdown" | "radio" | "check" | "tag" | "toggle" | "swap";
|
||||
|
||||
|
|
@ -660,11 +660,11 @@ interface UnifiedSelectProps extends BaseUnifiedProps {
|
|||
| AS-IS | TO-BE |
|
||||
| :---------------------------- | :----------------------------------- |
|
||||
| 연쇄 드롭다운 통합 관리 (6탭) | **삭제** |
|
||||
| - 2단계 연쇄관계 | → UnifiedSelect.cascading 속성 |
|
||||
| - 2단계 연쇄관계 | → V2Select.cascading 속성 |
|
||||
| - 다단계 계층 | → 테이블관리 > 계층 구조 설정 |
|
||||
| - 조건부 필터 | → 공통 conditional 속성 |
|
||||
| - 자동 입력 | → 공통 autoFill 속성 |
|
||||
| - 상호 배제 | → UnifiedSelect.mutualExclusion 속성 |
|
||||
| - 상호 배제 | → V2Select.mutualExclusion 속성 |
|
||||
| - 카테고리 값 연쇄 | → 카테고리 관리와 통합 |
|
||||
|
||||
---
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# V2 컴포넌트 및 Unified 폼 컴포넌트 결합도 분석 보고서
|
||||
# V2 컴포넌트 및 V2 폼 컴포넌트 결합도 분석 보고서
|
||||
|
||||
> 작성일: 2026-01-26
|
||||
> 목적: 컴포넌트 간 결합도 분석 및 느슨한 결합 전환 가능성 평가
|
||||
|
|
@ -29,23 +29,23 @@
|
|||
| 16 | v2-table-search-widget | `v2-table-search-widget/` | 테이블 검색 위젯 |
|
||||
| 17 | v2-tabs-widget | `v2-tabs-widget/` | 탭 위젯 |
|
||||
| 18 | v2-text-display | `v2-text-display/` | 텍스트 표시 |
|
||||
| 19 | v2-unified-repeater | `v2-unified-repeater/` | 통합 리피터 |
|
||||
| 19 | v2-repeater | `v2-repeater/` | 통합 리피터 |
|
||||
|
||||
### 1.2 Unified 폼 컴포넌트 (11개)
|
||||
### 1.2 V2 폼 컴포넌트 (11개)
|
||||
|
||||
| # | 컴포넌트 | 파일 | 주요 용도 |
|
||||
|---|---------|------|----------|
|
||||
| 1 | UnifiedInput | `UnifiedInput.tsx` | 텍스트/숫자/이메일 등 입력 |
|
||||
| 2 | UnifiedSelect | `UnifiedSelect.tsx` | 선택박스/라디오/체크박스 |
|
||||
| 3 | UnifiedDate | `UnifiedDate.tsx` | 날짜/시간 입력 |
|
||||
| 4 | UnifiedRepeater | `UnifiedRepeater.tsx` | 리피터 (테이블 형태) |
|
||||
| 5 | UnifiedLayout | `UnifiedLayout.tsx` | 레이아웃 컨테이너 |
|
||||
| 6 | UnifiedGroup | `UnifiedGroup.tsx` | 그룹 컨테이너 (카드/탭/접기) |
|
||||
| 7 | UnifiedHierarchy | `UnifiedHierarchy.tsx` | 계층 구조 표시 |
|
||||
| 8 | UnifiedList | `UnifiedList.tsx` | 리스트 표시 |
|
||||
| 9 | UnifiedMedia | `UnifiedMedia.tsx` | 파일/이미지/비디오 업로드 |
|
||||
| 10 | UnifiedBiz | `UnifiedBiz.tsx` | 비즈니스 컴포넌트 |
|
||||
| 11 | UnifiedFormContext | `UnifiedFormContext.tsx` | 폼 상태 관리 컨텍스트 |
|
||||
| 1 | V2Input | `V2Input.tsx` | 텍스트/숫자/이메일 등 입력 |
|
||||
| 2 | V2Select | `V2Select.tsx` | 선택박스/라디오/체크박스 |
|
||||
| 3 | V2Date | `V2Date.tsx` | 날짜/시간 입력 |
|
||||
| 4 | V2Repeater | `V2Repeater.tsx` | 리피터 (테이블 형태) |
|
||||
| 5 | V2Layout | `V2Layout.tsx` | 레이아웃 컨테이너 |
|
||||
| 6 | V2Group | `V2Group.tsx` | 그룹 컨테이너 (카드/탭/접기) |
|
||||
| 7 | V2Hierarchy | `V2Hierarchy.tsx` | 계층 구조 표시 |
|
||||
| 8 | V2List | `V2List.tsx` | 리스트 표시 |
|
||||
| 9 | V2Media | `V2Media.tsx` | 파일/이미지/비디오 업로드 |
|
||||
| 10 | V2Biz | `V2Biz.tsx` | 비즈니스 컴포넌트 |
|
||||
| 11 | V2FormContext | `V2FormContext.tsx` | 폼 상태 관리 컨텍스트 |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -140,29 +140,29 @@ window.addEventListener("checkboxSelectionChange", handleSelectionChange);
|
|||
| v2-table-search-widget | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| v2-text-display | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| v2-repeat-screen-modal | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| v2-unified-repeater | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| v2-repeater | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
|
||||
### 2.3 Unified 폼 컴포넌트 결합도 상세
|
||||
### 2.3 V2 폼 컴포넌트 결합도 상세
|
||||
|
||||
| 컴포넌트 | buttonActions Import | CustomEvent 사용 | window.__ 사용 | 결합도 점수 |
|
||||
|---------|---------------------|------------------|----------------|------------|
|
||||
| **UnifiedRepeater** | ❌ | 7개 수신/발생 | 2개 사용 | 🔴 8/10 |
|
||||
| **UnifiedFormContext** | ❌ | 3개 발생 | ❌ | 🟠 4/10 |
|
||||
| UnifiedInput | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| UnifiedSelect | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| UnifiedDate | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| UnifiedLayout | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| UnifiedGroup | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| UnifiedHierarchy | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| UnifiedList | ❌ | 0개 (TableList 래핑) | ❌ | 🟢 2/10 |
|
||||
| UnifiedMedia | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| UnifiedBiz | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| **V2Repeater** | ❌ | 7개 수신/발생 | 2개 사용 | 🔴 8/10 |
|
||||
| **V2FormContext** | ❌ | 3개 발생 | ❌ | 🟠 4/10 |
|
||||
| V2Input | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| V2Select | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| V2Date | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| V2Layout | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| V2Group | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| V2Hierarchy | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| V2List | ❌ | 0개 (TableList 래핑) | ❌ | 🟢 2/10 |
|
||||
| V2Media | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
| V2Biz | ❌ | 0개 | ❌ | 🟢 1/10 |
|
||||
|
||||
**UnifiedRepeater 상세:**
|
||||
**V2Repeater 상세:**
|
||||
```typescript
|
||||
// 전역 상태 사용
|
||||
window.__unifiedRepeaterInstances = new Set();
|
||||
window.__unifiedRepeaterInstances.add(targetTableName);
|
||||
window.__v2RepeaterInstances = new Set();
|
||||
window.__v2RepeaterInstances.add(targetTableName);
|
||||
|
||||
// CustomEvent 수신
|
||||
window.addEventListener("repeaterSave", handleSaveEvent);
|
||||
|
|
@ -171,7 +171,7 @@ window.addEventListener("componentDataTransfer", handleComponentDataTransfer);
|
|||
window.addEventListener("splitPanelDataTransfer", handleSplitPanelDataTransfer);
|
||||
```
|
||||
|
||||
**UnifiedFormContext 상세:**
|
||||
**V2FormContext 상세:**
|
||||
```typescript
|
||||
// CustomEvent 발생 (레거시 호환)
|
||||
window.dispatchEvent(new CustomEvent("beforeFormSave", { detail: eventDetail }));
|
||||
|
|
@ -211,7 +211,7 @@ window.dispatchEvent(new CustomEvent("afterFormSave", { detail: { ... } }));
|
|||
│ │
|
||||
▼ ▼
|
||||
┌───────────┐ ┌───────────┐
|
||||
│Unified │ │Unified │
|
||||
│V2 │ │V2 │
|
||||
│Repeater │ │FormContext│
|
||||
└───────────┘ └───────────┘
|
||||
```
|
||||
|
|
@ -227,10 +227,10 @@ window.dispatchEvent(new CustomEvent("afterFormSave", { detail: { ... } }));
|
|||
| `refreshTable` | v2-button-primary, buttonActions | 테이블 데이터 새로고침 |
|
||||
| `closeEditModal` | v2-button-primary, buttonActions | 수정 모달 닫기 |
|
||||
| `saveSuccessInModal` | v2-button-primary, buttonActions | 저장 성공 알림 (연속 등록) |
|
||||
| `beforeFormSave` | UnifiedFormContext, buttonActions | 저장 전 데이터 수집 |
|
||||
| `afterFormSave` | UnifiedFormContext | 저장 완료 알림 |
|
||||
| `beforeFormSave` | V2FormContext, buttonActions | 저장 전 데이터 수집 |
|
||||
| `afterFormSave` | V2FormContext | 저장 완료 알림 |
|
||||
| `tableListDataChange` | v2-table-list | 테이블 데이터 변경 알림 |
|
||||
| `repeaterDataChange` | UnifiedRepeater | 리피터 데이터 변경 알림 |
|
||||
| `repeaterDataChange` | V2Repeater | 리피터 데이터 변경 알림 |
|
||||
| `repeaterSave` | buttonActions | 리피터 저장 요청 |
|
||||
| `openScreenModal` | v2-split-panel-layout | 화면 모달 열기 |
|
||||
| `refreshCardDisplay` | buttonActions | 카드 디스플레이 새로고침 |
|
||||
|
|
@ -240,13 +240,13 @@ window.dispatchEvent(new CustomEvent("afterFormSave", { detail: { ... } }));
|
|||
| 이벤트명 | 수신 컴포넌트 | 처리 내용 |
|
||||
|---------|-------------|----------|
|
||||
| `refreshTable` | v2-table-list, v2-split-panel-layout | 데이터 재조회 |
|
||||
| `beforeFormSave` | v2-repeat-container, UnifiedRepeater | formData에 섹션 데이터 추가 |
|
||||
| `beforeFormSave` | v2-repeat-container, V2Repeater | formData에 섹션 데이터 추가 |
|
||||
| `tableListDataChange` | v2-aggregation-widget, v2-repeat-container | 집계 재계산, 데이터 동기화 |
|
||||
| `repeaterDataChange` | v2-aggregation-widget, v2-repeat-container | 집계 재계산, 데이터 동기화 |
|
||||
| `repeaterSave` | UnifiedRepeater | 리피터 데이터 저장 실행 |
|
||||
| `repeaterSave` | V2Repeater | 리피터 데이터 저장 실행 |
|
||||
| `selectionChange` | v2-aggregation-widget | 선택 기반 집계 |
|
||||
| `componentDataTransfer` | UnifiedRepeater | 컴포넌트 간 데이터 전달 |
|
||||
| `splitPanelDataTransfer` | UnifiedRepeater | 분할 패널 데이터 전달 |
|
||||
| `componentDataTransfer` | V2Repeater | 컴포넌트 간 데이터 전달 |
|
||||
| `splitPanelDataTransfer` | V2Repeater | 분할 패널 데이터 전달 |
|
||||
| `refreshCardDisplay` | v2-card-display | 카드 데이터 재조회 |
|
||||
|
||||
---
|
||||
|
|
@ -255,7 +255,7 @@ window.dispatchEvent(new CustomEvent("afterFormSave", { detail: { ... } }));
|
|||
|
||||
| 전역 변수 | 사용 컴포넌트 | 용도 | 위험도 |
|
||||
|----------|-------------|------|--------|
|
||||
| `window.__unifiedRepeaterInstances` | UnifiedRepeater, buttonActions | 리피터 인스턴스 추적 | 🟠 중간 |
|
||||
| `window.__v2RepeaterInstances` | V2Repeater, buttonActions | 리피터 인스턴스 추적 | 🟠 중간 |
|
||||
| `window.__relatedButtonsTargetTables` | v2-table-list | 관련 버튼 대상 테이블 | 🟠 중간 |
|
||||
| `window.__relatedButtonsSelectedData` | v2-table-list, buttonActions | 관련 버튼 선택 데이터 | 🟠 중간 |
|
||||
| `window.__dataRegistry` | v2-table-list (v1/v2) | 테이블 데이터 레지스트리 | 🟠 중간 |
|
||||
|
|
@ -272,12 +272,12 @@ window.dispatchEvent(new CustomEvent("afterFormSave", { detail: { ... } }));
|
|||
| 🟠 중간 (4-6점) | 4개 | v2-repeat-container, v2-split-panel-layout, v2-aggregation-widget, v2-tabs-widget |
|
||||
| 🟢 낮음 (1-3점) | 12개 | 나머지 |
|
||||
|
||||
### 6.2 Unified 컴포넌트 (11개)
|
||||
### 6.2 V2 컴포넌트 (11개)
|
||||
|
||||
| 결합도 수준 | 개수 | 컴포넌트 |
|
||||
|------------|------|---------|
|
||||
| 🔴 높음 (7-10점) | 1개 | UnifiedRepeater |
|
||||
| 🟠 중간 (4-6점) | 1개 | UnifiedFormContext |
|
||||
| 🔴 높음 (7-10점) | 1개 | V2Repeater |
|
||||
| 🟠 중간 (4-6점) | 1개 | V2FormContext |
|
||||
| 🟢 낮음 (1-3점) | 9개 | 나머지 |
|
||||
|
||||
### 6.3 전체 결합도 분포
|
||||
|
|
@ -288,14 +288,14 @@ window.dispatchEvent(new CustomEvent("afterFormSave", { detail: { ... } }));
|
|||
높은 결합도 (🔴): 3개 (10.3%)
|
||||
├── v2-button-primary
|
||||
├── v2-table-list
|
||||
└── UnifiedRepeater
|
||||
└── V2Repeater
|
||||
|
||||
중간 결합도 (🟠): 5개 (17.2%)
|
||||
├── v2-repeat-container
|
||||
├── v2-split-panel-layout
|
||||
├── v2-aggregation-widget
|
||||
├── v2-tabs-widget
|
||||
└── UnifiedFormContext
|
||||
└── V2FormContext
|
||||
|
||||
낮은 결합도 (🟢): 21개 (72.5%)
|
||||
└── 나머지 모든 컴포넌트
|
||||
|
|
@ -318,7 +318,7 @@ v2-table-list 오류 발생 시:
|
|||
├── related-button 이벤트 미발생 → 관련 버튼 비활성화
|
||||
└── 전역 상태 오염 가능성
|
||||
|
||||
UnifiedRepeater 오류 발생 시:
|
||||
V2Repeater 오류 발생 시:
|
||||
├── beforeFormSave 처리 실패 → 리피터 데이터 저장 누락
|
||||
├── repeaterSave 수신 실패 → 저장 요청 무시
|
||||
└── 전역 인스턴스 레지스트리 오류
|
||||
|
|
@ -330,7 +330,7 @@ UnifiedRepeater 오류 발생 시:
|
|||
|---------|-----------------|----------|
|
||||
| v2-button-primary | 저장/삭제 전체 | ❌ 격리 안됨 |
|
||||
| v2-table-list | 집계/관련버튼 | ❌ 격리 안됨 |
|
||||
| UnifiedRepeater | 리피터 저장 | ❌ 격리 안됨 |
|
||||
| V2Repeater | 리피터 저장 | ❌ 격리 안됨 |
|
||||
| v2-aggregation-widget | 자신만 | ✅ 부분 격리 |
|
||||
| v2-repeat-container | 자신만 | ✅ 부분 격리 |
|
||||
| 나머지 21개 | 자신만 | ✅ 완전 격리 |
|
||||
|
|
@ -357,7 +357,7 @@ UnifiedRepeater 오류 발생 시:
|
|||
|---------|---------|----------|
|
||||
| 1 | v2-button-primary | buttonActions 의존성 제거, 독립 저장 서비스 |
|
||||
| 2 | v2-table-list | 전역 상태 제거, EventBus 전환 |
|
||||
| 3 | UnifiedRepeater | 전역 상태 제거, EventBus 전환 |
|
||||
| 3 | V2Repeater | 전역 상태 제거, EventBus 전환 |
|
||||
|
||||
### 8.3 3단계: 이벤트 통합 (2-3일)
|
||||
|
||||
|
|
@ -422,7 +422,7 @@ UnifiedRepeater 오류 발생 시:
|
|||
|---------|-----------------|-------------------|-------------|------|
|
||||
| **v2-button-primary** | ✅ | ✅ | ✅ | 완료 |
|
||||
| **v2-table-list** | ✅ | - | ✅ | 완료 |
|
||||
| **UnifiedRepeater** | ✅ | - | ✅ | 완료 |
|
||||
| **V2Repeater** | ✅ | - | ✅ | 완료 |
|
||||
|
||||
### 10.3 아키텍처 특징
|
||||
|
||||
|
|
@ -469,7 +469,7 @@ useEffect(() => {
|
|||
### 11.1 현재 상태 요약
|
||||
|
||||
- **전체 29개 컴포넌트 중 72.5%(21개)는 이미 낮은 결합도**를 가지고 있어 독립적으로 동작
|
||||
- **핵심 문제 컴포넌트 3개 (v2-button-primary, v2-table-list, UnifiedRepeater) 마이그레이션 완료**
|
||||
- **핵심 문제 컴포넌트 3개 (v2-button-primary, v2-table-list, V2Repeater) 마이그레이션 완료**
|
||||
- **buttonActions.ts (7,145줄)**는 추후 분할 예정 (현재는 동작 유지)
|
||||
|
||||
### 11.2 달성 목표
|
||||
|
|
@ -514,22 +514,22 @@ frontend/
|
|||
│ │ ├── v2-table-search-widget/
|
||||
│ │ ├── v2-tabs-widget/
|
||||
│ │ ├── v2-text-display/
|
||||
│ │ └── v2-unified-repeater/
|
||||
│ │ └── v2-repeater/
|
||||
│ └── utils/
|
||||
│ └── buttonActions.ts (7,145줄)
|
||||
└── components/
|
||||
└── unified/
|
||||
├── UnifiedInput.tsx
|
||||
├── UnifiedSelect.tsx
|
||||
├── UnifiedDate.tsx
|
||||
├── UnifiedRepeater.tsx
|
||||
├── UnifiedLayout.tsx
|
||||
├── UnifiedGroup.tsx
|
||||
├── UnifiedHierarchy.tsx
|
||||
├── UnifiedList.tsx
|
||||
├── UnifiedMedia.tsx
|
||||
├── UnifiedBiz.tsx
|
||||
└── UnifiedFormContext.tsx
|
||||
└── v2/
|
||||
├── V2Input.tsx
|
||||
├── V2Select.tsx
|
||||
├── V2Date.tsx
|
||||
├── V2Repeater.tsx
|
||||
├── V2Layout.tsx
|
||||
├── V2Group.tsx
|
||||
├── V2Hierarchy.tsx
|
||||
├── V2List.tsx
|
||||
├── V2Media.tsx
|
||||
├── V2Biz.tsx
|
||||
└── V2FormContext.tsx
|
||||
```
|
||||
|
||||
## 부록 B: V2 Core 파일 구조 (구현됨)
|
||||
|
|
@ -67,7 +67,7 @@ V2(Version 2) 컴포넌트는 기존 레거시 컴포넌트의 문제점을 해
|
|||
|------------|------|------|
|
||||
| `v2-rack-structure` | 렉 구조 | 창고 렉 구조 표시 |
|
||||
| `v2-repeat-screen-modal` | 반복 화면 모달 | 반복 가능한 화면 모달 |
|
||||
| `v2-unified-repeater` | 통합 리피터 | 통합 리피터 테이블 |
|
||||
| `v2-repeater` | 통합 리피터 | 통합 리피터 테이블 |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -172,10 +172,10 @@ import { V2ErrorBoundary } from "@/lib/v2-core";
|
|||
|--------|------|--------|--------|
|
||||
| `v2:table:refresh` | 테이블 새로고침 | v2-button-primary | v2-table-list |
|
||||
| `v2:table:data:change` | 테이블 데이터 변경 | v2-table-list | v2-aggregation-widget |
|
||||
| `v2:form:save:collect` | 폼 저장 전 데이터 수집 | buttonActions | v2-repeat-container, UnifiedRepeater |
|
||||
| `v2:form:save:collect` | 폼 저장 전 데이터 수집 | buttonActions | v2-repeat-container, V2Repeater |
|
||||
| `v2:modal:close` | 모달 닫기 | v2-button-primary | EditModal |
|
||||
| `v2:modal:save:success` | 모달 저장 성공 | v2-button-primary | EditModal |
|
||||
| `v2:repeater:save` | 리피터 저장 | buttonActions | UnifiedRepeater |
|
||||
| `v2:repeater:save` | 리피터 저장 | buttonActions | V2Repeater |
|
||||
| `v2:component:error` | 컴포넌트 에러 | V2ErrorBoundary | 로깅/모니터링 |
|
||||
|
||||
### 4.2 이벤트 흐름 다이어그램
|
||||
|
|
@ -333,7 +333,7 @@ export const BadConfigPanel: React.FC<Props> = ({ config, onChange }) => {
|
|||
|---------|-------------|---------------|-------------|
|
||||
| v2-button-primary | ✅ | ✅ | ✅ |
|
||||
| v2-table-list | ✅ | ✅ | ✅ |
|
||||
| UnifiedRepeater | ✅ | ✅ | ✅ |
|
||||
| V2Repeater | ✅ | ✅ | ✅ |
|
||||
|
||||
### 7.3 장애 격리 검증
|
||||
|
||||
|
|
@ -341,7 +341,7 @@ export const BadConfigPanel: React.FC<Props> = ({ config, onChange }) => {
|
|||
v2-button-primary 에러 발생 시:
|
||||
├── V2ErrorBoundary 캐치 → 버튼만 에러 UI 표시
|
||||
├── v2-table-list: 정상 동작 ✅
|
||||
└── UnifiedRepeater: 정상 동작 ✅
|
||||
└── V2Repeater: 정상 동작 ✅
|
||||
|
||||
v2-table-list 에러 발생 시:
|
||||
├── V2ErrorBoundary 캐치 → 테이블만 에러 UI 표시
|
||||
|
|
@ -351,30 +351,30 @@ v2-table-list 에러 발생 시:
|
|||
|
||||
---
|
||||
|
||||
## 8. Unified 폼 컴포넌트
|
||||
## 8. V2 폼 컴포넌트
|
||||
|
||||
### 8.1 목록 (11개)
|
||||
|
||||
| 컴포넌트 | 파일 | 용도 |
|
||||
|---------|------|------|
|
||||
| UnifiedInput | UnifiedInput.tsx | 텍스트/숫자/이메일/채번 입력 |
|
||||
| UnifiedSelect | UnifiedSelect.tsx | 선택박스/라디오/체크박스/카테고리 |
|
||||
| UnifiedDate | UnifiedDate.tsx | 날짜/시간 입력 |
|
||||
| UnifiedRepeater | UnifiedRepeater.tsx | 리피터 테이블 |
|
||||
| UnifiedLayout | UnifiedLayout.tsx | 레이아웃 컨테이너 |
|
||||
| UnifiedGroup | UnifiedGroup.tsx | 그룹 컨테이너 |
|
||||
| UnifiedHierarchy | UnifiedHierarchy.tsx | 계층 구조 표시 |
|
||||
| UnifiedList | UnifiedList.tsx | 리스트 표시 |
|
||||
| UnifiedMedia | UnifiedMedia.tsx | 파일/이미지/비디오 |
|
||||
| UnifiedBiz | UnifiedBiz.tsx | 비즈니스 컴포넌트 |
|
||||
| UnifiedFormContext | UnifiedFormContext.tsx | 폼 상태 관리 |
|
||||
| V2Input | V2Input.tsx | 텍스트/숫자/이메일/채번 입력 |
|
||||
| V2Select | V2Select.tsx | 선택박스/라디오/체크박스/카테고리 |
|
||||
| V2Date | V2Date.tsx | 날짜/시간 입력 |
|
||||
| V2Repeater | V2Repeater.tsx | 리피터 테이블 |
|
||||
| V2Layout | V2Layout.tsx | 레이아웃 컨테이너 |
|
||||
| V2Group | V2Group.tsx | 그룹 컨테이너 |
|
||||
| V2Hierarchy | V2Hierarchy.tsx | 계층 구조 표시 |
|
||||
| V2List | V2List.tsx | 리스트 표시 |
|
||||
| V2Media | V2Media.tsx | 파일/이미지/비디오 |
|
||||
| V2Biz | V2Biz.tsx | 비즈니스 컴포넌트 |
|
||||
| V2FormContext | V2FormContext.tsx | 폼 상태 관리 |
|
||||
|
||||
### 8.2 inputType 자동 처리
|
||||
|
||||
Unified 컴포넌트는 `inputType`에 따라 자동으로 적절한 UI를 렌더링합니다:
|
||||
V2 컴포넌트는 `inputType`에 따라 자동으로 적절한 UI를 렌더링합니다:
|
||||
|
||||
```typescript
|
||||
// UnifiedInput.tsx
|
||||
// V2Input.tsx
|
||||
switch (inputType) {
|
||||
case "numbering":
|
||||
// 채번 규칙 자동 조회 및 코드 생성
|
||||
|
|
@ -386,7 +386,7 @@ switch (inputType) {
|
|||
break;
|
||||
}
|
||||
|
||||
// UnifiedSelect.tsx
|
||||
// V2Select.tsx
|
||||
switch (inputType) {
|
||||
case "category":
|
||||
// 카테고리 값 자동 조회 및 드롭다운 표시
|
||||
|
|
@ -468,8 +468,8 @@ V2 Core:
|
|||
V2 컴포넌트:
|
||||
- frontend/lib/registry/components/v2-*/
|
||||
|
||||
Unified 폼 컴포넌트:
|
||||
- frontend/components/unified/
|
||||
V2 폼 컴포넌트:
|
||||
- frontend/components/v2/
|
||||
|
||||
채번/카테고리 테스트 테이블:
|
||||
- db/migrations/040_create_numbering_rules_test.sql
|
||||
|
|
@ -534,6 +534,6 @@ frontend/lib/registry/components/
|
|||
├── v2-table-search-widget/
|
||||
├── v2-tabs-widget/
|
||||
├── v2-text-display/
|
||||
└── v2-unified-repeater/
|
||||
└── v2-repeater/
|
||||
```
|
||||
|
||||
|
|
@ -15,29 +15,29 @@
|
|||
|
||||
### 상위 15개 컴포넌트
|
||||
|
||||
| 순위 | 컴포넌트 | 사용 횟수 | 사용 화면 수 | Unified 매핑 |
|
||||
| 순위 | 컴포넌트 | 사용 횟수 | 사용 화면 수 | V2 매핑 |
|
||||
| :--: | :-------------------------- | :-------: | :----------: | :------------------------------ |
|
||||
| 1 | button-primary | 571 | 364 | UnifiedInput (type: button) |
|
||||
| 2 | text-input | 805 | 166 | **UnifiedInput (type: text)** |
|
||||
| 3 | table-list | 130 | 130 | UnifiedList (viewMode: table) |
|
||||
| 4 | table-search-widget | 127 | 127 | UnifiedList (searchable: true) |
|
||||
| 5 | select-basic | 121 | 76 | **UnifiedSelect** |
|
||||
| 6 | number-input | 86 | 34 | **UnifiedInput (type: number)** |
|
||||
| 7 | date-input | 83 | 51 | **UnifiedDate** |
|
||||
| 8 | file-upload | 41 | 18 | UnifiedMedia (type: file) |
|
||||
| 9 | tabs-widget | 39 | 39 | UnifiedGroup (type: tabs) |
|
||||
| 10 | split-panel-layout | 39 | 39 | UnifiedLayout (type: split) |
|
||||
| 11 | category-manager | 38 | 38 | UnifiedBiz (type: category) |
|
||||
| 12 | numbering-rule | 31 | 31 | UnifiedBiz (type: numbering) |
|
||||
| 1 | button-primary | 571 | 364 | V2Input (type: button) |
|
||||
| 2 | text-input | 805 | 166 | **V2Input (type: text)** |
|
||||
| 3 | table-list | 130 | 130 | V2List (viewMode: table) |
|
||||
| 4 | table-search-widget | 127 | 127 | V2List (searchable: true) |
|
||||
| 5 | select-basic | 121 | 76 | **V2Select** |
|
||||
| 6 | number-input | 86 | 34 | **V2Input (type: number)** |
|
||||
| 7 | date-input | 83 | 51 | **V2Date** |
|
||||
| 8 | file-upload | 41 | 18 | V2Media (type: file) |
|
||||
| 9 | tabs-widget | 39 | 39 | V2Group (type: tabs) |
|
||||
| 10 | split-panel-layout | 39 | 39 | V2Layout (type: split) |
|
||||
| 11 | category-manager | 38 | 38 | V2Biz (type: category) |
|
||||
| 12 | numbering-rule | 31 | 31 | V2Biz (type: numbering) |
|
||||
| 13 | selected-items-detail-input | 29 | 29 | 복합 컴포넌트 |
|
||||
| 14 | modal-repeater-table | 25 | 25 | UnifiedList (modal: true) |
|
||||
| 15 | image-widget | 29 | 29 | UnifiedMedia (type: image) |
|
||||
| 14 | modal-repeater-table | 25 | 25 | V2List (modal: true) |
|
||||
| 15 | image-widget | 29 | 29 | V2Media (type: image) |
|
||||
|
||||
---
|
||||
|
||||
## 2. Unified 컴포넌트별 통합 대상 분석
|
||||
## 2. V2 컴포넌트별 통합 대상 분석
|
||||
|
||||
### UnifiedInput (예상 통합 대상: 891개)
|
||||
### V2Input (예상 통합 대상: 891개)
|
||||
|
||||
| 기존 컴포넌트 | 사용 횟수 | 비율 |
|
||||
| :------------ | :-------: | :---: |
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
|
||||
**우선순위: 1위** - 가장 많이 사용되는 컴포넌트
|
||||
|
||||
### UnifiedSelect (예상 통합 대상: 140개)
|
||||
### V2Select (예상 통합 대상: 140개)
|
||||
|
||||
| 기존 컴포넌트 | 사용 횟수 | widgetType |
|
||||
| :------------------------ | :-------: | :--------- |
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
|
||||
**우선순위: 2위** - 다양한 모드 지원 필요
|
||||
|
||||
### UnifiedDate (예상 통합 대상: 83개)
|
||||
### V2Date (예상 통합 대상: 83개)
|
||||
|
||||
| 기존 컴포넌트 | 사용 횟수 |
|
||||
| :---------------- | :-------: |
|
||||
|
|
@ -69,7 +69,7 @@
|
|||
|
||||
**우선순위: 3위**
|
||||
|
||||
### UnifiedList (예상 통합 대상: 283개)
|
||||
### V2List (예상 통합 대상: 283개)
|
||||
|
||||
| 기존 컴포넌트 | 사용 횟수 | 비고 |
|
||||
| :-------------------- | :-------: | :---------- |
|
||||
|
|
@ -82,14 +82,14 @@
|
|||
|
||||
**우선순위: 4위** - 핵심 데이터 표시 컴포넌트
|
||||
|
||||
### UnifiedMedia (예상 통합 대상: 70개)
|
||||
### V2Media (예상 통합 대상: 70개)
|
||||
|
||||
| 기존 컴포넌트 | 사용 횟수 |
|
||||
| :------------ | :-------: |
|
||||
| file-upload | 41 |
|
||||
| image-widget | 29 |
|
||||
|
||||
### UnifiedLayout (예상 통합 대상: 62개)
|
||||
### V2Layout (예상 통합 대상: 62개)
|
||||
|
||||
| 기존 컴포넌트 | 사용 횟수 |
|
||||
| :------------------ | :-------: |
|
||||
|
|
@ -97,7 +97,7 @@
|
|||
| screen-split-panel | 21 |
|
||||
| split-panel-layout2 | 2 |
|
||||
|
||||
### UnifiedGroup (예상 통합 대상: 99개)
|
||||
### V2Group (예상 통합 대상: 99개)
|
||||
|
||||
| 기존 컴포넌트 | 사용 횟수 |
|
||||
| :-------------------- | :-------: |
|
||||
|
|
@ -109,7 +109,7 @@
|
|||
| universal-form-modal | 7 |
|
||||
| repeat-screen-modal | 5 |
|
||||
|
||||
### UnifiedBiz (예상 통합 대상: 79개)
|
||||
### V2Biz (예상 통합 대상: 79개)
|
||||
|
||||
| 기존 컴포넌트 | 사용 횟수 |
|
||||
| :--------------------- | :-------: |
|
||||
|
|
@ -127,27 +127,27 @@
|
|||
|
||||
### Phase 1 우선순위 (즉시 효과가 큰 컴포넌트)
|
||||
|
||||
| 순위 | Unified 컴포넌트 | 통합 대상 수 | 영향 화면 수 | 이유 |
|
||||
| 순위 | V2 컴포넌트 | 통합 대상 수 | 영향 화면 수 | 이유 |
|
||||
| :---: | :---------------- | :----------: | :----------: | :--------------- |
|
||||
| **1** | **UnifiedInput** | 891개 | 200+ | 가장 많이 사용 |
|
||||
| **2** | **UnifiedSelect** | 140개 | 100+ | 다양한 모드 필요 |
|
||||
| **3** | **UnifiedDate** | 83개 | 51 | 비교적 단순 |
|
||||
| **1** | **V2Input** | 891개 | 200+ | 가장 많이 사용 |
|
||||
| **2** | **V2Select** | 140개 | 100+ | 다양한 모드 필요 |
|
||||
| **3** | **V2Date** | 83개 | 51 | 비교적 단순 |
|
||||
|
||||
### Phase 2 우선순위 (데이터 표시 컴포넌트)
|
||||
|
||||
| 순위 | Unified 컴포넌트 | 통합 대상 수 | 이유 |
|
||||
| 순위 | V2 컴포넌트 | 통합 대상 수 | 이유 |
|
||||
| :---: | :---------------- | :----------: | :--------------- |
|
||||
| **4** | **UnifiedList** | 283개 | 핵심 데이터 표시 |
|
||||
| **5** | **UnifiedLayout** | 62개 | 레이아웃 구조 |
|
||||
| **6** | **UnifiedGroup** | 99개 | 콘텐츠 그룹화 |
|
||||
| **4** | **V2List** | 283개 | 핵심 데이터 표시 |
|
||||
| **5** | **V2Layout** | 62개 | 레이아웃 구조 |
|
||||
| **6** | **V2Group** | 99개 | 콘텐츠 그룹화 |
|
||||
|
||||
### Phase 3 우선순위 (특수 컴포넌트)
|
||||
|
||||
| 순위 | Unified 컴포넌트 | 통합 대상 수 | 이유 |
|
||||
| 순위 | V2 컴포넌트 | 통합 대상 수 | 이유 |
|
||||
| :---: | :------------------- | :----------: | :------------ |
|
||||
| **7** | **UnifiedMedia** | 70개 | 파일/이미지 |
|
||||
| **8** | **UnifiedBiz** | 79개 | 비즈니스 특화 |
|
||||
| **9** | **UnifiedHierarchy** | 0개 | 신규 기능 |
|
||||
| **7** | **V2Media** | 70개 | 파일/이미지 |
|
||||
| **8** | **V2Biz** | 79개 | 비즈니스 특화 |
|
||||
| **9** | **V2Hierarchy** | 0개 | 신규 기능 |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -156,8 +156,8 @@
|
|||
### 4.1 button-primary 분리 검토
|
||||
|
||||
- 사용량: 571개 (1위)
|
||||
- 현재 계획: UnifiedInput에 포함
|
||||
- **제안**: 별도 `UnifiedButton` 컴포넌트로 분리 검토
|
||||
- 현재 계획: V2Input에 포함
|
||||
- **제안**: 별도 `V2Button` 컴포넌트로 분리 검토
|
||||
- 버튼은 입력과 성격이 다름
|
||||
- 액션 타입, 스타일, 권한 등 복잡한 설정 필요
|
||||
|
||||
|
|
@ -181,5 +181,5 @@
|
|||
1. [ ] 데이터 마이그레이션 전략 설계 (Phase 0-2)
|
||||
2. [ ] sys_input_type JSON Schema 설계 (Phase 0-3)
|
||||
3. [ ] DynamicConfigPanel 프로토타입 (Phase 0-4)
|
||||
4. [ ] UnifiedInput 구현 시작 (Phase 1-1)
|
||||
4. [ ] V2Input 구현 시작 (Phase 1-1)
|
||||
|
||||
|
|
@ -67,8 +67,8 @@
|
|||
"componentConfig": { ... },
|
||||
|
||||
// 신규 필드 추가
|
||||
"unifiedType": "UnifiedInput", // 새로운 통합 컴포넌트 타입
|
||||
"unifiedConfig": { // 새로운 설정 구조
|
||||
"v2Type": "V2Input", // 새로운 통합 컴포넌트 타입
|
||||
"v2Config": { // 새로운 설정 구조
|
||||
"type": "text",
|
||||
"format": "none",
|
||||
"placeholder": "텍스트를 입력하세요"
|
||||
|
|
@ -87,13 +87,13 @@
|
|||
### 2.2 렌더링 로직 수정
|
||||
|
||||
```typescript
|
||||
// 렌더러에서 unifiedType 우선 사용
|
||||
// 렌더러에서 v2Type 우선 사용
|
||||
function renderComponent(props: ComponentProps) {
|
||||
// 신규 타입이 있으면 Unified 컴포넌트 사용
|
||||
if (props.unifiedType) {
|
||||
return <UnifiedComponentRenderer
|
||||
type={props.unifiedType}
|
||||
config={props.unifiedConfig}
|
||||
// 신규 타입이 있으면 V2 컴포넌트 사용
|
||||
if (props.v2Type) {
|
||||
return <V2ComponentRenderer
|
||||
type={props.v2Type}
|
||||
config={props.v2Config}
|
||||
/>;
|
||||
}
|
||||
|
||||
|
|
@ -109,7 +109,7 @@ function renderComponent(props: ComponentProps) {
|
|||
|
||||
## 3. 컴포넌트별 매핑 규칙
|
||||
|
||||
### 3.1 text-input → UnifiedInput
|
||||
### 3.1 text-input → V2Input
|
||||
|
||||
```typescript
|
||||
// AS-IS
|
||||
|
|
@ -126,8 +126,8 @@ function renderComponent(props: ComponentProps) {
|
|||
|
||||
// TO-BE
|
||||
{
|
||||
"unifiedType": "UnifiedInput",
|
||||
"unifiedConfig": {
|
||||
"v2Type": "V2Input",
|
||||
"v2Config": {
|
||||
"type": "text", // componentConfig.webType 또는 "text"
|
||||
"format": "none", // componentConfig.format
|
||||
"placeholder": "..." // componentConfig.placeholder
|
||||
|
|
@ -135,7 +135,7 @@ function renderComponent(props: ComponentProps) {
|
|||
}
|
||||
```
|
||||
|
||||
### 3.2 number-input → UnifiedInput
|
||||
### 3.2 number-input → V2Input
|
||||
|
||||
```typescript
|
||||
// AS-IS
|
||||
|
|
@ -152,8 +152,8 @@ function renderComponent(props: ComponentProps) {
|
|||
|
||||
// TO-BE
|
||||
{
|
||||
"unifiedType": "UnifiedInput",
|
||||
"unifiedConfig": {
|
||||
"v2Type": "V2Input",
|
||||
"v2Config": {
|
||||
"type": "number",
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
|
|
@ -162,7 +162,7 @@ function renderComponent(props: ComponentProps) {
|
|||
}
|
||||
```
|
||||
|
||||
### 3.3 select-basic → UnifiedSelect
|
||||
### 3.3 select-basic → V2Select
|
||||
|
||||
```typescript
|
||||
// AS-IS (code 타입)
|
||||
|
|
@ -178,8 +178,8 @@ function renderComponent(props: ComponentProps) {
|
|||
|
||||
// TO-BE
|
||||
{
|
||||
"unifiedType": "UnifiedSelect",
|
||||
"unifiedConfig": {
|
||||
"v2Type": "V2Select",
|
||||
"v2Config": {
|
||||
"mode": "dropdown",
|
||||
"source": "code",
|
||||
"codeGroup": "ORDER_STATUS"
|
||||
|
|
@ -200,8 +200,8 @@ function renderComponent(props: ComponentProps) {
|
|||
|
||||
// TO-BE
|
||||
{
|
||||
"unifiedType": "UnifiedSelect",
|
||||
"unifiedConfig": {
|
||||
"v2Type": "V2Select",
|
||||
"v2Config": {
|
||||
"mode": "dropdown",
|
||||
"source": "entity",
|
||||
"searchable": true,
|
||||
|
|
@ -211,7 +211,7 @@ function renderComponent(props: ComponentProps) {
|
|||
}
|
||||
```
|
||||
|
||||
### 3.4 date-input → UnifiedDate
|
||||
### 3.4 date-input → V2Date
|
||||
|
||||
```typescript
|
||||
// AS-IS
|
||||
|
|
@ -226,8 +226,8 @@ function renderComponent(props: ComponentProps) {
|
|||
|
||||
// TO-BE
|
||||
{
|
||||
"unifiedType": "UnifiedDate",
|
||||
"unifiedConfig": {
|
||||
"v2Type": "V2Date",
|
||||
"v2Config": {
|
||||
"type": "date",
|
||||
"format": "YYYY-MM-DD"
|
||||
}
|
||||
|
|
@ -245,11 +245,11 @@ function renderComponent(props: ComponentProps) {
|
|||
|
||||
interface MigrationResult {
|
||||
success: boolean;
|
||||
unifiedType: string;
|
||||
unifiedConfig: Record<string, any>;
|
||||
v2Type: string;
|
||||
v2Config: Record<string, any>;
|
||||
}
|
||||
|
||||
export function migrateToUnified(
|
||||
export function migrateToV2(
|
||||
componentType: string,
|
||||
componentConfig: Record<string, any>
|
||||
): MigrationResult {
|
||||
|
|
@ -258,8 +258,8 @@ export function migrateToUnified(
|
|||
case 'text-input':
|
||||
return {
|
||||
success: true,
|
||||
unifiedType: 'UnifiedInput',
|
||||
unifiedConfig: {
|
||||
v2Type: 'V2Input',
|
||||
v2Config: {
|
||||
type: componentConfig.webType || 'text',
|
||||
format: componentConfig.format || 'none',
|
||||
placeholder: componentConfig.placeholder
|
||||
|
|
@ -269,8 +269,8 @@ export function migrateToUnified(
|
|||
case 'number-input':
|
||||
return {
|
||||
success: true,
|
||||
unifiedType: 'UnifiedInput',
|
||||
unifiedConfig: {
|
||||
v2Type: 'V2Input',
|
||||
v2Config: {
|
||||
type: 'number',
|
||||
min: componentConfig.min,
|
||||
max: componentConfig.max,
|
||||
|
|
@ -281,8 +281,8 @@ export function migrateToUnified(
|
|||
case 'select-basic':
|
||||
return {
|
||||
success: true,
|
||||
unifiedType: 'UnifiedSelect',
|
||||
unifiedConfig: {
|
||||
v2Type: 'V2Select',
|
||||
v2Config: {
|
||||
mode: 'dropdown',
|
||||
source: componentConfig.webType || 'static',
|
||||
codeGroup: componentConfig.codeCategory,
|
||||
|
|
@ -295,8 +295,8 @@ export function migrateToUnified(
|
|||
case 'date-input':
|
||||
return {
|
||||
success: true,
|
||||
unifiedType: 'UnifiedDate',
|
||||
unifiedConfig: {
|
||||
v2Type: 'V2Date',
|
||||
v2Config: {
|
||||
type: componentConfig.webType || 'date',
|
||||
format: componentConfig.format
|
||||
}
|
||||
|
|
@ -305,8 +305,8 @@ export function migrateToUnified(
|
|||
default:
|
||||
return {
|
||||
success: false,
|
||||
unifiedType: '',
|
||||
unifiedConfig: {}
|
||||
v2Type: '',
|
||||
v2Config: {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -322,8 +322,8 @@ SELECT * FROM screen_layouts;
|
|||
-- 마이그레이션 실행 (text-input 예시)
|
||||
UPDATE screen_layouts
|
||||
SET properties = properties || jsonb_build_object(
|
||||
'unifiedType', 'UnifiedInput',
|
||||
'unifiedConfig', jsonb_build_object(
|
||||
'v2Type', 'V2Input',
|
||||
'v2Config', jsonb_build_object(
|
||||
'type', COALESCE(properties->'componentConfig'->>'webType', 'text'),
|
||||
'format', COALESCE(properties->'componentConfig'->>'format', 'none'),
|
||||
'placeholder', properties->'componentConfig'->>'placeholder'
|
||||
|
|
@ -352,7 +352,7 @@ WHERE sl.layout_id = slb.layout_id;
|
|||
|
||||
-- 또는 신규 필드만 제거
|
||||
UPDATE screen_layouts
|
||||
SET properties = properties - 'unifiedType' - 'unifiedConfig' - '_migration';
|
||||
SET properties = properties - 'v2Type' - 'v2Config' - '_migration';
|
||||
```
|
||||
|
||||
### 5.2 단계적 롤백
|
||||
|
|
@ -362,7 +362,7 @@ SET properties = properties - 'unifiedType' - 'unifiedConfig' - '_migration';
|
|||
async function rollbackScreen(screenId: number) {
|
||||
await db.query(`
|
||||
UPDATE screen_layouts sl
|
||||
SET properties = properties - 'unifiedType' - 'unifiedConfig' - '_migration'
|
||||
SET properties = properties - 'v2Type' - 'v2Config' - '_migration'
|
||||
WHERE screen_id = $1
|
||||
`, [screenId]);
|
||||
}
|
||||
|
|
@ -375,9 +375,9 @@ async function rollbackScreen(screenId: number) {
|
|||
| 단계 | 작업 | 대상 | 시점 |
|
||||
|:---:|:---|:---|:---|
|
||||
| 1 | 백업 테이블 생성 | 전체 | Phase 1 시작 전 |
|
||||
| 2 | UnifiedInput 마이그레이션 | text-input, number-input | Phase 1 중 |
|
||||
| 3 | UnifiedSelect 마이그레이션 | select-basic | Phase 1 중 |
|
||||
| 4 | UnifiedDate 마이그레이션 | date-input | Phase 1 중 |
|
||||
| 2 | V2Input 마이그레이션 | text-input, number-input | Phase 1 중 |
|
||||
| 3 | V2Select 마이그레이션 | select-basic | Phase 1 중 |
|
||||
| 4 | V2Date 마이그레이션 | date-input | Phase 1 중 |
|
||||
| 5 | 검증 및 테스트 | 전체 | Phase 1 완료 후 |
|
||||
| 6 | 레거시 필드 제거 | 전체 | Phase 5 (추후) |
|
||||
|
||||
|
|
@ -477,7 +477,7 @@ className={cn(
|
|||
|
||||
- ✅ `FileComponentConfigPanel.tsx`: `text-gray-900` → `text-foreground`, `text-blue-*` → `text-primary`
|
||||
- ✅ `ButtonConfigPanel.tsx`: 모든 `text-gray-*`, `bg-gray-*`, `hover:bg-gray-*` 교체
|
||||
- ✅ `UnifiedPropertiesPanel.tsx`: 모든 `text-gray-*`, `border-gray-*` 교체
|
||||
- ✅ `V2PropertiesPanel.tsx`: 모든 `text-gray-*`, `border-gray-*` 교체
|
||||
- ✅ `app/(main)/admin/page.tsx`: 전체 페이지 하드코딩 색상 교체
|
||||
- ✅ `CardDisplayComponent.tsx`: 모든 `text-gray-*`, `bg-gray-*`, 인라인 색상 교체
|
||||
- ✅ `getComponentConfigPanel.tsx`: 로딩 상태 하드코딩 색상 교체
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue