Files
vexplor/화면관리_타입_문제_분석_및_해결방안.md
2025-09-19 15:22:25 +09:00

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. **테스트 강화**: 타입 변경 시마다 충분한 테스트 수행
이 계획에 대한 의견이나 수정사항이 있으시면 말씀해 주세요.