diff --git a/frontend/components/screen/InteractiveScreenViewer.tsx b/frontend/components/screen/InteractiveScreenViewer.tsx index 4b5d70e8..4c3e6506 100644 --- a/frontend/components/screen/InteractiveScreenViewer.tsx +++ b/frontend/components/screen/InteractiveScreenViewer.tsx @@ -346,6 +346,14 @@ export const InteractiveScreenViewer: React.FC = ( // 실제 사용 가능한 위젯 렌더링 const renderInteractiveWidget = (comp: ComponentData) => { + console.log("🎯 renderInteractiveWidget 호출:", { + type: comp.type, + id: comp.id, + componentId: (comp as any).componentId, + hasComponentConfig: !!(comp as any).componentConfig, + componentConfig: (comp as any).componentConfig, + }); + // 데이터 테이블 컴포넌트 처리 if (comp.type === "datatable") { return ( @@ -398,7 +406,8 @@ export const InteractiveScreenViewer: React.FC = ( } // 탭 컴포넌트 처리 - if (comp.type === "tabs" || (comp.type === "component" && comp.componentId === "tabs-widget")) { + const componentType = (comp as any).componentType || (comp as any).componentId; + if (comp.type === "tabs" || (comp.type === "component" && componentType === "tabs-widget")) { const TabsWidget = require("@/components/screen/widgets/TabsWidget").TabsWidget; // componentConfig에서 탭 정보 추출 @@ -416,6 +425,7 @@ export const InteractiveScreenViewer: React.FC = ( console.log("🔍 탭 컴포넌트 렌더링:", { originalType: comp.type, + componentType, componentId: (comp as any).componentId, tabs: tabsComponent.tabs, tabsConfig, diff --git a/frontend/components/screen/RealtimePreview.tsx b/frontend/components/screen/RealtimePreview.tsx index 7a0dbc34..0270ffa8 100644 --- a/frontend/components/screen/RealtimePreview.tsx +++ b/frontend/components/screen/RealtimePreview.tsx @@ -555,41 +555,70 @@ export const RealtimePreviewDynamic: React.FC = ({ })()} {/* 탭 컴포넌트 타입 */} - {(type === "tabs" || (type === "component" && (component as any).componentId === "tabs-widget")) && + {(type === "tabs" || (type === "component" && ((component as any).componentType === "tabs-widget" || (component as any).componentId === "tabs-widget"))) && (() => { - const tabsComponent = component as any; - // componentConfig에서 탭 정보 가져오기 - const tabs = tabsComponent.componentConfig?.tabs || tabsComponent.tabs || []; + console.log("🎯 탭 컴포넌트 조건 충족:", { + type, + componentType: (component as any).componentType, + componentId: (component as any).componentId, + isDesignMode, + }); - return ( -
-
-
- -
-

탭 컴포넌트

-

- {tabs.length > 0 - ? `${tabs.length}개의 탭 (실제 화면에서 표시됩니다)` - : "탭이 없습니다. 설정 패널에서 탭을 추가하세요"} -

- {tabs.length > 0 && ( -
- {tabs.map((tab: any, index: number) => ( - - {tab.label || `탭 ${index + 1}`} - {tab.screenName && ( - - ({tab.screenName}) - - )} - - ))} + if (isDesignMode) { + // 디자인 모드: 미리보기 표시 + const tabsComponent = component as any; + const tabs = tabsComponent.componentConfig?.tabs || tabsComponent.tabs || []; + + return ( +
+
+
+
- )} +

탭 컴포넌트

+

+ {tabs.length > 0 + ? `${tabs.length}개의 탭 (실제 화면에서 표시됩니다)` + : "탭이 없습니다. 설정 패널에서 탭을 추가하세요"} +

+ {tabs.length > 0 && ( +
+ {tabs.map((tab: any, index: number) => ( + + {tab.label || `탭 ${index + 1}`} + {tab.screenName && ( + + ({tab.screenName}) + + )} + + ))} +
+ )} +
-
- ); + ); + } else { + // 실제 화면: TabsWidget 렌더링 + const TabsWidget = require("@/components/screen/widgets/TabsWidget").TabsWidget; + const tabsConfig = (component as any).componentConfig || {}; + const tabsComponent = { + ...component, + type: "tabs" as const, + tabs: tabsConfig.tabs || [], + defaultTab: tabsConfig.defaultTab, + orientation: tabsConfig.orientation || "horizontal", + variant: tabsConfig.variant || "default", + allowCloseable: tabsConfig.allowCloseable || false, + persistSelection: tabsConfig.persistSelection || false, + }; + + return ( +
+ +
+ ); + } })()} {/* 그룹 타입 */} diff --git a/frontend/components/screen/widgets/TabsWidget.tsx b/frontend/components/screen/widgets/TabsWidget.tsx index 90608a4b..683017cf 100644 --- a/frontend/components/screen/widgets/TabsWidget.tsx +++ b/frontend/components/screen/widgets/TabsWidget.tsx @@ -60,24 +60,45 @@ export function TabsWidget({ component, className, style }: TabsWidgetProps) { } }, [selectedTab, persistSelection, storageKey]); + // 초기 로드 시 선택된 탭의 화면 불러오기 + useEffect(() => { + const currentTab = visibleTabs.find((t) => t.id === selectedTab); + console.log("🔄 초기 탭 로드:", { + selectedTab, + currentTab, + hasScreenId: !!currentTab?.screenId, + screenId: currentTab?.screenId, + }); + + if (currentTab && currentTab.screenId && !screenLayouts[currentTab.screenId]) { + console.log("📥 초기 화면 로딩 시작:", currentTab.screenId); + loadScreenLayout(currentTab.screenId); + } + }, [selectedTab, visibleTabs]); + // 화면 레이아웃 로드 const loadScreenLayout = async (screenId: number) => { if (screenLayouts[screenId]) { + console.log("✅ 이미 로드된 화면:", screenId); return; // 이미 로드됨 } + console.log("📥 화면 레이아웃 로딩 시작:", screenId); setLoadingScreens((prev) => ({ ...prev, [screenId]: true })); try { - const response = await fetch(`/api/screen-management/screens/${screenId}/layout`); - if (response.ok) { - const data = await response.json(); - if (data.success && data.data) { - setScreenLayouts((prev) => ({ ...prev, [screenId]: data.data })); - } + const { apiClient } = await import("@/lib/api/client"); + const response = await apiClient.get(`/screen-management/screens/${screenId}/layout`); + console.log("📦 API 응답:", { screenId, success: response.data.success, hasData: !!response.data.data }); + + if (response.data.success && response.data.data) { + console.log("✅ 화면 레이아웃 로드 완료:", screenId); + setScreenLayouts((prev) => ({ ...prev, [screenId]: response.data.data })); + } else { + console.error("❌ 화면 레이아웃 로드 실패 - success false"); } } catch (error) { - console.error(`Failed to load screen layout ${screenId}:`, error); + console.error(`❌ 화면 레이아웃 로드 실패 ${screenId}:`, error); } finally { setLoadingScreens((prev) => ({ ...prev, [screenId]: false })); } @@ -85,11 +106,15 @@ export function TabsWidget({ component, className, style }: TabsWidgetProps) { // 탭 변경 핸들러 const handleTabChange = (tabId: string) => { + console.log("🔄 탭 변경:", tabId); setSelectedTab(tabId); // 해당 탭의 화면 로드 const tab = visibleTabs.find((t) => t.id === tabId); + console.log("🔍 선택된 탭 정보:", { tab, hasScreenId: !!tab?.screenId, screenId: tab?.screenId }); + if (tab && tab.screenId && !screenLayouts[tab.screenId]) { + console.log("📥 탭 변경 시 화면 로딩:", tab.screenId); loadScreenLayout(tab.screenId); } }; @@ -120,6 +145,7 @@ export function TabsWidget({ component, className, style }: TabsWidgetProps) { }; if (visibleTabs.length === 0) { + console.log("⚠️ 보이는 탭이 없음"); return (

