"use client"; import React from "react"; import { ComponentData } from "@/types/screen"; import { DynamicLayoutRenderer } from "./DynamicLayoutRenderer"; import { ComponentRegistry } from "./ComponentRegistry"; import { filterDOMProps } from "@/lib/utils/domPropsFilter"; // 컴포넌트 렌더러 인터페이스 export interface ComponentRenderer { (props: { component: ComponentData; isSelected?: boolean; isInteractive?: boolean; formData?: Record; originalData?: Record; // 부분 업데이트용 원본 데이터 onFormDataChange?: (fieldName: string, value: any) => void; onClick?: (e?: React.MouseEvent) => void; onDragStart?: (e: React.DragEvent) => void; onDragEnd?: () => void; children?: React.ReactNode; onZoneComponentDrop?: (e: React.DragEvent, zoneId: string, layoutId: string) => void; onZoneClick?: (zoneId: string) => void; // 버튼 액션을 위한 추가 props screenId?: number; tableName?: string; onRefresh?: () => void; onClose?: () => void; // 테이블 선택된 행 정보 (다중 선택 액션용) selectedRows?: any[]; selectedRowsData?: any[]; onSelectedRowsChange?: (selectedRows: any[], selectedRowsData: any[]) => void; // 테이블 새로고침 키 refreshKey?: number; // 편집 모드 mode?: "view" | "edit"; // 설정 변경 핸들러 (상세설정과 연동) onConfigChange?: (config: any) => void; [key: string]: any; }): React.ReactElement; } // 레거시 렌더러 레지스트리 (기존 컴포넌트들용) class LegacyComponentRegistry { private renderers: Map = new Map(); // 컴포넌트 렌더러 등록 register(componentType: string, renderer: ComponentRenderer) { this.renderers.set(componentType, renderer); } // 컴포넌트 렌더러 조회 get(componentType: string): ComponentRenderer | undefined { return this.renderers.get(componentType); } // 등록된 모든 컴포넌트 타입 조회 getRegisteredTypes(): string[] { return Array.from(this.renderers.keys()); } // 컴포넌트 타입이 등록되어 있는지 확인 has(componentType: string): boolean { const result = this.renderers.has(componentType); return result; } } // 전역 레거시 레지스트리 인스턴스 export const legacyComponentRegistry = new LegacyComponentRegistry(); // 하위 호환성을 위한 기존 이름 유지 export const componentRegistry = legacyComponentRegistry; // 동적 컴포넌트 렌더러 컴포넌트 export interface DynamicComponentRendererProps { component: ComponentData; isSelected?: boolean; isPreview?: boolean; // 반응형 모드 플래그 onClick?: (e?: React.MouseEvent) => void; onDragStart?: (e: React.DragEvent) => void; onDragEnd?: () => void; children?: React.ReactNode; // 폼 데이터 관련 formData?: Record; originalData?: Record; // 부분 업데이트용 원본 데이터 onFormDataChange?: (fieldName: string, value: any) => void; // 버튼 액션을 위한 추가 props screenId?: number; tableName?: string; onRefresh?: () => void; onClose?: () => void; // 테이블 선택된 행 정보 (다중 선택 액션용) selectedRows?: any[]; selectedRowsData?: any[]; onSelectedRowsChange?: (selectedRows: any[], selectedRowsData: any[]) => void; // 테이블 새로고침 키 refreshKey?: number; // 편집 모드 mode?: "view" | "edit"; // 모달 내에서 렌더링 여부 isInModal?: boolean; [key: string]: any; } export const DynamicComponentRenderer: React.FC = ({ component, isSelected = false, isPreview = false, onClick, onDragStart, onDragEnd, children, ...props }) => { // 컴포넌트 타입 추출 - 새 시스템에서는 componentType 속성 사용, 레거시는 type 사용 const componentType = (component as any).componentType || component.type; // 레이아웃 컴포넌트 처리 if (componentType === "layout") { // DOM 안전한 props만 전달 const safeLayoutProps = filterDOMProps(props); return ( ); } // 1. 새 컴포넌트 시스템에서 먼저 조회 const newComponent = ComponentRegistry.getComponent(componentType); if (newComponent) { // 새 컴포넌트 시스템으로 렌더링 try { const NewComponentRenderer = newComponent.component; if (NewComponentRenderer) { // React 전용 props들을 명시적으로 분리하고 DOM 안전한 props만 전달 const { isInteractive, formData, onFormDataChange, tableName, onRefresh, onClose, screenId, mode, isInModal, originalData, allComponents, onUpdateLayout, onZoneClick, selectedRows, selectedRowsData, onSelectedRowsChange, refreshKey, onConfigChange, ...safeProps } = props; // 컴포넌트의 columnName에 해당하는 formData 값 추출 const fieldName = (component as any).columnName || component.id; const currentValue = formData?.[fieldName] || ""; console.log("🔍 DynamicComponentRenderer - 새 컴포넌트 시스템:", { componentType, componentId: component.id, columnName: (component as any).columnName, fieldName, currentValue, hasFormData: !!formData, formDataKeys: formData ? Object.keys(formData) : [], autoGeneration: component.autoGeneration, hidden: component.hidden, isInteractive, }); // 렌더러 props 구성 const rendererProps = { component, isSelected, onClick, onDragStart, onDragEnd, size: component.size || newComponent.defaultSize, position: component.position, style: component.style, config: component.componentConfig, componentConfig: component.componentConfig, value: currentValue, // formData에서 추출한 현재 값 전달 // 새로운 기능들 전달 autoGeneration: component.autoGeneration, hidden: component.hidden, // React 전용 props들은 직접 전달 (DOM에 전달되지 않음) isInteractive, formData, onFormDataChange, onChange: onFormDataChange, // onChange도 전달 tableName, onRefresh, onClose, screenId, mode, isInModal, readonly: component.readonly, disabled: component.readonly, originalData, allComponents, onUpdateLayout, onZoneClick, // 테이블 선택된 행 정보 전달 selectedRows, selectedRowsData, onSelectedRowsChange, // 설정 변경 핸들러 전달 onConfigChange, refreshKey, // 반응형 모드 플래그 전달 isPreview, }; // 렌더러가 클래스인지 함수인지 확인 if ( typeof NewComponentRenderer === "function" && NewComponentRenderer.prototype && NewComponentRenderer.prototype.render ) { // 클래스 기반 렌더러 (AutoRegisteringComponentRenderer 상속) const rendererInstance = new NewComponentRenderer(rendererProps); return rendererInstance.render(); } else { // 함수형 컴포넌트 return ; } } } catch (error) { console.error(`❌ 새 컴포넌트 렌더링 실패 (${componentType}):`, error); } } // 2. 레거시 시스템에서 조회 const renderer = legacyComponentRegistry.get(componentType); if (!renderer) { console.error(`⚠️ 등록되지 않은 컴포넌트 타입: ${componentType}`, { component: component, componentType: componentType, componentConfig: component.componentConfig, availableNewComponents: ComponentRegistry.getAllComponents().map((c) => c.id), availableLegacyComponents: legacyComponentRegistry.getRegisteredTypes(), }); // 폴백 렌더링 - 기본 플레이스홀더 return (
{component.label || component.id}
미구현 컴포넌트: {componentType}
); } // 동적 렌더링 실행 try { // 레거시 시스템에서도 DOM 안전한 props만 전달 const safeLegacyProps = filterDOMProps(props); return renderer({ component, isSelected, onClick, onDragStart, onDragEnd, children, // React 전용 props들은 명시적으로 전달 (레거시 컴포넌트가 필요한 경우) isInteractive: props.isInteractive, formData: props.formData, onFormDataChange: props.onFormDataChange, screenId: props.screenId, tableName: props.tableName, onRefresh: props.onRefresh, onClose: props.onClose, mode: props.mode, isInModal: props.isInModal, originalData: props.originalData, onUpdateLayout: props.onUpdateLayout, onZoneClick: props.onZoneClick, onZoneComponentDrop: props.onZoneComponentDrop, allComponents: props.allComponents, // 테이블 선택된 행 정보 전달 selectedRows: props.selectedRows, selectedRowsData: props.selectedRowsData, onSelectedRowsChange: props.onSelectedRowsChange, refreshKey: props.refreshKey, // DOM 안전한 props들 ...safeLegacyProps, }); } catch (error) { console.error(`❌ 컴포넌트 렌더링 실패 (${componentType}):`, error); // 오류 발생 시 폴백 렌더링 return (
렌더링 오류
{componentType}: {error instanceof Error ? error.message : "알 수 없는 오류"}
); } }; export default DynamicComponentRenderer;