ERP-node/화면관리_타입_문제_분석_및_해결방안.md

15 KiB

화면관리 시스템 타입 문제 분석 및 해결방안

📋 현재 상황 분석

주요 시스템들

  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 정의

구체적 충돌 사례:

// 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 스키마 기반 정의

문제 코드:

// 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 스키마:

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 인터페이스:

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 타입 불일치

문제: 테이블 컬럼 정보 타입이 프론트엔드/백엔드에서 다름

백엔드 타입:

// 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;
}

프론트엔드 타입:

// 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 타입 캐스팅 문제

문제: 런타임에 타입 안전성이 보장되지 않는 강제 캐스팅

문제 코드:

// frontend/components/screen/RealtimePreview.tsx
const widget = component as WidgetComponent; // 위험한 강제 캐스팅

// frontend/components/screen/InteractiveScreenViewer.tsx
component: any; // any 타입으로 타입 안전성 상실

3.2 DynamicWebTypeRenderer Props 불일치

문제: 동적 렌더링 시 props 타입이 일관되지 않음

문제 위치:

// 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 타입 복잡성

문제: 제어관리 설정이 복잡하고 타입 안전성 부족

현재 타입:

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 통합 정의

// 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 통합 정의

// 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 스키마 기반 타입 생성

// 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 타입 통합

// 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 타입 가드 구현

// 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 타입 강화

// 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 타입 명확화

// 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 타입 정리

// 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 검증 도구 구축

// scripts/type-validation.ts
// 타입 일관성 검증 스크립트 작성
// DB 스키마와 TypeScript 타입 간 일치성 검증
// 컴포넌트 Props 타입 검증

📋 구현 우선순위

🔥 즉시 해결 필요 (Critical)

  1. WebType 통합 - 가장 많이 사용되는 기본 타입
  2. ButtonActionType 통합 - 제어관리 시스템 안정성 확보
  3. ColumnInfo 타입 표준화 - 테이블 관리 기능 정상화

단기간 해결 (High)

  1. ComponentData 타입 가드 - 런타임 안전성 확보
  2. DB 타입 매핑 - 프론트엔드/백엔드 연동 안정화
  3. DynamicWebTypeRenderer Props 정리 - 동적 렌더링 안정성

📅 중장기 해결 (Medium)

  1. OptimizedButtonDataflowService any 타입 제거 - 코드 품질 향상
  2. ButtonDataflowConfig 구조 개선 - 제어관리 시스템 고도화
  3. 타입 검증 도구 구축 - 지속적인 품질 관리

💡 기대 효과

개발 경험 개선

  • 타입 자동완성 정확도 향상
  • 컴파일 타임 오류 감소
  • IDE 지원 기능 활용도 증대

시스템 안정성 향상

  • 런타임 타입 오류 방지
  • API 연동 안정성 확보
  • 데이터 일관성 보장

유지보수성 향상

  • 코드 가독성 개선
  • 리팩토링 안정성 확보
  • 새 기능 추가 시 사이드 이펙트 최소화

🚀 다음 단계

이 분석을 바탕으로 다음과 같은 단계로 진행하는 것을 권장합니다:

  1. 우선순위 검토: 위의 우선순위가 프로젝트 상황에 적합한지 검토
  2. Phase 1 착수: 통합 타입 파일 생성부터 시작
  3. 점진적 적용: 한 번에 모든 것을 바꾸지 말고 단계적으로 적용
  4. 테스트 강화: 타입 변경 시마다 충분한 테스트 수행

이 계획에 대한 의견이나 수정사항이 있으시면 말씀해 주세요.