[251124] fix: Log정리-4

🕐 커밋 시간: 2025. 11. 24. 12:19:40

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

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/actions/mainActions.js
  ~ com.twin.app.shoptime/src/reducers/mainReducer.js
  ~ com.twin.app.shoptime/src/reducers/searchReducer.js
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/v2/TabContainer.v2.jsx
  ~ com.twin.app.shoptime/src/views/SearchPanel/SearchPanel.new.v2.jsx
  ~ com.twin.app.shoptime/src/views/SearchPanel/VoiceInputOverlay/VoiceInputOverlay.jsx

🔧 함수 변경 내용:
  📄 com.twin.app.shoptime/src/actions/mainActions.js (javascript):
    🔄 Modified: clearSubCategory()
  📄 com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/v2/TabContainer.v2.jsx (javascript):
    🔄 Modified: Spottable()
  📄 com.twin.app.shoptime/src/views/SearchPanel/VoiceInputOverlay/VoiceInputOverlay.jsx (javascript):
     Added: Spottable()
    🔄 Modified: clearAllTimers()

🔧 주요 변경 내용:
  • 핵심 비즈니스 로직 개선
This commit is contained in:
2025-11-24 12:19:40 +09:00
parent 5c3324c120
commit b95628de24
6 changed files with 343 additions and 358 deletions

View File

