From 73e3d5638136d591aad492301182ecee05c9d033 Mon Sep 17 00:00:00 2001 From: SeongHyun Kim Date: Tue, 10 Feb 2026 12:20:44 +0900 Subject: [PATCH] =?UTF-8?q?fix(pop-dashboard):=20React=20Hooks=20=EA=B7=9C?= =?UTF-8?q?=EC=B9=99=20=EC=9C=84=EB=B0=98=20=EC=88=98=EC=A0=95=20+=20Confi?= =?UTF-8?q?gPanel=20props=20=EC=A0=95=ED=95=A9=EC=84=B1=20+=20=EB=B0=A9?= =?UTF-8?q?=EC=96=B4=20=EC=BD=94=EB=93=9C=20=EA=B0=95=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PopDashboardComponent: early return을 모든 hooks 이후로 이동 (Rules of Hooks) - PopDashboardConfigPanel: onChange -> onUpdate prop 이름 정합, 빈 객체 config 방어 - PopDashboardPreview: Array.isArray 방어 추가 Co-authored-by: Cursor --- .../pop-dashboard/PopDashboardComponent.tsx | 30 ++++++++++--------- .../pop-dashboard/PopDashboardConfig.tsx | 7 +++-- .../pop-dashboard/PopDashboardPreview.tsx | 3 +- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/frontend/lib/registry/pop-components/pop-dashboard/PopDashboardComponent.tsx b/frontend/lib/registry/pop-components/pop-dashboard/PopDashboardComponent.tsx index 53940b0a..7c6e7c50 100644 --- a/frontend/lib/registry/pop-components/pop-dashboard/PopDashboardComponent.tsx +++ b/frontend/lib/registry/pop-components/pop-dashboard/PopDashboardComponent.tsx @@ -111,19 +111,10 @@ export function PopDashboardComponent({ const containerRef = useRef(null); const [containerWidth, setContainerWidth] = useState(300); - // 빈 설정 - if (!config || !config.items.length) { - return ( -
- - 대시보드 아이템을 추가하세요 - -
- ); - } - - // 보이는 아이템만 필터링 - const visibleItems = config.items.filter((item) => item.visible); + // 보이는 아이템만 필터링 (hooks 이전에 early return 불가하므로 빈 배열 허용) + const visibleItems = Array.isArray(config?.items) + ? config.items.filter((item) => item.visible) + : []; // 컨테이너 크기 감지 useEffect(() => { @@ -140,6 +131,7 @@ export function PopDashboardComponent({ }, []); // 데이터 로딩 함수 + // eslint-disable-next-line react-hooks/exhaustive-deps const fetchAllData = useCallback(async () => { if (!visibleItems.length) { setLoading(false); @@ -165,7 +157,6 @@ export function PopDashboardComponent({ setDataMap(newDataMap); setLoading(false); - // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify(visibleItems.map((i) => i.id))]); // 초기 로딩 + 주기적 새로고침 @@ -186,6 +177,17 @@ export function PopDashboardComponent({ }; }, [fetchAllData, visibleItems]); + // 빈 설정 (모든 hooks 이후에 early return) + if (!config || !config.items?.length) { + return ( +
+ + 대시보드 아이템을 추가하세요 + +
+ ); + } + // 단일 아이템 렌더링 const renderSingleItem = (item: DashboardItem) => { const itemData = dataMap[item.id]; diff --git a/frontend/lib/registry/pop-components/pop-dashboard/PopDashboardConfig.tsx b/frontend/lib/registry/pop-components/pop-dashboard/PopDashboardConfig.tsx index 74126c22..26da10f5 100644 --- a/frontend/lib/registry/pop-components/pop-dashboard/PopDashboardConfig.tsx +++ b/frontend/lib/registry/pop-components/pop-dashboard/PopDashboardConfig.tsx @@ -45,7 +45,7 @@ import { validateExpression } from "./utils/formula"; interface ConfigPanelProps { config: PopDashboardConfig | undefined; - onChange: (config: PopDashboardConfig) => void; + onUpdate: (config: PopDashboardConfig) => void; } // ===== 기본값 ===== @@ -806,9 +806,10 @@ function GridLayoutEditor({ export function PopDashboardConfigPanel({ config, - onChange, + onUpdate: onChange, }: ConfigPanelProps) { - const cfg = config ?? DEFAULT_CONFIG; + // config가 빈 객체 {}로 전달될 수 있으므로 spread로 기본값 보장 + const cfg: PopDashboardConfig = { ...DEFAULT_CONFIG, ...(config || {}) }; const [activeTab, setActiveTab] = useState<"basic" | "items" | "layout">( "basic" ); diff --git a/frontend/lib/registry/pop-components/pop-dashboard/PopDashboardPreview.tsx b/frontend/lib/registry/pop-components/pop-dashboard/PopDashboardPreview.tsx index db64f04c..8d530b96 100644 --- a/frontend/lib/registry/pop-components/pop-dashboard/PopDashboardPreview.tsx +++ b/frontend/lib/registry/pop-components/pop-dashboard/PopDashboardPreview.tsx @@ -64,7 +64,8 @@ export function PopDashboardPreviewComponent({ }: { config?: PopDashboardConfig; }) { - if (!config || !config.items.length) { + // config가 빈 객체 {} 또는 items가 없는 경우 방어 + if (!config || !Array.isArray(config.items) || !config.items.length) { return (