[251122] fix: Comment정리-2

🕐 커밋 시간: 2025. 11. 22. 18:35:56

📊 변경 통계:
  • 총 파일: 6개
  • 추가: +129줄
  • 삭제: -100줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/App/App.js
  ~ com.twin.app.shoptime/src/actions/productActions.js
  ~ com.twin.app.shoptime/src/api/TAxios.js
  ~ com.twin.app.shoptime/src/utils/panelUtils.js
  ~ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx
  ~ com.twin.app.shoptime/src/views/TrendingNowPanel/PopularShow/PopularVideoPlayer/PopularVideoPlayer.jsx

🔧 함수 변경 내용:
  📄 com.twin.app.shoptime/src/App/App.js (javascript):
    🔄 Modified: sendVoiceLogToPanel()
     Deleted: resolveSpotlightIdFromNode()
  📄 com.twin.app.shoptime/src/actions/productActions.js (javascript):
     Added: onSuccess()
    🔄 Modified: pickParams(), createGetThunk()
     Deleted: onSuccess()
  📄 com.twin.app.shoptime/src/api/TAxios.js (javascript):
    🔄 Modified: runDelayedAction()
  📄 com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx (javascript):
    🔄 Modified: SpotlightContainerDecorator()

🔧 주요 변경 내용:
  • 핵심 비즈니스 로직 개선
  • API 서비스 레이어 개선
  • 공통 유틸리티 함수 최적화
This commit is contained in:
2025-11-22 18:35:56 +09:00
parent 251e1ee3d4
commit 209d983954
6 changed files with 434 additions and 401 deletions

View File

