[251122] fix: DetailPaneel->ProductAllSection Focus-4

🕐 커밋 시간: 2025. 11. 22. 16:46:50

📊 변경 통계:
  • 총 파일: 7개
  • 추가: +184줄
  • 삭제: -1320줄

📁 추가된 파일:
  + com.twin.app.shoptime/src/hooks/useDetailFocus/index.js
  + com.twin.app.shoptime/src/hooks/useDetailFocus/useDetailFocus.js

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx

🗑️ 삭제된 파일:
  - com.twin.app.shoptime/DEBUG_MODE_IMPLEMENTATION.md
  - com.twin.app.shoptime/MEDIAPANEL_CLEANUP_IMPROVEMENTS.md
  - com.twin.app.shoptime/TIMER_CLEANUP_SUMMARY.md
  - com.twin.app.shoptime/[251116]_video_state_management_design.md

🔧 함수 변경 내용:
  📄 com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx (javascript):
    🔄 Modified: extractProductMeta()
     Deleted: tryFocusUp()
  📄 com.twin.app.shoptime/DEBUG_MODE_IMPLEMENTATION.md (md파일):
     Deleted: Before(), After()
  📄 com.twin.app.shoptime/MEDIAPANEL_CLEANUP_IMPROVEMENTS.md (md파일):
     Deleted: useCallback(), showControls(), areControlsVisible(), toggleControls(), useLayoutEffect(), useEffect(), clearTimeout(), dispatch(), forEach(), getVideoNode(), addEventListener()
  📄 com.twin.app.shoptime/TIMER_CLEANUP_SUMMARY.md (md파일):
     Deleted: clearTimeout(), clearAllVideoTimers(), pause(), useEffect(), setTimeout(), useCallback(), resume(), dispatch(), stopPropagation(), preventDefault(), disconnect(), updateProportionLoaded(), setInterval(), clearInterval(), useRef()
  📄 com.twin.app.shoptime/[251116]_video_state_management_design.md (md파일):
     Deleted: curry(), dispatch(), useSelector()

🔧 주요 변경 내용:
  • 핵심 비즈니스 로직 개선
  • 개발 문서 및 가이드 개선

Performance: 코드 최적화로 성능 개선 기대
This commit is contained in:
2025-11-22 16:46:51 +09:00
parent ef7615a538
commit c7ac0d7460
7 changed files with 308 additions and 1319 deletions

View File

@@ -0,0 +1,120 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import Spotlight from '@enact/spotlight';
/**
* useDetailFocus - 포커스 이동 보정용 Hook
*
* ProductAllSection의 복잡한 조건부 렌더링으로 인한 포커스 손실을 방지하기 위해
* arrow key에 따라 다음 포커스 항목을 queue 형태로 관리하고,
* 타이머로 포커스 이동을 수행합니다.
*
* useEffect의 의존성배열에 따라 타이머가 관리되고,
* 컴포넌트 unmount 시 자동으로 cleanup됩니다.
*
* @param {number} delayMs - 포커스 이동 지연 시간 (기본값: 250ms)
* @returns {Object} { enqueueFocus }
*
* @example
* const { enqueueFocus } = useDetailFocus(250);
*
* const handleArrowDown = (e) => {
* e.stopPropagation();
* enqueueFocus('next-button-id');
* };
*/
export default function useDetailFocus(delayMs = 500) {
const focusQueueRef = useRef([]);
const [queueTick, setQueueTick] = useState(0);
const timerRef = useRef(null);
/**
* 포커스 ID를 queue에 추가 (상태만 업데이트, 타이머는 useEffect에서 관리)
* @param {string} focusId - 포커스할 요소의 ID
*/
const enqueueFocus = useCallback(
(focusId) => {
if (!focusId) {
console.warn('[FocusDetail] ⚠️ focusId가 제공되지 않았습니다');
return;
}
// 큐에 추가하고 tick을 올려 useEffect를 트리거
focusQueueRef.current.push(focusId);
console.log(`[FocusDetail] 📋 Queue에 ID 추가: ${focusId} (${delayMs}ms 후 포커스)`);
setQueueTick((tick) => tick + 1);
},
[delayMs]
);
/**
* focusQueue 상태에 따라 타이머 관리
* focusQueue가 설정되면 타이머 시작
* 컴포넌트 unmount 시 useEffect cleanup에서 자동으로 타이머 정리
*/
useEffect(() => {
// queue에 아무것도 없으면 종료
if (!focusQueueRef.current.length) {
console.log(`[FocusDetail] 📭 Queue 비어있음 (포커스 보정 불필요)`);
return undefined;
}
// 기존 타이머가 있으면 취소
if (timerRef.current) {
console.log(
`[FocusDetail] ⏹️ 기존 타이머 취소 - 대기 중인 Queue: ${focusQueueRef.current.join(',')}`
);
clearTimeout(timerRef.current);
}
// 새로운 타이머 설정
const targetId = focusQueueRef.current[focusQueueRef.current.length - 1]; // 마지막 ID
console.log(`[FocusDetail] ⏱️ 타이머 시작 - ${delayMs}ms 후 포커스 이동: ${targetId}`);
timerRef.current = setTimeout(() => {
console.log(`[FocusDetail] ⏱️ 타이머 만료 - 포커스 이동 시도: ${targetId}`);
// 현재 포커스된 요소 확인
const currentElement = Spotlight.getCurrent();
const currentId = currentElement?.dataset?.spotlightId || currentElement?.id || 'unknown';
console.log(`[FocusDetail] 📌 현재 포커스 상태: ${currentId}, 타깃: ${targetId}`);
try {
const success = Spotlight.focus(targetId);
if (!success) {
const afterElement = Spotlight.getCurrent();
const afterId = afterElement?.dataset?.spotlightId || afterElement?.id || 'unknown';
console.warn(`[FocusDetail] ❌ 포커스 이동 실패: ${targetId} (현재: ${afterId})`);
console.warn(
`[FocusDetail] 📋 요소 존재 확인: ${document.querySelector(`[data-spotlight-id="${targetId}"]`) ? '✅ 존재' : '❌ 없음'}`
);
} else {
console.log(`[FocusDetail] ✅ 포커스 이동 성공: ${targetId}`);
}
} catch (error) {
console.error('[FocusDetail] 💥 포커스 이동 중 에러:', error);
} finally {
// 타이머 정리
console.log(`[FocusDetail] 🧹 타이머 정리 - 처리 완료: ${targetId}`);
timerRef.current = null;
// Queue 초기화
focusQueueRef.current = [];
}
}, delayMs);
// cleanup: 의존성배열 변경 또는 컴포넌트 unmount 시 타이머 정리
return () => {
if (timerRef.current) {
console.log(
`[FocusDetail] 🧹 useEffect cleanup - 대기 중인 타이머 취소 (Queue: ${focusQueueRef.current.join(',')})`
);
clearTimeout(timerRef.current);
timerRef.current = null;
}
};
}, [queueTick, delayMs]);
return {
enqueueFocus,
focusQueue: focusQueueRef.current, // 디버깅용
};
}