/** * 레거시 CustomEvent ↔ V2 EventBus 어댑터 * * 기존 window.dispatchEvent/addEventListener 기반 이벤트를 * V2 EventBus로 브릿지하여 점진적 마이그레이션을 지원합니다. * * 특징: * - 양방향 브릿지 (레거시 → V2, V2 → 레거시) * - 이벤트 중복 방지 * - 선택적 브릿지 활성화/비활성화 */ import { v2EventBus, V2_EVENTS, V2EventName, V2EventPayloadMap } from "../events"; // ============================================================================ // 이벤트 매핑 정의 // ============================================================================ interface EventMapping { legacy: string; v2: V2EventName; /** 레거시 → V2 페이로드 변환 함수 */ toV2?: (legacyDetail: any) => any; /** V2 → 레거시 페이로드 변환 함수 */ toLegacy?: (v2Payload: any) => any; } const EVENT_MAPPINGS: EventMapping[] = [ // 테이블 관련 { legacy: "refreshTable", v2: V2_EVENTS.TABLE_REFRESH, toV2: (detail) => ({ tableName: detail?.tableName, target: detail?.target ?? "all", screenCode: detail?.screenCode, }), toLegacy: (payload) => ({ tableName: payload.tableName, target: payload.target, screenCode: payload.screenCode, }), }, { legacy: "tableListDataChange", v2: V2_EVENTS.TABLE_DATA_CHANGE, toV2: (detail) => ({ tableName: detail?.tableName, data: detail?.data ?? [], totalCount: detail?.totalCount ?? 0, source: detail?.source ?? "legacy", }), toLegacy: (payload) => ({ tableName: payload.tableName, data: payload.data, totalCount: payload.totalCount, source: payload.source, }), }, { legacy: "tableSelectionChange", v2: V2_EVENTS.TABLE_SELECTION_CHANGE, toV2: (detail) => ({ tableName: detail?.tableName, selectedRows: detail?.selectedRows ?? [], selectedRowIds: detail?.selectedRowIds ?? [], source: detail?.source ?? "legacy", }), toLegacy: (payload) => ({ tableName: payload.tableName, selectedRows: payload.selectedRows, selectedRowIds: payload.selectedRowIds, source: payload.source, }), }, { legacy: "selectionChange", v2: V2_EVENTS.TABLE_SELECTION_CHANGE, toV2: (detail) => ({ tableName: detail?.tableName ?? "", selectedRows: detail?.selectedRows ?? [], selectedRowIds: detail?.selectedRowIds ?? [], source: "legacy-selectionChange", }), toLegacy: (payload) => ({ tableName: payload.tableName, selectedRows: payload.selectedRows, selectedRowIds: payload.selectedRowIds, source: payload.source, }), }, // 폼 저장 관련 { legacy: "beforeFormSave", v2: V2_EVENTS.FORM_SAVE_COLLECT, toV2: (detail) => ({ requestId: detail?.requestId ?? `req_${Date.now()}`, formData: detail?.formData ?? {}, componentId: detail?.componentId ?? "legacy", }), toLegacy: (payload) => ({ requestId: payload.requestId, formData: payload.formData, componentId: payload.componentId, }), }, { legacy: "saveSuccess", v2: V2_EVENTS.FORM_SAVE_COMPLETE, toV2: (detail) => ({ requestId: detail?.requestId ?? "", success: true, savedData: detail?.savedData ?? detail, tableName: detail?.tableName ?? "", }), toLegacy: (payload) => ({ requestId: payload.requestId, savedData: payload.savedData, tableName: payload.tableName, }), }, // 리피터 관련 { legacy: "repeaterSave", v2: V2_EVENTS.REPEATER_SAVE, toV2: (detail) => ({ repeaterId: detail?.repeaterId ?? "", tableName: detail?.tableName ?? "", items: detail?.items ?? [], joinData: detail?.joinData, }), toLegacy: (payload) => ({ repeaterId: payload.repeaterId, tableName: payload.tableName, items: payload.items, joinData: payload.joinData, }), }, { legacy: "repeaterDataChange", v2: V2_EVENTS.REPEATER_DATA_CHANGE, toV2: (detail) => ({ repeaterId: detail?.repeaterId ?? "", tableName: detail?.tableName ?? "", data: detail?.data ?? [], action: detail?.action ?? "update", }), toLegacy: (payload) => ({ repeaterId: payload.repeaterId, tableName: payload.tableName, data: payload.data, action: payload.action, }), }, // 모달 관련 { legacy: "closeEditModal", v2: V2_EVENTS.MODAL_CLOSE, toV2: (detail) => ({ modalId: detail?.modalId ?? "edit-modal", reason: detail?.reason ?? "close", }), toLegacy: (payload) => ({ modalId: payload.modalId, reason: payload.reason, }), }, { legacy: "saveSuccessInModal", v2: V2_EVENTS.MODAL_SAVE_SUCCESS, toV2: (detail) => ({ modalId: detail?.modalId ?? "edit-modal", savedData: detail?.savedData ?? {}, tableName: detail?.tableName ?? "", }), toLegacy: (payload) => ({ modalId: payload.modalId, savedData: payload.savedData, tableName: payload.tableName, }), }, { legacy: "openScreenModal", v2: V2_EVENTS.MODAL_OPEN, toV2: (detail) => ({ modalId: detail?.modalId ?? "", screenCode: detail?.screenCode, data: detail?.data, mode: detail?.mode ?? "view", }), toLegacy: (payload) => ({ modalId: payload.modalId, screenCode: payload.screenCode, data: payload.data, mode: payload.mode, }), }, // 카드 디스플레이 { legacy: "refreshCardDisplay", v2: V2_EVENTS.CARD_REFRESH, toV2: (detail) => ({ cardId: detail?.cardId, tableName: detail?.tableName, }), toLegacy: (payload) => ({ cardId: payload.cardId, tableName: payload.tableName, }), }, // 분할 패널 { legacy: "splitPanelDataTransfer", v2: V2_EVENTS.SPLIT_PANEL_DATA_TRANSFER, toV2: (detail) => ({ sourcePanel: detail?.sourcePanel ?? "left", targetPanel: detail?.targetPanel ?? "right", data: detail?.data ?? {}, tableName: detail?.tableName ?? "", }), toLegacy: (payload) => ({ sourcePanel: payload.sourcePanel, targetPanel: payload.targetPanel, data: payload.data, tableName: payload.tableName, }), }, // 컴포넌트 데이터 전송 { legacy: "componentDataTransfer", v2: V2_EVENTS.COMPONENT_DATA_TRANSFER, toV2: (detail) => ({ sourceComponentId: detail?.sourceComponentId ?? "", targetComponentId: detail?.targetComponentId, data: detail?.data ?? {}, tableName: detail?.tableName, }), toLegacy: (payload) => ({ sourceComponentId: payload.sourceComponentId, targetComponentId: payload.targetComponentId, data: payload.data, tableName: payload.tableName, }), }, // 관련 버튼 { legacy: "related-button-register", v2: V2_EVENTS.RELATED_BUTTON_REGISTER, toV2: (detail) => ({ buttonId: detail?.buttonId ?? "", targetTables: detail?.targetTables ?? [], }), toLegacy: (payload) => ({ buttonId: payload.buttonId, targetTables: payload.targetTables, }), }, { legacy: "related-button-unregister", v2: V2_EVENTS.RELATED_BUTTON_UNREGISTER, toV2: (detail) => ({ buttonId: detail?.buttonId ?? "", }), toLegacy: (payload) => ({ buttonId: payload.buttonId, }), }, { legacy: "related-button-select", v2: V2_EVENTS.RELATED_BUTTON_SELECT, toV2: (detail) => ({ tableName: detail?.tableName ?? "", selectedData: detail?.selectedData ?? [], }), toLegacy: (payload) => ({ tableName: payload.tableName, selectedData: payload.selectedData, }), }, ]; // ============================================================================ // 어댑터 클래스 // ============================================================================ class LegacyEventAdapter { private isActive = false; private legacyListeners: Map void> = new Map(); private v2Unsubscribes: Map void> = new Map(); /** 브릿지에서 발생한 이벤트 추적 (무한 루프 방지) */ private bridgedEvents: Set = new Set(); /** 브릿지 방향 설정 */ private config = { legacyToV2: true, v2ToLegacy: true, }; /** * 어댑터 초기화 및 브릿지 시작 * * @param options - 브릿지 설정 */ init(options?: { legacyToV2?: boolean; v2ToLegacy?: boolean }): void { if (this.isActive) { console.warn("[LegacyEventAdapter] 이미 초기화되어 있습니다."); return; } if (options) { this.config = { ...this.config, ...options }; } console.log("[LegacyEventAdapter] 초기화 시작", this.config); EVENT_MAPPINGS.forEach((mapping) => { // 레거시 → V2 브릿지 if (this.config.legacyToV2) { this.setupLegacyToV2Bridge(mapping); } // V2 → 레거시 브릿지 if (this.config.v2ToLegacy) { this.setupV2ToLegacyBridge(mapping); } }); this.isActive = true; console.log( `[LegacyEventAdapter] 초기화 완료 (${EVENT_MAPPINGS.length}개 매핑)` ); } private setupLegacyToV2Bridge(mapping: EventMapping): void { const listener = (event: Event) => { const customEvent = event as CustomEvent; const bridgeKey = `${mapping.legacy}-${Date.now()}`; // 무한 루프 방지: 브릿지에서 발생한 이벤트인지 확인 if (customEvent.detail?.__v2Bridged) { return; } this.bridgedEvents.add(bridgeKey); // 페이로드 변환 const v2Payload = mapping.toV2 ? mapping.toV2(customEvent.detail) : customEvent.detail; // V2 EventBus로 발행 v2EventBus.emitSync(mapping.v2, v2Payload); // 잠시 후 브릿지 키 정리 setTimeout(() => { this.bridgedEvents.delete(bridgeKey); }, 100); }; window.addEventListener(mapping.legacy, listener); this.legacyListeners.set(mapping.legacy, listener); } private setupV2ToLegacyBridge(mapping: EventMapping): void { const unsubscribe = v2EventBus.subscribe( mapping.v2, (payload) => { // 무한 루프 방지 표시 추가 const legacyPayload = mapping.toLegacy ? { ...mapping.toLegacy(payload), __v2Bridged: true } : { ...payload, __v2Bridged: true }; // 레거시 이벤트 발행 window.dispatchEvent( new CustomEvent(mapping.legacy, { detail: legacyPayload }) ); }, { componentId: "legacy-adapter" } ); this.v2Unsubscribes.set(mapping.v2, unsubscribe); } /** * 어댑터 정지 및 정리 */ destroy(): void { if (!this.isActive) { return; } // 레거시 리스너 정리 this.legacyListeners.forEach((listener, eventName) => { window.removeEventListener(eventName, listener); }); this.legacyListeners.clear(); // V2 구독 정리 this.v2Unsubscribes.forEach((unsubscribe) => { unsubscribe(); }); this.v2Unsubscribes.clear(); this.bridgedEvents.clear(); this.isActive = false; console.log("[LegacyEventAdapter] 정리 완료"); } /** * 현재 활성 상태 확인 */ get active(): boolean { return this.isActive; } /** * 매핑된 이벤트 목록 조회 */ getMappings(): Array<{ legacy: string; v2: string }> { return EVENT_MAPPINGS.map((m) => ({ legacy: m.legacy, v2: m.v2, })); } } // 싱글톤 인스턴스 export const legacyEventAdapter = new LegacyEventAdapter(); // 개발 환경에서 window에 노출 (디버깅용) if (typeof window !== "undefined" && process.env.NODE_ENV === "development") { (window as any).__legacyEventAdapter = legacyEventAdapter; }