@@ -72,6 +72,9 @@ import { types } from '../actions/actionTypes';
// } from "../utils/focus-monitor";
// import { PanelHoc } from "../components/TPanel/TPanel";
// DEBUG_MODE - true인 경우에만 로그 출력
const DEBUG_MODE = false;
let foreGroundChangeTimer = null;
// 기존 콘솔 메서드를 백업
@@ -185,86 +188,92 @@ const sendVoiceLogToPanel = (args) => {
};
console.log = function (...args) {
// Voice 로그를 VoicePanel로 전송
sendVoiceLogToPanel(args);
// 원래 console.log 실행
originalConsoleLog.apply(console, processArgs(args));
if (DEBUG_MODE) {
// Voice 로그를 VoicePanel로 전송
sendVoiceLogToPanel(args);
// 원래 console.log 실행
originalConsoleLog.apply(console, processArgs(args));
}
};
console.error = function (...args) {
// Voice 로그를 VoicePanel로 전송 (에러는 강제로 ERROR 타입)
try {
const firstArg = args[0];
if (
typeof firstArg === 'string' &&
(firstArg.includes('[Voice]') || firstArg.includes('[VoiceConductor]'))
) {
const logData = {};
if (args.length > 1) {
args.slice(1).forEach((arg, index) => {
if (typeof arg === 'object') {
Object.assign(logData, arg);
} else {
logData[`arg${index + 1}`] = arg;
}
if (DEBUG_MODE) {
// Voice 로그를 VoicePanel로 전송 (에러는 강제로 ERROR 타입)
try {
const firstArg = args[0];
if (
typeof firstArg === 'string' &&
(firstArg.includes('[Voice]') || firstArg.includes('[VoiceConductor]'))
) {
const logData = {};
if (args.length > 1) {
args.slice(1).forEach((arg, index) => {
if (typeof arg === 'object') {
Object.assign(logData, arg);
} else {
logData[`arg${index + 1}`] = arg;
}
});
}
store.dispatch({
type: types.VOICE_ADD_LOG,
payload: {
timestamp: new Date().toISOString(),
type: 'ERROR',
title: firstArg.replace(/^\[Voice\]\s*/, '').replace(/^\[VoiceConductor\]\s*/, ''),
data: Object.keys(logData).length > 0 ? logData : { message: firstArg },
success: false,
},
});
}
store.dispatch({
type: types.VOICE_ADD_LOG,
payload: {
timestamp: new Date().toISOString(),
type: 'ERROR',
title: firstArg.replace(/^\[Voice\]\s*/, '').replace(/^\[VoiceConductor\]\s*/, ''),
data: Object.keys(logData).length > 0 ? logData : { message: firstArg },
success: false,
},
});
} catch (error) {
originalConsoleError.call(console, '[VoiceLog] Error sending error to panel:', error);
}
} catch (error) {
originalConsoleError.call(console, '[VoiceLog] Error sending error to panel:', error);
}
originalConsoleError.apply(console, processArgs(args));
originalConsoleError.apply(console, processArgs(args));
}
};
console.warn = function (...args) {
// Voice 로그를 VoicePanel로 전송 (경고는 ERROR 타입으로)
try {
const firstArg = args[0];
if (
typeof firstArg === 'string' &&
(firstArg.includes('[Voice]') || firstArg.includes('[VoiceConductor]'))
) {
const logData = {};
if (args.length > 1) {
args.slice(1).forEach((arg, index) => {
if (typeof arg === 'object') {
Object.assign(logData, arg);
} else {
logData[`arg${index + 1}`] = arg;
}
if (DEBUG_MODE) {
// Voice 로그를 VoicePanel로 전송 (경고는 ERROR 타입으로)
try {
const firstArg = args[0];
if (
typeof firstArg === 'string' &&
(firstArg.includes('[Voice]') || firstArg.includes('[VoiceConductor]'))
) {
const logData = {};
if (args.length > 1) {
args.slice(1).forEach((arg, index) => {
if (typeof arg === 'object') {
Object.assign(logData, arg);
} else {
logData[`arg${index + 1}`] = arg;
}
});
}
store.dispatch({
type: types.VOICE_ADD_LOG,
payload: {
timestamp: new Date().toISOString(),
type: 'ERROR',
title:
'WARNING: ' +
firstArg.replace(/^\[Voice\]\s*/, '').replace(/^\[VoiceConductor\]\s*/, ''),
data: Object.keys(logData).length > 0 ? logData : { message: firstArg },
success: false,
},
});
}
store.dispatch({
type: types.VOICE_ADD_LOG,
payload: {
timestamp: new Date().toISOString(),
type: 'ERROR',
title:
'WARNING: ' +
firstArg.replace(/^\[Voice\]\s*/, '').replace(/^\[VoiceConductor\]\s*/, ''),
data: Object.keys(logData).length > 0 ? logData : { message: firstArg },
success: false,
},
});
} catch (error) {
originalConsoleWarn.call(console, '[VoiceLog] Error sending warning to panel:', error);
}
} catch (error) {
originalConsoleWarn.call(console, '[VoiceLog] Error sending warning to panel:', error);
}
originalConsoleWarn.apply(console, processArgs(args));
originalConsoleWarn.apply(console, processArgs(args));
}
};
const originFocus = Spotlight.focus;
@@ -304,12 +313,12 @@ const logFocusTransition = (previousNode, currentNode) => {
const currentId = resolveSpotlightIdFromNode(currentNode);
if (previousId && previousId !== currentId) {
console.log(`[SpotlightFocus] blur - ${previousId}`);
if (DEBUG_MODE) console.log(`[SpotlightFocus] blur - ${previousId}`);
lastLoggedBlurSpotlightId = previousId;
}
if (currentId && currentId !== lastLoggedSpotlightId) {
console.log(`[SpotlightFocus] focus - ${currentId}`);
if (DEBUG_MODE) console.log(`[SpotlightFocus] focus - ${currentId}`);
lastLoggedSpotlightId = currentId;
}
};

View File

@@ -7,6 +7,9 @@ import { reduce, set, get } from '../utils/fp';
// CustomerImages용 리뷰 이미지 import
import reviewSampleImage from '../../assets/images/image-review-sample-1.png';
// DEBUG_MODE - true인 경우에만 로그 출력
const DEBUG_MODE = false;
// Best Seller 상품 목록 조회 IF-LGSP-303
// FP helpers
const pickParams = (keys) => (src) =>
@@ -36,57 +39,60 @@ const createRequestThunk =
const body = data(props);
// 📡 REQUEST 로그: API 호출 전 (tag별로 다르게 표시)
console.log(
`%c[${tag}] 📤 REQUEST - ${method.toUpperCase()} ${url}`,
'background: #4CAF50; color: white; font-weight: bold; padding: 3px;',
{
method: method.toUpperCase(),
url: url,
params: query,
body: body,
timestamp: new Date().toISOString(),
}
);
const onSuccess = (response) => {
// ✅ RESPONSE 로그: API 호출 성공 (tag별로 다르게 표시)
if (DEBUG_MODE)
console.log(
`%c[${tag}] RESPONSE SUCCESS - ${method.toUpperCase()} ${url}`,
'background: #2196F3; color: white; font-weight: bold; padding: 3px;',
`%c[${tag}] 📤 REQUEST - ${method.toUpperCase()} ${url}`,
'background: #4CAF50; color: white; font-weight: bold; padding: 3px;',
{
method: method.toUpperCase(),
url: url,
httpStatus: response?.status,
httpStatusText: response?.statusText,
retCode: response?.data?.retCode,
retMsg: response?.data?.retMsg,
responseData: response?.data,
params: query,
body: body,
timestamp: new Date().toISOString(),
}
);
const onSuccess = (response) => {
// ✅ RESPONSE 로그: API 호출 성공 (tag별로 다르게 표시)
if (DEBUG_MODE)
console.log(
`%c[${tag}] ✅ RESPONSE SUCCESS - ${method.toUpperCase()} ${url}`,
'background: #2196F3; color: white; font-weight: bold; padding: 3px;',
{
method: method.toUpperCase(),
url: url,
httpStatus: response?.status,
httpStatusText: response?.statusText,
retCode: response?.data?.retCode,
retMsg: response?.data?.retMsg,
responseData: response?.data,
timestamp: new Date().toISOString(),
}
);
dispatch({ type, payload: selectPayload(response) });
onSuccessExtra(props, dispatch, getState, response);
};
const onFail = (error) => {
// ❌ ERROR 로그: API 호출 실패 (tag별로 다르게 표시)
console.error(
`%c[${tag}] ❌ RESPONSE ERROR - ${method.toUpperCase()} ${url}`,
'background: #F44336; color: white; font-weight: bold; padding: 3px;',
{
method: method.toUpperCase(),
url: url,
errorMessage: error?.message,
errorType: error?.type,
httpStatus: error?.response?.status,
httpStatusText: error?.response?.statusText,
responseRetCode: error?.response?.data?.retCode,
responseRetMsg: error?.response?.data?.retMsg,
responseData: error?.response?.data,
timestamp: new Date().toISOString(),
}
);
if (DEBUG_MODE)
console.error(
`%c[${tag}] ❌ RESPONSE ERROR - ${method.toUpperCase()} ${url}`,
'background: #F44336; color: white; font-weight: bold; padding: 3px;',
{
method: method.toUpperCase(),
url: url,
errorMessage: error?.message,
errorType: error?.type,
httpStatus: error?.response?.status,
httpStatusText: error?.response?.statusText,
responseRetCode: error?.response?.data?.retCode,
responseRetMsg: error?.response?.data?.retMsg,
responseData: error?.response?.data,
timestamp: new Date().toISOString(),
}
);
onFailExtra(props, dispatch, getState, error);
};
@@ -100,14 +106,14 @@ const createGetThunk = ({ url, type, params = () => ({}), tag }) =>
export const getBestSeller = (callback) => (dispatch, getState) => {
const onSuccess = (response) => {
console.log('getBestSeller onSuccess', response.data);
if (DEBUG_MODE) console.log('getBestSeller onSuccess', response.data);
dispatch({ type: types.GET_BEST_SELLER, payload: get('data.data', response) });
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
callback && callback();
};
const onFail = (error) => {
console.error('getBestSeller onFail', error);
if (DEBUG_MODE) console.error('getBestSeller onFail', error);
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
callback && callback();
};

View File

@@ -120,7 +120,7 @@ export const TAxios = (
if (axiosInstance) {
axiosInstance
.then((res) => {
console.log('TAxios response', url, res);
// console.log('TAxios response', url, res);
const apiSysStatus = res.headers['api-sys-status'];
const apiSysMessage = res.headers['api-sys-message'];

View File

@@ -1,238 +1,239 @@
/**
* src/utils/panelUtils.js
* Panel 관련 유틸리티 함수들
*
* MainView의 Panel Stack 관리 로직을 기반으로
* 정확한 isOnTop 상태 계산을 제공합니다.
*/
import { panel_names } from './Config';
// DEBUG_MODE - true인 경우에만 로그 출력
const DEBUG_MODE = true;
/**
* 🎯 [isOnTop 계산] MainView의 Panel Stack 기반으로 특정 패널의 isOnTop 상태 계산
*
* @param {Array} panels - Redux store의 panels 배열
* @param {string} targetPanelName - isOnTop 상태를 확인할 패널 이름
* @returns {boolean} 해당 패널이 현재 최상위에 있는지 여부
*/
export const calculateIsPanelOnTop = (panels, targetPanelName) => {
if (!panels || panels.length === 0 || !targetPanelName) {
if (DEBUG_MODE) {
console.log('[PanelUtils] calculateIsPanelOnTop: Invalid input', {
panelsLength: panels?.length || 0,
targetPanelName,
});
}
return false;
}
// MainView의 renderTopPanel 로직과 동일한 방식으로 isOnTop 계산
let renderingPanels = [];
// 3-layer 구조 체크: PlayerPanel + DetailPanel + MediaPanel(modal)
const hasThreeLayerStructure =
panels.length >= 3 &&
// PlayerPanel이 맨 아래
(panels[panels.length - 3]?.name === panel_names.PLAYER_PANEL ||
panels[panels.length - 3]?.name === panel_names.PLAYER_PANEL_NEW) &&
// DetailPanel이 중간
panels[panels.length - 2]?.name === panel_names.DETAIL_PANEL &&
// MediaPanel modal이 맨 위
panels[panels.length - 1]?.name === panel_names.MEDIA_PANEL &&
panels[panels.length - 1]?.panelInfo?.modal === true;
if (hasThreeLayerStructure) {
renderingPanels = panels.slice(-3);
if (DEBUG_MODE) {
console.log('[PanelUtils] 3-layer structure detected', {
renderingPanels: renderingPanels.map(p => p.name),
});
}
} else if (
panels[panels.length - 1]?.name === panel_names.PLAYER_PANEL ||
panels[panels.length - 1]?.name === panel_names.PLAYER_PANEL_NEW ||
panels[panels.length - 1]?.name === panel_names.MEDIA_PANEL ||
panels[panels.length - 2]?.name === panel_names.PLAYER_PANEL ||
panels[panels.length - 2]?.name === panel_names.MEDIA_PANEL
) {
renderingPanels = panels.slice(-2);
} else {
renderingPanels = panels.slice(-1);
}
// 각 패널의 isOnTop 상태 계산 (MainView 로직과 동일)
for (let i = 0; i < renderingPanels.length; i++) {
const panel = renderingPanels[i];
let isPanelOnTop = false;
// 3-layer 케이스: 중간 패널(DetailPanel)이 onTop
if (renderingPanels.length === 3) {
if (i === 1) {
// DetailPanel (중간)
isPanelOnTop = true;
}
// PlayerPanel (index 0): isOnTop = false (백그라운드)
// MediaPanel (index 2): isOnTop = false (modal overlay)
}
// 2-layer 케이스: modal이면 첫 번째가 onTop
else if (
i === 0 &&
renderingPanels.length === 2 &&
(renderingPanels[1].name === panel_names.PLAYER_PANEL ||
renderingPanels[1].name === panel_names.PLAYER_PANEL_NEW ||
renderingPanels[1].name === panel_names.MEDIA_PANEL) &&
renderingPanels[1].panelInfo.modal
) {
isPanelOnTop = true;
}
// 일반 케이스: 마지막 패널이 onTop
else if (i === renderingPanels.length - 1) {
isPanelOnTop = true;
}
if (panel.name === targetPanelName && isPanelOnTop) {
if (DEBUG_MODE) {
console.log('[PanelUtils] calculateIsPanelOnTop: Panel is on top', {
targetPanelName,
panelsLength: panels.length,
renderingPanels: renderingPanels.map(p => p.name),
foundAt: i,
});
}
return true;
}
}
if (DEBUG_MODE) {
console.log('[PanelUtils] calculateIsPanelOnTop: Panel is NOT on top', {
targetPanelName,
panelsLength: panels.length,
renderingPanels: renderingPanels.map(p => p.name),
});
}
return false;
};
/**
* 🎯 [현재 최상위 패널] 현재 렌더링되는 패널 중 최상위 패널 이름 반환
*
* @param {Array} panels - Redux store의 panels 배열
* @returns {string|null} 최상위 패널 이름
*/
export const getTopPanelName = (panels) => {
if (!panels || panels.length === 0) {
return null;
}
// MainView의 topPanelName 로직과 동일
let targetName = panels[panels.length - 1].name;
if (
(panels[panels.length - 1].name === panel_names.PLAYER_PANEL ||
panels[panels.length - 1].name === panel_names.MEDIA_PANEL) &&
panels[panels.length - 1].panelInfo.modal
) {
targetName = panels[panels.length - 2]?.name;
}
return targetName;
};
/**
* 🎯 [패널 렌더링 정보] 현재 렌더링되는 패널들의 상세 정보 반환
*
* @param {Array} panels - Redux store의 panels 배열
* @returns {Object} 렌더링 패널 정보 객체
*/
export const getRenderingPanelsInfo = (panels) => {
if (!panels || panels.length === 0) {
return {
renderingPanels: [],
topPanelName: null,
isThreeLayerStructure: false,
isTwoLayerModalStructure: false,
};
}
let renderingPanels = [];
let isThreeLayerStructure = false;
let isTwoLayerModalStructure = false;
// 3-layer 구조 체크
if (
panels.length >= 3 &&
(panels[panels.length - 3]?.name === panel_names.PLAYER_PANEL ||
panels[panels.length - 3]?.name === panel_names.PLAYER_PANEL_NEW) &&
panels[panels.length - 2]?.name === panel_names.DETAIL_PANEL &&
panels[panels.length - 1]?.name === panel_names.MEDIA_PANEL &&
panels[panels.length - 1]?.panelInfo?.modal === true
) {
isThreeLayerStructure = true;
renderingPanels = panels.slice(-3);
} else if (
panels[panels.length - 1]?.name === panel_names.PLAYER_PANEL ||
panels[panels.length - 1]?.name === panel_names.PLAYER_PANEL_NEW ||
panels[panels.length - 1]?.name === panel_names.MEDIA_PANEL ||
panels[panels.length - 2]?.name === panel_names.PLAYER_PANEL ||
panels[panels.length - 2]?.name === panel_names.MEDIA_PANEL
) {
renderingPanels = panels.slice(-2);
isTwoLayerModalStructure = true;
} else {
renderingPanels = panels.slice(-1);
}
const topPanelName = getTopPanelName(panels);
return {
renderingPanels,
topPanelName,
isThreeLayerStructure,
isTwoLayerModalStructure,
};
};
/**
* 🎯 [디버그 정보] Panel stack 상태에 대한 상세 디버그 정보 반환
*
* @param {Array} panels - Redux store의 panels 배열
* @param {string} targetPanelName - 확인할 패널 이름 (선택사항)
* @returns {Object} 디버그 정보 객체
*/
export const getPanelStackDebugInfo = (panels, targetPanelName = null) => {
const renderingInfo = getRenderingPanelsInfo(panels);
const topPanelName = renderingInfo.topPanelName;
const isTargetOnTop = targetPanelName ? calculateIsPanelOnTop(panels, targetPanelName) : null;
return {
// 기본 정보
panelsCount: panels?.length || 0,
topPanelName,
// 렌더링 정보
renderingPanels: renderingInfo.renderingPanels.map(p => p.name),
isThreeLayerStructure: renderingInfo.isThreeLayerStructure,
isTwoLayerModalStructure: renderingInfo.isTwoLayerModalStructure,
// 타겟 패널 정보
targetPanelName,
isTargetOnTop,
// 전체 패널 목록
allPanels: panels?.map(p => ({
name: p.name,
hasModal: !!p.panelInfo?.modal,
})) || [],
timestamp: new Date().toISOString(),
};
};
export default {
calculateIsPanelOnTop,
getTopPanelName,
getRenderingPanelsInfo,
getPanelStackDebugInfo,
};
/**
* src/utils/panelUtils.js
* Panel 관련 유틸리티 함수들
*
* MainView의 Panel Stack 관리 로직을 기반으로
* 정확한 isOnTop 상태 계산을 제공합니다.
*/
import { panel_names } from './Config';
// DEBUG_MODE - true인 경우에만 로그 출력
const DEBUG_MODE = false;
/**
* 🎯 [isOnTop 계산] MainView의 Panel Stack 기반으로 특정 패널의 isOnTop 상태 계산
*
* @param {Array} panels - Redux store의 panels 배열
* @param {string} targetPanelName - isOnTop 상태를 확인할 패널 이름
* @returns {boolean} 해당 패널이 현재 최상위에 있는지 여부
*/
export const calculateIsPanelOnTop = (panels, targetPanelName) => {
if (!panels || panels.length === 0 || !targetPanelName) {
if (DEBUG_MODE) {
console.log('[PanelUtils] calculateIsPanelOnTop: Invalid input', {
panelsLength: panels?.length || 0,
targetPanelName,
});
}
return false;
}
// MainView의 renderTopPanel 로직과 동일한 방식으로 isOnTop 계산
let renderingPanels = [];
// 3-layer 구조 체크: PlayerPanel + DetailPanel + MediaPanel(modal)
const hasThreeLayerStructure =
panels.length >= 3 &&
// PlayerPanel이 맨 아래
(panels[panels.length - 3]?.name === panel_names.PLAYER_PANEL ||
panels[panels.length - 3]?.name === panel_names.PLAYER_PANEL_NEW) &&
// DetailPanel이 중간
panels[panels.length - 2]?.name === panel_names.DETAIL_PANEL &&
// MediaPanel modal이 맨 위
panels[panels.length - 1]?.name === panel_names.MEDIA_PANEL &&
panels[panels.length - 1]?.panelInfo?.modal === true;
if (hasThreeLayerStructure) {
renderingPanels = panels.slice(-3);
if (DEBUG_MODE) {
console.log('[PanelUtils] 3-layer structure detected', {
renderingPanels: renderingPanels.map((p) => p.name),
});
}
} else if (
panels[panels.length - 1]?.name === panel_names.PLAYER_PANEL ||
panels[panels.length - 1]?.name === panel_names.PLAYER_PANEL_NEW ||
panels[panels.length - 1]?.name === panel_names.MEDIA_PANEL ||
panels[panels.length - 2]?.name === panel_names.PLAYER_PANEL ||
panels[panels.length - 2]?.name === panel_names.MEDIA_PANEL
) {
renderingPanels = panels.slice(-2);
} else {
renderingPanels = panels.slice(-1);
}
// 각 패널의 isOnTop 상태 계산 (MainView 로직과 동일)
for (let i = 0; i < renderingPanels.length; i++) {
const panel = renderingPanels[i];
let isPanelOnTop = false;
// 3-layer 케이스: 중간 패널(DetailPanel)이 onTop
if (renderingPanels.length === 3) {
if (i === 1) {
// DetailPanel (중간)
isPanelOnTop = true;
}
// PlayerPanel (index 0): isOnTop = false (백그라운드)
// MediaPanel (index 2): isOnTop = false (modal overlay)
}
// 2-layer 케이스: modal이면 첫 번째가 onTop
else if (
i === 0 &&
renderingPanels.length === 2 &&
(renderingPanels[1].name === panel_names.PLAYER_PANEL ||
renderingPanels[1].name === panel_names.PLAYER_PANEL_NEW ||
renderingPanels[1].name === panel_names.MEDIA_PANEL) &&
renderingPanels[1].panelInfo.modal
) {
isPanelOnTop = true;
}
// 일반 케이스: 마지막 패널이 onTop
else if (i === renderingPanels.length - 1) {
isPanelOnTop = true;
}
if (panel.name === targetPanelName && isPanelOnTop) {
if (DEBUG_MODE) {
console.log('[PanelUtils] calculateIsPanelOnTop: Panel is on top', {
targetPanelName,
panelsLength: panels.length,
renderingPanels: renderingPanels.map((p) => p.name),
foundAt: i,
});
}
return true;
}
}
if (DEBUG_MODE) {
console.log('[PanelUtils] calculateIsPanelOnTop: Panel is NOT on top', {
targetPanelName,
panelsLength: panels.length,
renderingPanels: renderingPanels.map((p) => p.name),
});
}
return false;
};
/**
* 🎯 [현재 최상위 패널] 현재 렌더링되는 패널 중 최상위 패널 이름 반환
*
* @param {Array} panels - Redux store의 panels 배열
* @returns {string|null} 최상위 패널 이름
*/
export const getTopPanelName = (panels) => {
if (!panels || panels.length === 0) {
return null;
}
// MainView의 topPanelName 로직과 동일
let targetName = panels[panels.length - 1].name;
if (
(panels[panels.length - 1].name === panel_names.PLAYER_PANEL ||
panels[panels.length - 1].name === panel_names.MEDIA_PANEL) &&
panels[panels.length - 1].panelInfo.modal
) {
targetName = panels[panels.length - 2]?.name;
}
return targetName;
};
/**
* 🎯 [패널 렌더링 정보] 현재 렌더링되는 패널들의 상세 정보 반환
*
* @param {Array} panels - Redux store의 panels 배열
* @returns {Object} 렌더링 패널 정보 객체
*/
export const getRenderingPanelsInfo = (panels) => {
if (!panels || panels.length === 0) {
return {
renderingPanels: [],
topPanelName: null,
isThreeLayerStructure: false,
isTwoLayerModalStructure: false,
};
}
let renderingPanels = [];
let isThreeLayerStructure = false;
let isTwoLayerModalStructure = false;
// 3-layer 구조 체크
if (
panels.length >= 3 &&
(panels[panels.length - 3]?.name === panel_names.PLAYER_PANEL ||
panels[panels.length - 3]?.name === panel_names.PLAYER_PANEL_NEW) &&
panels[panels.length - 2]?.name === panel_names.DETAIL_PANEL &&
panels[panels.length - 1]?.name === panel_names.MEDIA_PANEL &&
panels[panels.length - 1]?.panelInfo?.modal === true
) {
isThreeLayerStructure = true;
renderingPanels = panels.slice(-3);
} else if (
panels[panels.length - 1]?.name === panel_names.PLAYER_PANEL ||
panels[panels.length - 1]?.name === panel_names.PLAYER_PANEL_NEW ||
panels[panels.length - 1]?.name === panel_names.MEDIA_PANEL ||
panels[panels.length - 2]?.name === panel_names.PLAYER_PANEL ||
panels[panels.length - 2]?.name === panel_names.MEDIA_PANEL
) {
renderingPanels = panels.slice(-2);
isTwoLayerModalStructure = true;
} else {
renderingPanels = panels.slice(-1);
}
const topPanelName = getTopPanelName(panels);
return {
renderingPanels,
topPanelName,
isThreeLayerStructure,
isTwoLayerModalStructure,
};
};
/**
* 🎯 [디버그 정보] Panel stack 상태에 대한 상세 디버그 정보 반환
*
* @param {Array} panels - Redux store의 panels 배열
* @param {string} targetPanelName - 확인할 패널 이름 (선택사항)
* @returns {Object} 디버그 정보 객체
*/
export const getPanelStackDebugInfo = (panels, targetPanelName = null) => {
const renderingInfo = getRenderingPanelsInfo(panels);
const topPanelName = renderingInfo.topPanelName;
const isTargetOnTop = targetPanelName ? calculateIsPanelOnTop(panels, targetPanelName) : null;
return {
// 기본 정보
panelsCount: panels?.length || 0,
topPanelName,
// 렌더링 정보
renderingPanels: renderingInfo.renderingPanels.map((p) => p.name),
isThreeLayerStructure: renderingInfo.isThreeLayerStructure,
isTwoLayerModalStructure: renderingInfo.isTwoLayerModalStructure,
// 타겟 패널 정보
targetPanelName,
isTargetOnTop,
// 전체 패널 목록
allPanels:
panels?.map((p) => ({
name: p.name,
hasModal: !!p.panelInfo?.modal,
})) || [],
timestamp: new Date().toISOString(),
};
};
export default {
calculateIsPanelOnTop,
getTopPanelName,
getRenderingPanelsInfo,
getPanelStackDebugInfo,
};

View File

@@ -1,5 +1,8 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
// 디버그 모드 설정 - true일 때만 console.log 출력
const DEBUG_MODE = false;
import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
@@ -266,24 +269,28 @@ export default function RandomUnit({
// 포커스 인
const handleFocus = useCallback(() => {
// 🔽 [251116] 현재 비디오 상태 로그 출력
console.log('[RandomUnit] onFocus - 현재 비디오 상태', {
playback: videoPlayState.playback,
display: videoPlayState.display,
videoId: videoPlayState.videoId,
loadingProgress: videoPlayState.loadingProgress,
loadingError: videoPlayState.loadingError,
isPlaying: videoPlayState.isPlaying,
isPaused: videoPlayState.isPaused,
lastUpdate: videoPlayState.lastUpdate,
});
if (DEBUG_MODE) {
console.log('[RandomUnit] onFocus - 현재 비디오 상태', {
playback: videoPlayState.playback,
display: videoPlayState.display,
videoId: videoPlayState.videoId,
loadingProgress: videoPlayState.loadingProgress,
loadingError: videoPlayState.loadingError,
isPlaying: videoPlayState.isPlaying,
isPaused: videoPlayState.isPaused,
lastUpdate: videoPlayState.lastUpdate,
});
}
console.log('[RandomUnit] onFocus - 비디오 재생 의도 저장', {
bannerId: spotlightId,
videoPlayerable,
currentIntent: videoPlayIntentRef.current?.bannerId,
currentVideoBannerId,
randomData: randomData?.showId || 'no-show-id',
});
if (DEBUG_MODE) {
console.log('[RandomUnit] onFocus - 비디오 재생 의도 저장', {
bannerId: spotlightId,
videoPlayerable,
currentIntent: videoPlayIntentRef.current?.bannerId,
currentVideoBannerId,
randomData: randomData?.showId || 'no-show-id',
});
}
setIsFocused(true);
@@ -305,10 +312,12 @@ export default function RandomUnit({
timestamp: Date.now(),
};
console.log('[RandomUnit] 비디오 재생 의도 저장됨', {
intentBannerId: spotlightId,
videoUrl: randomData.showUrl,
});
if (DEBUG_MODE) {
console.log('[RandomUnit] 비디오 재생 의도 저장됨', {
intentBannerId: spotlightId,
videoUrl: randomData.showUrl,
});
}
setVideoIntentVersion((version) => version + 1);
@@ -320,10 +329,12 @@ export default function RandomUnit({
// video가 플레이 불가능한 경우: 재생 중인 비디오를 1px로 축소
if (!videoPlayerable && currentVideoBannerId && isCurrentBannerVideoPlaying) {
console.log('[RandomUnit] videoPlayerable=false, shrinking video to 1px', {
videoPlayerable,
currentVideoBannerId,
});
if (DEBUG_MODE) {
console.log('[RandomUnit] videoPlayerable=false, shrinking video to 1px', {
videoPlayerable,
currentVideoBannerId,
});
}
dispatch(hideModalVideo());
}
@@ -582,10 +593,12 @@ export default function RandomUnit({
// ✅ modal=true → modal=false로 전환 (또는 초기 로드 시 modal=false)
// playActions의 shouldSkipVideoPlayback이 modal 상태를 확인하므로
// 모드 전환은 중복 방지 로직을 스킵하여 정상 작동
console.log(
'[RandomUnit] videoClick: current playerPanel modal state:',
playerPanelInfo?.modal
);
if (DEBUG_MODE) {
console.log(
'[RandomUnit] videoClick: current playerPanel modal state:',
playerPanelInfo?.modal
);
}
handleStartVideo({
bannerId: spotlightId,
@@ -739,17 +752,19 @@ export default function RandomUnit({
const currentVideoId = videoPlayState.videoId;
const activeBannerId = currentVideoBannerId;
console.log('[RandomUnit] videoPlayIntent useEffect', {
intentBannerId: intent.bannerId,
currentVideoId,
activeBannerId,
currentPlayback: videoPlayState.playback,
currentDisplay: videoPlayState.display,
loadingProgress: videoPlayState.loadingProgress,
shouldStart: intent.bannerId !== activeBannerId,
isVideoTransitionLocked,
timestamp: Date.now(),
});
if (DEBUG_MODE) {
console.log('[RandomUnit] videoPlayIntent useEffect', {
intentBannerId: intent.bannerId,
currentVideoId,
activeBannerId,
currentPlayback: videoPlayState.playback,
currentDisplay: videoPlayState.display,
loadingProgress: videoPlayState.loadingProgress,
shouldStart: intent.bannerId !== activeBannerId,
isVideoTransitionLocked,
timestamp: Date.now(),
});
}
if (intent.bannerId === activeBannerId) {
return undefined;
@@ -764,10 +779,12 @@ export default function RandomUnit({
return;
}
console.log('[RandomUnit] 비디오 변경 실행', {
from: currentVideoId,
to: intent.bannerId,
});
if (DEBUG_MODE) {
console.log('[RandomUnit] 비디오 변경 실행', {
from: currentVideoId,
to: intent.bannerId,
});
}
if (activeBannerId) {
dispatch(finishVideoPreview());

View File

@@ -1,9 +1,9 @@
import React, { memo, useCallback } from "react";
import React, { memo, useCallback } from 'react';
import ReactPlayer from "react-player";
import ReactPlayer from 'react-player';
import VideoPlayer from "../../../../components/VideoPlayer/VideoPlayer";
import { scaleH, scaleW } from "../../../../utils/helperMethods";
import VideoPlayer from '../../../../components/VideoPlayer/VideoPlayer';
import { scaleH, scaleW } from '../../../../utils/helperMethods';
export default memo(function PopularVideoPlayer({
className,
@@ -20,16 +20,16 @@ export default memo(function PopularVideoPlayer({
const mediainfoHandler = useCallback((e) => {
const type = e.type;
if (type !== "timeupdate") {
console.log("@@ [mediaInfoHandler].....", type);
if (type !== 'timeupdate') {
console.log('[mediaInfoHandler].....', type);
}
switch (type) {
case "loadeddata":
console.log("@@ [mediaInfoHandler] loaded data");
case 'loadeddata':
console.log('[mediaInfoHandler] loaded data');
break;
case "durationchange":
console.log("@@ [mediaInfoHandler] duration", e.currentTarget.duration);
case 'durationchange':
console.log('[mediaInfoHandler] duration', e.currentTarget.duration);
break;
}
}, []);
@@ -52,7 +52,7 @@ export default memo(function PopularVideoPlayer({
className={className}
onReady={handleReady}
playing={shouldPlay}
url={shouldPlay ? src : ""}
url={shouldPlay ? src : ''}
width={scaleW(width)}
height={scaleH(height)}
/>