@@ -1,37 +1,19 @@
// src/views/SearchPanel/SearchPanel.new.jsx
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import classNames from 'classnames';
import {
useDispatch,
useSelector,
} from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import micIcon from '../../../assets/images/searchpanel/image-mic.png';
import hotPicksImage from '../../../assets/images/searchpanel/img-hotpicks.png';
import hotPicksBrandImage
from '../../../assets/images/searchpanel/img-search-hotpicks.png';
import {
sendLogGNB,
sendLogTotalRecommend,
} from '../../actions/logActions';
import hotPicksBrandImage from '../../../assets/images/searchpanel/img-search-hotpicks.png';
import { sendLogGNB, sendLogTotalRecommend } from '../../actions/logActions';
import { getMyRecommandedKeyword } from '../../actions/myPageActions';
import {
popPanel,
pushPanel,
updatePanel,
} from '../../actions/panelActions';
import { popPanel, pushPanel, updatePanel } from '../../actions/panelActions';
import {
clearPanelCommand,
clearShopperHouseData,
@@ -51,36 +33,27 @@ import {
// showWarningToast,
// } from '../../actions/toastActions';
import TBody from '../../components/TBody/TBody';
import TItemCardNew, {
removeDotAndColon,
} from '../../components/TItemCard/TItemCard.new';
import TItemCardNew, { removeDotAndColon } from '../../components/TItemCard/TItemCard.new';
import TPanel from '../../components/TPanel/TPanel';
import TVerticalPagenator
from '../../components/TVerticalPagenator/TVerticalPagenator';
import TVirtualGridList
from '../../components/TVirtualGridList/TVirtualGridList';
import TVerticalPagenator from '../../components/TVerticalPagenator/TVerticalPagenator';
import TVirtualGridList from '../../components/TVirtualGridList/TVirtualGridList';
import usePanelHistory from '../../hooks/usePanelHistory/usePanelHistory';
// import VirtualKeyboardContainer from "../../components/TToast/VirtualKeyboardContainer";
import usePrevious from '../../hooks/usePrevious';
import { useSearchHistory } from '../../hooks/useSearchHistory';
import {
LOG_CONTEXT_NAME,
LOG_MENU,
LOG_MESSAGE_ID,
panel_names,
} from '../../utils/Config';
import { LOG_CONTEXT_NAME, LOG_MENU, LOG_MESSAGE_ID, panel_names } from '../../utils/Config';
import NoSearchResults from './NoSearchResults/NoSearchResults';
// import NoSearchResults from './NoSearchResults/NoSearchResults';
import SearchInputOverlay from './SearchInputOverlay';
import css from './SearchPanel.new.module.less';
import SearchResultsNew from './SearchResults.new.v2';
import TInputSimple, {
ICONS,
KINDS,
} from './TInput/TInputSimple';
import VoiceInputOverlay, {
VOICE_MODES,
} from './VoiceInputOverlay/VoiceInputOverlay';
import TInputSimple, { ICONS, KINDS } from './TInput/TInputSimple';
import VoiceInputOverlay, { VOICE_MODES } from './VoiceInputOverlay/VoiceInputOverlay';
import { createDebugHelpers } from '../../utils/debug';
// 디버그 헬퍼 설정
const DEBUG_MODE = false;
const { dlog, dwarn, derror } = createDebugHelpers(DEBUG_MODE);
/**
* ✨ Mode-Based Architecture 도입
@@ -240,7 +213,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// 🐛 [DEBUG] shopperHouseData 상태 변경 추적 (DEBUG_MODE가 true일 경우에만)
useEffect(() => {
if (DEBUG_MODE) {
console.log('[DEBUG] 📊 SearchPanel shopperHouseData 상태 변경:', {
dlog('[DEBUG] 📊 SearchPanel shopperHouseData 상태 변경:', {
hasData: !!shopperHouseData,
dataLength: shopperHouseData?.results?.[0]?.docs?.length || 0,
searchId: shopperHouseData?.results?.[0]?.searchId || '(없음)',
@@ -256,7 +229,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// 🐛 [DEBUG] SearchPanel 마운트/언마운트 추적 (DEBUG_MODE가 true일 경우에만)
useEffect(() => {
if (DEBUG_MODE) {
console.log('[DEBUG] 🚀 SearchPanel 마운트됨 - 초기 shopperHouseData 상태:', {
dlog('[DEBUG] 🚀 SearchPanel 마운트됨 - 초기 shopperHouseData 상태:', {
hasData: !!shopperHouseData,
dataLength: shopperHouseData?.results?.[0]?.docs?.length || 0,
currentMode,
@@ -266,7 +239,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
return () => {
if (DEBUG_MODE) {
console.log('[DEBUG] 🔚 SearchPanel 언마운트됨 - shopperHouseData 상태:', {
dlog('[DEBUG] 🔚 SearchPanel 언마운트됨 - shopperHouseData 상태:', {
hasData: !!shopperHouseData,
dataLength: shopperHouseData?.results?.[0]?.docs?.length || 0,
timestamp: new Date().toISOString(),
@@ -278,7 +251,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// 🐛 [DEBUG] isOnTop 상태 변경 추적 (DetailPanel <-> SearchPanel 전환, DEBUG_MODE가 true일 경우에만)
useEffect(() => {
if (isOnTopRef.current !== isOnTop && DEBUG_MODE) {
console.log('[DEBUG] 🔄 SearchPanel isOnTop 상태 변경:', {
dlog('[DEBUG] 🔄 SearchPanel isOnTop 상태 변경:', {
from: isOnTopRef.current,
to: isOnTop,
shopperHouseData_preserved: !!shopperHouseData,
@@ -317,7 +290,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// API 실패 시 fallback reference 초기화
useEffect(() => {
if (shopperHouseErrorPopup?.visible && shopperHouseErrorPopup?.type === 'API_FAILURE') {
console.log('[SearchPanel] 🧹 API 실패 감지 - fallbackShopperHouseData 초기화');
dlog('[SearchPanel] 🧹 API 실패 감지 - fallbackShopperHouseData 초기화');
shopperHouseDataRef.current = null;
}
}, [shopperHouseErrorPopup?.visible, shopperHouseErrorPopup?.type]);
@@ -356,7 +329,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
currentPanel?.panelInfo?.currentSpot;
if (DEBUG_MODE && isReturning) {
console.log('[FOCUS] 🎯 DetailPanel 복귀 감지:', {
dlog('[FOCUS] 🎯 DetailPanel 복귀 감지:', {
current: currentPanel?.panelName,
previous: previousPanel?.panelName,
action: currentPanel?.action,
@@ -449,9 +422,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// ✨ [Phase 4] Enter/OK 키 처리 - SearchInputOverlay 표시
if (e.key === 'Enter' || e.keyCode === 13) {
console.log(
'[DEBUG] [SearchPanel] TInputSimple에서 Enter/OK 키 감지 → SearchInputOverlay 오픈'
);
dlog('[DEBUG] [SearchPanel] TInputSimple에서 Enter/OK 키 감지 → SearchInputOverlay 오픈');
e.preventDefault();
// ✨ [Phase 6] SearchInputOverlay 오픈 후 자동으로 입력 준비 완료
@@ -525,7 +496,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// * 0hun: input에 포커스 발생하여 가상 키보드 활성 시, `isInputModeActive` 상태 Boolean 값 설정
// */
// const handleInputModeChange = useCallback((isActive) => {
// console.log(
// dlog(
// "[SearchPanel] TInput 입력 모드:",
// isActive ? "활성화 (키보드 표시)" : "비활성화 (키보드 숨김)"
// );
@@ -613,7 +584,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
}
if (DEBUG_MODE) {
console.log(
dlog(
'🎤 [DEBUG][SearchPanel] openVoiceOverlay called, current isVoiceOverlayVisible:',
isVoiceOverlayVisible
);
@@ -636,7 +607,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
*/
const onCancel = useCallback(() => {
if (DEBUG_MODE) {
console.log('[DEBUG]-onCancel called', {
dlog('[DEBUG]-onCancel called', {
isOnTop: isOnTopRef.current,
isVoiceOverlayVisible,
isSearchOverlayVisible,
@@ -649,7 +620,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
if (!isOnTopRef.current) {
if (DEBUG_MODE) {
console.log('[DEBUG]-onCancel: isOnTopRef is false, returning');
dlog('[DEBUG]-onCancel: isOnTopRef is false, returning');
}
return;
}
@@ -657,7 +628,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// VoiceInputOverlay가 열려있으면 먼저 닫기
if (isVoiceOverlayVisible) {
if (DEBUG_MODE) {
console.log('[DEBUG]-onCancel: closing VoiceInputOverlay');
dlog('[DEBUG]-onCancel: closing VoiceInputOverlay');
}
setIsVoiceOverlayVisible(false);
return;
@@ -666,7 +637,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// SearchInputOverlay가 열려있으면 먼저 닫기
if (isSearchOverlayVisible) {
if (DEBUG_MODE) {
console.log('[DEBUG]-onCancel: closing SearchInputOverlay');
dlog('[DEBUG]-onCancel: closing SearchInputOverlay');
}
handleSearchOverlayClose();
return;
@@ -674,7 +645,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// ✨ [Phase 5] VOICE_RESULT 모드에서 ESC/뒤로가기 누르면 INITIAL 모드로 돌아가기
if (DEBUG_MODE) {
console.log('[DEBUG]-VOICE_RESULT check:', {
dlog('[DEBUG]-VOICE_RESULT check:', {
currentMode,
isVoiceResultMode: currentMode === SEARCH_PANEL_MODES.VOICE_RESULT,
VOICE_RESULT_value: SEARCH_PANEL_MODES.VOICE_RESULT,
@@ -683,11 +654,11 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
if (currentMode === SEARCH_PANEL_MODES.VOICE_RESULT) {
if (DEBUG_MODE) {
console.log(
dlog(
'[DEBUG]-VOICE_RESULT: Clearing ShopperHouse data (searchId will be preserved for 2nd search)'
);
console.log('[VoiceInput]-SearchPanel-onCancel-VOICE_RESULT');
console.log('[VoiceInput] 🧹 VOICE_RESULT 모드에서 ESC 누름 - clearShopperHouseData 호출');
dlog('[VoiceInput]-SearchPanel-onCancel-VOICE_RESULT');
dlog('[VoiceInput] 🧹 VOICE_RESULT 모드에서 ESC 누름 - clearShopperHouseData 호출');
}
// 🎯 [포커스 로직 통합] 포커스는 상태 변경에 의해 자동으로 처리됨
setIsShopperHousePending(false);
@@ -696,17 +667,17 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
}
if (DEBUG_MODE) {
console.log('[DEBUG]-onCancel: normal cancel logic', { searchQuery });
dlog('[DEBUG]-onCancel: normal cancel logic', { searchQuery });
}
if (searchQuery === null || searchQuery === '') {
if (DEBUG_MODE) {
console.log('[DEBUG]-onCancel: popping panel');
dlog('[DEBUG]-onCancel: popping panel');
}
dispatch(popPanel(panel_names.SEARCH_PANEL));
} else {
if (DEBUG_MODE) {
console.log('[DEBUG]-onCancel: resetting search query');
dlog('[DEBUG]-onCancel: resetting search query');
}
setSearchQuery('');
// 🎯 [포커스 로직 통합] 포커스는 상태 변경(searchQuery)에 의해 자동으로 처리됨
@@ -820,7 +791,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
const analyzeCurrentScenario = useCallback(() => {
// DEBUG: 모든 기본 상태값 출력
if (DEBUG_MODE) {
console.log('[DEBUG] analyzeCurrentScenario 호출됨:', {
dlog('[DEBUG] analyzeCurrentScenario 호출됨:', {
// 🎯 기존 isOnTop과 usePanelHistory의 isOnTop 비교
propIsOnTop: isOnTop,
historyIsOnTop: currentIsOnTop,
@@ -845,8 +816,8 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
if (isReturningFromDetailPanel) {
const currentSpot = currentPanel?.panelInfo?.currentSpot;
if (DEBUG_MODE) {
console.log('[Focus] usePanelHistory로 DetailPanel 복귀 감지 - 이전 상품으로 포커스 이동');
console.log('[FOCUS] 🎯 Scenario: DETAIL_PANEL_RETURN (usePanelHistory)', {
dlog('[Focus] usePanelHistory로 DetailPanel 복귀 감지 - 이전 상품으로 포커스 이동');
dlog('[FOCUS] 🎯 Scenario: DETAIL_PANEL_RETURN (usePanelHistory)', {
currentSpot,
mode: currentMode,
fromSearchResult: currentMode === SEARCH_PANEL_MODES.SEARCH_RESULT,
@@ -862,7 +833,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// 🎯 [개선된 fallback] usePanelHistory의 isOnTop 정보 활용
// DetailPanel에서 방금 복귀한 상황 (usePanelHistory가 없을 경우를 대비)
if (DEBUG_MODE) {
console.log('[DEBUG] 개선된 DETAIL_PANEL_RETURN 조건 확인 (fallback):', {
dlog('[DEBUG] 개선된 DETAIL_PANEL_RETURN 조건 확인 (fallback):', {
// 🎯 여러 isOnTop 소스 비교
propIsOnTop: isOnTop,
historyIsOnTop: currentIsOnTop,
@@ -889,8 +860,8 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
) {
const usedHistoryOnTop = currentIsOnTop && isOnTopChange?.becameOnTop;
if (DEBUG_MODE) {
console.log('[Focus] 개선된 방식으로 DetailPanel 복귀 감지 - 이전 상품으로 포커스 이동');
console.log('[FOCUS] 🎯 Scenario: DETAIL_PANEL_RETURN (improved fallback)', {
dlog('[Focus] 개선된 방식으로 DetailPanel 복귀 감지 - 이전 상품으로 포커스 이동');
dlog('[FOCUS] 🎯 Scenario: DETAIL_PANEL_RETURN (improved fallback)', {
currentSpot: panelInfo.currentSpot,
mode: currentMode,
fromSearchResult: currentMode === SEARCH_PANEL_MODES.SEARCH_RESULT,
@@ -908,7 +879,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// - 위의 DETAIL_PANEL_RETURN이 아닌 경우 (= currentSpot이 없거나 모드가 검색 결과 아님)
if (isOnTop && !isOnTopRef.current) {
if (DEBUG_MODE) {
console.log('[FOCUS] 🎯 Scenario: INITIAL_OPEN', {
dlog('[FOCUS] 🎯 Scenario: INITIAL_OPEN', {
currentSpot: panelInfo?.currentSpot,
mode: currentMode,
});
@@ -927,7 +898,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
currentModeRef.current !== SEARCH_PANEL_MODES.SEARCH_RESULT
) {
if (DEBUG_MODE) {
console.log('[FOCUS] 🎯 Scenario: SEARCH_RESULT_LOADED (Mode Changed)', {
dlog('[FOCUS] 🎯 Scenario: SEARCH_RESULT_LOADED (Mode Changed)', {
themeCount: searchDatas?.theme?.length || 0,
itemCount: searchDatas?.item?.length || 0,
showCount: searchDatas?.show?.length || 0,
@@ -948,10 +919,11 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
currentMode === SEARCH_PANEL_MODES.VOICE_RESULT &&
shopperHouseData &&
// 🎯 [개선] 모드 변경 OR 새로운 데이터 도착 감지
(currentModeRef.current !== SEARCH_PANEL_MODES.VOICE_RESULT || shopperHouseDataRef.current !== shopperHouseData)
(currentModeRef.current !== SEARCH_PANEL_MODES.VOICE_RESULT ||
shopperHouseDataRef.current !== shopperHouseData)
) {
if (DEBUG_MODE) {
console.log('[FOCUS] 🎯 Scenario: NEW_SEARCH_LOADED (Voice Result Mode)', {
dlog('[FOCUS] 🎯 Scenario: NEW_SEARCH_LOADED (Voice Result Mode)', {
itemCount: shopperHouseData?.results?.[0]?.docs?.length || 0,
prevMode: currentModeRef.current,
nextMode: currentMode,
@@ -966,7 +938,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// Voice Overlay가 닫힌 상황
if (!isVoiceOverlayVisible && isVoiceOverlayVisibleRef.current) {
if (DEBUG_MODE) {
console.log('[FOCUS] 🎯 Scenario: VOICE_OVERLAY_CLOSED', {
dlog('[FOCUS] 🎯 Scenario: VOICE_OVERLAY_CLOSED', {
hasShopperHouseData: !!shopperHouseData,
});
}
@@ -974,7 +946,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// 이렇게 하면 VOICE_OVERLAY_CLOSED 시나리오에서 TInput으로 가는 것을 방지
if (shopperHouseData && currentMode === SEARCH_PANEL_MODES.VOICE_RESULT) {
if (DEBUG_MODE) {
console.log('[FOCUS] 🔄 VOICE_OVERLAY_CLOSED + new data → NEW_SEARCH_LOADED 우선 처리');
dlog('[FOCUS] 🔄 VOICE_OVERLAY_CLOSED + new data → NEW_SEARCH_LOADED 우선 처리');
}
return 'NEW_SEARCH_LOADED';
}
@@ -986,7 +958,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// - 검색이 수행되지 않았거나 SearchPanel이 SEARCH_RESULT 모드가 아닌 경우
if (!isSearchOverlayVisible && isSearchOverlayVisibleRef.current) {
if (DEBUG_MODE) {
console.log('[FOCUS] 🎯 Scenario: SEARCH_OVERLAY_CLOSED', {
dlog('[FOCUS] 🎯 Scenario: SEARCH_OVERLAY_CLOSED', {
isSearchOverlayVisible,
prevIsSearchOverlayVisible: isSearchOverlayVisibleRef.current,
currentMode,
@@ -1040,25 +1012,25 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
if (isReturningFromDetailPanel && currentPanel?.panelInfo?.currentSpot) {
currentSpot = currentPanel.panelInfo.currentSpot;
if (DEBUG_MODE) {
console.log('[FOCUS] 🎯 usePanelHistory currentSpot 사용:', currentSpot);
dlog('[FOCUS] 🎯 usePanelHistory currentSpot 사용:', currentSpot);
}
}
// 2. fallback: 기존 panelInfo.currentSpot 사용
else if (panelInfo?.currentSpot) {
currentSpot = panelInfo.currentSpot;
if (DEBUG_MODE) {
console.log('[FOCUS] 🔄 fallback으로 panelInfo.currentSpot 사용:', currentSpot);
dlog('[FOCUS] 🔄 fallback으로 panelInfo.currentSpot 사용:', currentSpot);
}
}
if (currentSpot && currentSpot.startsWith('searchItemContents')) {
if (DEBUG_MODE) {
console.log('[FOCUS] 🎯 DETAIL_PANEL_RETURN: 이전 상품으로 포커스 복원:', currentSpot);
dlog('[FOCUS] 🎯 DETAIL_PANEL_RETURN: 이전 상품으로 포커스 복원:', currentSpot);
}
return currentSpot;
} else {
if (DEBUG_MODE) {
console.log(
dlog(
'[FOCUS] ⚠️ DETAIL_PANEL_RETURN: currentSpot이 유효하지 않음, fallback으로 이동:',
{
currentSpot,
@@ -1094,7 +1066,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// SearchInputOverlay에서 검색을 실행하면 isSearchOverlayVisible이 false로 설정되고
// 동시에 검색 결과에 따라 모드가 변경되므로, 이 케이스는 검색어 선택 후 닫을 때만 발생
if (DEBUG_MODE) {
console.log('[FOCUS] 🎯 Scenario: SEARCH_OVERLAY_CLOSED - TInputSimple으로 포커스');
dlog('[FOCUS] 🎯 Scenario: SEARCH_OVERLAY_CLOSED - TInputSimple으로 포커스');
}
return SPOTLIGHT_IDS.SEARCH_INPUT_BOX;
@@ -1118,7 +1090,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
*/
const handleTransitionToSearchInput = useCallback(() => {
if (DEBUG_MODE) {
console.log('[SearchPanel] 🔄 handleTransitionToSearchInput 호출');
dlog('[SearchPanel] 🔄 handleTransitionToSearchInput 호출');
}
// Redux Thunk 액션으로 모든 전환 로직 처리
@@ -1135,12 +1107,12 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
* Search overlay close handler
*/
const handleSearchOverlayClose = useCallback(() => {
console.log('[DEBUG] 🚪 handleSearchOverlayClose 호출됨 - 직접 확인!', {
dlog('[DEBUG] 🚪 handleSearchOverlayClose 호출됨 - 직접 확인!', {
timestamp: new Date().toISOString(),
});
if (DEBUG_MODE) {
console.log('[DEBUG] 🚪 SearchInputOverlay closing');
dlog('[DEBUG] 🚪 SearchInputOverlay closing');
}
const hasSearchResults =
@@ -1149,11 +1121,11 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
(searchDatas?.show?.length || 0) > 0;
// 🎯 SearchInputOverlay 닫힘 후 TInputSimple으로 포커스 이동을 위한 플래그 설정
console.log('[DEBUG] setShouldFocusSearchInput(true) 설정 직전', {
dlog('[DEBUG] setShouldFocusSearchInput(true) 설정 직전', {
timestamp: new Date().toISOString(),
});
setShouldFocusSearchInput(true);
console.log('[DEBUG] setShouldFocusSearchInput(true) 설정됨!', {
dlog('[DEBUG] setShouldFocusSearchInput(true) 설정됨!', {
timestamp: new Date().toISOString(),
});
@@ -1171,7 +1143,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
*/
const handleVoiceOverlayClose = useCallback(() => {
if (DEBUG_MODE) {
console.log(
dlog(
'🚪 [DEBUG][SearchPanel] handleVoiceOverlayClose called, setting isVoiceOverlayVisible to FALSE'
);
}
@@ -1213,7 +1185,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// Redux state 업데이트를 위해 약간의 지연 후 API 호출
setTimeout(() => {
console.log('[HowAboutThese] 🔄 Redux 업데이트 후 API 호출');
dlog('[HowAboutThese] 🔄 Redux 업데이트 후 API 호출');
dispatch(getShopperHouseSearch(trimmedQuery, shopperHouseSearchId));
}, 50); // 50ms 지연
@@ -1337,40 +1309,40 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
[hotPicksForYou, dispatch, SafeImage]
);
const handleClick = useCallback((patnrId, prdtId) => {
dispatch(
pushPanel({
name: panel_names.DETAIL_PANEL,
panelInfo: { patnrId, prdtId },
})
);
},[dispatch])
const handleClick = useCallback(
(patnrId, prdtId) => {
dispatch(
pushPanel({
name: panel_names.DETAIL_PANEL,
panelInfo: { patnrId, prdtId },
})
);
},
[dispatch]
);
const renderTsvItem = useCallback(
({ index, ...rest }) => {
const { offerInfo, prdtId, imgUrl, patnrId, prdtNm, priceInfo } =
tsvInfo[index];
return (
<TItemCardNew
imageAlt={prdtNm}
imageSource={imgUrl}
onClick={()=>{handleClick(patnrId,prdtId)}}
offerInfo={offerInfo}
priceInfo={priceInfo}
productId={prdtId}
productName={prdtNm}
spotlightId={
"searchMain-tsvInfo-spotlightId-" + removeDotAndColon(prdtId)
}
{...rest}
/>
);
},
[tsvInfo,handleClick]
);
({ index, ...rest }) => {
const { offerInfo, prdtId, imgUrl, patnrId, prdtNm, priceInfo } = tsvInfo[index];
return (
<TItemCardNew
imageAlt={prdtNm}
imageSource={imgUrl}
onClick={() => {
handleClick(patnrId, prdtId);
}}
offerInfo={offerInfo}
priceInfo={priceInfo}
productId={prdtId}
productName={prdtNm}
spotlightId={'searchMain-tsvInfo-spotlightId-' + removeDotAndColon(prdtId)}
{...rest}
/>
);
},
[tsvInfo, handleClick]
);
/**
* ✨ [Phase 2] 모드별 콘텐츠 렌더링 (VoiceInputOverlay의 renderModeContent와 동일한 패턴)
@@ -1553,7 +1525,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
</div>
<div className={css.itemList}>
<TVirtualGridList
dataSize={tsvInfo.length}
dataSize={tsvInfo.length}
direction="horizontal"
renderItem={renderTsvItem}
itemWidth={324}
@@ -1561,11 +1533,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
spacing={18}
/>
</div>
</SectionContainer>
</>
)}
</ContainerBasic>
);
}
@@ -1606,7 +1576,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
let nextMode = SEARCH_PANEL_MODES.INITIAL;
if (DEBUG_MODE) {
console.log('[DEBUG]-MODE DECISION useEffect running', {
dlog('[DEBUG]-MODE DECISION useEffect running', {
isVoiceOverlayVisible,
hasShopperHouseData: !!shopperHouseData,
shopperHouseData_detail: shopperHouseData ? 'EXISTS' : 'NULL',
@@ -1626,14 +1596,14 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// 우선순위 1: 음성 입력 오버레이가 열려있으면 VOICE_INPUT 모드
if (isVoiceOverlayVisible) {
if (DEBUG_MODE) {
console.log('[DEBUG]-MODE: isVoiceOverlayVisible is TRUE VOICE_INPUT');
dlog('[DEBUG]-MODE: isVoiceOverlayVisible is TRUE VOICE_INPUT');
}
nextMode = SEARCH_PANEL_MODES.VOICE_INPUT;
}
// 우선순위 2: 음성 검색 결과가 있으면 VOICE_RESULT 모드
else if (shopperHouseData || isShopperHousePending) {
if (DEBUG_MODE) {
console.log('[DEBUG]-MODE: shopperHouseData EXISTS or pending VOICE_RESULT', {
dlog('[DEBUG]-MODE: shopperHouseData EXISTS or pending VOICE_RESULT', {
hasData: !!shopperHouseData,
isPending: isShopperHousePending,
});
@@ -1648,21 +1618,21 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
searchDatas?.show?.length > 0
) {
if (DEBUG_MODE) {
console.log('[DEBUG]-MODE: searchResults EXISTS SEARCH_RESULT');
dlog('[DEBUG]-MODE: searchResults EXISTS SEARCH_RESULT');
}
nextMode = SEARCH_PANEL_MODES.SEARCH_RESULT;
}
// 우선순위 4: 검색 입력 오버레이가 열려있으면 SEARCH_INPUT 모드
else if (isSearchOverlayVisible) {
if (DEBUG_MODE) {
console.log('[DEBUG]-MODE: isSearchOverlayVisible is TRUE SEARCH_INPUT');
dlog('[DEBUG]-MODE: isSearchOverlayVisible is TRUE SEARCH_INPUT');
}
nextMode = SEARCH_PANEL_MODES.SEARCH_INPUT;
}
// 우선순위 5: 초기 상태 (기본값)
else {
if (DEBUG_MODE) {
console.log('[DEBUG]-MODE: No condition met INITIAL');
dlog('[DEBUG]-MODE: No condition met INITIAL');
}
nextMode = SEARCH_PANEL_MODES.INITIAL;
}
@@ -1670,7 +1640,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// 모드가 변경되었을 때만 업데이트
if (nextMode !== currentMode) {
if (DEBUG_MODE) {
console.log(`[DEBUG]-VOICE_RESULT 🔀 Mode changed: ${currentMode} → ${nextMode}`, {
dlog(`[DEBUG]-VOICE_RESULT 🔀 Mode changed: ${currentMode} → ${nextMode}`, {
isVoiceOverlayVisible,
shopperHouseData: !!shopperHouseData,
isShopperHousePending,
@@ -1688,7 +1658,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
setCurrentMode(nextMode);
} else {
if (DEBUG_MODE) {
console.log('[DEBUG]-MODE: Mode unchanged -', currentMode);
dlog('[DEBUG]-MODE: Mode unchanged -', currentMode);
}
}
}, [
@@ -1711,11 +1681,11 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
*/
useEffect(() => {
if (DEBUG_MODE) {
console.log('[DEBUG]-searchQuery useEffect:', { searchQuery });
dlog('[DEBUG]-searchQuery useEffect:', { searchQuery });
}
if (!searchQuery) {
if (DEBUG_MODE) {
console.log('[DEBUG]-VOICE_RESULT: searchQuery is empty, calling resetSearch');
dlog('[DEBUG]-VOICE_RESULT: searchQuery is empty, calling resetSearch');
}
dispatch(resetSearch());
}
@@ -1757,7 +1727,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
*/
useEffect(() => {
if (DEBUG_MODE) {
console.log('[DEBUG][Focus] Focus useEffect 호출됨 - 상태값 확인:', {
dlog('[DEBUG][Focus] Focus useEffect 호출됨 - 상태값 확인:', {
isOnTop,
panelInfo: panelInfo,
currentMode,
@@ -1809,9 +1779,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// NEW_SEARCH_LOADED: 음성 검색 결과 로드 시 VoiceInputOverlay와 충돌 방지
// 다른 시나리오에서는 기존과 같은 지연 시간 (100ms)
const focusDelay =
scenario === 'DETAIL_PANEL_RETURN' || scenario === 'NEW_SEARCH_LOADED'
? 50
: 100;
scenario === 'DETAIL_PANEL_RETURN' || scenario === 'NEW_SEARCH_LOADED' ? 50 : 100;
unifiedFocusTimerRef.current = setTimeout(() => {
const targetElement = document.querySelector(`[data-spotlight-id="${targetId}"]`);
@@ -1819,7 +1787,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
if (targetElement || targetId === SPOTLIGHT_IDS.SEARCH_INPUT_BOX) {
Spotlight.focus(targetId);
if (DEBUG_MODE) {
console.log('[FOCUS] 포커스 이동 완료:', {
dlog('[FOCUS] 포커스 이동 완료:', {
targetId,
scenario,
hasElement: !!targetElement,
@@ -1829,7 +1797,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
}
} else {
if (DEBUG_MODE) {
console.log('[FOCUS] 포커스 대상 요소를 찾지 못했습니다:', {
dlog('[FOCUS] 포커스 대상 요소를 찾지 못했습니다:', {
targetId,
scenario,
timestamp: new Date().toISOString(),
@@ -1842,7 +1810,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
const fallbackElement = document.querySelector(`[data-spotlight-id="${fallbackTarget}"]`);
if (fallbackElement) {
if (DEBUG_MODE) {
console.log(
dlog(
'[FOCUS] 🔄 DETAIL_PANEL_RETURN fallback: 번째 상품으로 포커스:',
fallbackTarget
);
@@ -1860,7 +1828,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
if (scenario === 'NEW_SEARCH_LOADED' && targetId === 'searchItemContents0') {
setTimeout(() => {
if (DEBUG_MODE) {
console.log('[FOCUS] 🔄 NEW_SEARCH_LOADED: 1 번째 상품으로 다시 포커스 이동');
dlog('[FOCUS] 🔄 NEW_SEARCH_LOADED: 1 번째 상품으로 다시 포커스 이동');
}
Spotlight.focus('searchItemContents0');
}, 500); // 0.5초 후
@@ -1908,7 +1876,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
* - 자동으로 플래그 초기화
*/
useEffect(() => {
console.log('[DEBUG] shouldFocusSearchInput useEffect 실행됨!', {
dlog('[DEBUG] shouldFocusSearchInput useEffect 실행됨!', {
shouldFocusSearchInput,
timestamp: new Date().toISOString(),
});
@@ -1916,12 +1884,12 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
if (shouldFocusSearchInput) {
let focusTimer = null;
console.log('[DEBUG] shouldFocusSearchInput === true, 타이머 설정 ...', {
dlog('[DEBUG] shouldFocusSearchInput === true, 타이머 설정 ...', {
timestamp: new Date().toISOString(),
});
if (DEBUG_MODE) {
console.log('[FOCUS] 🎯 SearchInputOverlay 닫힘 포커스 관리 useEffect 실행', {
dlog('[FOCUS] 🎯 SearchInputOverlay 닫힘 포커스 관리 useEffect 실행', {
shouldFocusSearchInput,
timestamp: new Date().toISOString(),
});
@@ -1929,65 +1897,62 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// 500ms 후 TInputSimple에 포커스 이동
focusTimer = setTimeout(() => {
console.log('[DEBUG] 500ms 타이머 콜백 실행!', {
dlog('[DEBUG] 500ms 타이머 콜백 실행!', {
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
timestamp: new Date().toISOString(),
});
if (DEBUG_MODE) {
console.log('[FOCUS] 500ms 타이머 콜백 실행 - TInputSimple으로 포커스 이동', {
dlog('[FOCUS] 500ms 타이머 콜백 실행 - TInputSimple으로 포커스 이동', {
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
timestamp: new Date().toISOString(),
});
}
console.log('[DEBUG] Spotlight.focus() 호출 직전', {
dlog('[DEBUG] Spotlight.focus() 호출 직전', {
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
});
Spotlight.focus(SPOTLIGHT_IDS.SEARCH_INPUT_BOX);
console.log('[DEBUG] Spotlight.focus() 호출 완료!', {
dlog('[DEBUG] Spotlight.focus() 호출 완료!', {
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
timestamp: new Date().toISOString(),
});
if (DEBUG_MODE) {
console.log('[FOCUS] Spotlight.focus() 호출 완료', {
dlog('[FOCUS] Spotlight.focus() 호출 완료', {
targetId: SPOTLIGHT_IDS.SEARCH_INPUT_BOX,
timestamp: new Date().toISOString(),
});
}
console.log('[DEBUG] setShouldFocusSearchInput(false) 호출 직전', {
dlog('[DEBUG] setShouldFocusSearchInput(false) 호출 직전', {
timestamp: new Date().toISOString(),
});
// 포커스 이동 완료 후 플래그 초기화
setShouldFocusSearchInput(false);
console.log('[DEBUG] setShouldFocusSearchInput(false) 호출 완료!', {
dlog('[DEBUG] setShouldFocusSearchInput(false) 호출 완료!', {
timestamp: new Date().toISOString(),
});
}, 500);
console.log('[DEBUG] 타이머 설정 완료 - 500ms 포커스 이동 예약됨', {
dlog('[DEBUG] 타이머 설정 완료 - 500ms 포커스 이동 예약됨', {
timestamp: new Date().toISOString(),
});
// Cleanup: 타이머 정리
return () => {
console.log('[DEBUG] shouldFocusSearchInput useEffect cleanup 실행', {
dlog('[DEBUG] shouldFocusSearchInput useEffect cleanup 실행', {
timestamp: new Date().toISOString(),
});
if (focusTimer) {
if (DEBUG_MODE) {
console.log(
'[FOCUS] 🧹 SearchInputOverlay 포커스 관리 useEffect cleanup - 타이머 정리',
{
timestamp: new Date().toISOString(),
}
);
dlog('[FOCUS] 🧹 SearchInputOverlay 포커스 관리 useEffect cleanup - 타이머 정리', {
timestamp: new Date().toISOString(),
});
}
clearTimeout(focusTimer);
}
@@ -2149,7 +2114,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
} // ✅ INITIAL, VOICE_RESULT & SEARCH_RESULT 모드에서 TInputSimple 내부 포커스 활성화
onKeyDown={handleKeydown}
onMouseDown={() => {
console.log(
dlog(
'[DEBUG] [SearchPanel] TInputSimple에서 마우스 클릭 감지 SearchInputOverlay 오픈'
);
setIsSearchOverlayVisible(true);