606 lines
15 KiB
Markdown
606 lines
15 KiB
Markdown
# 화면관리 시스템 타입 문제 분석 및 해결방안
|
|
|
|
## 📋 현재 상황 분석
|
|
|
|
### 주요 시스템들
|
|
|
|
1. **화면관리 시스템** (Screen Management)
|
|
2. **제어관리 시스템** (Button Dataflow Control)
|
|
3. **테이블 타입관리 시스템** (Table Type Management)
|
|
|
|
### 발견된 문제점들
|
|
|
|
## 🚨 1. 타입 정의 분산 및 중복 문제
|
|
|
|
### 1.1 WebType 타입 정의 분산
|
|
|
|
**문제**: WebType이 여러 파일에서 서로 다르게 정의되어 불일치 발생
|
|
|
|
#### 현재 상황:
|
|
|
|
- `frontend/types/screen.ts`: 화면관리용 WebType 정의
|
|
- `backend-node/src/types/tableManagement.ts`: 테이블관리용 타입 정의
|
|
- `backend-node/prisma/schema.prisma`: DB 스키마의 web_type_standards 모델
|
|
- `frontend/lib/registry/types.ts`: 레지스트리용 WebType 정의
|
|
|
|
#### 구체적 충돌 사례:
|
|
|
|
```typescript
|
|
// frontend/types/screen.ts
|
|
export type WebType =
|
|
| "text"
|
|
| "number"
|
|
| "date"
|
|
| "code"
|
|
| "entity"
|
|
| "textarea"
|
|
| "boolean"
|
|
| "decimal"
|
|
| "button"
|
|
| "datetime"
|
|
| "dropdown"
|
|
| "text_area"
|
|
| "checkbox"
|
|
| "radio"
|
|
| "file"
|
|
| "email"
|
|
| "tel"
|
|
| "url";
|
|
|
|
// 실제 DB에서는 다른 web_type 값들이 존재할 수 있음
|
|
// 예: "varchar", "integer", "timestamp" 등
|
|
```
|
|
|
|
### 1.2 ButtonActionType 중복 정의
|
|
|
|
**문제**: 버튼 액션 타입이 여러 곳에서 다르게 정의됨
|
|
|
|
#### 충돌 위치:
|
|
|
|
- `frontend/types/screen.ts`: `"control"` 포함, `"modal"` 포함
|
|
- `frontend/lib/utils/buttonActions.ts`: `"cancel"` 포함, `"modal"` 포함
|
|
- `frontend/hooks/admin/useButtonActions.ts`: DB 스키마 기반 정의
|
|
|
|
#### 문제 코드:
|
|
|
|
```typescript
|
|
// frontend/types/screen.ts
|
|
export type ButtonActionType =
|
|
| "save"
|
|
| "delete"
|
|
| "edit"
|
|
| "add"
|
|
| "search"
|
|
| "reset"
|
|
| "submit"
|
|
| "close"
|
|
| "popup"
|
|
| "modal"
|
|
| "newWindow"
|
|
| "navigate"
|
|
| "control";
|
|
|
|
// frontend/lib/utils/buttonActions.ts
|
|
export type ButtonActionType =
|
|
| "save"
|
|
| "cancel"
|
|
| "delete"
|
|
| "edit"
|
|
| "add"
|
|
| "search"
|
|
| "reset"
|
|
| "submit"
|
|
| "close"
|
|
| "popup"
|
|
| "navigate"
|
|
| "modal"
|
|
| "newWindow";
|
|
```
|
|
|
|
## 🚨 2. 데이터베이스 스키마와 TypeScript 타입 불일치
|
|
|
|
### 2.1 web_type_standards 테이블 불일치
|
|
|
|
**문제**: Prisma 스키마와 TypeScript 인터페이스 간 필드명/타입 차이
|
|
|
|
#### DB 스키마:
|
|
|
|
```sql
|
|
model web_type_standards {
|
|
web_type String @id @db.VarChar(50)
|
|
type_name String @db.VarChar(100)
|
|
type_name_eng String? @db.VarChar(100)
|
|
description String?
|
|
category String? @default("input") @db.VarChar(50)
|
|
default_config Json? -- JSON 타입
|
|
validation_rules Json? -- JSON 타입
|
|
component_name String? @default("TextWidget") @db.VarChar(100)
|
|
config_panel String? @db.VarChar(100)
|
|
}
|
|
```
|
|
|
|
#### TypeScript 인터페이스:
|
|
|
|
```typescript
|
|
export interface WebTypeDefinition {
|
|
id: string; // web_type와 매핑되지 않음
|
|
name: string; // type_name과 매핑?
|
|
category: string;
|
|
description: string;
|
|
defaultConfig: Record<string, any>; // default_config Json과 타입 불일치
|
|
validationRules?: Record<string, any>; // validation_rules Json과 타입 불일치
|
|
isActive: boolean; // DB에는 is_active String 필드
|
|
}
|
|
```
|
|
|
|
### 2.2 ColumnInfo 타입 불일치
|
|
|
|
**문제**: 테이블 컬럼 정보 타입이 프론트엔드/백엔드에서 다름
|
|
|
|
#### 백엔드 타입:
|
|
|
|
```typescript
|
|
// backend-node/src/types/tableManagement.ts
|
|
export interface ColumnTypeInfo {
|
|
columnName: string;
|
|
displayName: string;
|
|
dataType: string;
|
|
dbType: string;
|
|
webType: string; // string 타입
|
|
inputType?: "direct" | "auto";
|
|
detailSettings: string; // JSON 문자열
|
|
isNullable: string; // "Y" | "N" 문자열
|
|
isPrimaryKey: boolean;
|
|
}
|
|
```
|
|
|
|
#### 프론트엔드 타입:
|
|
|
|
```typescript
|
|
// frontend/types/screen.ts
|
|
export interface ColumnInfo {
|
|
tableName: string;
|
|
columnName: string;
|
|
columnLabel?: string;
|
|
dataType: string;
|
|
webType?: WebType; // WebType union 타입 (불일치!)
|
|
inputType?: "direct" | "auto";
|
|
isNullable: string;
|
|
detailSettings?: string; // optional vs required 차이
|
|
}
|
|
```
|
|
|
|
## 🚨 3. 컴포넌트 인터페이스 타입 안전성 문제
|
|
|
|
### 3.1 ComponentData 타입 캐스팅 문제
|
|
|
|
**문제**: 런타임에 타입 안전성이 보장되지 않는 강제 캐스팅
|
|
|
|
#### 문제 코드:
|
|
|
|
```typescript
|
|
// frontend/components/screen/RealtimePreview.tsx
|
|
const widget = component as WidgetComponent; // 위험한 강제 캐스팅
|
|
|
|
// frontend/components/screen/InteractiveScreenViewer.tsx
|
|
component: any; // any 타입으로 타입 안전성 상실
|
|
```
|
|
|
|
### 3.2 DynamicWebTypeRenderer Props 불일치
|
|
|
|
**문제**: 동적 렌더링 시 props 타입이 일관되지 않음
|
|
|
|
#### 문제 위치:
|
|
|
|
```typescript
|
|
// frontend/lib/registry/DynamicWebTypeRenderer.tsx
|
|
export interface DynamicComponentProps {
|
|
webType: string; // WebType이 아닌 string
|
|
props?: Record<string, any>; // any 타입 사용
|
|
config?: Record<string, any>; // any 타입 사용
|
|
onEvent?: (event: string, data: any) => void; // any 타입
|
|
}
|
|
|
|
// 실제 사용 시
|
|
<DynamicWebTypeRenderer
|
|
webType={component.webType || "text"} // WebType | undefined 전달
|
|
config={component.webTypeConfig} // WebTypeConfig 타입 전달
|
|
props={{
|
|
component: component, // ComponentData 타입
|
|
value: formData[component.columnName || component.id] || "",
|
|
onChange: (value: any) => {...} // any 타입 콜백
|
|
}}
|
|
/>
|
|
```
|
|
|
|
## 🚨 4. 제어관리 시스템 타입 문제
|
|
|
|
### 4.1 ButtonDataflowConfig 타입 복잡성
|
|
|
|
**문제**: 제어관리 설정이 복잡하고 타입 안전성 부족
|
|
|
|
#### 현재 타입:
|
|
|
|
```typescript
|
|
export interface ButtonDataflowConfig {
|
|
controlMode: "simple" | "advanced";
|
|
selectedDiagramId?: number;
|
|
selectedRelationshipId?: number;
|
|
directControl?: {
|
|
conditions: DataflowCondition[]; // 복잡한 중첩 타입
|
|
actions: any[]; // any 타입 사용
|
|
};
|
|
}
|
|
```
|
|
|
|
### 4.2 OptimizedButtonDataflowService 타입 문제
|
|
|
|
**문제**: 서비스 클래스에서 any 타입 남용으로 타입 안전성 상실
|
|
|
|
#### Linter 오류 (57개):
|
|
|
|
- `Unexpected any` 경고 26개
|
|
- `unknown` 타입 오류 2개
|
|
- 사용되지 않는 변수 경고 29개
|
|
|
|
## 🎯 해결방안 및 구현 계획
|
|
|
|
## Phase 1: 중앙집중식 타입 정의 통합 (우선순위: 높음)
|
|
|
|
### 1.1 통합 타입 파일 생성
|
|
|
|
```
|
|
frontend/types/
|
|
├── unified-core.ts # 핵심 공통 타입들
|
|
├── screen-management.ts # 화면관리 전용 타입
|
|
├── control-management.ts # 제어관리 전용 타입
|
|
├── table-management.ts # 테이블관리 전용 타입
|
|
└── index.ts # 모든 타입 re-export
|
|
```
|
|
|
|
### 1.2 WebType 통합 정의
|
|
|
|
```typescript
|
|
// frontend/types/unified-core.ts
|
|
export type WebType =
|
|
| "text"
|
|
| "number"
|
|
| "decimal"
|
|
| "date"
|
|
| "datetime"
|
|
| "select"
|
|
| "dropdown"
|
|
| "radio"
|
|
| "checkbox"
|
|
| "boolean"
|
|
| "textarea"
|
|
| "code"
|
|
| "entity"
|
|
| "file"
|
|
| "email"
|
|
| "tel"
|
|
| "url"
|
|
| "button";
|
|
|
|
// DB에서 동적으로 로드되는 웹타입도 지원
|
|
export type DynamicWebType = WebType | string;
|
|
```
|
|
|
|
### 1.3 ButtonActionType 통합 정의
|
|
|
|
```typescript
|
|
// frontend/types/unified-core.ts
|
|
export type ButtonActionType =
|
|
| "save"
|
|
| "cancel"
|
|
| "delete"
|
|
| "edit"
|
|
| "add"
|
|
| "search"
|
|
| "reset"
|
|
| "submit"
|
|
| "close"
|
|
| "popup"
|
|
| "modal"
|
|
| "navigate"
|
|
| "control";
|
|
```
|
|
|
|
## Phase 2: 데이터베이스 타입 매핑 표준화 (우선순위: 높음)
|
|
|
|
### 2.1 Prisma 스키마 기반 타입 생성
|
|
|
|
```typescript
|
|
// frontend/types/database-mappings.ts
|
|
import { web_type_standards, button_action_standards } from "@prisma/client";
|
|
|
|
// Prisma 타입을 프론트엔드 타입으로 변환하는 매퍼
|
|
export type WebTypeStandard = web_type_standards;
|
|
|
|
export interface WebTypeDefinition {
|
|
webType: string; // web_type 필드
|
|
typeName: string; // type_name 필드
|
|
typeNameEng?: string; // type_name_eng 필드
|
|
description?: string;
|
|
category: string;
|
|
defaultConfig: Record<string, any>; // Json 타입 매핑
|
|
validationRules?: Record<string, any>; // Json 타입 매핑
|
|
componentName?: string; // component_name 필드
|
|
configPanel?: string; // config_panel 필드
|
|
isActive: boolean; // is_active "Y"/"N" → boolean 변환
|
|
}
|
|
|
|
// 변환 함수
|
|
export const mapWebTypeStandardToDefinition = (
|
|
standard: WebTypeStandard
|
|
): WebTypeDefinition => ({
|
|
webType: standard.web_type,
|
|
typeName: standard.type_name,
|
|
typeNameEng: standard.type_name_eng || undefined,
|
|
description: standard.description || undefined,
|
|
category: standard.category || "input",
|
|
defaultConfig: (standard.default_config as any) || {},
|
|
validationRules: (standard.validation_rules as any) || undefined,
|
|
componentName: standard.component_name || undefined,
|
|
configPanel: standard.config_panel || undefined,
|
|
isActive: standard.is_active === "Y",
|
|
});
|
|
```
|
|
|
|
### 2.2 ColumnInfo 타입 통합
|
|
|
|
```typescript
|
|
// frontend/types/table-management.ts
|
|
export interface UnifiedColumnInfo {
|
|
// 공통 필드
|
|
tableName: string;
|
|
columnName: string;
|
|
displayName: string;
|
|
dataType: string; // DB 데이터 타입
|
|
webType: DynamicWebType; // 웹 타입 (동적 지원)
|
|
|
|
// 상세 정보
|
|
inputType: "direct" | "auto";
|
|
detailSettings?: Record<string, any>; // JSON 파싱된 객체
|
|
description?: string;
|
|
isNullable: boolean; // "Y"/"N" → boolean 변환
|
|
isPrimaryKey: boolean;
|
|
|
|
// 표시 옵션
|
|
isVisible?: boolean;
|
|
displayOrder?: number;
|
|
|
|
// 메타데이터
|
|
maxLength?: number;
|
|
numericPrecision?: number;
|
|
numericScale?: number;
|
|
defaultValue?: string;
|
|
|
|
// 참조 관계
|
|
codeCategory?: string;
|
|
referenceTable?: string;
|
|
referenceColumn?: string;
|
|
displayColumn?: string;
|
|
}
|
|
```
|
|
|
|
## Phase 3: 컴포넌트 타입 안전성 강화 (우선순위: 중간)
|
|
|
|
### 3.1 ComponentData 타입 가드 구현
|
|
|
|
```typescript
|
|
// frontend/types/screen-management.ts
|
|
export type ComponentData =
|
|
| ContainerComponent
|
|
| WidgetComponent
|
|
| GroupComponent
|
|
| DataTableComponent;
|
|
|
|
// 타입 가드 함수들
|
|
export const isWidgetComponent = (
|
|
component: ComponentData
|
|
): component is WidgetComponent => {
|
|
return component.type === "widget";
|
|
};
|
|
|
|
export const isContainerComponent = (
|
|
component: ComponentData
|
|
): component is ContainerComponent => {
|
|
return component.type === "container";
|
|
};
|
|
|
|
// 안전한 타입 캐스팅 유틸리티
|
|
export const asWidgetComponent = (
|
|
component: ComponentData
|
|
): WidgetComponent => {
|
|
if (!isWidgetComponent(component)) {
|
|
throw new Error(`Expected WidgetComponent, got ${component.type}`);
|
|
}
|
|
return component;
|
|
};
|
|
```
|
|
|
|
### 3.2 DynamicWebTypeRenderer Props 타입 강화
|
|
|
|
```typescript
|
|
// frontend/lib/registry/types.ts
|
|
export interface StrictDynamicComponentProps {
|
|
webType: DynamicWebType;
|
|
component: ComponentData;
|
|
config?: WebTypeConfig;
|
|
value?: unknown;
|
|
onChange?: (value: unknown) => void;
|
|
onEvent?: (event: WebTypeEvent) => void;
|
|
readonly?: boolean;
|
|
required?: boolean;
|
|
className?: string;
|
|
}
|
|
|
|
export interface WebTypeEvent {
|
|
type: "change" | "blur" | "focus" | "click";
|
|
value: unknown;
|
|
field?: string;
|
|
}
|
|
|
|
export type WebTypeConfig = Record<string, unknown>;
|
|
```
|
|
|
|
## Phase 4: 제어관리 시스템 타입 정리 (우선순위: 중간)
|
|
|
|
### 4.1 ButtonDataflowConfig 타입 명확화
|
|
|
|
```typescript
|
|
// frontend/types/control-management.ts
|
|
export interface ButtonDataflowConfig {
|
|
// 기본 설정
|
|
controlMode: "simple" | "advanced";
|
|
|
|
// 관계도 방식
|
|
selectedDiagramId?: number;
|
|
selectedRelationshipId?: number;
|
|
|
|
// 직접 설정 방식
|
|
directControl?: DirectControlConfig;
|
|
}
|
|
|
|
export interface DirectControlConfig {
|
|
conditions: DataflowCondition[];
|
|
actions: DataflowAction[];
|
|
logic?: "AND" | "OR";
|
|
}
|
|
|
|
export interface DataflowCondition {
|
|
id: string;
|
|
type: "condition" | "group";
|
|
field?: string;
|
|
operator?: ConditionOperator;
|
|
value?: unknown;
|
|
dataSource?: "form" | "table-selection" | "both";
|
|
}
|
|
|
|
export interface DataflowAction {
|
|
id: string;
|
|
type: ActionType;
|
|
tableName?: string;
|
|
operation?: "INSERT" | "UPDATE" | "DELETE" | "SELECT";
|
|
fields?: ActionField[];
|
|
conditions?: DataflowCondition[];
|
|
}
|
|
|
|
export type ConditionOperator =
|
|
| "="
|
|
| "!="
|
|
| ">"
|
|
| "<"
|
|
| ">="
|
|
| "<="
|
|
| "LIKE"
|
|
| "IN"
|
|
| "NOT IN";
|
|
export type ActionType = "database" | "api" | "notification" | "redirect";
|
|
```
|
|
|
|
### 4.2 OptimizedButtonDataflowService 타입 정리
|
|
|
|
```typescript
|
|
// frontend/lib/services/optimizedButtonDataflowService.ts
|
|
|
|
// any 타입 제거 및 구체적 타입 정의
|
|
export interface ExecutionContext {
|
|
formData: Record<string, unknown>;
|
|
selectedRows?: unknown[];
|
|
selectedRowsData?: Record<string, unknown>[];
|
|
controlDataSource: ControlDataSource;
|
|
buttonId: string;
|
|
componentData?: ComponentData;
|
|
timestamp: string;
|
|
clickCount?: number;
|
|
}
|
|
|
|
export interface ActionResult {
|
|
success: boolean;
|
|
message: string;
|
|
data?: Record<string, unknown>;
|
|
error?: string;
|
|
}
|
|
|
|
export interface ValidationResult {
|
|
success: boolean;
|
|
message?: string;
|
|
canExecuteImmediately: boolean;
|
|
actions?: DataflowAction[];
|
|
}
|
|
```
|
|
|
|
## Phase 5: 마이그레이션 및 검증 (우선순위: 낮음)
|
|
|
|
### 5.1 점진적 마이그레이션 계획
|
|
|
|
1. **Step 1**: 새로운 통합 타입 파일들 생성
|
|
2. **Step 2**: 기존 파일들에서 새 타입 import로 변경
|
|
3. **Step 3**: 타입 가드 및 유틸리티 함수 적용
|
|
4. **Step 4**: any 타입 제거 및 구체적 타입 적용
|
|
5. **Step 5**: 기존 타입 정의 파일들 제거
|
|
|
|
### 5.2 검증 도구 구축
|
|
|
|
```typescript
|
|
// scripts/type-validation.ts
|
|
// 타입 일관성 검증 스크립트 작성
|
|
// DB 스키마와 TypeScript 타입 간 일치성 검증
|
|
// 컴포넌트 Props 타입 검증
|
|
```
|
|
|
|
## 📋 구현 우선순위
|
|
|
|
### 🔥 즉시 해결 필요 (Critical)
|
|
|
|
1. **WebType 통합** - 가장 많이 사용되는 기본 타입
|
|
2. **ButtonActionType 통합** - 제어관리 시스템 안정성 확보
|
|
3. **ColumnInfo 타입 표준화** - 테이블 관리 기능 정상화
|
|
|
|
### ⚡ 단기간 해결 (High)
|
|
|
|
4. **ComponentData 타입 가드** - 런타임 안전성 확보
|
|
5. **DB 타입 매핑** - 프론트엔드/백엔드 연동 안정화
|
|
6. **DynamicWebTypeRenderer Props 정리** - 동적 렌더링 안정성
|
|
|
|
### 📅 중장기 해결 (Medium)
|
|
|
|
7. **OptimizedButtonDataflowService any 타입 제거** - 코드 품질 향상
|
|
8. **ButtonDataflowConfig 구조 개선** - 제어관리 시스템 고도화
|
|
9. **타입 검증 도구 구축** - 지속적인 품질 관리
|
|
|
|
## 💡 기대 효과
|
|
|
|
### 개발 경험 개선
|
|
|
|
- 타입 자동완성 정확도 향상
|
|
- 컴파일 타임 오류 감소
|
|
- IDE 지원 기능 활용도 증대
|
|
|
|
### 시스템 안정성 향상
|
|
|
|
- 런타임 타입 오류 방지
|
|
- API 연동 안정성 확보
|
|
- 데이터 일관성 보장
|
|
|
|
### 유지보수성 향상
|
|
|
|
- 코드 가독성 개선
|
|
- 리팩토링 안정성 확보
|
|
- 새 기능 추가 시 사이드 이펙트 최소화
|
|
|
|
---
|
|
|
|
## 🚀 다음 단계
|
|
|
|
이 분석을 바탕으로 다음과 같은 단계로 진행하는 것을 권장합니다:
|
|
|
|
1. **우선순위 검토**: 위의 우선순위가 프로젝트 상황에 적합한지 검토
|
|
2. **Phase 1 착수**: 통합 타입 파일 생성부터 시작
|
|
3. **점진적 적용**: 한 번에 모든 것을 바꾸지 말고 단계적으로 적용
|
|
4. **테스트 강화**: 타입 변경 시마다 충분한 테스트 수행
|
|
|
|
이 계획에 대한 의견이나 수정사항이 있으시면 말씀해 주세요.
|