/** * 화면 컨텍스트 * 같은 화면 내의 컴포넌트들이 서로 통신할 수 있도록 합니다. */ "use client"; import React, { createContext, useContext, useCallback, useRef } from "react"; import type { DataProvidable, DataReceivable } from "@/types/data-transfer"; import { logger } from "@/lib/utils/logger"; import type { SplitPanelPosition } from "@/contexts/SplitPanelContext"; interface ScreenContextValue { screenId?: number; tableName?: string; splitPanelPosition?: SplitPanelPosition; // 🆕 분할 패널 위치 (left/right) // 컴포넌트 등록 registerDataProvider: (componentId: string, provider: DataProvidable) => void; unregisterDataProvider: (componentId: string) => void; registerDataReceiver: (componentId: string, receiver: DataReceivable) => void; unregisterDataReceiver: (componentId: string) => void; // 컴포넌트 조회 getDataProvider: (componentId: string) => DataProvidable | undefined; getDataReceiver: (componentId: string) => DataReceivable | undefined; // 모든 컴포넌트 조회 getAllDataProviders: () => Map; getAllDataReceivers: () => Map; } const ScreenContext = createContext(null); interface ScreenContextProviderProps { screenId?: number; tableName?: string; splitPanelPosition?: SplitPanelPosition; // 🆕 분할 패널 위치 children: React.ReactNode; } /** * 화면 컨텍스트 프로바이더 */ export function ScreenContextProvider({ screenId, tableName, splitPanelPosition, children }: ScreenContextProviderProps) { const dataProvidersRef = useRef>(new Map()); const dataReceiversRef = useRef>(new Map()); const registerDataProvider = useCallback((componentId: string, provider: DataProvidable) => { dataProvidersRef.current.set(componentId, provider); logger.debug("데이터 제공자 등록", { componentId, componentType: provider.componentType }); }, []); const unregisterDataProvider = useCallback((componentId: string) => { dataProvidersRef.current.delete(componentId); logger.debug("데이터 제공자 해제", { componentId }); }, []); const registerDataReceiver = useCallback((componentId: string, receiver: DataReceivable) => { dataReceiversRef.current.set(componentId, receiver); logger.debug("데이터 수신자 등록", { componentId, componentType: receiver.componentType }); }, []); const unregisterDataReceiver = useCallback((componentId: string) => { dataReceiversRef.current.delete(componentId); logger.debug("데이터 수신자 해제", { componentId }); }, []); const getDataProvider = useCallback((componentId: string) => { return dataProvidersRef.current.get(componentId); }, []); const getDataReceiver = useCallback((componentId: string) => { return dataReceiversRef.current.get(componentId); }, []); const getAllDataProviders = useCallback(() => { return new Map(dataProvidersRef.current); }, []); const getAllDataReceivers = useCallback(() => { return new Map(dataReceiversRef.current); }, []); // 🆕 useMemo로 value 객체 메모이제이션 (무한 루프 방지) const value = React.useMemo(() => ({ screenId, tableName, splitPanelPosition, registerDataProvider, unregisterDataProvider, registerDataReceiver, unregisterDataReceiver, getDataProvider, getDataReceiver, getAllDataProviders, getAllDataReceivers, }), [ screenId, tableName, splitPanelPosition, registerDataProvider, unregisterDataProvider, registerDataReceiver, unregisterDataReceiver, getDataProvider, getDataReceiver, getAllDataProviders, getAllDataReceivers, ]); return {children}; } /** * 화면 컨텍스트 훅 */ export function useScreenContext() { const context = useContext(ScreenContext); if (!context) { throw new Error("useScreenContext는 ScreenContextProvider 내부에서만 사용할 수 있습니다."); } return context; } /** * 화면 컨텍스트 훅 (선택적) * 컨텍스트가 없어도 에러를 발생시키지 않습니다. */ export function useScreenContextOptional() { return useContext(ScreenContext); }