From 5b5a0d1a23f4aa95ed1c50b3eeed04274a7475e5 Mon Sep 17 00:00:00 2001 From: DDD1542 Date: Fri, 30 Jan 2026 10:51:33 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20V2=20=EB=A0=88=EC=9D=B4=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=B6=94=EC=B6=9C=20=EB=B0=8F=20=EC=83=88=EB=A1=9C?= =?UTF-8?q?=EC=9A=B4=20V2=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - V2 레이아웃에서 URL을 기반으로 컴포넌트 타입을 추출하는 헬퍼 함수를 추가하였습니다. - DynamicComponentRenderer에서 V2 레이아웃의 URL에서 컴포넌트 타입을 추출하도록 수정하였습니다. - 새로운 V2 통합 입력, 선택, 날짜 컴포넌트를 등록하여 컴포넌트 목록을 업데이트하였습니다. - 이를 통해 V2 컴포넌트의 일관성을 높이고, 레거시 타입과의 매핑을 개선하였습니다. --- .../src/services/screenManagementService.ts | 32 ++++++--- .../lib/registry/DynamicComponentRenderer.tsx | 10 ++- frontend/lib/registry/components/index.ts | 3 + .../components/v2-date/V2DateRenderer.tsx | 64 +++++++++++++++++ .../components/v2-input/V2InputRenderer.tsx | 72 +++++++++++++++++++ .../components/v2-select/V2SelectRenderer.tsx | 71 ++++++++++++++++++ 6 files changed, 240 insertions(+), 12 deletions(-) create mode 100644 frontend/lib/registry/components/v2-date/V2DateRenderer.tsx create mode 100644 frontend/lib/registry/components/v2-input/V2InputRenderer.tsx create mode 100644 frontend/lib/registry/components/v2-select/V2SelectRenderer.tsx diff --git a/backend-node/src/services/screenManagementService.ts b/backend-node/src/services/screenManagementService.ts index 52ed357b..05e3afe9 100644 --- a/backend-node/src/services/screenManagementService.ts +++ b/backend-node/src/services/screenManagementService.ts @@ -1665,18 +1665,28 @@ export class ScreenManagementService { console.log(`V2 레이아웃 발견, V2 형식으로 반환`); const layoutData = v2Layout.layout_data; + // URL에서 컴포넌트 타입 추출하는 헬퍼 함수 + const getTypeFromUrl = (url: string | undefined): string => { + if (!url) return "component"; + const parts = url.split("/"); + return parts[parts.length - 1] || "component"; + }; + // V2 형식의 components를 LayoutData 형식으로 변환 - const components = (layoutData.components || []).map((comp: any) => ({ - id: comp.id, - type: comp.overrides?.type || "component", - position: comp.position || { x: 0, y: 0, z: 1 }, - size: comp.size || { width: 200, height: 100 }, - componentUrl: comp.url, - componentType: comp.overrides?.type, - componentConfig: comp.overrides || {}, - displayOrder: comp.displayOrder || 0, - ...comp.overrides, - })); + const components = (layoutData.components || []).map((comp: any) => { + const componentType = getTypeFromUrl(comp.url); + return { + id: comp.id, + type: componentType, + position: comp.position || { x: 0, y: 0, z: 1 }, + size: comp.size || { width: 200, height: 100 }, + componentUrl: comp.url, + componentType: componentType, + componentConfig: comp.overrides || {}, + displayOrder: comp.displayOrder || 0, + ...comp.overrides, + }; + }); // screenResolution이 없으면 컴포넌트 위치 기반으로 자동 계산 let screenResolution = layoutData.screenResolution; diff --git a/frontend/lib/registry/DynamicComponentRenderer.tsx b/frontend/lib/registry/DynamicComponentRenderer.tsx index 23b684ac..d3c911ef 100644 --- a/frontend/lib/registry/DynamicComponentRenderer.tsx +++ b/frontend/lib/registry/DynamicComponentRenderer.tsx @@ -173,7 +173,15 @@ export const DynamicComponentRenderer: React.FC = ...props }) => { // 컴포넌트 타입 추출 - 새 시스템에서는 componentType 속성 사용, 레거시는 type 사용 - const rawComponentType = (component as any).componentType || component.type; + // 🆕 V2 레이아웃의 경우 url에서 컴포넌트 타입 추출 (예: "@/lib/registry/components/v2-input" → "v2-input") + const extractTypeFromUrl = (url: string | undefined): string | undefined => { + if (!url) return undefined; + // url의 마지막 세그먼트를 컴포넌트 타입으로 사용 + const segments = url.split("/"); + return segments[segments.length - 1]; + }; + + const rawComponentType = (component as any).componentType || component.type || extractTypeFromUrl((component as any).url); // 레거시 타입을 v2 컴포넌트로 매핑 (v2 컴포넌트가 없으면 원본 유지) const mapToV2ComponentType = (type: string | undefined): string | undefined => { diff --git a/frontend/lib/registry/components/index.ts b/frontend/lib/registry/components/index.ts index 8a2ac932..19f33cd1 100644 --- a/frontend/lib/registry/components/index.ts +++ b/frontend/lib/registry/components/index.ts @@ -106,6 +106,9 @@ import "./v2-table-search-widget"; import "./v2-tabs-widget/tabs-component"; import "./v2-category-manager/V2CategoryManagerRenderer"; import "./v2-media"; // 통합 미디어 컴포넌트 +import "./v2-input/V2InputRenderer"; // V2 통합 입력 컴포넌트 +import "./v2-select/V2SelectRenderer"; // V2 통합 선택 컴포넌트 +import "./v2-date/V2DateRenderer"; // V2 통합 날짜 컴포넌트 /** * 컴포넌트 초기화 함수 diff --git a/frontend/lib/registry/components/v2-date/V2DateRenderer.tsx b/frontend/lib/registry/components/v2-date/V2DateRenderer.tsx new file mode 100644 index 00000000..dfbbceb1 --- /dev/null +++ b/frontend/lib/registry/components/v2-date/V2DateRenderer.tsx @@ -0,0 +1,64 @@ +"use client"; + +import React from "react"; +import { AutoRegisteringComponentRenderer } from "../../AutoRegisteringComponentRenderer"; +import { V2DateDefinition } from "./index"; +import { V2Date } from "@/components/v2/V2Date"; + +/** + * V2Date 렌더러 + * 자동 등록 시스템을 사용하여 컴포넌트를 레지스트리에 등록 + */ +export class V2DateRenderer extends AutoRegisteringComponentRenderer { + static componentDefinition = V2DateDefinition; + + render(): React.ReactElement { + const { component, formData, onFormDataChange, isDesignMode, isSelected, isInteractive, ...restProps } = this.props; + + // 컴포넌트 설정 추출 + const config = component.componentConfig || component.config || {}; + const columnName = component.columnName; + + // formData에서 현재 값 가져오기 + const currentValue = formData?.[columnName] ?? component.value ?? ""; + + // 값 변경 핸들러 + const handleChange = (value: any) => { + if (isInteractive && onFormDataChange && columnName) { + onFormDataChange(columnName, value); + } + }; + + return ( + + ); + } +} + +// 자동 등록 실행 +V2DateRenderer.registerSelf(); + +// Hot Reload 지원 (개발 모드) +if (process.env.NODE_ENV === "development") { + V2DateRenderer.enableHotReload(); +} diff --git a/frontend/lib/registry/components/v2-input/V2InputRenderer.tsx b/frontend/lib/registry/components/v2-input/V2InputRenderer.tsx new file mode 100644 index 00000000..1afc2075 --- /dev/null +++ b/frontend/lib/registry/components/v2-input/V2InputRenderer.tsx @@ -0,0 +1,72 @@ +"use client"; + +import React from "react"; +import { AutoRegisteringComponentRenderer } from "../../AutoRegisteringComponentRenderer"; +import { V2InputDefinition } from "./index"; +import { V2Input } from "@/components/v2/V2Input"; + +/** + * V2Input 렌더러 + * 자동 등록 시스템을 사용하여 컴포넌트를 레지스트리에 등록 + */ +export class V2InputRenderer extends AutoRegisteringComponentRenderer { + static componentDefinition = V2InputDefinition; + + render(): React.ReactElement { + const { component, formData, onFormDataChange, isDesignMode, isSelected, isInteractive, ...restProps } = this.props; + + // 컴포넌트 설정 추출 + const config = component.componentConfig || component.config || {}; + const columnName = component.columnName; + const tableName = component.tableName || this.props.tableName; + + // formData에서 현재 값 가져오기 + const currentValue = formData?.[columnName] ?? component.value ?? ""; + + // 값 변경 핸들러 + const handleChange = (value: any) => { + if (isInteractive && onFormDataChange && columnName) { + onFormDataChange(columnName, value); + } + }; + + return ( + + ); + } +} + +// 자동 등록 실행 +V2InputRenderer.registerSelf(); + +// Hot Reload 지원 (개발 모드) +if (process.env.NODE_ENV === "development") { + V2InputRenderer.enableHotReload(); +} diff --git a/frontend/lib/registry/components/v2-select/V2SelectRenderer.tsx b/frontend/lib/registry/components/v2-select/V2SelectRenderer.tsx new file mode 100644 index 00000000..5fbdfcf7 --- /dev/null +++ b/frontend/lib/registry/components/v2-select/V2SelectRenderer.tsx @@ -0,0 +1,71 @@ +"use client"; + +import React from "react"; +import { AutoRegisteringComponentRenderer } from "../../AutoRegisteringComponentRenderer"; +import { V2SelectDefinition } from "./index"; +import { V2Select } from "@/components/v2/V2Select"; + +/** + * V2Select 렌더러 + * 자동 등록 시스템을 사용하여 컴포넌트를 레지스트리에 등록 + */ +export class V2SelectRenderer extends AutoRegisteringComponentRenderer { + static componentDefinition = V2SelectDefinition; + + render(): React.ReactElement { + const { component, formData, onFormDataChange, isDesignMode, isSelected, isInteractive, ...restProps } = this.props; + + // 컴포넌트 설정 추출 + const config = component.componentConfig || component.config || {}; + const columnName = component.columnName; + const tableName = component.tableName || this.props.tableName; + + // formData에서 현재 값 가져오기 + const currentValue = formData?.[columnName] ?? component.value ?? ""; + + // 값 변경 핸들러 + const handleChange = (value: any) => { + if (isInteractive && onFormDataChange && columnName) { + onFormDataChange(columnName, value); + } + }; + + return ( + + ); + } +} + +// 자동 등록 실행 +V2SelectRenderer.registerSelf(); + +// Hot Reload 지원 (개발 모드) +if (process.env.NODE_ENV === "development") { + V2SelectRenderer.enableHotReload(); +}