[251122] fix: Comment정리-1

🕐 커밋 시간: 2025. 11. 22. 18:19:45

📊 변경 통계:
  • 총 파일: 10개
  • 추가: +150줄
  • 삭제: -152줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/App/App.js
  ~ com.twin.app.shoptime/src/actions/panelActions.js
  ~ com.twin.app.shoptime/src/middleware/panelHistoryMiddleware.js
  ~ com.twin.app.shoptime/src/utils/ImagePreloader.js
  ~ com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx
  ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx
  ~ com.twin.app.shoptime/src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.jsx
  ~ com.twin.app.shoptime/src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.v2.jsx
  ~ com.twin.app.shoptime/src/views/DetailPanel/components/FavoriteBtn.jsx
  ~ com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx

🔧 함수 변경 내용:
  📄 com.twin.app.shoptime/src/App/App.js (javascript):
     Deleted: resolveSpotlightIdFromEvent()
  📄 com.twin.app.shoptime/src/actions/panelActions.js (javascript):
    🔄 Modified: resetPanels()
  📄 com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx (javascript):
    🔄 Modified: extractProductMeta()
  📄 com.twin.app.shoptime/src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.v2.jsx (javascript):
     Deleted: logDetailPanelInit(), logImageLoaded(), logImageError()
  📄 com.twin.app.shoptime/src/views/DetailPanel/components/FavoriteBtn.jsx (javascript):
    🔄 Modified: Spottable()

🔧 주요 변경 내용:
  • 핵심 비즈니스 로직 개선
  • 공통 유틸리티 함수 최적화
  • UI 컴포넌트 아키텍처 개선

Performance: 코드 최적화로 성능 개선 기대
This commit is contained in:
2025-11-22 18:19:45 +09:00
parent 9878c39512
commit 251e1ee3d4
10 changed files with 304 additions and 301 deletions

View File