탭이 없습니다

@@ -127,61 +153,106 @@ export function TabsWidget({ component, className, style }: TabsWidgetProps) { ); } - return ( - - - {visibleTabs.map((tab) => ( -
- - {tab.label} - - {allowCloseable && ( - - )} -
- ))} -
+ console.log("🎨 TabsWidget 최종 렌더링:", { + visibleTabsCount: visibleTabs.length, + selectedTab, + screenLayoutsKeys: Object.keys(screenLayouts), + loadingScreensKeys: Object.keys(loadingScreens), + }); - {visibleTabs.map((tab) => ( - - {tab.screenId ? ( - loadingScreens[tab.screenId] ? ( -
- - 화면 로딩 중... + return ( +
+ +
+ + {visibleTabs.map((tab) => ( +
+ + {tab.label} + + {allowCloseable && ( + + )}
- ) : screenLayouts[tab.screenId] ? ( -
- -
- ) : ( -
-

화면을 불러올 수 없습니다

-
- ) - ) : ( -
-

연결된 화면이 없습니다

-
- )} - - ))} - + ))} +
+
+ +
+ {visibleTabs.map((tab) => ( + + {tab.screenId ? ( + loadingScreens[tab.screenId] ? ( +
+ + 화면 로딩 중... +
+ ) : screenLayouts[tab.screenId] ? ( + (() => { + const layoutData = screenLayouts[tab.screenId]; + const { components = [], screenResolution } = layoutData; + + console.log("🎯 렌더링할 화면 데이터:", { + screenId: tab.screenId, + componentsCount: components.length, + screenResolution, + }); + + const designWidth = screenResolution?.width || 1920; + const designHeight = screenResolution?.height || 1080; + + return ( +
+
+ {components.map((component: any) => ( + + ))} +
+
+ ); + })() + ) : ( +
+

화면을 불러올 수 없습니다

+
+ ) + ) : ( +
+

연결된 화면이 없습니다

+
+ )} +
+ ))} +
+
+
); } diff --git a/frontend/lib/registry/components/tabs/tabs-component.tsx b/frontend/lib/registry/components/tabs/tabs-component.tsx index 18fbf297..9006d78e 100644 --- a/frontend/lib/registry/components/tabs/tabs-component.tsx +++ b/frontend/lib/registry/components/tabs/tabs-component.tsx @@ -6,6 +6,40 @@ import { ComponentCategory } from "@/types/component"; import { Folder } from "lucide-react"; import type { TabsComponent, TabItem } from "@/types/screen-management"; +// TabsWidget 래퍼 컴포넌트 +const TabsWidgetWrapper: React.FC = (props) => { + const { component, ...restProps } = props; + + // componentConfig에서 탭 정보 추출 + const tabsConfig = component.componentConfig || {}; + const tabsComponent = { + ...component, + type: "tabs" as const, + tabs: tabsConfig.tabs || [], + defaultTab: tabsConfig.defaultTab, + orientation: tabsConfig.orientation || "horizontal", + variant: tabsConfig.variant || "default", + allowCloseable: tabsConfig.allowCloseable || false, + persistSelection: tabsConfig.persistSelection || false, + }; + + console.log("🎨 TabsWidget 렌더링:", { + componentId: component.id, + tabs: tabsComponent.tabs, + tabsLength: tabsComponent.tabs.length, + component, + }); + + // TabsWidget 동적 로드 + const TabsWidget = require("@/components/screen/widgets/TabsWidget").TabsWidget; + + return ( +
+ +
+ ); +}; + /** * 탭 컴포넌트 정의 * @@ -17,7 +51,7 @@ ComponentRegistry.registerComponent({ description: "화면을 탭으로 전환할 수 있는 컴포넌트입니다. 각 탭마다 다른 화면을 연결할 수 있습니다.", category: ComponentCategory.LAYOUT, webType: "text" as any, // 레이아웃 컴포넌트이므로 임시값 - component: () => null as any, // 레이아웃 컴포넌트이므로 임시값 + component: TabsWidgetWrapper, // ✅ 실제 TabsWidget 렌더러 defaultConfig: {}, tags: ["tabs", "navigation", "layout", "screen"], icon: Folder,