/** * src/middleware/panelHistoryMiddleware.js * Panel history 자동 추적 middleware * * Panel action (PUSH, POP, UPDATE, RESET)을 감지하고 * 자동으로 panel history에 기록 * * ⚠️ [251122] DEBUG_MODE = false로 설정되어 모든 로그 출력 비활성화됨 * - 로그가 필요하면 DEBUG_MODE를 true로 변경하면 됨 * - middleware 동작 자체는 영향받지 않음 (로그만 출력 안됨) */ import { types } from '../actions/actionTypes'; import { enqueuePanelHistory, clearPanelHistory } from '../actions/panelHistoryActions'; import { calculateIsPanelOnTop } from '../utils/panelUtils'; // 🎯 isOnTop 유틸리티 함수 import // DEBUG_MODE - true인 경우에만 로그 출력 // ⚠️ [251122] panelHistory 로그 비활성화 - 로그 생성 차단 const DEBUG_MODE = true; /** * Panel history middleware * 모든 panel action을 감지하여 panelHistory reducer로 자동 기록 * * @param {Object} store - Redux store * @returns {Function} middleware function */ export const panelHistoryMiddleware = (store) => (next) => (action) => { // 모든 PANEL 관련 액션 로깅 if ( DEBUG_MODE && action.type && (action.type.includes('PANEL') || action.type === 'CLEAR_PANEL_HISTORY') ) { const caller = new Error().stack.split('\n')[1]?.trim(); console.log(`[PANEL DEBUG] ${action.type} from: ${caller}`); console.log(' Payload:', action.payload); } // GNB 호출 식별을 위한 helper 함수 const isGNBCall = () => { const stack = new Error().stack; if (!stack) return false; const stackLines = stack.split('\n'); for (const line of stackLines) { if (line.includes('TabLayout.jsx') || line.includes('TIconButton.jsx')) { return true; } } return false; }; // 임시: CLEAR_PANEL_HISTORY 무력화 if (action.type === 'CLEAR_PANEL_HISTORY') { if (DEBUG_MODE) console.log('[PANEL DEBUG] CLEAR_PANEL_HISTORY BLOCKED!'); return action; // 액션을 전달하지 않음 } // 원래 액션 실행 const result = next(action); // Panel action 후 history 업데이트 const state = store.getState(); const panels = state.panels?.panels || []; // 🎯 [isOnTop 계산] 유틸리티 함수 사용 (MainView 로직과 정확히 일치) // calculateIsPanelOnTop 함수는 MainView의 renderTopPanel 로직을 기반으로 정확한 isOnTop 상태 계산 const calculateIsOnTop = (targetPanelName) => { return calculateIsPanelOnTop(panels, targetPanelName); }; try { switch (action.type) { // PUSH_PANEL: 새 panel 추가 case types.PUSH_PANEL: { if (action.payload) { const { name: panelName, panelInfo = {} } = action.payload; if (panelName) { const isGNB = isGNBCall(); const isOnTop = calculateIsOnTop(panelName); // 🎯 isOnTop 계산 if (DEBUG_MODE) console.log('[PANEL] PUSH_PANEL:', { panelName, panelInfo, isGNB, isOnTop, timestamp: new Date().toISOString(), }); store.dispatch( enqueuePanelHistory( panelName, panelInfo, 'PUSH', new Date().toISOString(), isGNB, false, isOnTop ) ); // PanelHistory 상태 로그 (state 업데이트 후) const logPanelHistoryAfter = () => { if (DEBUG_MODE) { const stateAfter = store.getState(); const panelHistoryAfter = stateAfter.panelHistory; const panelsAfter = stateAfter.panels.panels; console.log('[PANEL_HISTORY] After PUSH_PANEL:', { panelHistory: panelHistoryAfter, panels: panelsAfter, }); } }; // state 업데이트가 완료된 후 로그 Promise.resolve().then(logPanelHistoryAfter); } } break; } // POP_PANEL: panel 제거 후 이전 panel 기록 case types.POP_PANEL: { // POP 후 top panel을 기록 (이전 패널로 돌아감) if (panels.length > 0) { const topPanel = panels[panels.length - 1]; if (DEBUG_MODE) { console.log('[PANEL-TRACE] POP_PANEL middleware stack', { stack: panels.map((p) => p.name), topPanel: topPanel?.name, payload: action.payload, caller: new Error().stack?.split('\n')[2]?.trim(), }); } if (topPanel && topPanel.name) { const isGNB = isGNBCall(); const isOnTop = calculateIsOnTop(topPanel.name); // 🎯 isOnTop 계산 if (DEBUG_MODE) console.log('[PANEL] POP_PANEL:', { panelName: topPanel.name, panelInfo: topPanel.panelInfo || {}, isGNB, isOnTop, timestamp: new Date().toISOString(), }); store.dispatch( enqueuePanelHistory( topPanel.name, topPanel.panelInfo || {}, 'POP', new Date().toISOString(), isGNB, false, isOnTop ) ); // PanelHistory 상태 로그 (state 업데이트 후) const logPanelHistoryAfter = () => { if (DEBUG_MODE) { const stateAfter = store.getState(); const panelHistoryAfter = stateAfter.panelHistory; const panelsAfter = stateAfter.panels.panels; console.log('[PANEL_HISTORY] After POP_PANEL:', { panelHistory: panelHistoryAfter, panels: panelsAfter, }); } }; // state 업데이트가 완료된 후 로그 Promise.resolve().then(logPanelHistoryAfter); } } break; } // UPDATE_PANEL: panel 정보 업데이트 기록 case types.UPDATE_PANEL: { if (action.payload) { const { name: panelName, panelInfo = {} } = action.payload; if (panelName) { const isGNB = isGNBCall(); const isOnTop = calculateIsOnTop(panelName); // 🎯 isOnTop 계산 if (DEBUG_MODE) console.log('[PANEL] UPDATE_PANEL:', { panelName, panelInfo, isGNB, isOnTop, timestamp: new Date().toISOString(), }); store.dispatch( enqueuePanelHistory( panelName, panelInfo, 'UPDATE', new Date().toISOString(), isGNB, false, isOnTop ) ); // PanelHistory 상태 로그 (state 업데이트 후) const logPanelHistoryAfter = () => { if (DEBUG_MODE) { const stateAfter = store.getState(); const panelHistoryAfter = stateAfter.panelHistory; const panelsAfter = stateAfter.panels.panels; console.log('[PANEL_HISTORY] After UPDATE_PANEL:', { panelHistory: panelHistoryAfter, panels: panelsAfter, }); } }; // state 업데이트가 완료된 후 로그 Promise.resolve().then(logPanelHistoryAfter); } } break; } // RESET_PANELS: GNB 네비게이션 또는 완전 초기화 case types.RESET_PANELS: { if (DEBUG_MODE) console.log('[PANEL] RESET_PANELS:', { payload: action.payload, timestamp: new Date().toISOString(), }); if (DEBUG_MODE) console.log('[PANEL_HISTORY] Before RESET_PANELS:', store.getState().panelHistory); // 모든 RESET_PANELS를 기록 const isGNB = isGNBCall(); if (action.payload && action.payload.length > 0) { // payload가 있는 경우 (GNB 탭, 패널 내 네비게이션 등) const firstPanel = action.payload[0]; if (firstPanel && firstPanel.name) { const isOnTop = calculateIsOnTop(firstPanel.name); // 🎯 isOnTop 계산 if (DEBUG_MODE) console.log('[PANEL_DEBUG] RESET_PANELS to:', firstPanel.name, { isGNB, isOnTop, fromResetPanel: true, }); // RESET 이동을 히스토리에 기록 store.dispatch( enqueuePanelHistory( firstPanel.name, firstPanel.panelInfo || {}, 'RESET', new Date().toISOString(), isGNB, // TabLayout/TIconButton이면 true true, // fromResetPanel: 항상 true isOnTop // 🎯 isOnTop 정보 추가 ) ); } } else { // 완전 초기화 (payload가 없는 경우 - HomePanel, 앱 초기화 등) if (DEBUG_MODE) console.log('[PANEL_DEBUG] Complete panel history reset (payload empty)', { isGNB, fromResetPanel: true, }); store.dispatch(clearPanelHistory()); // HomePanel 초기화 기록 (앱 시작 시) if (DEBUG_MODE) console.log('[PANEL_DEBUG] Recording initial HomePanel'); const isOnTop = calculateIsOnTop('homepanel'); // 🎯 isOnTop 계산 store.dispatch( enqueuePanelHistory( 'homepanel', {}, 'APP_INITIALIZE', new Date().toISOString(), isGNB, // TIconButton Home 버튼이면 true true, // fromResetPanel: true isOnTop // 🎯 isOnTop 정보 추가 ) ); } // PanelHistory 상태 로그 (초기화 후) const logPanelHistoryAfter = () => { if (DEBUG_MODE) { const stateAfter = store.getState(); const panelHistoryAfter = stateAfter.panelHistory; const panelsAfter = stateAfter.panels.panels; console.log('[PANEL_HISTORY] After RESET_PANELS:', { panelHistory: panelHistoryAfter, panels: panelsAfter, }); } }; // state 업데이트가 완료된 후 로그 Promise.resolve().then(logPanelHistoryAfter); break; } default: // Other actions are ignored break; } } catch (error) { if (DEBUG_MODE) console.error('[panelHistoryMiddleware] 오류:', error); // Middleware 오류가 앱 전체에 영향 주지 않도록 처리 } return result; }; export default panelHistoryMiddleware;