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, // 디버깅용 }; }