@@ -424,20 +424,20 @@ const resolveSpotlightIdFromEvent = (event) => {
// Spotlight Focus 추적 로그 [251115] // Spotlight Focus 추적 로그 [251115]
// DOM 이벤트 리스너로 대체 // DOM 이벤트 리스너로 대체
document.addEventListener('focusin', (ev) => { // document.addEventListener('focusin', (ev) => {
console.log('[SPOTLIGHT FOCUS-IN]', ev.target); // console.log('[SPOTLIGHT FOCUS-IN]', ev.target);
}); // });
document.addEventListener('focusout', (ev) => { // document.addEventListener('focusout', (ev) => {
console.log('[SPOTLIGHT FOCUS-OUT]', ev.target); // console.log('[SPOTLIGHT FOCUS-OUT]', ev.target);
}); // });
// Spotlight 커스텀 이벤트가 있다면 추가 // // Spotlight 커스텀 이벤트가 있다면 추가
if (typeof Spotlight !== 'undefined' && Spotlight.addEventListener) { // if (typeof Spotlight !== 'undefined' && Spotlight.addEventListener) {
Spotlight.addEventListener('focus', (ev) => { // Spotlight.addEventListener('focus', (ev) => {
console.log('[SPOTLIGHT: focus]', ev.target); // console.log('[SPOTLIGHT: focus]', ev.target);
}); // });
} // }
function AppBase(props) { function AppBase(props) {
const dispatch = useDispatch(); const dispatch = useDispatch();

View File

@@ -17,8 +17,8 @@ export const SOURCE_MENUS = {
HOME_GENERAL: 'home_general', HOME_GENERAL: 'home_general',
THEMED_PRODUCT: 'themed_product', THEMED_PRODUCT: 'themed_product',
GENERAL_PRODUCT: 'general_product', GENERAL_PRODUCT: 'general_product',
PLAYER_SHOP_NOW: 'player_shop_now', // PlayerPanel의 ShopNow에서 진입 PLAYER_SHOP_NOW: 'player_shop_now', // PlayerPanel의 ShopNow에서 진입
PLAYER_MEDIA: 'player_media', // PlayerPanel의 Media에서 진입 PLAYER_MEDIA: 'player_media', // PlayerPanel의 Media에서 진입
}; };
/* /*
@@ -117,19 +117,22 @@ export const navigateToDetail = ({
case SOURCE_MENUS.HOME_RANDOM_UNIT: case SOURCE_MENUS.HOME_RANDOM_UNIT:
case SOURCE_MENUS.HOME_ROLLING_UNIT: case SOURCE_MENUS.HOME_ROLLING_UNIT:
case SOURCE_MENUS.HOME_GENERAL: { case SOURCE_MENUS.HOME_GENERAL: {
// ✅ 그라데이션 배경 표시 - HomePanel→DetailPanel 전환 시 (PlayerPanel 출신 제외) // ✅ 그라데이션 배경 표시 - HomePanel→DetailPanel 전환 시 (PlayerPanel 출신 제외)
if (!panelInfo.launchedFromPlayer) { if (!panelInfo.launchedFromPlayer) {
dispatch(updateHomeInfo({ dispatch(
name: panel_names.HOME_PANEL, updateHomeInfo({
panelInfo: { name: panel_names.HOME_PANEL,
showGradientBackground: true, panelInfo: {
} showGradientBackground: true,
})); },
console.log('[TRACE-GRADIENT] 🟢 navigateToDetail set showGradientBackground: true - source:', sourceMenu); })
);
// console.log('[TRACE-GRADIENT] 🟢 navigateToDetail set showGradientBackground: true - source:', sourceMenu);
} else { } else {
console.log('[TRACE-GRADIENT] 🔵 navigateToDetail skipped gradient - launchedFromPlayer: true'); console.log(
'[TRACE-GRADIENT] 🔵 navigateToDetail skipped gradient - launchedFromPlayer: true'
);
} }
// HomePanel Redux 상태에 포커스 스냅샷 저장 (Detail→Home 복귀 시 사용) // HomePanel Redux 상태에 포커스 스냅샷 저장 (Detail→Home 복귀 시 사용)
@@ -228,7 +231,7 @@ export const navigateToDetail = ({
}, },
}) })
); );
panelInfo.sourcePanel = panel_names.HOME_PANEL; // ✅ source panel 정보 panelInfo.sourcePanel = panel_names.HOME_PANEL; // ✅ source panel 정보
panelInfo.fromHome = true; panelInfo.fromHome = true;
break; break;
} }
@@ -247,14 +250,14 @@ export const navigateToDetail = ({
}) })
); );
} }
panelInfo.sourcePanel = panel_names.SEARCH_PANEL; // ✅ source panel 정보 panelInfo.sourcePanel = panel_names.SEARCH_PANEL; // ✅ source panel 정보
panelInfo.fromSearch = true; panelInfo.fromSearch = true;
panelInfo.searchQuery = additionalInfo.searchVal; panelInfo.searchQuery = additionalInfo.searchVal;
break; break;
case SOURCE_MENUS.THEMED_PRODUCT: case SOURCE_MENUS.THEMED_PRODUCT:
// 테마 상품: 별도 처리 필요할 경우 // 테마 상품: 별도 처리 필요할 경우
panelInfo.sourcePanel = panel_names.HOME_PANEL; // ✅ source panel 정보 (HOME으로 간주) panelInfo.sourcePanel = panel_names.HOME_PANEL; // ✅ source panel 정보 (HOME으로 간주)
break; break;
case SOURCE_MENUS.PLAYER_SHOP_NOW: case SOURCE_MENUS.PLAYER_SHOP_NOW:
@@ -271,7 +274,7 @@ export const navigateToDetail = ({
} }
// PlayerPanel 정보 보존 (복귀 시 필요) // PlayerPanel 정보 보존 (복귀 시 필요)
panelInfo.sourcePanel = panel_names.PLAYER_PANEL; // ✅ source panel 정보 panelInfo.sourcePanel = panel_names.PLAYER_PANEL; // ✅ source panel 정보
panelInfo.fromPlayer = true; panelInfo.fromPlayer = true;
break; break;
} }

View File

@@ -4,6 +4,10 @@
* *
* Panel action (PUSH, POP, UPDATE, RESET)을 감지하고 * Panel action (PUSH, POP, UPDATE, RESET)을 감지하고
* 자동으로 panel history에 기록 * 자동으로 panel history에 기록
*
* ⚠️ [251122] DEBUG_MODE = false로 설정되어 모든 로그 출력 비활성화됨
* - 로그가 필요하면 DEBUG_MODE를 true로 변경하면 됨
* - middleware 동작 자체는 영향받지 않음 (로그만 출력 안됨)
*/ */
import { types } from '../actions/actionTypes'; import { types } from '../actions/actionTypes';
@@ -11,7 +15,8 @@ import { enqueuePanelHistory, clearPanelHistory } from '../actions/panelHistoryA
import { calculateIsPanelOnTop } from '../utils/panelUtils'; // 🎯 isOnTop 유틸리티 함수 import import { calculateIsPanelOnTop } from '../utils/panelUtils'; // 🎯 isOnTop 유틸리티 함수 import
// DEBUG_MODE - true인 경우에만 로그 출력 // DEBUG_MODE - true인 경우에만 로그 출력
const DEBUG_MODE = true; // ⚠️ [251122] panelHistory 로그 비활성화 - 로그 생성 차단
const DEBUG_MODE = false;
/** /**
* Panel history middleware * Panel history middleware

View File

@@ -3,6 +3,10 @@
* HomePanel에서 백그라운드로 DetailPanelBackground 이미지들을 미리 로드하여 * HomePanel에서 백그라운드로 DetailPanelBackground 이미지들을 미리 로드하여
* DetailPanel 진입 시 로딩 지연을 방지 * DetailPanel 진입 시 로딩 지연을 방지
*/ */
// DEBUG_MODE - true인 경우에만 로그 출력
const DEBUG_MODE = false;
class ImagePreloader { class ImagePreloader {
constructor() { constructor() {
this.cache = new Map(); // 로드된 이미지 캐시 this.cache = new Map(); // 로드된 이미지 캐시
@@ -33,13 +37,13 @@ class ImagePreloader {
img.onload = () => { img.onload = () => {
this.cache.set(src, img); this.cache.set(src, img);
this.loadPromises.delete(src); this.loadPromises.delete(src);
console.log(`[ImagePreloader] Image loaded: ${src}`); if (DEBUG_MODE) console.log(`[ImagePreloader] Image loaded: ${src}`);
resolve(img); resolve(img);
}; };
img.onerror = () => { img.onerror = () => {
this.loadPromises.delete(src); this.loadPromises.delete(src);
console.error(`[ImagePreloader] Failed to load: ${src}`); if (DEBUG_MODE) console.error(`[ImagePreloader] Failed to load: ${src}`);
reject(new Error(`Failed to load image: ${src}`)); reject(new Error(`Failed to load image: ${src}`));
}; };
@@ -58,17 +62,17 @@ class ImagePreloader {
*/ */
preloadAllImages(imageMap) { preloadAllImages(imageMap) {
if (this.preloadStarted) { if (this.preloadStarted) {
console.log('[ImagePreloader] Preloading already started'); if (DEBUG_MODE) console.log('[ImagePreloader] Preloading already started');
return Promise.resolve([]); return Promise.resolve([]);
} }
this.preloadStarted = true; this.preloadStarted = true;
console.log('[ImagePreloader] Starting background preload...'); if (DEBUG_MODE) console.log('[ImagePreloader] Starting background preload...');
const promises = Object.values(imageMap).map(src => const promises = Object.values(imageMap).map((src) =>
this.preloadImage(src).catch(error => { this.preloadImage(src).catch((error) => {
// 개별 이미지 로드 실패 시 전체 작업을 중단하지 않음 // 개별 이미지 로드 실패 시 전체 작업을 중단하지 않음
console.warn('[ImagePreloader] Single image load failed:', error.message); if (DEBUG_MODE) console.warn('[ImagePreloader] Single image load failed:', error.message);
return null; return null;
}) })
); );
@@ -102,7 +106,7 @@ class ImagePreloader {
return { return {
cached: this.cache.size, cached: this.cache.size,
loading: this.loadPromises.size, loading: this.loadPromises.size,
preloadStarted: this.preloadStarted preloadStarted: this.preloadStarted,
}; };
} }

View File

@@ -1,32 +1,16 @@
// src/views/DetailPanel/DetailPanel.new.jsx // src/views/DetailPanel/DetailPanel.new.jsx
import React, { import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
useCallback,
useEffect,
useLayoutEffect,
useMemo,
useRef,
useState,
} from 'react';
import { import { useDispatch, useSelector } from 'react-redux';
useDispatch,
useSelector,
} from 'react-redux';
import Spotlight from '@enact/spotlight'; import Spotlight from '@enact/spotlight';
import { setContainerLastFocusedElement } from '@enact/spotlight/src/container'; import { setContainerLastFocusedElement } from '@enact/spotlight/src/container';
import { getDeviceAdditionInfo } from '../../actions/deviceActions'; import { getDeviceAdditionInfo } from '../../actions/deviceActions';
import { getThemeCurationDetailInfo, updateHomeInfo } from '../../actions/homeActions'; import { getThemeCurationDetailInfo, updateHomeInfo } from '../../actions/homeActions';
import { import { getMainCategoryDetail, getMainYouMayLike } from '../../actions/mainActions';
getMainCategoryDetail,
getMainYouMayLike,
} from '../../actions/mainActions';
import { finishModalMediaForce } from '../../actions/mediaActions'; import { finishModalMediaForce } from '../../actions/mediaActions';
import { import { popPanel, updatePanel } from '../../actions/panelActions';
popPanel,
updatePanel,
} from '../../actions/panelActions';
import { import {
finishVideoPreview, finishVideoPreview,
pauseFullscreenVideo, pauseFullscreenVideo,
@@ -34,10 +18,7 @@ import {
pauseModalVideo, pauseModalVideo,
resumeModalVideo, resumeModalVideo,
} from '../../actions/playActions'; } from '../../actions/playActions';
import { import { clearProductDetail, getProductOptionId } from '../../actions/productActions';
clearProductDetail,
getProductOptionId,
} from '../../actions/productActions';
import { clearAllToasts } from '../../actions/toastActions'; import { clearAllToasts } from '../../actions/toastActions';
import TBody from '../../components/TBody/TBody'; import TBody from '../../components/TBody/TBody';
import TPanel from '../../components/TPanel/TPanel'; import TPanel from '../../components/TPanel/TPanel';
@@ -152,13 +133,15 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
// ✅ DetailPanelBackground 이미지 완전 렌더링 후 그라데이션 배경 숨기기 // ✅ DetailPanelBackground 이미지 완전 렌더링 후 그라데이션 배경 숨기기
const handleBackgroundImageReady = useCallback(() => { const handleBackgroundImageReady = useCallback(() => {
console.log('[TRACE-GRADIENT] ✅ DetailPanel - BackgroundImage fully rendered, hiding gradient'); // console.log('[TRACE-GRADIENT] ✅ DetailPanel - BackgroundImage fully rendered, hiding gradient');
dispatch(updateHomeInfo({ dispatch(
name: panel_names.HOME_PANEL, updateHomeInfo({
panelInfo: { name: panel_names.HOME_PANEL,
showGradientBackground: false, panelInfo: {
} showGradientBackground: false,
})); },
})
);
}, [dispatch]); }, [dispatch]);
// ✅ [251120] DetailPanel이 사라질 때 처리 - sourcePanel에 따라 switch 문으로 처리 // ✅ [251120] DetailPanel이 사라질 때 처리 - sourcePanel에 따라 switch 문으로 처리
@@ -179,32 +162,36 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
case panel_names.PLAYER_PANEL: { case panel_names.PLAYER_PANEL: {
// PlayerPanel에서 온 경우: PlayerPanel에 detailPanelClosed flag 전달 // PlayerPanel에서 온 경우: PlayerPanel에 detailPanelClosed flag 전달
console.log('[DetailPanel] unmount - PlayerPanel에 detailPanelClosed flag 전달'); console.log('[DetailPanel] unmount - PlayerPanel에 detailPanelClosed flag 전달');
dispatch(updatePanel({ dispatch(
name: panel_names.PLAYER_PANEL, updatePanel({
panelInfo: { name: panel_names.PLAYER_PANEL,
detailPanelClosed: true, // ✅ flag panelInfo: {
detailPanelClosedAt: Date.now(), // ✅ 시점 기록 detailPanelClosed: true, // ✅ flag
detailPanelClosedFromSource: sourceMenu, // ✅ 출처 detailPanelClosedAt: Date.now(), // ✅ 시점 기록
lastFocusedTargetId: panelInfo?.lastFocusedTargetId, // ✅ 포커스 복원 타겟 전달 detailPanelClosedFromSource: sourceMenu, // ✅ 출처
} lastFocusedTargetId: panelInfo?.lastFocusedTargetId, // ✅ 포커스 복원 타겟 전달
})); },
})
);
break; break;
} }
case panel_names.HOME_PANEL: { case panel_names.HOME_PANEL: {
// HomePanel에서 온 경우: HomePanel에 detailPanelClosed flag 전달 // HomePanel에서 온 경우: HomePanel에 detailPanelClosed flag 전달
console.log('[DetailPanel] unmount - HomePanel에 detailPanelClosed flag 전달'); // console.log('[DetailPanel] unmount - HomePanel에 detailPanelClosed flag 전달');
console.log('[TRACE-GRADIENT] 🔶 DetailPanel unmount - HomePanel 복귀'); // console.log('[TRACE-GRADIENT] 🔶 DetailPanel unmount - HomePanel 복귀');
dispatch(updateHomeInfo({ dispatch(
name: panel_names.HOME_PANEL, updateHomeInfo({
panelInfo: { name: panel_names.HOME_PANEL,
detailPanelClosed: true, // ✅ flag panelInfo: {
detailPanelClosedAt: Date.now(), // ✅ 시점 기록 detailPanelClosed: true, // ✅ flag
detailPanelClosedFromSource: sourceMenu, // ✅ 출처 detailPanelClosedAt: Date.now(), // ✅ 시점 기록
showGradientBackground: false, // ✅ 명시적으로 그라데이션 끔기 detailPanelClosedFromSource: sourceMenu, // ✅ 출처
} showGradientBackground: false, // ✅ 명시적으로 그라데이션 끔기
})); },
})
);
break; break;
} }
@@ -215,8 +202,8 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
updatePanel({ updatePanel({
name: panel_names.SEARCH_PANEL, name: panel_names.SEARCH_PANEL,
panelInfo: { panelInfo: {
detailPanelClosed: true, // ✅ flag detailPanelClosed: true, // ✅ flag
detailPanelClosedAt: Date.now(), // ✅ 시점 기록 detailPanelClosedAt: Date.now(), // ✅ 시점 기록
detailPanelClosedFromSource: sourceMenu, // ✅ 출처 detailPanelClosedFromSource: sourceMenu, // ✅ 출처
}, },
}) })
@@ -253,7 +240,11 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
case panel_names.SEARCH_PANEL: case panel_names.SEARCH_PANEL:
default: default:
// HomePanel, SearchPanel 등에서 온 경우: 백그라운드 비디오 일시 중지 // HomePanel, SearchPanel 등에서 온 경우: 백그라운드 비디오 일시 중지
console.log('[DetailPanel] onBackClick - source panel:', sourcePanel, '백그라운드 비디오 일시 중지'); console.log(
'[DetailPanel] onBackClick - source panel:',
sourcePanel,
'백그라운드 비디오 일시 중지'
);
dispatch(pauseFullscreenVideo()); // PLAYER_PANEL 비디오 중지 dispatch(pauseFullscreenVideo()); // PLAYER_PANEL 비디오 중지
dispatch(finishModalMediaForce()); // MEDIA_PANEL(ProductVideo) 강제 종료 dispatch(finishModalMediaForce()); // MEDIA_PANEL(ProductVideo) 강제 종료
dispatch(finishVideoPreview()); dispatch(finishVideoPreview());
@@ -271,7 +262,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
fp.pipe( fp.pipe(
() => panels, () => panels,
fp.get('length'), fp.get('length'),
(length) => length === 3 // PlayerPanel이 [1]에 있고 DetailPanel이 [2]에 있는 상태 (length) => length === 3 // PlayerPanel이 [1]에 있고 DetailPanel이 [2]에 있는 상태
)() && )() &&
fp.pipe( fp.pipe(
() => panels, () => panels,
@@ -280,14 +271,16 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
)(); )();
if (shouldUpdatePanel) { if (shouldUpdatePanel) {
console.log('[DetailPanel] onBackClick - PlayerPanel에 detailPanelClosed flag 전달'); console.log(
'[DetailPanel] onBackClick - PlayerPanel에 detailPanelClosed flag 전달'
);
dispatch( dispatch(
updatePanel({ updatePanel({
name: panel_names.PLAYER_PANEL, name: panel_names.PLAYER_PANEL,
panelInfo: { panelInfo: {
thumbnail: fp.pipe(() => panelInfo, fp.get('thumbnailUrl'))(), thumbnail: fp.pipe(() => panelInfo, fp.get('thumbnailUrl'))(),
detailPanelClosed: true, // ✅ flag detailPanelClosed: true, // ✅ flag
detailPanelClosedAt: Date.now(), // ✅ 시점 기록 detailPanelClosedAt: Date.now(), // ✅ 시점 기록
detailPanelClosedFromSource: sourceMenu, // ✅ 출처 detailPanelClosedFromSource: sourceMenu, // ✅ 출처
lastFocusedTargetId: panelInfo?.lastFocusedTargetId, // ✅ 포커스 복원 타겟 전달 lastFocusedTargetId: panelInfo?.lastFocusedTargetId, // ✅ 포커스 복원 타겟 전달
}, },
@@ -300,15 +293,17 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
case panel_names.HOME_PANEL: { case panel_names.HOME_PANEL: {
// HomePanel에서 온 경우: HomePanel에 detailPanelClosed flag 전달 // HomePanel에서 온 경우: HomePanel에 detailPanelClosed flag 전달
console.log('[DetailPanel] onBackClick - HomePanel에 detailPanelClosed flag 전달'); console.log('[DetailPanel] onBackClick - HomePanel에 detailPanelClosed flag 전달');
dispatch(updateHomeInfo({ dispatch(
name: panel_names.HOME_PANEL, updateHomeInfo({
panelInfo: { name: panel_names.HOME_PANEL,
detailPanelClosed: true, // ✅ flag panelInfo: {
detailPanelClosedAt: Date.now(), // ✅ 시점 기록 detailPanelClosed: true, // ✅ flag
detailPanelClosedFromSource: sourceMenu, // ✅ 출처 detailPanelClosedAt: Date.now(), // ✅ 시점 기록
showGradientBackground: false, detailPanelClosedFromSource: sourceMenu, // ✅ 출처
} showGradientBackground: false,
})); },
})
);
break; break;
} }
@@ -319,8 +314,8 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
updatePanel({ updatePanel({
name: panel_names.SEARCH_PANEL, name: panel_names.SEARCH_PANEL,
panelInfo: { panelInfo: {
detailPanelClosed: true, // ✅ flag detailPanelClosed: true, // ✅ flag
detailPanelClosedAt: Date.now(), // ✅ 시점 기록 detailPanelClosedAt: Date.now(), // ✅ 시점 기록
detailPanelClosedFromSource: sourceMenu, // ✅ 출처 detailPanelClosedFromSource: sourceMenu, // ✅ 출처
}, },
}) })
@@ -843,9 +838,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
// 백그라운드 전체화면 비디오 제어: DetailPanel 진입/퇴장 시 // 백그라운드 전체화면 비디오 제어: DetailPanel 진입/퇴장 시
useEffect(() => { useEffect(() => {
// PlayerPanel이 존재하는지 확인 (Modal 또는 Fullscreen) // PlayerPanel이 존재하는지 확인 (Modal 또는 Fullscreen)
const playerPanel = panels.find( const playerPanel = panels.find((panel) => panel.name === panel_names.PLAYER_PANEL);
(panel) => panel.name === panel_names.PLAYER_PANEL
);
const hasPlayerPanel = !!playerPanel; const hasPlayerPanel = !!playerPanel;
const isModal = playerPanel?.panelInfo?.modal; const isModal = playerPanel?.panelInfo?.modal;
@@ -856,7 +849,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
hasPlayerPanel, hasPlayerPanel,
isModal, isModal,
hasProductVideo, hasProductVideo,
sourceMenu: panelInfo?.sourceMenu sourceMenu: panelInfo?.sourceMenu,
}); });
// PlayerPanel이 있고, 제품에 비디오가 있을 때만 비디오 멈춤 // PlayerPanel이 있고, 제품에 비디오가 있을 때만 비디오 멈춤
@@ -916,7 +909,6 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
return ( return (
<div ref={containerRef}> <div ref={containerRef}>
<DetailPanelBackground <DetailPanelBackground
launchedFromPlayer={panelLaunchedFromPlayer} launchedFromPlayer={panelLaunchedFromPlayer}
patnrId={panelPatnrId} patnrId={panelPatnrId}

View File

@@ -619,12 +619,12 @@ export default function ProductAllSection({
// console.log('[BuyNow] Buy Now button clicked'); // console.log('[BuyNow] Buy Now button clicked');
e.stopPropagation(); e.stopPropagation();
console.log('[ProductAllSection] 🛒 BUY NOW clicked - productData:', { // console.log('[ProductAllSection] 🛒 BUY NOW clicked - productData:', {
prdtId: productData?.prdtId, // prdtId: productData?.prdtId,
patnrId: productData?.patnrId, // patnrId: productData?.patnrId,
prdtNm: productData?.prdtNm, // prdtNm: productData?.prdtNm,
hasProductData: !!productData, // hasProductData: !!productData,
}); // });
if (openToast === false) { if (openToast === false) {
dispatch( dispatch(
showToast({ showToast({
@@ -665,41 +665,41 @@ export default function ProductAllSection({
// 스크롤 컨테이너의 클릭 이벤트 추적용 로깅 // 스크롤 컨테이너의 클릭 이벤트 추적용 로깅
const handleScrollContainerClick = useCallback((e) => { const handleScrollContainerClick = useCallback((e) => {
console.log('📱 [ProductAllSection] TScrollerDetail onClick 감지됨', { // console.log('📱 [ProductAllSection] TScrollerDetail onClick 감지됨', {
eventType: e.type, // eventType: e.type,
target: e.target?.className, // target: e.target?.className,
currentTarget: e.currentTarget?.className, // currentTarget: e.currentTarget?.className,
bubbles: e.bubbles, // bubbles: e.bubbles,
defaultPrevented: e.defaultPrevented, // defaultPrevented: e.defaultPrevented,
timestamp: new Date().getTime(), // timestamp: new Date().getTime(),
eventPath: e // eventPath: e
.composedPath?.() // .composedPath?.()
.slice(0, 5) // .slice(0, 5)
.map((el) => ({ // .map((el) => ({
tag: el.tagName, // tag: el.tagName,
className: el.className, // className: el.className,
id: el.id, // id: el.id,
})), // })),
}); // });
}, []); }, []);
// ContentContainer 레벨 클릭 이벤트 추적 // ContentContainer 레벨 클릭 이벤트 추적
const handleContentContainerClick = useCallback((e) => { const handleContentContainerClick = useCallback((e) => {
console.log('🎯 [ProductAllSection] ContentContainer onClick 감지됨', { // console.log('🎯 [ProductAllSection] ContentContainer onClick 감지됨', {
eventType: e.type, // eventType: e.type,
target: e.target?.className, // target: e.target?.className,
currentTarget: e.currentTarget?.className, // currentTarget: e.currentTarget?.className,
bubbles: e.bubbles, // bubbles: e.bubbles,
defaultPrevented: e.defaultPrevented, // defaultPrevented: e.defaultPrevented,
eventPath: e // eventPath: e
.composedPath?.() // .composedPath?.()
.slice(0, 8) // .slice(0, 8)
.map((el) => ({ // .map((el) => ({
tag: el.tagName, // tag: el.tagName,
className: el.className, // className: el.className,
id: el.id, // id: el.id,
})), // })),
}); // });
}, []); }, []);
// ADD TO CART 버튼 클릭 핸들러 // ADD TO CART 버튼 클릭 핸들러
@@ -998,7 +998,7 @@ export default function ProductAllSection({
// 아래로 스크롤: 즉시 1px 축소 (HomePanel과 동일) // 아래로 스크롤: 즉시 1px 축소 (HomePanel과 동일)
if (isScrollingDown && currentScrollTop > 0) { if (isScrollingDown && currentScrollTop > 0) {
console.log('🚀 [ProductVideo.v3] onScroll Down - immediate minimize'); // console.log('🚀 [ProductVideo.v3] onScroll Down - immediate minimize');
dispatch(minimizeModalMedia()); dispatch(minimizeModalMedia());
setShouldMinimizeMedia(true); setShouldMinimizeMedia(true);
@@ -1010,7 +1010,7 @@ export default function ProductAllSection({
} }
// 위로 스크롤 (최상단 아님): 1초 후 복구 // 위로 스크롤 (최상단 아님): 1초 후 복구
else if (!isScrollingDown && currentScrollTop > 1) { else if (!isScrollingDown && currentScrollTop > 1) {
console.log('🚀 [ProductVideo.v3] onScroll Up - expand after 1s'); // console.log('🚀 [ProductVideo.v3] onScroll Up - expand after 1s');
if (scrollExpandTimerRef.current) { if (scrollExpandTimerRef.current) {
clearTimeout(scrollExpandTimerRef.current); clearTimeout(scrollExpandTimerRef.current);
@@ -1023,7 +1023,7 @@ export default function ProductAllSection({
} }
// 최상단 도달: 즉시 복구 (HomePanel과 동일) // 최상단 도달: 즉시 복구 (HomePanel과 동일)
else if (currentScrollTop <= 1) { else if (currentScrollTop <= 1) {
console.log('🚀 [ProductVideo.v3] onScroll AtTop - immediate expand'); // console.log('🚀 [ProductVideo.v3] onScroll AtTop - immediate expand');
dispatch(restoreModalMedia()); dispatch(restoreModalMedia());
setShouldMinimizeMedia(false); setShouldMinimizeMedia(false);
@@ -1053,12 +1053,12 @@ export default function ProductAllSection({
} }
} }
console.log('📍 [ProductAllSection] 스크롤 멈춤 - 위치:', currentScrollTop); // console.log('📍 [ProductAllSection] 스크롤 멈춤 - 위치:', currentScrollTop);
const shouldMinimize = currentScrollTop > 0; const shouldMinimize = currentScrollTop > 0;
setShouldMinimizeMedia((prev) => { setShouldMinimizeMedia((prev) => {
if (prev === shouldMinimize) return prev; if (prev === shouldMinimize) return prev;
console.log('📍 [ProductAllSection] setShouldMinimizeMedia 호출:', shouldMinimize); // console.log('📍 [ProductAllSection] setShouldMinimizeMedia 호출:', shouldMinimize);
return shouldMinimize; return shouldMinimize;
}); });
}, },
@@ -1067,11 +1067,11 @@ export default function ProductAllSection({
useEffect(() => { useEffect(() => {
if (shouldMinimizeMedia && !mediaMinimizedRef.current) { if (shouldMinimizeMedia && !mediaMinimizedRef.current) {
console.log('📍 [ProductAllSection] MediaPanel 최소화 (effect)'); // console.log('📍 [ProductAllSection] MediaPanel 최소화 (effect)');
dispatch(minimizeModalMedia()); dispatch(minimizeModalMedia());
mediaMinimizedRef.current = true; mediaMinimizedRef.current = true;
} else if (!shouldMinimizeMedia && mediaMinimizedRef.current) { } else if (!shouldMinimizeMedia && mediaMinimizedRef.current) {
console.log('📍 [ProductAllSection] MediaPanel 복원 (effect)'); // console.log('📍 [ProductAllSection] MediaPanel 복원 (effect)');
dispatch(restoreModalMedia()); dispatch(restoreModalMedia());
mediaMinimizedRef.current = false; mediaMinimizedRef.current = false;
} }
@@ -1181,7 +1181,7 @@ export default function ProductAllSection({
// 컴포넌트 unmount 시 timer cleanup // 컴포넌트 unmount 시 timer cleanup
useEffect(() => { useEffect(() => {
return () => { return () => {
console.log('[ProductAllSection] unmount - cleanup 시작'); // console.log('[ProductAllSection] unmount - cleanup 시작');
// QR code timer cleanup // QR code timer cleanup
if (timerRef.current) { if (timerRef.current) {
@@ -1208,7 +1208,7 @@ export default function ProductAllSection({
// 🔽 useDetailFocus 포커스 타이머는 Hook의 useEffect cleanup에서 자동으로 정리됨 // 🔽 useDetailFocus 포커스 타이머는 Hook의 useEffect cleanup에서 자동으로 정리됨
console.log('[ProductAllSection] cleanup 완료 on unmount'); // console.log('[ProductAllSection] cleanup 완료 on unmount');
}; };
}, []); }, []);
@@ -1501,7 +1501,7 @@ export default function ProductAllSection({
continuousPlay={true} continuousPlay={true}
onVideoPlaying={() => setIsVideoPlaying(true)} onVideoPlaying={() => setIsVideoPlaying(true)}
onScrollToImages={handleScrollToImagesV1} onScrollToImages={handleScrollToImagesV1}
onFocus={() => console.log('[ProductVideo V1] Focused')} onFocus={() => {}}
data-spotlight-id="product-video-player-container" data-spotlight-id="product-video-player-container"
/> />
) : ( ) : (

View File

@@ -1,18 +1,11 @@
// src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.jsx // src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.jsx
import React, { import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import ImagePreloader from '../../../../utils/ImagePreloader'; import ImagePreloader from '../../../../utils/ImagePreloader';
import hsn from '../../../../../assets/images/bg/hsn_new.png'; import hsn from '../../../../../assets/images/bg/hsn_new.png';
import koreaKiosk from '../../../../../assets/images/bg/koreaKiosk_new.png'; import koreaKiosk from '../../../../../assets/images/bg/koreaKiosk_new.png';
import lgelectronics import lgelectronics from '../../../../../assets/images/bg/lgelectronics_new.png';
from '../../../../../assets/images/bg/lgelectronics_new.png';
import ontv4u from '../../../../../assets/images/bg/ontv4u_new.png'; import ontv4u from '../../../../../assets/images/bg/ontv4u_new.png';
import Pinkfong from '../../../../../assets/images/bg/Pinkfong_new.png'; import Pinkfong from '../../../../../assets/images/bg/Pinkfong_new.png';
import qvc from '../../../../../assets/images/bg/qvc_new.png'; import qvc from '../../../../../assets/images/bg/qvc_new.png';
@@ -29,7 +22,11 @@ import css from './DetailPanelBackground.module.less';
* - 이 값에 따라 배경 UI를 다르게 표시할 수 있음 * - 이 값에 따라 배경 UI를 다르게 표시할 수 있음
* @param {Function} onImageReady - 이미지가 DOM에 완전히 렌더링되었을 때 호출되는 콜백 * @param {Function} onImageReady - 이미지가 DOM에 완전히 렌더링되었을 때 호출되는 콜백
*/ */
export default function DetailPanelBackground({ launchedFromPlayer = false, patnrId, onImageReady }) { export default function DetailPanelBackground({
launchedFromPlayer = false,
patnrId,
onImageReady,
}) {
const [imageReady, setImageReady] = useState(false); const [imageReady, setImageReady] = useState(false);
const imgRef = useRef(null); const imgRef = useRef(null);
@@ -59,16 +56,16 @@ export default function DetailPanelBackground({ launchedFromPlayer = false, patn
const isDOMReady = isLoaded && isRendered; const isDOMReady = isLoaded && isRendered;
if (isDOMReady) { // if (isDOMReady) {
console.log('[DetailPanelBackground] ✅ 이미지 DOM 완전 렌더링 완료:', { // console.log('[DetailPanelBackground] ✅ 이미지 DOM 완전 렌더링 완료:', {
imgSrc: detailPanelBg, // imgSrc: detailPanelBg,
complete: img.complete, // complete: img.complete,
naturalWidth: img.naturalWidth, // naturalWidth: img.naturalWidth,
naturalHeight: img.naturalHeight, // naturalHeight: img.naturalHeight,
offsetHeight: img.offsetHeight, // offsetHeight: img.offsetHeight,
offsetWidth: img.offsetWidth, // offsetWidth: img.offsetWidth,
}); // });
} // }
return isDOMReady; return isDOMReady;
}, [detailPanelBg]); }, [detailPanelBg]);
@@ -78,26 +75,26 @@ export default function DetailPanelBackground({ launchedFromPlayer = false, patn
useEffect(() => { useEffect(() => {
// launchedFromPlayer가 true이면 배경 이미지를 로드하지 않음 (PlayerPanel 비디오 보이도록) // launchedFromPlayer가 true이면 배경 이미지를 로드하지 않음 (PlayerPanel 비디오 보이도록)
if (launchedFromPlayer) { if (launchedFromPlayer) {
console.log('[DetailPanelBackground] Skip background image loading - launchedFromPlayer=true (showing PlayerPanel video)'); // console.log('[DetailPanelBackground] Skip background image loading - launchedFromPlayer=true (showing PlayerPanel video)');
setImageReady(false); setImageReady(false);
return; return;
} }
// launchedFromPlayer가 false일 때만 배경 이미지 로드 // launchedFromPlayer가 false일 때만 배경 이미지 로드
console.log('[DetailPanelBackground] Loading background image - launchedFromPlayer=false'); // console.log('[DetailPanelBackground] Loading background image - launchedFromPlayer=false');
if (ImagePreloader.isLoaded(detailPanelBg)) { if (ImagePreloader.isLoaded(detailPanelBg)) {
console.log('[DetailPanelBackground] Using preloaded image:', detailPanelBg); // console.log('[DetailPanelBackground] Using preloaded image:', detailPanelBg);
setImageReady(true); setImageReady(true);
} else { } else {
// 프리로드되지 않았다면 즉시 로드 시도 // 프리로드되지 않았다면 즉시 로드 시도
console.log('[DetailPanelBackground] Image not preloaded, loading on-demand:', detailPanelBg); // console.log('[DetailPanelBackground] Image not preloaded, loading on-demand:', detailPanelBg);
ImagePreloader.preloadImage(detailPanelBg) ImagePreloader.preloadImage(detailPanelBg)
.then(() => { .then(() => {
console.log('[DetailPanelBackground] On-demand image loaded:', detailPanelBg); // console.log('[DetailPanelBackground] On-demand image loaded:', detailPanelBg);
setImageReady(true); setImageReady(true);
}) })
.catch((e) => { .catch((e) => {
console.error('[DetailPanelBackground] On-demand image load failed:', e); // console.error('[DetailPanelBackground] On-demand image load failed:', e);
// 실패해도 이미지를 표시해야 함 // 실패해도 이미지를 표시해야 함
setImageReady(true); setImageReady(true);
}); });
@@ -137,23 +134,18 @@ export default function DetailPanelBackground({ launchedFromPlayer = false, patn
} }
}, [imageReady, onImageReady, checkImageFullyLoaded]); }, [imageReady, onImageReady, checkImageFullyLoaded]);
useEffect(() => { // useEffect(() => {
console.log('[DetailPanelBackground] 배경 이미지 경로:', detailPanelBg); // console.log('[DetailPanelBackground] 배경 이미지 경로:', detailPanelBg);
console.log('[DetailPanelBackground] launchedFromPlayer:', launchedFromPlayer); // console.log('[DetailPanelBackground] launchedFromPlayer:', launchedFromPlayer);
console.log('[DetailPanelBackground] imageReady:', imageReady); // console.log('[DetailPanelBackground] imageReady:', imageReady);
}, [detailPanelBg, launchedFromPlayer, imageReady]); // }, [detailPanelBg, launchedFromPlayer, imageReady]);
//partnrId 1 = QVC, 2 = HSN, 4 = ONTV, 9 = LG ELECTRONICS, 11 = SHOPLC, 19 = PINKPONG, 16 = KOREA KIOSK, //partnrId 1 = QVC, 2 = HSN, 4 = ONTV, 9 = LG ELECTRONICS, 11 = SHOPLC, 19 = PINKPONG, 16 = KOREA KIOSK,
return ( return (
<div className={css.backgroundContainer}> <div className={css.backgroundContainer}>
{/* 이미지가 준비되지 않았을 때 placeholder 표시 */} {/* 이미지가 준비되지 않았을 때 placeholder 표시 */}
{!imageReady && ( {!imageReady && <div className={css.backgroundPlaceholder} aria-hidden="true" />}
<div
className={css.backgroundPlaceholder}
aria-hidden="true"
/>
)}
{/* 실제 배경 이미지 - launchedFromPlayer가 false일 때만 표시 */} {/* 실제 배경 이미지 - launchedFromPlayer가 false일 때만 표시 */}
{imageReady && !launchedFromPlayer && ( {imageReady && !launchedFromPlayer && (
@@ -163,24 +155,18 @@ export default function DetailPanelBackground({ launchedFromPlayer = false, patn
alt="" alt=""
className={css.backgroundImage} className={css.backgroundImage}
aria-hidden="true" aria-hidden="true"
onLoad={() => console.log('[DetailPanelBackground] 이미지 로드 완료')} // onLoad={() => console.log('[DetailPanelBackground] 이미지 로드 완료')}
onError={(e) => console.error('[DetailPanelBackground] 이미지 로드 실패:', e)} // onError={(e) => console.error('[DetailPanelBackground] 이미지 로드 실패:', e)}
/> />
)} )}
{/* 그라데이션 레이어들 - launchedFromPlayer일 때만 추가 */} {/* 그라데이션 레이어들 - launchedFromPlayer일 때만 추가 */}
{/* 1. 270도 방향 그라데이션 (왼쪽→오른쪽, 투명→불투명) */} {/* 1. 270도 방향 그라데이션 (왼쪽→오른쪽, 투명→불투명) */}
{launchedFromPlayer && ( {launchedFromPlayer && <div className={css.gradientLayer1} aria-hidden="true" />}
<div className={css.gradientLayer1} aria-hidden="true" />
)}
{/* 2. 180도 방향 그라데이션 (위→아래, 투명→불투명) */} {/* 2. 180도 방향 그라데이션 (위→아래, 투명→불투명) */}
{launchedFromPlayer && ( {launchedFromPlayer && <div className={css.gradientLayer2} aria-hidden="true" />}
<div className={css.gradientLayer2} aria-hidden="true" />
)}
{/* 3. 투명 그라데이션 */} {/* 3. 투명 그라데이션 */}
{launchedFromPlayer && ( {launchedFromPlayer && <div className={css.gradientLayer3} aria-hidden="true" />}
<div className={css.gradientLayer3} aria-hidden="true" />
)}
</div> </div>
); );
} }

View File

@@ -18,26 +18,26 @@ import css from './DetailPanelBackground.module.less';
* @param {boolean} visible - 표시 여부 * @param {boolean} visible - 표시 여부
* @param {string} imageUrl - 이미지 URL * @param {string} imageUrl - 이미지 URL
*/ */
const logDetailPanelInit = (patnrId, visible, imageUrl) => { // const logDetailPanelInit = (patnrId, visible, imageUrl) => {
console.log(`[DetailPanelBackgroundV2] patnrId: ${patnrId}, visible: ${visible}, imageUrl: ${imageUrl}`); // console.log(`[DetailPanelBackgroundV2] patnrId: ${patnrId}, visible: ${visible}, imageUrl: ${imageUrl}`);
}; // };
/** /**
* 이미지 로드 성공 로그 * 이미지 로드 성공 로그
* @param {number} patnrId - 파트너사 ID * @param {number} patnrId - 파트너사 ID
*/ */
const logImageLoaded = (patnrId) => { // const logImageLoaded = (patnrId) => {
console.log(`[DetailPanelBackgroundV2] Image loaded: patnrId=${patnrId}`); // console.log(`[DetailPanelBackgroundV2] Image loaded: patnrId=${patnrId}`);
}; // };
/** /**
* 이미지 로드 실패 로그 * 이미지 로드 실패 로그
* @param {number} patnrId - 파트너사 ID * @param {number} patnrId - 파트너사 ID
* @param {Error} error - 에러 객체 * @param {Error} error - 에러 객체
*/ */
const logImageError = (patnrId, error) => { // const logImageError = (patnrId, error) => {
console.error(`[DetailPanelBackgroundV2] Image load failed: patnrId=${patnrId}`, error); // console.error(`[DetailPanelBackgroundV2] Image load failed: patnrId=${patnrId}`, error);
}; // };
/** /**
* 개선된 배경 이미지 컴포넌트 v2 * 개선된 배경 이미지 컴포넌트 v2
@@ -53,18 +53,21 @@ export default function DetailPanelBackgroundV2({
patnrId, patnrId,
visible = true, visible = true,
launchedFromPlayer = false, launchedFromPlayer = false,
usePlaceholder = false usePlaceholder = false,
}) { }) {
// 파트너사별 배경 이미지 맵 // 파트너사별 배경 이미지 맵
const BG_MAP = useMemo(() => ({ const BG_MAP = useMemo(
1: qvc, // QVC () => ({
2: hsn, // HSN 1: qvc, // QVC
4: ontv4u, // ONTV4U 2: hsn, // HSN
9: lgelectronics,// LG ELECTRONICS 4: ontv4u, // ONTV4U
11: shoplc, // SHOPLC 9: lgelectronics, // LG ELECTRONICS
16: koreaKiosk, // KOREA KIOSK 11: shoplc, // SHOPLC
19: Pinkfong, // PINKFONG 16: koreaKiosk, // KOREA KIOSK
}), []); 19: Pinkfong, // PINKFONG
}),
[]
);
const backgroundImageUrl = useMemo(() => { const backgroundImageUrl = useMemo(() => {
return BG_MAP[patnrId] || qvc; // 기본값은 QVC return BG_MAP[patnrId] || qvc; // 기본값은 QVC
@@ -75,9 +78,12 @@ export default function DetailPanelBackgroundV2({
logImageLoaded(patnrId); logImageLoaded(patnrId);
}, [patnrId]); }, [patnrId]);
const handleImageError = useCallback((e) => { const handleImageError = useCallback(
logImageError(patnrId, e); (e) => {
}, [patnrId]); logImageError(patnrId, e);
},
[patnrId]
);
// 개발 환경에서만 로깅 // 개발 환경에서만 로깅
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
@@ -113,7 +119,8 @@ export default function DetailPanelBackgroundV2({
left: 0, left: 0,
width: '100%', width: '100%',
height: '100%', height: '100%',
background: 'linear-gradient(270deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.77) 70%, rgba(0, 0, 0, 1) 100%)', background:
'linear-gradient(270deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.77) 70%, rgba(0, 0, 0, 1) 100%)',
zIndex: 3, zIndex: 3,
}} }}
aria-hidden="true" aria-hidden="true"
@@ -190,7 +197,7 @@ export default function DetailPanelBackgroundV2({
export function PreloadedBackgroundImages({ export function PreloadedBackgroundImages({
selectedPatnrId, selectedPatnrId,
isHomePanelOnTop = true, isHomePanelOnTop = true,
launchedFromPlayer = false launchedFromPlayer = false,
}) { }) {
// 모든 파트너사 ID 목록 // 모든 파트너사 ID 목록
const allPatnrIds = useMemo(() => [1, 2, 4, 9, 11, 16, 19], []); const allPatnrIds = useMemo(() => [1, 2, 4, 9, 11, 16, 19], []);
@@ -205,7 +212,7 @@ export function PreloadedBackgroundImages({
isHomePanelOnTop, isHomePanelOnTop,
launchedFromPlayer, launchedFromPlayer,
shouldShowBackground, shouldShowBackground,
allPatnrIds allPatnrIds,
}); });
}, [selectedPatnrId, isHomePanelOnTop, launchedFromPlayer, shouldShowBackground]); }, [selectedPatnrId, isHomePanelOnTop, launchedFromPlayer, shouldShowBackground]);

View File

@@ -67,16 +67,6 @@ export default function FavoriteBtn({
onFavoriteFlagChanged(favoriteFlag === 'Y' ? 'N' : 'Y'); onFavoriteFlagChanged(favoriteFlag === 'Y' ? 'N' : 'Y');
}, [dispatch, favoriteFlag, onFavoriteFlagChanged]); }, [dispatch, favoriteFlag, onFavoriteFlagChanged]);
const handleFavoriteKeyDown = useCallback(
(e) => {
if (e.key === 'Enter') {
e.preventDefault();
handleFavoriteClick();
}
},
[handleFavoriteClick]
);
const handleSpotlightDown = useCallback( const handleSpotlightDown = useCallback(
(e) => { (e) => {
e.stopPropagation(); e.stopPropagation();
@@ -107,7 +97,6 @@ export default function FavoriteBtn({
aria-label="Register in Favorites" aria-label="Register in Favorites"
tabIndex={0} tabIndex={0}
onClick={handleFavoriteClick} onClick={handleFavoriteClick}
onKeyDown={handleFavoriteKeyDown}
onSpotlightDown={handleSpotlightDown} onSpotlightDown={handleSpotlightDown}
data-spotlight-next-down={ data-spotlight-next-down={
kind === 'item_detail' ? nextDownId || 'product-details-button' : undefined kind === 'item_detail' ? nextDownId || 'product-details-button' : undefined

View File

@@ -57,13 +57,13 @@ import shoplc from '../../../assets/images/bg/shoplc_new.png';
// 파트너사별 배경 이미지 맵 // 파트너사별 배경 이미지 맵
const BACKGROUND_IMAGES = { const BACKGROUND_IMAGES = {
1: qvc, // QVC 1: qvc, // QVC
2: hsn, // HSN 2: hsn, // HSN
4: ontv4u, // ONTV 4: ontv4u, // ONTV
9: lgelectronics, // LG ELECTRONICS 9: lgelectronics, // LG ELECTRONICS
11: shoplc, // SHOPLC 11: shoplc, // SHOPLC
16: koreaKiosk, // KOREA KIOSK 16: koreaKiosk, // KOREA KIOSK
19: Pinkfong, // PINKFONG 19: Pinkfong, // PINKFONG
}; };
// [COMMENTED OUT] useVideoMove 관련 코드 주석 처리 - 향후 사용 검토 필요 // [COMMENTED OUT] useVideoMove 관련 코드 주석 처리 - 향후 사용 검토 필요
// import { useVideoMove } from '../../hooks/useVideoTransition/useVideoMove'; // import { useVideoMove } from '../../hooks/useVideoTransition/useVideoMove';
@@ -98,16 +98,16 @@ const HomePanel = ({ isOnTop, showGradientBackground = false }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
// ✅ showGradientBackground prop 변경 추적 로그 // ✅ showGradientBackground prop 변경 추적 로그
const prevShowGradientBackground = usePrevious(showGradientBackground); // const prevShowGradientBackground = usePrevious(showGradientBackground);
useEffect(() => { // useEffect(() => {
if (prevShowGradientBackground !== showGradientBackground) { // if (prevShowGradientBackground !== showGradientBackground) {
console.log('[TRACE-GRADIENT] 📊 HomePanel prop changed:', { // console.log('[TRACE-GRADIENT] 📊 HomePanel prop changed:', {
prev: prevShowGradientBackground, // prev: prevShowGradientBackground,
current: showGradientBackground, // current: showGradientBackground,
isOnTop: isOnTop // isOnTop: isOnTop
}); // });
} // }
}, [showGradientBackground, prevShowGradientBackground, isOnTop]); // }, [showGradientBackground, prevShowGradientBackground, isOnTop]);
useDebugKey({ isLandingPage: true }); useDebugKey({ isLandingPage: true });
@@ -187,7 +187,6 @@ const HomePanel = ({ isOnTop, showGradientBackground = false }) => {
const verticalPagenatorRef = useRef(null); const verticalPagenatorRef = useRef(null);
const currentSentMenuRef = useRef(null); const currentSentMenuRef = useRef(null);
// ✅ [251119] DetailPanelBackground 이미지 프리로딩 // ✅ [251119] DetailPanelBackground 이미지 프리로딩
// HomePanel 마운트 시 백그라운드로 모든 파트너사 배경 이미지를 미리 로드하여 // HomePanel 마운트 시 백그라운드로 모든 파트너사 배경 이미지를 미리 로드하여
// DetailPanel 진입 시 로딩 지연을 방지함 // DetailPanel 진입 시 로딩 지연을 방지함
@@ -198,8 +197,10 @@ const HomePanel = ({ isOnTop, showGradientBackground = false }) => {
setTimeout(() => { setTimeout(() => {
ImagePreloader.preloadAllImages(BACKGROUND_IMAGES) ImagePreloader.preloadAllImages(BACKGROUND_IMAGES)
.then((results) => { .then((results) => {
const successCount = results.filter(r => r !== null).length; const successCount = results.filter((r) => r !== null).length;
console.log(`[HomePanel] Background images preloaded: ${successCount}/${results.length} images`); console.log(
`[HomePanel] Background images preloaded: ${successCount}/${results.length} images`
);
// 프리로딩 통계 정보 로깅 (디버깅용) // 프리로딩 통계 정보 로깅 (디버깅용)
const stats = ImagePreloader.getStats(); const stats = ImagePreloader.getStats();
@@ -756,36 +757,36 @@ const HomePanel = ({ isOnTop, showGradientBackground = false }) => {
// 비디오가 재생이 아니면 videoPlayIntentRef의 bannerId로 비디오 재생 // 비디오가 재생이 아니면 videoPlayIntentRef의 bannerId로 비디오 재생
// [251116] isHomeOnTop인 경우에는 비디오가 항상 재생되어야 함 // [251116] isHomeOnTop인 경우에는 비디오가 항상 재생되어야 함
useEffect(() => { useEffect(() => {
console.log('[HomeActive] useEffect 실행 - isOnTop:', isOnTop); // console.log('[HomeActive] useEffect 실행 - isOnTop:', isOnTop);
console.log('[HomeActive] videoPlayIntentRef.current:', videoPlayIntentRef.current); // console.log('[HomeActive] videoPlayIntentRef.current:', videoPlayIntentRef.current);
console.log( // console.log(
'[HomeActive] panels 상태:', // '[HomeActive] panels 상태:',
panels.map((p) => ({ name: p.name, modal: p.panelInfo?.modal })) // panels.map((p) => ({ name: p.name, modal: p.panelInfo?.modal }))
); // );
const isHomePanelActive = isOnTop; const isHomePanelActive = isOnTop;
console.log('[HomeActive] isHomePanelActive:', isHomePanelActive); // console.log('[HomeActive] isHomePanelActive:', isHomePanelActive);
if (!isHomePanelActive) { if (!isHomePanelActive) {
console.log('[HomeActive] 조건 실패: isHomePanelActive가 false'); // console.log('[HomeActive] 조건 실패: isHomePanelActive가 false');
return; return;
} }
if (!videoPlayIntentRef.current?.bannerId) { if (!videoPlayIntentRef.current?.bannerId) {
console.log('[HomeActive] 조건 실패: videoPlayIntentRef.current?.bannerId가 없음', { // console.log('[HomeActive] 조건 실패: videoPlayIntentRef.current?.bannerId가 없음', {
hasRef: !!videoPlayIntentRef.current, // hasRef: !!videoPlayIntentRef.current,
hasBannerId: !!videoPlayIntentRef.current?.bannerId, // hasBannerId: !!videoPlayIntentRef.current?.bannerId,
}); // });
return; return;
} }
const bannerId = videoPlayIntentRef.current.bannerId; const bannerId = videoPlayIntentRef.current.bannerId;
console.log('[HomeActive] 비디오 재생 시도 - bannerId:', bannerId); // console.log('[HomeActive] 비디오 재생 시도 - bannerId:', bannerId);
console.log('[HomeActive] 마지막 재생한 배너:', lastPlayedBannerIdRef.current); // console.log('[HomeActive] 마지막 재생한 배너:', lastPlayedBannerIdRef.current);
// ✅ [251116] 중복 재생 방지: 같은 배너면 스킵 // ✅ [251116] 중복 재생 방지: 같은 배너면 스킵
if (lastPlayedBannerIdRef.current === bannerId) { if (lastPlayedBannerIdRef.current === bannerId) {
console.log('[HomeActive] 중복 호출 감지 - 스킵 (이미 재생 중)'); // console.log('[HomeActive] 중복 호출 감지 - 스킵 (이미 재생 중)');
return; return;
} }
@@ -799,7 +800,7 @@ const HomePanel = ({ isOnTop, showGradientBackground = false }) => {
// } // }
// ✅ [251116] startVideoPlayerNew로 비디오 자동 재생 // ✅ [251116] startVideoPlayerNew로 비디오 자동 재생
console.log('[HomeActive] dispatch(startVideoPlayerNew) 호출 직전:', { bannerId, modal: true }); // console.log('[HomeActive] dispatch(startVideoPlayerNew) 호출 직전:', { bannerId, modal: true });
// Spotlight.focus(bannerId); // Spotlight.focus(bannerId);
// dispatch( // dispatch(
@@ -809,11 +810,11 @@ const HomePanel = ({ isOnTop, showGradientBackground = false }) => {
// modalContainerId: 'banner-modal-' + bannerId, // modalContainerId: 'banner-modal-' + bannerId,
// }) // })
// ); // );
console.log('[HomeActive] dispatch(startVideoPlayerNew) 호출 완료'); // console.log('[HomeActive] dispatch(startVideoPlayerNew) 호출 완료');
// 재생 기록 업데이트 // 재생 기록 업데이트
lastPlayedBannerIdRef.current = bannerId; lastPlayedBannerIdRef.current = bannerId;
console.log('[HomeActive] 재생 기록 업데이트:', bannerId); // console.log('[HomeActive] 재생 기록 업데이트:', bannerId);
}, [isOnTop, dispatch]); }, [isOnTop, dispatch]);
// ✅ [251120] DetailPanel 닫힘 감지 useEffect - detailPanelClosed flag 사용 // ✅ [251120] DetailPanel 닫힘 감지 useEffect - detailPanelClosed flag 사용
@@ -826,13 +827,13 @@ const HomePanel = ({ isOnTop, showGradientBackground = false }) => {
useEffect(() => { useEffect(() => {
if (detailPanelClosed && isOnTop) { if (detailPanelClosed && isOnTop) {
console.log('[TRACE-GRADIENT] 🔄 detailPanelClosed flag triggered - HomePanel reactivated'); // console.log('[TRACE-GRADIENT] 🔄 detailPanelClosed flag triggered - HomePanel reactivated');
console.log('[HomePanel] *** ✅ HomePanel isOnTop = true'); // console.log('[HomePanel] *** ✅ HomePanel isOnTop = true');
console.log('[HomePanel] *** detailPanelClosed:', detailPanelClosed); // console.log('[HomePanel] *** detailPanelClosed:', detailPanelClosed);
console.log('[HomePanel] *** detailPanelClosedTime:', detailPanelClosedTime); // console.log('[HomePanel] *** detailPanelClosedTime:', detailPanelClosedTime);
console.log('[HomePanel] *** isOnTop:', isOnTop); // console.log('[HomePanel] *** isOnTop:', isOnTop);
console.log('[HomePanel] *** videoPlayIntentRef.current:', videoPlayIntentRef.current); // console.log('[HomePanel] *** videoPlayIntentRef.current:', videoPlayIntentRef.current);
console.log('[HomePanel] *** lastPlayedBannerIdRef.current:', lastPlayedBannerIdRef.current); // console.log('[HomePanel] *** lastPlayedBannerIdRef.current:', lastPlayedBannerIdRef.current);
// 🔽 videoPlayIntentRef가 null인 경우: 비디오 재생 가능한 첫 번째 배너 찾기 // 🔽 videoPlayIntentRef가 null인 경우: 비디오 재생 가능한 첫 번째 배너 찾기
if (!videoPlayIntentRef.current && bannerDataList) { if (!videoPlayIntentRef.current && bannerDataList) {
@@ -858,7 +859,9 @@ const HomePanel = ({ isOnTop, showGradientBackground = false }) => {
break; break;
} }
} else if ( } else if (
bannerDetailInfos.find((el) => el.shptmBanrTpNm === 'LIVE' || el.shptmBanrTpNm === 'VOD') bannerDetailInfos.find(
(el) => el.shptmBanrTpNm === 'LIVE' || el.shptmBanrTpNm === 'VOD'
)
) { ) {
targetIndex = i; targetIndex = i;
targetBannerData = data; targetBannerData = data;
@@ -904,7 +907,12 @@ const HomePanel = ({ isOnTop, showGradientBackground = false }) => {
// 🔽 [251118] 현재 스크롤 위치 확인하여 비디오 크기 결정 // 🔽 [251118] 현재 스크롤 위치 확인하여 비디오 크기 결정
const currentScrollTop = prevScrollTopRef.current; const currentScrollTop = prevScrollTopRef.current;
const shouldShrink = currentScrollTop > 0; const shouldShrink = currentScrollTop > 0;
console.log('[HomePanel] *** 비디오 복구 - currentScrollTop:', currentScrollTop, ', shouldShrink:', shouldShrink); console.log(
'[HomePanel] *** 비디오 복구 - currentScrollTop:',
currentScrollTop,
', shouldShrink:',
shouldShrink
);
dispatch( dispatch(
startVideoPlayerNew({ startVideoPlayerNew({
@@ -927,7 +935,15 @@ const HomePanel = ({ isOnTop, showGradientBackground = false }) => {
// 🔽 DetailPanel에서 돌아온 뒤 포커스를 마지막 포커스 대상에 복원 // 🔽 DetailPanel에서 돌아온 뒤 포커스를 마지막 포커스 대상에 복원
console.log('[HomePanel] *** 🎯 Focus 복원 준비'); console.log('[HomePanel] *** 🎯 Focus 복원 준비');
const targetFocusId = panelInfo.lastFocusedTargetId || lastFocusedTargetRef.current; const targetFocusId = panelInfo.lastFocusedTargetId || lastFocusedTargetRef.current;
console.log('[HomePanel] *** 📍 targetFocusId:', targetFocusId, '(panelInfo.lastFocusedTargetId:', panelInfo.lastFocusedTargetId, ', lastFocusedTargetRef:', lastFocusedTargetRef.current, ')'); console.log(
'[HomePanel] *** 📍 targetFocusId:',
targetFocusId,
'(panelInfo.lastFocusedTargetId:',
panelInfo.lastFocusedTargetId,
', lastFocusedTargetRef:',
lastFocusedTargetRef.current,
')'
);
if (targetFocusId) { if (targetFocusId) {
console.log('[HomePanel] *** ⏰ 300ms 후 Spotlight.focus 호출 예정'); console.log('[HomePanel] *** ⏰ 300ms 후 Spotlight.focus 호출 예정');
setTimeout(() => { setTimeout(() => {
@@ -944,14 +960,16 @@ const HomePanel = ({ isOnTop, showGradientBackground = false }) => {
// detailPanelClosed 플래그 초기화 (다음 사이클에서 재사용 방지) // detailPanelClosed 플래그 초기화 (다음 사이클에서 재사용 방지)
console.log('[HomePanel] *** detailPanelClosed flag 초기화'); console.log('[HomePanel] *** detailPanelClosed flag 초기화');
dispatch(updateHomeInfo({ dispatch(
name: panel_names.HOME_PANEL, updateHomeInfo({
panelInfo: { name: panel_names.HOME_PANEL,
detailPanelClosed: false, panelInfo: {
detailPanelClosedAt: undefined, detailPanelClosed: false,
detailPanelClosedFromSource: undefined, detailPanelClosedAt: undefined,
} detailPanelClosedFromSource: undefined,
})); },
})
);
} }
} }
}, [detailPanelClosed, isOnTop, bannerDataList, dispatch]); }, [detailPanelClosed, isOnTop, bannerDataList, dispatch]);
@@ -1003,11 +1021,10 @@ const HomePanel = ({ isOnTop, showGradientBackground = false }) => {
console.log('[HomePanel] 🔍 Modal 상태 체크:', { console.log('[HomePanel] 🔍 Modal 상태 체크:', {
prevModalState, prevModalState,
playerModalState, playerModalState,
shouldExecute: prevModalState === false && playerModalState === true shouldExecute: prevModalState === false && playerModalState === true,
}); });
if (prevModalState === false && playerModalState === true) { if (prevModalState === false && playerModalState === true) {
console.log('>>>>>[HomePanel] ▶️ PlayerPanel이 Fullscreen에서 Banner로 전환됨'); console.log('>>>>>[HomePanel] ▶️ PlayerPanel이 Fullscreen에서 Banner로 전환됨');
// 0.5초 후 비디오가 재생되는 배너에 포커스 테두리 효과 적용 // 0.5초 후 비디오가 재생되는 배너에 포커스 테두리 효과 적용
// const focusTimer = setTimeout(() => { // const focusTimer = setTimeout(() => {