diff --git a/com.twin.app.shoptime/src/actions/productActions.js b/com.twin.app.shoptime/src/actions/productActions.js index 381656f4..fe0b7797 100644 --- a/com.twin.app.shoptime/src/actions/productActions.js +++ b/com.twin.app.shoptime/src/actions/productActions.js @@ -10,6 +10,17 @@ import reviewSampleImage from '../../assets/images/image-review-sample-1.png'; // DEBUG_MODE - true인 경우에만 로그 출력 const DEBUG_MODE = false; +// Local debug helpers (matches src/lunaSend/common.js pattern) +const dlog = (...args) => { + if (DEBUG_MODE) console.log(...args); +}; +const dwarn = (...args) => { + if (DEBUG_MODE) console.warn(...args); +}; +const derror = (...args) => { + if (DEBUG_MODE) console.error(...args); +}; + // Best Seller 상품 목록 조회 IF-LGSP-303 // FP helpers const pickParams = (keys) => (src) => @@ -39,36 +50,34 @@ const createRequestThunk = const body = data(props); // 📡 REQUEST 로그: API 호출 전 (tag별로 다르게 표시) - if (DEBUG_MODE) - 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(), - } - ); + dlog( + `%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;', - { - 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(), - } - ); + dlog( + `%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); @@ -76,23 +85,22 @@ const createRequestThunk = const onFail = (error) => { // ❌ ERROR 로그: API 호출 실패 (tag별로 다르게 표시) - 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(), - } - ); + derror( + `%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); }; @@ -106,14 +114,14 @@ const createGetThunk = ({ url, type, params = () => ({}), tag }) => export const getBestSeller = (callback) => (dispatch, getState) => { const onSuccess = (response) => { - if (DEBUG_MODE) console.log('getBestSeller onSuccess', response.data); + dlog('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) => { - if (DEBUG_MODE) console.error('getBestSeller onFail', error); + derror('getBestSeller onFail', error); dispatch(changeAppStatus({ showLoadingPanel: { show: false } })); callback && callback(); }; @@ -718,20 +726,20 @@ export const getUserReviewList = (requestParams) => async (dispatch, getState) = // Review Filters 추출 함수 (IF-LGSP-100) const extractReviewFiltersApiData = (apiResponse) => { try { - console.log('[ReviewFilters] 📥 extractReviewFiltersApiData 호출 - 원본 응답:', apiResponse); + dlog('[ReviewFilters] 📥 extractReviewFiltersApiData 호출 - 원본 응답:', apiResponse); let data = null; // ⭐ 핵심: retCode가 0인지 먼저 확인 (HTTP 200이어도 API 에러일 수 있음) // 응답 구조: { retCode: 0, retMsg: "Success", data: { reviewFilterInfos: {...} } } if (!apiResponse) { - console.warn('[ReviewFilters] ⚠️ apiResponse가 null/undefined'); + dwarn('[ReviewFilters] ⚠️ apiResponse가 null/undefined'); return null; } const retCode = apiResponse.retCode; if (retCode !== 0) { - console.error('[ReviewFilters] ❌ API 에러 - retCode !== 0:', { + derror('[ReviewFilters] ❌ API 에러 - retCode !== 0:', { retCode: retCode, retMsg: apiResponse?.retMsg, fullResponse: apiResponse, @@ -742,7 +750,7 @@ const extractReviewFiltersApiData = (apiResponse) => { // reviewFilterInfos 추출: data.reviewFilterInfos const reviewFilterInfos = apiResponse.data?.reviewFilterInfos || {}; - console.log('[ReviewFilters] 🔍 reviewFilterInfos 분석:', { + dlog('[ReviewFilters] 🔍 reviewFilterInfos 분석:', { patnrId: reviewFilterInfos.patnrId, prdtId: reviewFilterInfos.prdtId, hasFilters: !!reviewFilterInfos.filters, @@ -753,11 +761,11 @@ const extractReviewFiltersApiData = (apiResponse) => { data = reviewFilterInfos; if (!data || !data.filters) { - console.warn('[ReviewFilters] ⚠️ filters가 없음:', apiResponse); + dwarn('[ReviewFilters] ⚠️ filters가 없음:', apiResponse); return null; } - console.log('[ReviewFilters] ✅ 추출 완료:', { + dlog('[ReviewFilters] ✅ 추출 완료:', { patnrId: data.patnrId, prdtId: data.prdtId, filtersLength: data.filters.length, @@ -765,7 +773,7 @@ const extractReviewFiltersApiData = (apiResponse) => { return data; } catch (error) { - console.error('[ReviewFilters] ❌ extractReviewFiltersApiData 에러:', error); + derror('[ReviewFilters] ❌ extractReviewFiltersApiData 에러:', error); return null; } }; @@ -783,7 +791,7 @@ export const getReviewFilters = (requestParams) => (dispatch, getState) => { const body = {}; - console.log('[ReviewFilters] 🚀 API 요청 시작:', { + dlog('[ReviewFilters] 🚀 API 요청 시작:', { requestParams, params, body, @@ -796,7 +804,7 @@ export const getReviewFilters = (requestParams) => (dispatch, getState) => { const retCode = response?.data?.retCode; const retMsg = response?.data?.retMsg; - console.log('[ReviewFilters] ✅ API 응답 수신 (retCode 확인):', { + dlog('[ReviewFilters] ✅ API 응답 수신 (retCode 확인):', { httpStatus: response?.status, retCode: retCode, retMsg: retMsg, @@ -808,7 +816,7 @@ export const getReviewFilters = (requestParams) => (dispatch, getState) => { const filtersData = extractReviewFiltersApiData(response.data); if (!filtersData) { - console.warn('[ReviewFilters] ⚠️ 필터 데이터 추출 실패:', { + dwarn('[ReviewFilters] ⚠️ 필터 데이터 추출 실패:', { retCode: retCode, retMsg: retMsg, reason: retCode !== 0 ? 'retCode !== 0' : 'filters 데이터 없음', @@ -816,7 +824,7 @@ export const getReviewFilters = (requestParams) => (dispatch, getState) => { return; // 실패 시 dispatch하지 않음 } - console.log('[ReviewFilters] 📊 필터 데이터 추출 성공:', { + dlog('[ReviewFilters] 📊 필터 데이터 추출 성공:', { patnrId: filtersData.patnrId, prdtId: filtersData.prdtId, filtersLength: filtersData.filters ? filtersData.filters.length : 0, @@ -831,7 +839,7 @@ export const getReviewFilters = (requestParams) => (dispatch, getState) => { }, }; - console.log('[ReviewFilters] 📦 Redux dispatch:', { + dlog('[ReviewFilters] 📦 Redux dispatch:', { actionType: types.GET_REVIEW_FILTERS, patnrId: patnrId, prdtId: prdtId, @@ -842,7 +850,7 @@ export const getReviewFilters = (requestParams) => (dispatch, getState) => { }; const onFail = (error) => { - console.error('[ReviewFilters] ❌ API 실패:', { + derror('[ReviewFilters] ❌ API 실패:', { errorMessage: error?.message || '알 수 없는 에러', errorType: typeof error, httpStatus: error?.response?.status, diff --git a/com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js b/com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js index ef01c6c9..bb589a8d 100644 --- a/com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js +++ b/com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js @@ -66,6 +66,21 @@ import Video from './Video'; import css from './VideoPlayer.module.less'; import { updateVideoPlayState } from '../../actions/playActions'; +// Toggle debug logging for this module +const DEBUG_MODE = false; + +const dlog = (...args) => { + if (DEBUG_MODE) console.log(...args); +}; + +const dwarn = (...args) => { + if (DEBUG_MODE) console.warn(...args); +}; + +const derror = (...args) => { + console.error(...args); +}; + const isEnter = is('enter'); const isLeft = is('left'); const isRight = is('right'); @@ -1488,7 +1503,7 @@ const VideoPlayerBase = class extends React.Component { sourceUnavailable: false, }; if (!el) { - console.log('yhcho VideoPlayer no el '); + dlog('yhcho VideoPlayer no el '); updatedState.error = true; this.setState(updatedState); return; @@ -1564,7 +1579,7 @@ const VideoPlayerBase = class extends React.Component { // 가장 중요한 이벤트만 로그 const shouldLogEvent = ['play', 'pause', 'ended'].includes(ev.type); if (shouldLogEvent) { - console.log('🔄 [PlayerPanel][VideoPlayer] Event-driven Redux update', { + dlog('🔄 [PlayerPanel][VideoPlayer] Event-driven Redux update', { eventType: ev.type, videoState: updatedState, updateState, @@ -1573,7 +1588,7 @@ const VideoPlayerBase = class extends React.Component { } // 🔍 Redux dispatch 확인 - console.log('📤 [PlayerPanel][VideoPlayer] Dispatching Redux update', { + dlog('📤 [PlayerPanel][VideoPlayer] Dispatching Redux update', { eventType: ev.type, updateState, hasDispatch: !!this.props.dispatch, @@ -1583,7 +1598,7 @@ const VideoPlayerBase = class extends React.Component { this.props.dispatch(updateVideoPlayState(updateState)); } } else { - console.log('❌ [PlayerPanel][VideoPlayer] No dispatch prop available', { + derror('❌ [PlayerPanel][VideoPlayer] No dispatch prop available', { props: Object.keys(this.props), hasDispatch: !!this.props.dispatch, hasVideoPlayState: !!this.props.videoPlayState, @@ -1595,7 +1610,7 @@ const VideoPlayerBase = class extends React.Component { if (this.props.videoPlayState && typeof this.props.videoPlayState === 'object') { // Redux 상태 디버깅 (최소한의 중요 이벤트만) if (ev.type === 'play' || ev.type === 'pause') { - console.log('🔍 [PlayerPanel][VideoPlayer] Redux state debug', { + dlog('🔍 [PlayerPanel][VideoPlayer] Redux state debug', { videoPlayState: this.props.videoPlayState, isPaused: this.props.videoPlayState?.isPaused, isPlaying: this.props.videoPlayState?.isPlaying, @@ -1624,7 +1639,7 @@ const VideoPlayerBase = class extends React.Component { // 중요한 상태 변화가 있고 빈번한 이벤트가 아닐 때만 로그 if (hasSignificantChange && !isFrequentEvent) { - console.log('🔄 [PlayerPanel][VideoPlayer] Syncing internal state with Redux', { + dlog('🔄 [PlayerPanel][VideoPlayer] Syncing internal state with Redux', { timeDiff, shouldUpdateTime, pausedDiff: paused !== this.state.paused, @@ -1703,7 +1718,7 @@ const VideoPlayerBase = class extends React.Component { * @public */ play = () => { - console.log('🟢 [PlayerPanel][VideoPlayer] play() called', { + dlog('🟢 [PlayerPanel][VideoPlayer] play() called', { currentTime: this.state.currentTime, duration: this.state.duration, paused: this.state.paused, @@ -1712,7 +1727,7 @@ const VideoPlayerBase = class extends React.Component { }); if (this.state.sourceUnavailable) { - console.log('⚠️ [PlayerPanel][VideoPlayer] play() aborted - source unavailable'); + dwarn('⚠️ [PlayerPanel][VideoPlayer] play() aborted - source unavailable'); return; } @@ -1747,7 +1762,7 @@ const VideoPlayerBase = class extends React.Component { * @public */ pause = () => { - console.log('🔴 [VideoPlayer] pause() called', { + dlog('🔴 [VideoPlayer] pause() called', { currentTime: this.state.currentTime, duration: this.state.duration, paused: this.state.paused, @@ -1756,7 +1771,7 @@ const VideoPlayerBase = class extends React.Component { }); if (this.state.sourceUnavailable) { - console.log('⚠️ [VideoPlayer] pause() aborted - source unavailable'); + dwarn('⚠️ [VideoPlayer] pause() aborted - source unavailable'); return; } @@ -1779,10 +1794,10 @@ const VideoPlayerBase = class extends React.Component { playbackRate: 1, }; - console.log('📤 [VideoPlayer] Dispatching pause state', pauseState); + dlog('📤 [VideoPlayer] Dispatching pause state', pauseState); this.props.dispatch(updateVideoPlayState(pauseState)); } else { - console.log('⚠️ [VideoPlayer] No dispatch prop available - Redux state not updated'); + dwarn('⚠️ [VideoPlayer] No dispatch prop available - Redux state not updated'); } }; @@ -1795,7 +1810,7 @@ const VideoPlayerBase = class extends React.Component { * @public */ seek = (timeIndex) => { - console.log('⏩ [VideoPlayer] seek() called', { + dlog('⏩ [VideoPlayer] seek() called', { timeIndex, currentTime: this.state.currentTime, duration: this.state.duration, @@ -1815,7 +1830,7 @@ const VideoPlayerBase = class extends React.Component { timeIndex >= this.video.duration ? this.video.duration - 1 : timeIndex; this.video.currentTime = actualSeekTime; - console.log('⏩ [VideoPlayer] Video seek completed', { + dlog('⏩ [VideoPlayer] Video seek completed', { requestedTime: timeIndex, actualTime: actualSeekTime, videoDuration: this.video.duration, @@ -1831,17 +1846,17 @@ const VideoPlayerBase = class extends React.Component { playbackRate: this.state.playbackRate, }; - console.log('📤 [VideoPlayer] Dispatching seek state', seekState); + dlog('📤 [VideoPlayer] Dispatching seek state', seekState); this.props.dispatch(updateVideoPlayState(seekState)); } else { - console.log('⚠️ [VideoPlayer] No dispatch prop available - Redux state not updated'); + dwarn('⚠️ [VideoPlayer] No dispatch prop available - Redux state not updated'); } } else { - console.log('❌ [VideoPlayer] seek failed - disabled or source unavailable'); + derror('❌ [VideoPlayer] seek failed - disabled or source unavailable'); forward('onSeekFailed', {}, this.props); } } else { - console.log('❌ [VideoPlayer] seek failed - no video element'); + derror('❌ [VideoPlayer] seek failed - no video element'); } }; diff --git a/com.twin.app.shoptime/src/reducers/panelReducer.js b/com.twin.app.shoptime/src/reducers/panelReducer.js index fd16d391..dabb2c3f 100644 --- a/com.twin.app.shoptime/src/reducers/panelReducer.js +++ b/com.twin.app.shoptime/src/reducers/panelReducer.js @@ -1,6 +1,21 @@ import { types } from '../actions/actionTypes'; import { panel_names } from '../utils/Config'; +// Toggle debug logging for this reducer +const DEBUG_MODE = false; + +const dlog = (...args) => { + if (DEBUG_MODE) console.log(...args); +}; + +const dwarn = (...args) => { + if (DEBUG_MODE) console.warn(...args); +}; + +const derror = (...args) => { + console.error(...args); +}; + const initialState = { // 기존 상태 - 완전히 호환됨 panels: [], @@ -29,7 +44,7 @@ const forceTopPanels = [panel_names.ERROR_PANEL, panel_names.INTRO_PANEL, panel_ export const panelsReducer = (state = initialState, action) => { switch (action.type) { case types.PUSH_PANEL: { - console.log('[panelReducer] 🔵 PUSH_PANEL START', { + dlog('[panelReducer] 🔵 PUSH_PANEL START', { newPanelName: action.payload.name, currentPanels: state.panels.map((p) => p.name), duplicatable: action.duplicatable, @@ -76,7 +91,7 @@ export const panelsReducer = (state = initialState, action) => { } } - console.log('[panelReducer] 🔵 PUSH_PANEL END', { + dlog('[panelReducer] 🔵 PUSH_PANEL END', { resultPanels: newState.map((p) => p.name), lastAction, }); @@ -89,7 +104,7 @@ export const panelsReducer = (state = initialState, action) => { } case types.POP_PANEL: { - console.log('[panelReducer] 🔴 POP_PANEL START', { + dlog('[panelReducer] 🔴 POP_PANEL START', { targetPanel: action.payload || 'last_panel', currentPanels: state.panels.map((p) => p.name), }); @@ -113,7 +128,7 @@ export const panelsReducer = (state = initialState, action) => { resultPanels = state.panels.slice(0, state.panels.length - 1); } - console.log('[panelReducer] 🔴 POP_PANEL END', { + dlog('[panelReducer] 🔴 POP_PANEL END', { resultPanels: resultPanels.map((p) => p.name), lastAction, }); @@ -159,7 +174,7 @@ export const panelsReducer = (state = initialState, action) => { }; } case types.RESET_PANELS: { - console.log('[panelReducer] 🟢 RESET_PANELS START', { + dlog('[panelReducer] 🟢 RESET_PANELS START', { currentPanels: state.panels.map((p) => p.name), payloadProvided: !!action.payload, }); @@ -171,7 +186,7 @@ export const panelsReducer = (state = initialState, action) => { })) : []; - console.log('[panelReducer] 🟢 RESET_PANELS END', { + dlog('[panelReducer] 🟢 RESET_PANELS END', { resultPanels: updatedPanels.map((p) => p.name), }); @@ -184,7 +199,7 @@ export const panelsReducer = (state = initialState, action) => { // [251106] 패널 액션 큐 관련 reducer 케이스들 case types.ENQUEUE_PANEL_ACTION: { - console.log('[panelReducer] 🟠 ENQUEUE_PANEL_ACTION', { + dlog('[panelReducer] 🟠 ENQUEUE_PANEL_ACTION', { action: action.payload.action, queueId: action.payload.id, currentQueueLength: state.panelActionQueue.length, @@ -201,7 +216,7 @@ export const panelsReducer = (state = initialState, action) => { } case types.PROCESS_PANEL_QUEUE: { - console.log('[panelReducer] 🟡 PROCESS_PANEL_QUEUE', { + dlog('[panelReducer] 🟡 PROCESS_PANEL_QUEUE', { isProcessing: state.isProcessingQueue, queueLength: state.panelActionQueue.length, }); @@ -215,7 +230,7 @@ export const panelsReducer = (state = initialState, action) => { const firstQueueItem = state.panelActionQueue[0]; const remainingQueue = state.panelActionQueue.slice(1); - console.log('[panelReducer] 🟡 PROCESSING_QUEUE_ITEM', { + dlog('[panelReducer] 🟡 PROCESSING_QUEUE_ITEM', { action: firstQueueItem.action, queueId: firstQueueItem.id, remainingQueueLength: remainingQueue.length, @@ -261,7 +276,7 @@ export const panelsReducer = (state = initialState, action) => { break; } default: - console.warn('[panelReducer] ⚠️ UNKNOWN_QUEUE_ACTION', firstQueueItem.action); + dwarn('[panelReducer] ⚠️ UNKNOWN_QUEUE_ACTION', firstQueueItem.action); newState = state; } @@ -272,7 +287,7 @@ export const panelsReducer = (state = initialState, action) => { processingTime) / newTotalProcessed; - console.log('[panelReducer] ✅ QUEUE_ITEM_PROCESSED', { + dlog('[panelReducer] ✅ QUEUE_ITEM_PROCESSED', { action: firstQueueItem.action, queueId: firstQueueItem.id, processingTime, @@ -290,7 +305,7 @@ export const panelsReducer = (state = initialState, action) => { }, }; } catch (error) { - console.error('[panelReducer] ❌ QUEUE_PROCESSING_ERROR', { + derror('[panelReducer] ❌ QUEUE_PROCESSING_ERROR', { action: firstQueueItem.action, queueId: firstQueueItem.id, error: error.message, @@ -315,7 +330,7 @@ export const panelsReducer = (state = initialState, action) => { } case types.CLEAR_PANEL_QUEUE: { - console.log('[panelReducer] 🔵 CLEAR_PANEL_QUEUE', { + dlog('[panelReducer] 🔵 CLEAR_PANEL_QUEUE', { currentQueueLength: state.panelActionQueue.length, }); @@ -328,7 +343,7 @@ export const panelsReducer = (state = initialState, action) => { } case types.SET_QUEUE_PROCESSING: { - console.log('[panelReducer] 🟣 SET_QUEUE_PROCESSING', { + dlog('[panelReducer] 🟣 SET_QUEUE_PROCESSING', { isProcessing: action.payload.isProcessing, queueLength: state.panelActionQueue.length, }); @@ -341,7 +356,7 @@ export const panelsReducer = (state = initialState, action) => { // [251106] 비동기 패널 액션 관련 reducer 케이스들 case types.ENQUEUE_ASYNC_PANEL_ACTION: { - console.log('[panelReducer] 🟠 ENQUEUE_ASYNC_PANEL_ACTION', { + dlog('[panelReducer] 🟠 ENQUEUE_ASYNC_PANEL_ACTION', { actionId: action.payload.id, timestamp: action.payload.timestamp, }); @@ -361,7 +376,7 @@ export const panelsReducer = (state = initialState, action) => { } case types.COMPLETE_ASYNC_PANEL_ACTION: { - console.log('[panelReducer] ✅ COMPLETE_ASYNC_PANEL_ACTION', { + dlog('[panelReducer] ✅ COMPLETE_ASYNC_PANEL_ACTION', { actionId: action.payload.actionId, timestamp: action.payload.timestamp, }); @@ -401,7 +416,7 @@ export const panelsReducer = (state = initialState, action) => { } case types.FAIL_ASYNC_PANEL_ACTION: { - console.log('[panelReducer] ❌ FAIL_ASYNC_PANEL_ACTION', { + derror('[panelReducer] ❌ FAIL_ASYNC_PANEL_ACTION', { actionId: action.payload.actionId, error: action.payload.error?.message || 'Unknown error', timestamp: action.payload.timestamp, @@ -440,7 +455,7 @@ export const panelsReducer = (state = initialState, action) => { // [251114] 명시적 포커스 이동 case types.FOCUS_PANEL: { - console.log('[panelReducer] 🎯 FOCUS_PANEL', { + dlog('[panelReducer] 🎯 FOCUS_PANEL', { panelName: action.payload.panelName, focusTarget: action.payload.focusTarget, timestamp: action.payload.timestamp, diff --git a/com.twin.app.shoptime/src/views/MyPagePanel/MyPageSub/TermsOfService/TermsOfOptional.jsx b/com.twin.app.shoptime/src/views/MyPagePanel/MyPageSub/TermsOfService/TermsOfOptional.jsx index 540f9101..5225cdbb 100644 --- a/com.twin.app.shoptime/src/views/MyPagePanel/MyPageSub/TermsOfService/TermsOfOptional.jsx +++ b/com.twin.app.shoptime/src/views/MyPagePanel/MyPageSub/TermsOfService/TermsOfOptional.jsx @@ -2,69 +2,91 @@ /** * ShopTime 애플리케이션의 선택적 약관 동의 컴포넌트 - * + * * @component TermsOfOptional - * @description + * @description * webOS TV 환경에서 선택적 약관 정보를 표시하고 사용자의 동의 여부를 관리합니다. * WebOS 버전에 따라 다른 뷰를 자동으로 표시합니다. */ /* eslint-disable no-console */ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useCallback, useEffect, useState, useMemo } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import Spotlight from "@enact/spotlight"; +import React, { useCallback, useEffect, useState, useMemo } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import Spotlight from '@enact/spotlight'; import { setHidePopup, setShowPopup, changeLocalSettings, setExitApp, - setHttpHeaderForServiceRequest -} from "../../../../actions/commonActions"; + setHttpHeaderForServiceRequest, +} from '../../../../actions/commonActions'; -import {registerDevice} from "../../../../actions/deviceActions"; -import TBody from "../../../../components/TBody/TBody"; +import { registerDevice } from '../../../../actions/deviceActions'; +import TBody from '../../../../components/TBody/TBody'; import { setMyTermsWithdraw, getMyPageTerms, getMyPageOptionalTerms, setMyPageTermsAgree, // 약관 동의 상태 업데이트 액션 -} from "../../../../actions/myPageActions"; -import { sendLogTerms } from "../../../../actions/logActions"; -import TButton, { TYPES } from "../../../../components/TButton/TButton"; -import TCheckBoxSquare from "../../../../components/TCheckBox/TCheckBoxSquare"; -import TPopUp from "../../../../components/TPopUp/TPopUp"; -import CustomTermButton from "./CustomTermButton"; -import * as Config from "../../../../utils/Config"; -import { $L } from "../../../../utils/helperMethods"; -import css from "./TermsOfOptional.module.less"; +} from '../../../../actions/myPageActions'; +import { sendLogTerms } from '../../../../actions/logActions'; +import TButton, { TYPES } from '../../../../components/TButton/TButton'; +import TCheckBoxSquare from '../../../../components/TCheckBox/TCheckBoxSquare'; +import TPopUp from '../../../../components/TPopUp/TPopUp'; +import CustomTermButton from './CustomTermButton'; +import * as Config from '../../../../utils/Config'; +import { $L } from '../../../../utils/helperMethods'; +import css from './TermsOfOptional.module.less'; import { TERMS_TYPE, TERMS_ID_MAP, TERMS_TPCD_MAP, TERMS_DISPLAY_NAMES, getInitialCheckboxStates, - isMandatoryTerms -} from "./termsShared"; -import { popPanel } from "../../../../actions/panelActions"; -import TPanel from "../../../../components/TPanel/TPanel"; -import TermsPopup from "./TermsPopup"; -import OptionalTermsInfo from "./OptionalTermsInfo"; - - + isMandatoryTerms, +} from './termsShared'; +import { popPanel } from '../../../../actions/panelActions'; +import TPanel from '../../../../components/TPanel/TPanel'; +import TermsPopup from './TermsPopup'; +import OptionalTermsInfo from './OptionalTermsInfo'; // SVG 아이콘 컴포넌트 const ExpandIcon = ({ className }) => ( - - - + + + ); +// Local debug helpers (matches src/lunaSend/common.js pattern) +const DEBUG_MODE = false; +const dlog = (...args) => { + if (DEBUG_MODE) console.log(...args); +}; +const dwarn = (...args) => { + if (DEBUG_MODE) console.warn(...args); +}; +const derror = (...args) => { + if (DEBUG_MODE) console.error(...args); +}; + // 혜택 이미지 컴포넌트 const BenefitImage = ({ className, onClick }) => { return ( -
{ e.stopPropagation(); @@ -79,66 +101,66 @@ const BenefitImage = ({ className, onClick }) => { // Home 데이터에서 약관 검색 (약관 내용 포함) const findTermsInHomeData = (termsData, termsId, type) => { if (!termsData) return null; - - console.log('🔍 Home 데이터에서 검색 시작'); - + + dlog('🔍 Home 데이터에서 검색 시작'); + // 1. 배열 형태 검색 if (Array.isArray(termsData)) { - const found = termsData.find(term => term.trmsTpCd === termsId); + const found = termsData.find((term) => term.trmsTpCd === termsId); if (found) { - console.log('✅ Home 배열에서 발견:', found); + dlog('✅ Home 배열에서 발견:', found); return found; } } - + // 2. 객체 형태 검색 if (termsData?.data) { const searchArrays = [ termsData.data.terms, termsData.data.optionalTerms, termsData.data.additionalTerms, - termsData.data.marketingTerms + termsData.data.marketingTerms, ]; - + for (const array of searchArrays) { if (Array.isArray(array)) { - const found = array.find(term => term.trmsTpCd === termsId); + const found = array.find((term) => term.trmsTpCd === termsId); if (found) { - console.log('✅ Home 객체에서 발견:', found); + dlog('✅ Home 객체에서 발견:', found); return found; } } } } - - console.log('❌ Home 데이터에서 찾지 못함'); + + derror('❌ Home 데이터에서 찾지 못함'); return null; }; // MyPage 데이터에서 약관 검색 (fallback) const findTermsInMyPageData = (myPageTermsData, myPageOptionalTermsData, termsId, type) => { - console.log('🔍 MyPage 데이터에서 검색 시작'); - + dlog('🔍 MyPage 데이터에서 검색 시작'); + const searchInData = (data, source) => { if (data?.data?.terms && Array.isArray(data.data.terms)) { - const found = data.data.terms.find(term => term.trmsTpCd === termsId); + const found = data.data.terms.find((term) => term.trmsTpCd === termsId); if (found) { - console.log(`✅ ${source}에서 발견:`, found); + dlog(`✅ ${source}에서 발견:`, found); return found; } } return null; }; - + // 필수 약관에서 검색 let found = searchInData(myPageTermsData, 'myPageTermsData'); if (found) return found; - + // 선택 약관에서 검색 found = searchInData(myPageOptionalTermsData, 'myPageOptionalTermsData'); if (found) return found; - - console.log('❌ MyPage 데이터에서도 찾지 못함'); + + derror('❌ MyPage 데이터에서도 찾지 못함'); return null; }; @@ -147,7 +169,7 @@ export default function TermsOfOptional({ isModalMode, closeModalCallback, panelInfo, - spotlightId = "termsOfOptionalPanel", + spotlightId = 'termsOfOptionalPanel', initialAgreeState, }) { const [hoveredTerm, setHoveredTerm] = useState(null); @@ -156,59 +178,55 @@ export default function TermsOfOptional({ const handleMouseEnter = useCallback((type) => { setHoveredTerm(type); }, []); - + const handleMouseLeave = useCallback(() => { setHoveredTerm(null); }, []); const dispatch = useDispatch(); - const termsData = useSelector((state) => state.home.termsData); + const termsData = useSelector((state) => state.home.termsData); const commonPopupState = useSelector((state) => state.common?.popup || {}); const { popupVisible = false, activePopup = null } = commonPopupState; - - // 약관 철회 결과도 가져오기 const { setMyTermsWithdrawResult } = useSelector((state) => state.myPage); // 약관동의여부 데이터 가져오기 const { myPageTermsData, myPageOptionalTermsData } = useSelector((state) => state.myPage); - + // 1. Redux에서 registerDevice 결과 상태 가져오기 const deviceState = useSelector((state) => state.device); - + useEffect(() => { if (termsData) { - console.log('🔍🔍🔍 TermsOfOptional - termsData 구조:', termsData); + dlog('🔍🔍🔍 TermsOfOptional - termsData 구조:', termsData); if (termsData.data) { - console.log('🔍🔍🔍 TermsOfOptional - termsData.data 키:', Object.keys(termsData.data)); - + dlog('🔍🔍🔍 TermsOfOptional - termsData.data 키:', Object.keys(termsData.data)); + if (termsData.data.optionalTerms) { - console.log('🔍🔍🔍 TermsOfOptional - optionalTerms 데이터:', termsData.data.optionalTerms); + dlog('🔍🔍🔍 TermsOfOptional - optionalTerms 데이터:', termsData.data.optionalTerms); } } } }, [termsData]); const introTermsData = termsData?.data?.terms.filter( - (item) => item.trmsTpCd === "MST00401" || item.trmsTpCd === "MST00402" - ); - - const webOSVersion = useSelector( - (state) => state.common.appStatus.webOSVersion + (item) => item.trmsTpCd === 'MST00401' || item.trmsTpCd === 'MST00402' ); - + + const webOSVersion = useSelector((state) => state.common.appStatus.webOSVersion); + const getDisplayMode = () => { - //temp + //temp const webOSVersion = 7; // 임시로 5로 설정 (테스트용) return webOSVersion < 6 ? 'image' : 'text'; }; - + // 상태 관리 const [checkboxStates, setCheckboxStates] = useState(() => getInitialCheckboxStates()); const [isAllChecked, setIsAllChecked] = useState(false); const [spotlightDisabled, setSpotlightDisabled] = useState(false); - + // 팝업 관련 state const [showTermsPopup, setShowTermsPopup] = useState(false); const [currentTermsData, setCurrentTermsData] = useState(null); @@ -221,233 +239,259 @@ export default function TermsOfOptional({ // 현재 약관 동의 상태를 파싱하는 함수 const parseCurrentTermsStatus = useCallback(() => { - console.log('[DEBUG] 약관 상태 파싱 시작'); - console.log('[DEBUG] myPageTermsData:', myPageTermsData); - console.log('[DEBUG] myPageOptionalTermsData:', myPageOptionalTermsData); - + dlog('[DEBUG] 약관 상태 파싱 시작'); + dlog('[DEBUG] myPageTermsData:', myPageTermsData); + dlog('[DEBUG] myPageOptionalTermsData:', myPageOptionalTermsData); + const currentStatus = {}; - + // 필수 약관 데이터에서 동의 상태 확인 if (myPageTermsData?.data?.terms) { - console.log('[DEBUG] 필수 약관 데이터 처리 시작:', myPageTermsData.data.terms); - myPageTermsData.data.terms.forEach(term => { - console.log(`[DEBUG] 필수 약관 처리 중 - trmsTpCd: ${term.trmsTpCd}, trmsAgrFlag: ${term.trmsAgrFlag}`); - + dlog('[DEBUG] 필수 약관 데이터 처리 시작:', myPageTermsData.data.terms); + myPageTermsData.data.terms.forEach((term) => { + dlog( + `[DEBUG] 필수 약관 처리 중 - trmsTpCd: ${term.trmsTpCd}, trmsAgrFlag: ${term.trmsAgrFlag}` + ); + // TERMS_ID_MAP에서 역으로 찾기 const termType = Object.keys(TERMS_TPCD_MAP).find( - key => TERMS_TPCD_MAP[key] === term.trmsTpCd + (key) => TERMS_TPCD_MAP[key] === term.trmsTpCd ); if (termType) { const isAgreed = term.trmsAgrFlag === 'Y'; currentStatus[termType] = isAgreed; - console.log(`[DEBUG] ${termType}: ${term.trmsAgrFlag} -> ${isAgreed}`); + dlog(`[DEBUG] ${termType}: ${term.trmsAgrFlag} -> ${isAgreed}`); } else { - console.log(`[DEBUG] 매핑되지 않은 약관 타입: ${term.trmsTpCd}`); + dlog(`[DEBUG] 매핑되지 않은 약관 타입: ${term.trmsTpCd}`); } }); } else { - console.log('[DEBUG] 필수 약관 데이터 없음'); + dlog('[DEBUG] 필수 약관 데이터 없음'); } - + // 선택 약관 데이터에서 동의 상태 확인 if (myPageOptionalTermsData?.data?.terms) { - console.log('[DEBUG] 선택 약관 데이터 처리 시작:', myPageOptionalTermsData.data.terms); - myPageOptionalTermsData.data.terms.forEach(term => { - console.log(`[DEBUG] 선택 약관 처리 중 - trmsTpCd: ${term.trmsTpCd}, trmsAgrFlag: ${term.trmsAgrFlag}`); - + dlog('[DEBUG] 선택 약관 데이터 처리 시작:', myPageOptionalTermsData.data.terms); + myPageOptionalTermsData.data.terms.forEach((term) => { + dlog( + `[DEBUG] 선택 약관 처리 중 - trmsTpCd: ${term.trmsTpCd}, trmsAgrFlag: ${term.trmsAgrFlag}` + ); + const termType = Object.keys(TERMS_TPCD_MAP).find( - key => TERMS_TPCD_MAP[key] === term.trmsTpCd + (key) => TERMS_TPCD_MAP[key] === term.trmsTpCd ); if (termType) { const isAgreed = term.trmsAgrFlag === 'Y'; currentStatus[termType] = isAgreed; - console.log(`[DEBUG] ${termType}: ${term.trmsAgrFlag} -> ${isAgreed}`); + dlog(`[DEBUG] ${termType}: ${term.trmsAgrFlag} -> ${isAgreed}`); } else { - console.log(`[DEBUG] 매핑되지 않은 약관 타입: ${term.trmsTpCd}`); + dlog(`[DEBUG] 매핑되지 않은 약관 타입: ${term.trmsTpCd}`); } }); } else { - console.log('[DEBUG] 선택 약관 데이터 없음'); + dlog('[DEBUG] 선택 약관 데이터 없음'); } - - console.log('[DEBUG] 최종 파싱 결과:', currentStatus); - console.log('[DEBUG] 파싱된 약관 개수:', Object.keys(currentStatus).length); - + + dlog('[DEBUG] 최종 파싱 결과:', currentStatus); + dlog('[DEBUG] 파싱된 약관 개수:', Object.keys(currentStatus).length); + return currentStatus; }, [myPageTermsData, myPageOptionalTermsData]); // 컴포넌트 마운트 시 약관 데이터가 있으면 체크박스 상태 초기화 (일반 패널 모드에서만) useEffect(() => { - console.log('[DEBUG] useEffect 트리거됨 - 의존성 변경'); - console.log('[DEBUG] isModalMode:', isModalMode); - console.log('[DEBUG] myPageTermsData 존재:', !!myPageTermsData); - console.log('[DEBUG] myPageOptionalTermsData 존재:', !!myPageOptionalTermsData); - + dlog('[DEBUG] useEffect 트리거됨 - 의존성 변경'); + dlog('[DEBUG] isModalMode:', isModalMode); + dlog('[DEBUG] myPageTermsData 존재:', !!myPageTermsData); + dlog('[DEBUG] myPageOptionalTermsData 존재:', !!myPageOptionalTermsData); + if (!isModalMode && (myPageTermsData || myPageOptionalTermsData)) { - console.log('[DEBUG] 조건 만족 - 체크박스 상태 파싱 시작'); + dlog('[DEBUG] 조건 만족 - 체크박스 상태 파싱 시작'); const currentStatus = parseCurrentTermsStatus(); - + if (Object.keys(currentStatus).length > 0) { - console.log('[DEBUG] 체크박스 상태 업데이트 실행'); - console.log('[DEBUG] 서버 상태로 업데이트:', currentStatus); - + dlog('[DEBUG] 체크박스 상태 업데이트 실행'); + dlog('[DEBUG] 서버 상태로 업데이트:', currentStatus); + setCheckboxStates(currentStatus); - + // 전체 선택 상태도 업데이트 - const allChecked = Object.values(currentStatus).every(state => state); - console.log('[DEBUG] 전체 선택 상태:', allChecked); + const allChecked = Object.values(currentStatus).every((state) => state); + dlog('[DEBUG] 전체 선택 상태:', allChecked); setIsAllChecked(allChecked); } else { - console.log('[DEBUG] 파싱된 상태가 비어있음 - 업데이트 건너뛰기'); + dlog('[DEBUG] 파싱된 상태가 비어있음 - 업데이트 건너뛰기'); } } else { - console.log('[DEBUG] 조건 불만족 - 업데이트 건너뛰기'); + dlog('[DEBUG] 조건 불만족 - 업데이트 건너뛰기'); } }, [myPageTermsData, myPageOptionalTermsData, isModalMode, parseCurrentTermsStatus]); // 메모이제이션된 핸들러들 - const handleCheckboxToggle = useCallback((type) => ({ selected }) => { - console.log(`[CHECKBOX-CLICK] ${type} 체크박스 클릭됨:`, selected); - console.log(`[CHECKBOX-CLICK] 이전 상태:`, checkboxStates[type]); - - setCheckboxStates(prev => { - const newStates = { - ...prev, - [type]: selected - }; - console.log(`[CHECKBOX-CLICK] 새로운 전체 상태:`, newStates); - - const allChecked = Object.values(newStates).every(state => state); - console.log(`[CHECKBOX-CLICK] 전체 선택 여부:`, allChecked); - setIsAllChecked(allChecked); - return newStates; - }); - }, []); + const handleCheckboxToggle = useCallback( + (type) => + ({ selected }) => { + dlog(`[CHECKBOX-CLICK] ${type} 체크박스 클릭됨:`, selected); + dlog(`[CHECKBOX-CLICK] 이전 상태:`, checkboxStates[type]); + + setCheckboxStates((prev) => { + const newStates = { + ...prev, + [type]: selected, + }; + dlog(`[CHECKBOX-CLICK] 새로운 전체 상태:`, newStates); + + const allChecked = Object.values(newStates).every((state) => state); + dlog(`[CHECKBOX-CLICK] 전체 선택 여부:`, allChecked); + setIsAllChecked(allChecked); + return newStates; + }); + }, + [] + ); // 체크박스 상태를 직접 토글하는 함수 추가 const toggleCheckboxDirectly = useCallback((type) => { - setCheckboxStates(prev => { + setCheckboxStates((prev) => { const newStates = { ...prev, - [type]: !prev[type] // 현재 상태 반전 + [type]: !prev[type], // 현재 상태 반전 }; - const allChecked = Object.values(newStates).every(state => state); + const allChecked = Object.values(newStates).every((state) => state); setIsAllChecked(allChecked); return newStates; }); }, []); - const handleSelectAllToggle = useCallback(({ selected }) => { - console.log(`[SELECT-ALL-CLICK] 전체 선택 클릭됨:`, selected); - console.log(`[SELECT-ALL-CLICK] 이전 isAllChecked:`, isAllChecked); - - setIsAllChecked(selected); - setCheckboxStates(prev => { - const newStates = Object.keys(prev).reduce((acc, key) => ({ - ...acc, - [key]: selected - }), {}); - console.log(`[SELECT-ALL-CLICK] 새로운 전체 상태:`, newStates); - return newStates; - }); - }, [isAllChecked]); + const handleSelectAllToggle = useCallback( + ({ selected }) => { + dlog(`[SELECT-ALL-CLICK] 전체 선택 클릭됨:`, selected); + dlog(`[SELECT-ALL-CLICK] 이전 isAllChecked:`, isAllChecked); + + setIsAllChecked(selected); + setCheckboxStates((prev) => { + const newStates = Object.keys(prev).reduce( + (acc, key) => ({ + ...acc, + [key]: selected, + }), + {} + ); + dlog(`[SELECT-ALL-CLICK] 새로운 전체 상태:`, newStates); + return newStates; + }); + }, + [isAllChecked] + ); // 약관 내용 보기 핸들러 - const handleTermsView = useCallback((type) => (event) => { - try { - console.log('🔍 handleTermsView 디버깅:'); - console.log(' - type:', type); - console.log(' - termsData (약관내용):', termsData); - console.log(' - myPageTermsData (동의상태):', myPageTermsData); - console.log(' - myPageOptionalTermsData (선택약관 동의상태):', myPageOptionalTermsData); - - // 이벤트 처리 - if (event) { - event.preventDefault(); - event.stopPropagation(); - } - - const termsId = TERMS_TPCD_MAP[type]; - console.log(' - 찾는 termsId:', termsId); - - // ✅ 1단계: termsData(home)에서 약관 내용 우선 검색 - let foundTerms = findTermsInHomeData(termsData, termsId, type); - - // ✅ 2단계: 없으면 myPage 데이터에서 검색 (fallback) - if (!foundTerms) { - foundTerms = findTermsInMyPageData(myPageTermsData, myPageOptionalTermsData, termsId, type); - } - - if (foundTerms) { - console.log('✅ 약관 데이터 발견:', foundTerms); - - // ✅ 다양한 필드명으로 약관 내용 추출 시도 - const termsContent = foundTerms.trmsCntt || - foundTerms.content || - foundTerms.trmsContent || - foundTerms.text || - foundTerms.cntt || - ''; - - const termsTitle = foundTerms.trmsNm || - foundTerms.title || - foundTerms.name || - TERMS_DISPLAY_NAMES[type] || - ''; - - console.log('📝 추출된 약관 내용 길이:', termsContent.length); - console.log('📝 추출된 약관 제목:', termsTitle); - - if (!termsContent) { - console.warn('⚠️ 약관 내용이 비어있습니다. 사용 가능한 필드들:'); - console.warn(' - foundTerms 객체의 모든 키:', Object.keys(foundTerms)); - - // 디버깅을 위해 모든 필드 출력 - Object.entries(foundTerms).forEach(([key, value]) => { - if (typeof value === 'string' && value.length > 50) { - console.warn(` - ${key}: ${value.substring(0, 100)}...`); - } - }); + const handleTermsView = useCallback( + (type) => (event) => { + try { + dlog('🔍 handleTermsView 디버깅:'); + dlog(' - type:', type); + dlog(' - termsData (약관내용):', termsData); + dlog(' - myPageTermsData (동의상태):', myPageTermsData); + dlog(' - myPageOptionalTermsData (선택약관 동의상태):', myPageOptionalTermsData); + + // 이벤트 처리 + if (event) { + event.preventDefault(); + event.stopPropagation(); } - - // ✅ TermsPopup에 전달할 데이터 객체 생성 - setCurrentTermsData({ - title: termsTitle, - content: termsContent - }); - setCurrentTermsType(type); // 현재 약관 타입 저장 - setShowTermsPopup(true); - - console.log('✅ 팝업 표시 요청 완료'); - console.log(' - 전달된 text 길이:', termsContent.length); - console.log(' - 전달된 title:', termsTitle); - } else { - // 모든 데이터 구조 로깅 - console.error('❌ 약관 데이터를 찾을 수 없음'); - console.error('📊 전체 데이터 구조 분석:'); - console.error(' - termsData:', JSON.stringify(termsData, null, 2)); - console.error(' - myPageTermsData:', JSON.stringify(myPageTermsData, null, 2)); - console.error(' - myPageOptionalTermsData:', JSON.stringify(myPageOptionalTermsData, null, 2)); - console.error(' - TERMS_TPCD_MAP:', TERMS_TPCD_MAP); - console.error(' - 찾는 type:', type, '-> termsId:', TERMS_TPCD_MAP[type]); + + const termsId = TERMS_TPCD_MAP[type]; + dlog(' - 찾는 termsId:', termsId); + + // ✅ 1단계: termsData(home)에서 약관 내용 우선 검색 + let foundTerms = findTermsInHomeData(termsData, termsId, type); + + // ✅ 2단계: 없으면 myPage 데이터에서 검색 (fallback) + if (!foundTerms) { + foundTerms = findTermsInMyPageData( + myPageTermsData, + myPageOptionalTermsData, + termsId, + type + ); + } + + if (foundTerms) { + dlog('✅ 약관 데이터 발견:', foundTerms); + + // ✅ 다양한 필드명으로 약관 내용 추출 시도 + const termsContent = + foundTerms.trmsCntt || + foundTerms.content || + foundTerms.trmsContent || + foundTerms.text || + foundTerms.cntt || + ''; + + const termsTitle = + foundTerms.trmsNm || + foundTerms.title || + foundTerms.name || + TERMS_DISPLAY_NAMES[type] || + ''; + + dlog('📝 추출된 약관 내용 길이:', termsContent.length); + dlog('📝 추출된 약관 제목:', termsTitle); + + if (!termsContent) { + dwarn('⚠️ 약관 내용이 비어있습니다. 사용 가능한 필드들:'); + dwarn(' - foundTerms 객체의 모든 키:', Object.keys(foundTerms)); + + // 디버깅을 위해 모든 필드 출력 + Object.entries(foundTerms).forEach(([key, value]) => { + if (typeof value === 'string' && value.length > 50) { + dwarn(` - ${key}: ${value.substring(0, 100)}...`); + } + }); + } + + // ✅ TermsPopup에 전달할 데이터 객체 생성 + setCurrentTermsData({ + title: termsTitle, + content: termsContent, + }); + setCurrentTermsType(type); // 현재 약관 타입 저장 + setShowTermsPopup(true); + + dlog('✅ 팝업 표시 요청 완료'); + dlog(' - 전달된 text 길이:', termsContent.length); + dlog(' - 전달된 title:', termsTitle); + } else { + // 모든 데이터 구조 로깅 + derror('❌ 약관 데이터를 찾을 수 없음'); + derror('📊 전체 데이터 구조 분석:'); + derror(' - termsData:', JSON.stringify(termsData, null, 2)); + derror(' - myPageTermsData:', JSON.stringify(myPageTermsData, null, 2)); + derror(' - myPageOptionalTermsData:', JSON.stringify(myPageOptionalTermsData, null, 2)); + derror(' - TERMS_TPCD_MAP:', TERMS_TPCD_MAP); + derror(' - 찾는 type:', type, '-> termsId:', TERMS_TPCD_MAP[type]); + } + } catch (error) { + derror('❌ handleTermsView 오류:', error); } - - } catch (error) { - console.error('❌ handleTermsView 오류:', error); - } - }, [termsData, myPageTermsData, myPageOptionalTermsData]); + }, + [termsData, myPageTermsData, myPageOptionalTermsData] + ); // 약관 버튼 클릭 시 약관 내용만 보여주는 핸들러 (체크박스 토글 제거) - const handleTermButtonClick = useCallback((type) => (event) => { - // 약관 내용 보기만 수행 (체크박스 토글은 팝업에서 Agree 클릭 시에만) - handleTermsView(type)(event); - }, [handleTermsView]); + const handleTermButtonClick = useCallback( + (type) => (event) => { + // 약관 내용 보기만 수행 (체크박스 토글은 팝업에서 Agree 클릭 시에만) + handleTermsView(type)(event); + }, + [handleTermsView] + ); /** * 팝업 닫기 핸들러 */ const handleClosePopup = useCallback(() => { - console.log('[TermsOfOptional] handleClosePopup triggered (e.g., OK on Terms Detail or Alert)'); + dlog('[TermsOfOptional] handleClosePopup triggered (e.g., OK on Terms Detail or Alert)'); dispatch(setHidePopup()); }, [dispatch]); @@ -455,152 +499,158 @@ export default function TermsOfOptional({ * 앱 종료 핸들러 (IntroPanel과 동일) */ const onExit = useCallback(() => { - console.log('[TermsOfOptional] onExit (Yes button on Exit Popup) triggered - 약관 철회 처리 시작'); + dlog('[TermsOfOptional] onExit (Yes button on Exit Popup) triggered - 약관 철회 처리 시작'); dispatch(setHidePopup()); - - console.log('[TermsOfOptional] 현재 체크박스 상태:', checkboxStates); - console.log('[TermsOfOptional] 3개 약관 ID 매핑:', TERMS_TPCD_MAP); - + + dlog('[TermsOfOptional] 현재 체크박스 상태:', checkboxStates); + dlog('[TermsOfOptional] 3개 약관 ID 매핑:', TERMS_TPCD_MAP); + // 현재 동의된 약관들을 확인 (3개 약관 모두 고려) const agreedTerms = Object.entries(checkboxStates) .filter(([type, checked]) => { - console.log(`[TermsOfOptional] 약관 ${type}: ${checked ? '동의됨' : '동의안됨'}`); + dlog(`[TermsOfOptional] 약관 ${type}: ${checked ? '동의됨' : '동의안됨'}`); return checked; }) .map(([type]) => { const termId = TERMS_ID_MAP[type]; - console.log(`[TermsOfOptional] ${type} -> ${termId}`); + dlog(`[TermsOfOptional] ${type} -> ${termId}`); return termId; }) .filter(Boolean); // undefined 값 제거 - - console.log('🚫 철회할 약관들 (3개 약관 처리):', agreedTerms); - console.log('🚫 철회할 약관 개수:', agreedTerms.length); + + dlog('🚫 철회할 약관들 (3개 약관 처리):', agreedTerms); + dlog('🚫 철회할 약관 개수:', agreedTerms.length); if (agreedTerms.length > 0) { // 동의된 약관이 있으면 철회 API 호출 const withdrawParams = { - trmsTpCdList: agreedTerms + trmsTpCdList: agreedTerms, }; - - console.log('🚫 약관 철회 API 호출 파라미터:', withdrawParams); - dispatch(setMyTermsWithdraw(withdrawParams, (result) => { - console.log('🚫 약관 철회 완료:', result); - - if (result && result.retCode === "000") { - console.log('✅ 약관 철회 성공'); - // 철회 성공 후 앱 종료 - dispatch(setExitApp()); - } else { - console.error('❌ 약관 철회 실패:', result); - // 실패 시 에러 팝업 표시 - dispatch(setShowPopup(Config.ACTIVE_POPUP.alertPopup, { - title: $L("Error"), - text: $L("약관 철회 중 오류가 발생했습니다. 다시 시도해주세요."), - button1Text: $L("확인") - })); - } - })); + + dlog('🚫 약관 철회 API 호출 파라미터:', withdrawParams); + dispatch( + setMyTermsWithdraw(withdrawParams, (result) => { + dlog('🚫 약관 철회 완료:', result); + + if (result && result.retCode === '000') { + dlog('✅ 약관 철회 성공'); + // 철회 성공 후 앱 종료 + dispatch(setExitApp()); + } else { + derror('❌ 약관 철회 실패:', result); + // 실패 시 에러 팝업 표시 + dispatch( + setShowPopup(Config.ACTIVE_POPUP.alertPopup, { + title: $L('Error'), + text: $L('약관 철회 중 오류가 발생했습니다. 다시 시도해주세요.'), + button1Text: $L('확인'), + }) + ); + } + }) + ); } else { // 동의된 약관이 없으면 바로 앱 종료 - console.log('🚫 동의된 약관이 없어 바로 앱 종료'); + dlog('🚫 동의된 약관이 없어 바로 앱 종료'); dispatch(setExitApp()); } }, [dispatch, checkboxStates]); // TermsOfService와 동일한 onClose 핸들러 (팝업 취소) const onClose = useCallback(() => { - console.log('[TermsOfOptional] onClose (No button on Exit Popup) triggered'); + dlog('[TermsOfOptional] onClose (No button on Exit Popup) triggered'); dispatch(setHidePopup()); }, [dispatch]); const handleBack = useCallback(() => { if (isModalMode && closeModalCallback) { - console.log('[TermsOfOptional] Modal mode back pressed, calling closeModalCallback.'); + dlog('[TermsOfOptional] Modal mode back pressed, calling closeModalCallback.'); closeModalCallback(); // App.js에 정의된 콜백 호출 (예: 앱 종료 팝업) } else if (!isModalMode && panelInfo && panelInfo.name) { - console.log('[TermsOfOptional] Panel mode back pressed, popping panel:', panelInfo.name); + dlog('[TermsOfOptional] Panel mode back pressed, popping panel:', panelInfo.name); dispatch(popPanel(panelInfo.name)); } else { // 기본 동작 또는 정의되지 않은 경우 (예: 앱 종료 팝업) - console.log('[TermsOfOptional] Back pressed with no specific mode, showing exit popup.'); + dlog('[TermsOfOptional] Back pressed with no specific mode, showing exit popup.'); dispatch(setShowPopup(Config.ACTIVE_POPUP.exitPopup)); } }, [isModalMode, closeModalCallback, dispatch, panelInfo]); const handleTest = () => { // 약관 동의할 항목들 (string array) - const termsList = ["TID0000222", "TID0000223", "TID0000232"]; - + const termsList = ['TID0000222', 'TID0000223', 'TID0000232']; + // 동의하지 않을 항목들 (빈 배열) const notTermsList = []; - + // 콜백 함수 - 성공 시 실행될 로직 const callback = (responseData) => { - console.log("약관 동의 처리 완료:", responseData); + dlog('약관 동의 처리 완료:', responseData); // 여기에 성공 후 추가 로직 작성 }; - - // Redux 액션 디스패치 - dispatch(setMyPageTermsAgree( - { termsList, notTermsList }, - callback - )); - }; + // Redux 액션 디스패치 + dispatch(setMyPageTermsAgree({ termsList, notTermsList }, callback)); + }; const handleAgree = useCallback(() => { // 필수 약관 검증 - const hasRequiredTerms = checkboxStates[TERMS_TYPE.TERMS_CONDITIONS] && - checkboxStates[TERMS_TYPE.PRIVACY_POLICY]; - - if (!hasRequiredTerms) { - dispatch(setShowPopup(Config.ACTIVE_POPUP.alertPopup, { - title: $L("Notice"), - text: $L("Please agree to all required terms."), - button1Text: $L("OK") - })); + const hasRequiredTerms = + checkboxStates[TERMS_TYPE.TERMS_CONDITIONS] && checkboxStates[TERMS_TYPE.PRIVACY_POLICY]; + + if (!hasRequiredTerms) { + dispatch( + setShowPopup(Config.ACTIVE_POPUP.alertPopup, { + title: $L('Notice'), + text: $L('Please agree to all required terms.'), + button1Text: $L('OK'), + }) + ); return; } - + // agreeTerms 배열 구성 const agreeTerms = []; if (checkboxStates[TERMS_TYPE.TERMS_CONDITIONS]) { - agreeTerms.push("TID0000222"); + agreeTerms.push('TID0000222'); } if (checkboxStates[TERMS_TYPE.PRIVACY_POLICY]) { - agreeTerms.push("TID0000223"); + agreeTerms.push('TID0000223'); } if (checkboxStates[TERMS_TYPE.OPTIONAL_TERMS]) { - agreeTerms.push("TID0000232"); + agreeTerms.push('TID0000232'); } - - console.log('최종 전송될 agreeTerms:', agreeTerms); - + + dlog('최종 전송될 agreeTerms:', agreeTerms); + // ✅ 콜백 없이 registerDevice 호출 - dispatch(registerDevice({ - agreeTerms: agreeTerms - })); - + dispatch( + registerDevice({ + agreeTerms: agreeTerms, + }) + ); }, [checkboxStates, dispatch]); - + // ✅ 별도 useEffect로 deviceState 변화 감지 useEffect(() => { - console.log('📊 deviceState 변화 감지:', deviceState); - + dlog('📊 deviceState 변화 감지:', deviceState); + // registerDevice 성공 시 (retCode가 0 또는 "000"인 경우) - if (deviceState?.regDeviceData && (deviceState.regDeviceData.retCode === 0 || deviceState.regDeviceData.retCode === "000")) { - console.log('✅ registerDevice 성공 감지 - 패널 닫기 시작'); - + if ( + deviceState?.regDeviceData && + (deviceState.regDeviceData.retCode === 0 || deviceState.regDeviceData.retCode === '000') + ) { + dlog('✅ registerDevice 성공 감지 - 패널 닫기 시작'); + // getAuthenticationCode 완료까지 잠시 대기 후 패널 닫기 setTimeout(() => { - console.log('🚀 패널 닫기 실행'); - + dlog('🚀 패널 닫기 실행'); + if (isModalMode && closeModalCallback) { - console.log('📱 Modal mode - closeModalCallback 호출'); + dlog('📱 Modal mode - closeModalCallback 호출'); closeModalCallback(true); } else { - console.log('📋 Panel mode - popPanel 호출'); + dlog('📋 Panel mode - popPanel 호출'); dispatch(popPanel()); } }, 2000); // 2초 대기 (getAuthenticationCode 완료 대기) @@ -608,42 +658,47 @@ export default function TermsOfOptional({ }, [deviceState, isModalMode, closeModalCallback, dispatch]); const handleDoNotAgree = useCallback(() => { - console.log('[TermsOfOptional] Do Not Agree pressed.'); - + dlog('[TermsOfOptional] Do Not Agree pressed.'); + // 로그 전송 (IntroPanel과 동일) dispatch(sendLogTerms({ logTpNo: Config.LOG_TP_NO.TERMS.DO_NOT_AGREE })); - + // ✅ TermsOfService와 동일한 방식으로 옵션 객체 포함 - dispatch(setShowPopup(Config.ACTIVE_POPUP.exitPopup, { - title: $L(""), - text: ( -
- {$L("Are you sure you want to opt out?")} -
-

- {$L("The service will not be available anymore after opting out.")} -

-
- ), - button1Text: $L("Yes"), - button2Text: $L("No"), - onButton1Press: onExit, - onButton2Press: onClose - })); + dispatch( + setShowPopup(Config.ACTIVE_POPUP.exitPopup, { + title: $L(''), + text: ( +
+ {$L('Are you sure you want to opt out?')} +
+

+ {$L('The service will not be available anymore after opting out.')} +

+
+ ), + button1Text: $L('Yes'), + button2Text: $L('No'), + onButton1Press: onExit, + onButton2Press: onClose, + }) + ); }, [dispatch, onExit, onClose]); useEffect(() => { - console.log('🔄 Redux popup 상태 변화 (commonPopupState):'); - console.log(' - commonPopupState.popupVisible:', commonPopupState.popupVisible); - console.log(' - commonPopupState.activePopup:', commonPopupState.activePopup); - console.log(' - commonPopupState 객체:', commonPopupState); - + dlog('🔄 Redux popup 상태 변화 (commonPopupState):'); + dlog(' - commonPopupState.popupVisible:', commonPopupState.popupVisible); + dlog(' - commonPopupState.activePopup:', commonPopupState.activePopup); + dlog(' - commonPopupState 객체:', commonPopupState); + if (commonPopupState.activePopup === Config.ACTIVE_POPUP.termsPopup) { - console.log('📋 약관 팝업 상태 상세 (commonPopupState):'); + dlog('📋 약관 팝업 상태 상세 (commonPopupState):'); if (commonPopupState.text) { - console.log(' - commonPopupState.text 미리보기:', commonPopupState.text.substring(0, 200) + '...'); + dlog( + ' - commonPopupState.text 미리보기:', + commonPopupState.text.substring(0, 200) + '...' + ); } else { - console.warn('⚠️ commonPopupState.text가 비어있습니다!'); + dwarn('⚠️ commonPopupState.text가 비어있습니다!'); } } }, [commonPopupState]); @@ -651,13 +706,13 @@ export default function TermsOfOptional({ // 약관 데이터 로드 시 Spotlight 활성화 useEffect(() => { if (termsData) { - console.log('termsData loaded:', termsData); - console.log('[SPOTLIGHT] spotlightDisabled를 false로 설정'); + dlog('termsData loaded:', termsData); + dlog('[SPOTLIGHT] spotlightDisabled를 false로 설정'); setSpotlightDisabled(false); - + // Select All 체크박스에 초기 포커스 설정 setTimeout(() => { - console.log('[SPOTLIGHT] checkbox-all에 포커스 설정 시도'); + dlog('[SPOTLIGHT] checkbox-all에 포커스 설정 시도'); Spotlight.set('checkbox-all'); }, 100); } @@ -670,36 +725,40 @@ export default function TermsOfOptional({ Spotlight.set('checkbox-all'); } }, 200); - + return () => clearTimeout(timer); }, [spotlightDisabled]); // 체크박스 상태 변경 시 로깅 추가 useEffect(() => { - console.log('[TermsOfOptional] 체크박스 상태 변경됨:'); + dlog('[TermsOfOptional] 체크박스 상태 변경됨:'); Object.entries(checkboxStates).forEach(([termType, checked]) => { const termId = TERMS_TPCD_MAP[termType]; const isMandatory = isMandatoryTerms(termType); - console.log(` - ${termType} (${termId}) [${isMandatory ? '필수' : '선택'}]: ${checked ? '✅ 동의' : '❌ 비동의'}`); + dlog( + ` - ${termType} (${termId}) [${isMandatory ? '필수' : '선택'}]: ${checked ? '✅ 동의' : '❌ 비동의'}` + ); }); - - const allChecked = Object.values(checkboxStates).every(state => state); - console.log(` - 전체 선택 상태: ${allChecked ? '✅ 모두 선택됨' : '❌ 일부 또는 미선택'}`); + + const allChecked = Object.values(checkboxStates).every((state) => state); + dlog(` - 전체 선택 상태: ${allChecked ? '✅ 모두 선택됨' : '❌ 일부 또는 미선택'}`); }, [checkboxStates]); // API 응답 처리 - 약관 철회 결과 useEffect(() => { - if (setMyTermsWithdrawResult?.data && setMyTermsWithdrawResult?.retCode === "000") { - console.log('✅ 약관 철회 API 응답 성공:', setMyTermsWithdrawResult); + if (setMyTermsWithdrawResult?.data && setMyTermsWithdrawResult?.retCode === '000') { + dlog('✅ 약관 철회 API 응답 성공:', setMyTermsWithdrawResult); // 철회 성공 후 추가 처리가 필요하다면 여기서 구현 } else if (setMyTermsWithdrawResult?.error) { - console.log('❌ 약관 철회 API 응답 실패:', setMyTermsWithdrawResult.error); + derror('❌ 약관 철회 API 응답 실패:', setMyTermsWithdrawResult.error); // 실패 시 에러 처리 - dispatch(setShowPopup(Config.ACTIVE_POPUP.alertPopup, { - title: $L("Error"), - text: $L("약관 철회 처리 중 오류가 발생했습니다."), - button1Text: $L("확인") - })); + dispatch( + setShowPopup(Config.ACTIVE_POPUP.alertPopup, { + title: $L('Error'), + text: $L('약관 철회 처리 중 오류가 발생했습니다.'), + button1Text: $L('확인'), + }) + ); } }, [setMyTermsWithdrawResult, dispatch]); @@ -718,12 +777,12 @@ export default function TermsOfOptional({ return true; } return false; - } + }, ]; - + for (const attempt of focusAttempts) { if (attempt()) { - console.log('포커스 설정 성공'); + dlog('포커스 설정 성공'); break; } } @@ -733,188 +792,207 @@ export default function TermsOfOptional({ } }, [activePopup, popupVisible]); -// API 테스트용 함수 - 단순화 버전 -const handleAllAgreeTestSimple = useCallback(() => { - console.log('🧪 API 테스트: 모든 약관 동의 처리 시작'); - - // 1. 체크박스 상태 업데이트 - const allAgreedStates = { - [TERMS_TYPE.TERMS_CONDITIONS]: true, - [TERMS_TYPE.PRIVACY_POLICY]: true, - [TERMS_TYPE.OPTIONAL_TERMS]: true - }; - - setCheckboxStates(allAgreedStates); - setIsAllChecked(true); - - // 2. API 파라미터 설정 - const termsList = [ - "TID0000222", // 이용약관 (필수) - ]; - - const notTermsList = [ - "TID0000223", // 개인정보처리방침 (필수) - "TID0000232" // 선택약관 - ]; // 비동의 약관 없음 - - console.log('📤 API 호출 파라미터:', { termsList, notTermsList }); - - // 3. 성공 콜백 - const onSuccess = (response) => { - console.log('✅ API 성공:', response); - - if (response?.retCode === 0) { - // MyPage 데이터 새로고침 - dispatch(getMyPageTerms()); - dispatch(getMyPageOptionalTerms()); - - // 성공 알림 - dispatch(setShowPopup(Config.ACTIVE_POPUP.alertPopup, { - title: "테스트 성공", - text: "약관 동의가 완료되었습니다.", - button1Text: "확인" - })); - } else { - console.error('❌ 서버 오류:', response); - dispatch(setShowPopup(Config.ACTIVE_POPUP.alertPopup, { - title: "서버 오류", - text: `오류: ${response?.retMsg || '알 수 없는 오류'}`, - button1Text: "확인" - })); - } - }; - - // 4. API 호출 - dispatch(setMyPageTermsAgree({ termsList, notTermsList }, onSuccess)); - -}, [dispatch, setCheckboxStates, setIsAllChecked]); - + // API 테스트용 함수 - 단순화 버전 + const handleAllAgreeTestSimple = useCallback(() => { + dlog('🧪 API 테스트: 모든 약관 동의 처리 시작'); + + // 1. 체크박스 상태 업데이트 + const allAgreedStates = { + [TERMS_TYPE.TERMS_CONDITIONS]: true, + [TERMS_TYPE.PRIVACY_POLICY]: true, + [TERMS_TYPE.OPTIONAL_TERMS]: true, + }; + + setCheckboxStates(allAgreedStates); + setIsAllChecked(true); + + // 2. API 파라미터 설정 + const termsList = [ + 'TID0000222', // 이용약관 (필수) + ]; + + const notTermsList = [ + 'TID0000223', // 개인정보처리방침 (필수) + 'TID0000232', // 선택약관 + ]; // 비동의 약관 없음 + + dlog('📤 API 호출 파라미터:', { termsList, notTermsList }); + + // 3. 성공 콜백 + const onSuccess = (response) => { + dlog('✅ API 성공:', response); + + if (response?.retCode === 0) { + // MyPage 데이터 새로고침 + dispatch(getMyPageTerms()); + dispatch(getMyPageOptionalTerms()); + + // 성공 알림 + dispatch( + setShowPopup(Config.ACTIVE_POPUP.alertPopup, { + title: '테스트 성공', + text: '약관 동의가 완료되었습니다.', + button1Text: '확인', + }) + ); + } else { + derror('❌ 서버 오류:', response); + dispatch( + setShowPopup(Config.ACTIVE_POPUP.alertPopup, { + title: '서버 오류', + text: `오류: ${response?.retMsg || '알 수 없는 오류'}`, + button1Text: '확인', + }) + ); + } + }; + + // 4. API 호출 + dispatch(setMyPageTermsAgree({ termsList, notTermsList }, onSuccess)); + }, [dispatch, setCheckboxStates, setIsAllChecked]); + // API 테스트용 함수 - setMyPageTermsAgree 사용하도록 수정 -const handleAllAgreeTest = useCallback(() => { - console.log('🧪 [API-TEST] registerDevice 테스트 시작'); - - // 1. 모든 체크박스를 true로 강제 설정 (기존 로직 유지) - const allAgreedStates = { - [TERMS_TYPE.TERMS_CONDITIONS]: true, - [TERMS_TYPE.PRIVACY_POLICY]: true, - [TERMS_TYPE.OPTIONAL_TERMS]: true - }; - - setCheckboxStates(allAgreedStates); - setIsAllChecked(true); - - // 2. 동의한 약관 ID 배열 생성 (기존 로직 유지) - const agreedTerms = [ - "TID0000222", // TERMS_CONDITIONS (필수) - "TID0000223", // PRIVACY_POLICY (필수) - "TID0000232" // OPTIONAL_TERMS (선택) - ]; + const handleAllAgreeTest = useCallback(() => { + dlog('🧪 [API-TEST] registerDevice 테스트 시작'); - console.log('🔥 [API-TEST] registerDevice 호출 파라미터:', agreedTerms); - - // 3. ✅ registerDevice로 교체 - const onTestSuccess = (response) => { - console.log('🧪 [API-TEST] ✅ registerDevice 성공 응답:', response); - - if (response && (response.retCode === "000" || response.retCode === 0)) { - console.log('🧪 [API-TEST] ✅ 디바이스 등록 성공!'); - - // MyPage 데이터 새로고침 (필요시) - dispatch(getMyPageTerms()); - dispatch(getMyPageOptionalTerms()); - - // 성공 알림 팝업 - dispatch(setShowPopup(Config.ACTIVE_POPUP.alertPopup, { - title: "🧪 registerDevice 테스트 성공", - text: `디바이스 등록 성공!\n응답: ${response.retMsg || 'OK'}\n동의한 약관: ${agreedTerms.length}개`, - button1Text: "확인" - })); - - } else { - console.error('🧪 [API-TEST] ❌ registerDevice 오류:', response); - - dispatch(setShowPopup(Config.ACTIVE_POPUP.alertPopup, { - title: "🧪 registerDevice 테스트 실패", - text: `retCode: ${response?.retCode}\nretMsg: ${response?.retMsg}`, - button1Text: "확인" - })); - } - }; + // 1. 모든 체크박스를 true로 강제 설정 (기존 로직 유지) + const allAgreedStates = { + [TERMS_TYPE.TERMS_CONDITIONS]: true, + [TERMS_TYPE.PRIVACY_POLICY]: true, + [TERMS_TYPE.OPTIONAL_TERMS]: true, + }; - // 4. ✅ registerDevice API 호출 - dispatch(registerDevice({ - // registerDevice에 맞는 파라미터 구성 - agreedTermsList: agreedTerms, - // 기타 필요한 디바이스 정보들... - }, onTestSuccess)); - -}, [dispatch, setCheckboxStates, setIsAllChecked]); + setCheckboxStates(allAgreedStates); + setIsAllChecked(true); + + // 2. 동의한 약관 ID 배열 생성 (기존 로직 유지) + const agreedTerms = [ + 'TID0000222', // TERMS_CONDITIONS (필수) + 'TID0000223', // PRIVACY_POLICY (필수) + 'TID0000232', // OPTIONAL_TERMS (선택) + ]; + + dlog('🔥 [API-TEST] registerDevice 호출 파라미터:', agreedTerms); + + // 3. ✅ registerDevice로 교체 + const onTestSuccess = (response) => { + dlog('🧪 [API-TEST] ✅ registerDevice 성공 응답:', response); + + if (response && (response.retCode === '000' || response.retCode === 0)) { + dlog('🧪 [API-TEST] ✅ 디바이스 등록 성공!'); + + // MyPage 데이터 새로고침 (필요시) + dispatch(getMyPageTerms()); + dispatch(getMyPageOptionalTerms()); + + // 성공 알림 팝업 + dispatch( + setShowPopup(Config.ACTIVE_POPUP.alertPopup, { + title: '🧪 registerDevice 테스트 성공', + text: `디바이스 등록 성공!\n응답: ${response.retMsg || 'OK'}\n동의한 약관: ${agreedTerms.length}개`, + button1Text: '확인', + }) + ); + } else { + derror('🧪 [API-TEST] ❌ registerDevice 오류:', response); + + dispatch( + setShowPopup(Config.ACTIVE_POPUP.alertPopup, { + title: '🧪 registerDevice 테스트 실패', + text: `retCode: ${response?.retCode}\nretMsg: ${response?.retMsg}`, + button1Text: '확인', + }) + ); + } + }; + + // 4. ✅ registerDevice API 호출 + dispatch( + registerDevice( + { + // registerDevice에 맞는 파라미터 구성 + agreedTermsList: agreedTerms, + // 기타 필요한 디바이스 정보들... + }, + onTestSuccess + ) + ); + }, [dispatch, setCheckboxStates, setIsAllChecked]); // TERMS_TPCD_MAP 검증 함수 const validateTermsIdMap = useCallback(() => { - console.log('🔍 [VALIDATION] TERMS_TPCD_MAP 검증 시작'); - console.log('🔍 [VALIDATION] 전체 TERMS_TPCD_MAP:', TERMS_TPCD_MAP); - console.log('🔍 [VALIDATION] 전체 TERMS_TYPE:', TERMS_TYPE); - + dlog('🔍 [VALIDATION] TERMS_TPCD_MAP 검증 시작'); + dlog('🔍 [VALIDATION] 전체 TERMS_TPCD_MAP:', TERMS_TPCD_MAP); + dlog('🔍 [VALIDATION] 전체 TERMS_TYPE:', TERMS_TYPE); + Object.entries(TERMS_TYPE).forEach(([key, type]) => { const id = TERMS_TPCD_MAP[type]; - console.log(`🔍 [VALIDATION] ${key} (${type}) -> ${id} (${typeof id})`); - + dlog(`🔍 [VALIDATION] ${key} (${type}) -> ${id} (${typeof id})`); + if (!id) { - console.error(`❌ [VALIDATION] ${type}에 대한 ID가 없습니다!`); + derror(`❌ [VALIDATION] ${type}에 대한 ID가 없습니다!`); } else { - console.log(`✅ [VALIDATION] ${type}: 유효한 ID`); + dlog(`✅ [VALIDATION] ${type}: 유효한 ID`); } }); - + // 팝업으로 결과 표시 - const validIds = Object.values(TERMS_TYPE).map(type => TERMS_TPCD_MAP[type]).filter(Boolean); + const validIds = Object.values(TERMS_TYPE) + .map((type) => TERMS_TPCD_MAP[type]) + .filter(Boolean); const totalTypes = Object.keys(TERMS_TYPE).length; - - dispatch(setShowPopup(Config.ACTIVE_POPUP.alertPopup, { - title: "🔍 TERMS_TPCD_MAP 검증 결과", - text: `전체 약관 타입: ${totalTypes}개\n유효한 ID: ${validIds.length}개\n\n${validIds.join('\n')}`, - button1Text: "확인" - })); + + dispatch( + setShowPopup(Config.ACTIVE_POPUP.alertPopup, { + title: '🔍 TERMS_TPCD_MAP 검증 결과', + text: `전체 약관 타입: ${totalTypes}개\n유효한 ID: ${validIds.length}개\n\n${validIds.join('\n')}`, + button1Text: '확인', + }) + ); }, [dispatch]); // 간단한 API 테스트 함수 const simpleApiTest = useCallback(() => { - console.log('🧪 [SIMPLE-TEST] 간단 API 테스트 시작'); - - const testIds = ["MST00402", "MST00401", "MST00405"]; - - console.log('🧪 [SIMPLE-TEST] 하드코딩된 테스트 ID:', testIds); + dlog('🧪 [SIMPLE-TEST] 간단 API 테스트 시작'); - dispatch(setMyPageTermsAgree({ - termsList: testIds, - notTermsList: [], - onComplete: (res) => { - console.log('🧪 [SIMPLE-TEST] ✅ 성공:', res); - const retCode = res?.data?.retCode; - const retMsg = res?.data?.retMsg; - - dispatch(setShowPopup(Config.ACTIVE_POPUP.alertPopup, { - title: "🧪 간단 API 테스트 성공", - text: `retCode: ${retCode}\nretMsg: ${retMsg}\n전송 ID: ${testIds.join(', ')}`, - button1Text: "확인" - })); - }, - onError: (err) => { - console.error('🧪 [SIMPLE-TEST] ❌ 실패:', err); - dispatch(setShowPopup(Config.ACTIVE_POPUP.alertPopup, { - title: "🧪 간단 API 테스트 실패", - text: `오류: ${err?.message || '알 수 없음'}\n전송 ID: ${testIds.join(', ')}`, - button1Text: "확인" - })); - } - })); + const testIds = ['MST00402', 'MST00401', 'MST00405']; + + dlog('🧪 [SIMPLE-TEST] 하드코딩된 테스트 ID:', testIds); + + dispatch( + setMyPageTermsAgree({ + termsList: testIds, + notTermsList: [], + onComplete: (res) => { + dlog('🧪 [SIMPLE-TEST] ✅ 성공:', res); + const retCode = res?.data?.retCode; + const retMsg = res?.data?.retMsg; + + dispatch( + setShowPopup(Config.ACTIVE_POPUP.alertPopup, { + title: '🧪 간단 API 테스트 성공', + text: `retCode: ${retCode}\nretMsg: ${retMsg}\n전송 ID: ${testIds.join(', ')}`, + button1Text: '확인', + }) + ); + }, + onError: (err) => { + derror('🧪 [SIMPLE-TEST] ❌ 실패:', err); + dispatch( + setShowPopup(Config.ACTIVE_POPUP.alertPopup, { + title: '🧪 간단 API 테스트 실패', + text: `오류: ${err?.message || '알 수 없음'}\n전송 ID: ${testIds.join(', ')}`, + button1Text: '확인', + }) + ); + }, + }) + ); }, [dispatch]); - return ( { > {/* */} -
{/* Welcome Section - 항상 표시 */}
- {$L("Welcome to")} + {$L('Welcome to')}

Sh o @@ -936,7 +1013,9 @@ const handleAllAgreeTest = useCallback(() => {

- {$L("Check out more LIVE SHOWS now and enjoy shopping at Shop Time's special price on your TV by agreeing to the LG TV Shopping Terms and Conditions.")} + {$L( + "Check out more LIVE SHOWS now and enjoy shopping at Shop Time's special price on your TV by agreeing to the LG TV Shopping Terms and Conditions." + )}

@@ -944,23 +1023,23 @@ const handleAllAgreeTest = useCallback(() => {
{Object.entries(TERMS_TYPE).map(([key, type], index) => { - console.log(`[RENDER] ${type} 체크박스 렌더링:`, { + dlog(`[RENDER] ${type} 체크박스 렌더링:`, { selected: checkboxStates[type], disabled: spotlightDisabled, - id: `checkbox-${type.toLowerCase()}` + id: `checkbox-${type.toLowerCase()}`, }); - + return (
- { - console.log(`[CHECKBOX-ONCLICK] ${type} 체크박스 직접 클릭됨`, e); - console.log(`[CHECKBOX-ONCLICK] 현재 disabled 상태:`, spotlightDisabled); - console.log(`[CHECKBOX-ONCLICK] 현재 selected 상태:`, checkboxStates[type]); + dlog(`[CHECKBOX-ONCLICK] ${type} 체크박스 직접 클릭됨`, e); + dlog(`[CHECKBOX-ONCLICK] 현재 disabled 상태:`, spotlightDisabled); + dlog(`[CHECKBOX-ONCLICK] 현재 selected 상태:`, checkboxStates[type]); }} className={css.termCheckbox} disabled={spotlightDisabled} @@ -990,7 +1069,9 @@ const handleAllAgreeTest = useCallback(() => { ) } > - + {TERMS_DISPLAY_NAMES[type]} @@ -999,36 +1080,35 @@ const handleAllAgreeTest = useCallback(() => { })}
-
+
{/*
*/} - + {/*
*/} -
- { - console.log(`[SELECT-ALL-ONCLICK] 전체 선택 체크박스 직접 클릭됨`, e); - console.log(`[SELECT-ALL-ONCLICK] 현재 disabled 상태:`, spotlightDisabled); - console.log(`[SELECT-ALL-ONCLICK] 현재 isAllChecked 상태:`, isAllChecked); + dlog(`[SELECT-ALL-ONCLICK] 전체 선택 체크박스 직접 클릭됨`, e); + dlog(`[SELECT-ALL-ONCLICK] 현재 disabled 상태:`, spotlightDisabled); + dlog(`[SELECT-ALL-ONCLICK] 현재 isAllChecked 상태:`, isAllChecked); }} className={css.selectAllCheckbox} disabled={spotlightDisabled} spotlightDisabled={spotlightDisabled} tabIndex={1} /> - {$L("Select All")} + {$L('Select All')}
{/* 하단 버튼 레이어 - 전체화면 모드에서는 항상 두 버튼 모두 표시 */} @@ -1036,19 +1116,18 @@ const handleAllAgreeTest = useCallback(() => { - {$L("Agree")} - - - + {$L('Agree')} + + {/* 전체화면 모드에서는 항상 "Do Not Agree" 버튼 표시 */} - - {$L("Do Not Agree")} + {$L('Do Not Agree')}
@@ -1057,7 +1136,7 @@ const handleAllAgreeTest = useCallback(() => { { - console.log('TermsPopup - Agree clicked for type:', currentTermsType); + dlog('TermsPopup - Agree clicked for type:', currentTermsType); // Agree 클릭 시 해당 약관의 체크박스를 토글 (동의) if (currentTermsType) { toggleCheckboxDirectly(currentTermsType); @@ -1068,7 +1147,7 @@ const handleAllAgreeTest = useCallback(() => { setCurrentTermsType(null); }} onClose={() => { - console.log('TermsPopup - Close clicked (no agreement change)'); + dlog('TermsPopup - Close clicked (no agreement change)'); // Close 클릭 시 체크박스 상태 변경 없이 팝업만 닫기 setShowTermsPopup(false); setCurrentTermsData(null); @@ -1077,7 +1156,7 @@ const handleAllAgreeTest = useCallback(() => { termsData={currentTermsData} isAlreadyAgreed={currentTermsType ? checkboxStates[currentTermsType] || false : false} /> - + {/* 알림 팝업 */} {activePopup === Config.ACTIVE_POPUP.alertPopup && popupVisible && ( { onClose={handleClosePopup} onButton1Press={handleClosePopup} hasButton - button1Text={commonPopupState.button1Text || $L("OK")} + button1Text={commonPopupState.button1Text || $L('OK')} hasText title={commonPopupState.title} text={commonPopupState.text} @@ -1103,16 +1182,16 @@ const handleAllAgreeTest = useCallback(() => { onExit={onExit} onClose={onClose} hasButton - button1Text={$L("Yes")} - button2Text={$L("No")} + button1Text={$L('Yes')} + button2Text={$L('No')} hasText - title={$L("")} + title={$L('')} text={
- {$L("Are you sure you want to opt out?")} + {$L('Are you sure you want to opt out?')}
-

- {$L("The service will not be available anymore after opting out.")} +

+ {$L('The service will not be available anymore after opting out.')}

} @@ -1121,4 +1200,4 @@ const handleAllAgreeTest = useCallback(() => { ); -} \ No newline at end of file +} diff --git a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx index 2818d20f..86a264e9 100644 --- a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx +++ b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx @@ -66,6 +66,21 @@ import TabContainerV2 from './PlayerTabContents/v2/TabContainer.v2'; const Container = SpotlightContainerDecorator({ enterTo: 'last-focused', preserveId: true }, 'div'); +// Toggle debug logging for this view +const DEBUG_MODE = false; + +const dlog = (...args) => { + if (DEBUG_MODE) console.log(...args); +}; + +const dwarn = (...args) => { + if (DEBUG_MODE) console.warn(...args); +}; + +const derror = (...args) => { + console.error(...args); +}; + const findSelector = (selector, maxAttempts = 5, currentAttempts = 0) => { try { if (currentAttempts >= maxAttempts) { @@ -363,7 +378,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props // PlayerPanel.jsx의 라인 313-327 useEffect 수정 - detailPanelClosed flag 감지 추가 useEffect(() => { - console.log('[PlayerPanel] 🔍 isOnTop useEffect 호출:', { + dlog('[PlayerPanel] 🔍 isOnTop useEffect 호출:', { isOnTop, modal: panelInfo?.modal, isPaused: panelInfo?.isPaused, @@ -374,18 +389,18 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props // 1. Resume Video if needed (isPaused or detailPanelClosed) if (panelInfo.isPaused || panelInfo.detailPanelClosed) { if (panelInfo.modal) { - console.log('[PlayerPanel] ▶️ Back on top (Modal) - resuming video'); + dlog('[PlayerPanel] ▶️ Back on top (Modal) - resuming video'); dispatch(resumeModalVideo()); } else { - console.log('[PlayerPanel] ▶️ Back on top (Fullscreen) - resuming video'); + dlog('[PlayerPanel] ▶️ Back on top (Fullscreen) - resuming video'); dispatch(resumeFullscreenVideo()); } } // 2. Reset detailPanelClosed flag if (panelInfo.detailPanelClosed) { - console.log('[PlayerPanel] 🔄 detailPanelClosed flag 초기화 시작'); - console.log('[PlayerPanel] 🔙 DetailPanel에서 복귀 정보:', { + dlog('[PlayerPanel] 🔄 detailPanelClosed flag 초기화 시작'); + dlog('[PlayerPanel] 🔙 DetailPanel에서 복귀 정보:', { detailPanelClosedAt: panelInfo.detailPanelClosedAt, detailPanelClosedFromSource: panelInfo.detailPanelClosedFromSource, lastFocusedTargetId: panelInfo.lastFocusedTargetId, @@ -393,13 +408,13 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props // 포커스 복원 로직 추가 (1000ms 지연) if (panelInfo.lastFocusedTargetId) { - console.log( + dlog( '[PlayerPanel] 🎯 DetailPanel 복귀 후 1000ms 지연 포커스 복원 예약:', panelInfo.lastFocusedTargetId ); const focusTimeoutId = setTimeout(() => { - console.log( + dlog( '[PlayerPanel] 🔍 DetailPanel 복귀 후 포커스 복원 실행:', panelInfo.lastFocusedTargetId ); @@ -410,7 +425,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props return () => { if (focusTimeoutId) { clearTimeout(focusTimeoutId); - console.log('[PlayerPanel] 🧹 포커스 복원 타이머 정리됨'); + dlog('[PlayerPanel] 🧹 포커스 복원 타이머 정리됨'); } }; } @@ -426,12 +441,12 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props }, }) ); - console.log('[PlayerPanel] 🏁 detailPanelClosed flag 초기화 완료'); + dlog('[PlayerPanel] 🏁 detailPanelClosed flag 초기화 완료'); } } else { // Not on top if (panelInfo && panelInfo.modal) { - console.log('[PlayerPanel] ⏸️ Not on top (Modal) - pausing video logic commented out'); + dlog('[PlayerPanel] ⏸️ Not on top (Modal) - pausing video logic commented out'); // dispatch(pauseModalVideo()); } } @@ -464,7 +479,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props (isOnTop && videoPlayState && hasSignificantChange) || (isDetailPanelOnTop && (isPlayingChanged || isPausedChanged)) ) { - console.log('📊 [PlayerPanel] Significant videoPlayState change', { + dlog('📊 [PlayerPanel] Significant videoPlayState change', { previousState: previousVideoPlayState.current, currentState: videoPlayState, topPanelName: topPanel?.name, @@ -492,7 +507,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props if (isOnTop && panelInfo?.isPaused !== undefined && isPausedChanged) { // 상태 변경 시에만 디버깅 로그 출력 - console.log('🔍 [PlayerPanel] PanelInfo isPaused changed', { + dlog('🔍 [PlayerPanel] PanelInfo isPaused changed', { previousIsPaused: previousPanelInfo.current?.isPaused, currentIsPaused: panelInfo.isPaused, isOnTop, @@ -500,7 +515,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props currentPlayingUrl, timestamp: new Date().toISOString(), }); - console.log('🎮 [PlayerPanel] PanelInfo isPaused changed', { + dlog('🎮 [PlayerPanel] PanelInfo isPaused changed', { previousIsPaused: previousPanelInfo.current?.isPaused, currentIsPaused: panelInfo.isPaused, videoPlayerExists: !!videoPlayer.current, @@ -513,10 +528,10 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props if (videoPlayer.current) { if (panelInfo.isPaused === true) { - console.log('🔴 [PlayerPanel] Calling VideoPlayer.pause() due to PanelInfo change'); + dlog('🔴 [PlayerPanel] Calling VideoPlayer.pause() due to PanelInfo change'); videoPlayer.current.pause(); } else if (panelInfo.isPaused === false) { - console.log('🟢 [PlayerPanel] Calling VideoPlayer.play() due to PanelInfo change'); + dlog('🟢 [PlayerPanel] Calling VideoPlayer.play() due to PanelInfo change'); videoPlayer.current.play(); } } @@ -550,7 +565,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props ? 'Top panel changed' : 'VideoPlayer created'; - console.log('🎬 [PlayerPanel] VideoPlayer state change', { + dlog('🎬 [PlayerPanel] VideoPlayer state change', { hasVideoPlayer: !!videoPlayer.current, currentPlayingUrl, previousVideoSource: previousVideoSource.current, @@ -570,7 +585,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props if (isVideoSourceChanged || videoPlayerJustCreated || isDetailPanelOnTopChanged) { const mediaState = videoPlayer.current.getMediaState?.(); if (mediaState) { - console.log('📊 [PlayerPanel] VideoPlayer current state', { + dlog('📊 [PlayerPanel] VideoPlayer current state', { mediaState, videoPlayState, changeReason, @@ -590,7 +605,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props const currentModalState = panelInfo?.modal; const prevModalState = modalPrevRef.current; - console.log('[PlayerPanel] 🔍 Modal 상태 체크:', { + dlog('[PlayerPanel] 🔍 Modal 상태 체크:', { prevModalState, currentModalState, shouldExecuteTrueToFalse: prevModalState === true && currentModalState === false, @@ -599,8 +614,8 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props // true → false 변화 감지 if (prevModalState === true && currentModalState === false) { - console.log('[PlayerPanel] ▶️ Modal 상태 변화: true → false (전체화면 모드로 복귀)'); - console.log( + dlog('[PlayerPanel] ▶️ Modal 상태 변화: true → false (전체화면 모드로 복귀)'); + dlog( '[PlayerPanel] 🎯 포커스 복원 준비 - lastFocusedTargetId:', panelInfo?.lastFocusedTargetId ); @@ -612,7 +627,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props if (lastFocusedTargetId) { // ShopNowContents가 렌더링될 때까지 대기 후 포커스 복원 setTimeout(() => { - console.log('[PlayerPanel] 🔍 800ms 후 포커스 복원 시도:', lastFocusedTargetId); + dlog('[PlayerPanel] 🔍 800ms 후 포커스 복원 시도:', lastFocusedTargetId); Spotlight.focus(lastFocusedTargetId); }, 800); } @@ -620,7 +635,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props // false → true 변화 감지 (모달이 열림) if (prevModalState === false && currentModalState === true) { - console.log('[PlayerPanel] 📱 Modal 상태 변화: false → true (모달 열림)'); + dlog('[PlayerPanel] 📱 Modal 상태 변화: false → true (모달 열림)'); setIsModalClosed(false); // 모달이 열렸음을 표시 } @@ -1273,7 +1288,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props // 🔍 DetailPanel 상태 변화 로깅 if (result) { - console.log('🎬 [PlayerPanel] DetailPanel is now on top (from Player)', { + dlog('🎬 [PlayerPanel] DetailPanel is now on top (from Player)', { topPanelName: topPanel?.name, launchedFromPlayer: topPanel?.panelInfo?.launchedFromPlayer, modalPlayerPanelExists: panels.some( @@ -1299,7 +1314,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props // PlayerPanel이 modal이고 밑에 깔렸을 때 if (isModalPlayerPanel && !isCurrentPanelOnTop && isDetailPanelOnTop) { - console.log('🔴 [PlayerPanel] Self-pausing due to DetailPanel on top', { + dlog('🔴 [PlayerPanel] Self-pausing due to DetailPanel on top', { isDetailPanelOnTop, isModalPlayerPanel, isCurrentPanelOnTop, @@ -1310,7 +1325,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props // PlayerPanel 자신의 상태를 일시정지로 업데이트 if (videoPlayState?.isPlaying === true || videoPlayState?.isPaused === false) { - console.log('🔄 [PlayerPanel] Dispatching self-pause to Redux'); + dlog('🔄 [PlayerPanel] Dispatching self-pause to Redux'); dispatch( updateVideoPlayState({ isPlaying: false, @@ -1404,7 +1419,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props } } else { // showId가 일치하지 않으면 이전 상태를 재활용하지 않고 초기화 - console.log('[PlayerPanel] VOD showDetailInfo mismatch. Clearing playListInfo.', { + dlog('[PlayerPanel] VOD showDetailInfo mismatch. Clearing playListInfo.', { panelInfoShowId: panelInfo.showId, showDetailInfoId: showDetailInfo[0]?.showId, }); @@ -1686,7 +1701,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props (ev) => { const type = ev.type; if (type !== 'timeupdate' && type !== 'durationchange') { - console.log('mediainfoHandler....', type, ev, videoPlayer.current?.getMediaState()); + dlog('mediainfoHandler....', type, ev, videoPlayer.current?.getMediaState()); } if (ev === 'hlsError' && isNaN(Number(videoPlayer.current?.getMediaState().playbackRate))) { dispatch( @@ -1717,7 +1732,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props const mediaId = videoPlayer.current?.video?.media?.mediaId; setMediaId(mediaId); setVideoLoaded(true); - console.log( + dlog( '[PlayerPanel] 🎬 Video Loaded - shptmBanrTpNm:', panelInfoRef.current?.shptmBanrTpNm ); @@ -1751,7 +1766,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props useEffect(() => { // 복구 직후: skipModalStyleRecalculation이 true면 저장된 modalStyle 적용 if (panelInfo.skipModalStyleRecalculation && !panelInfo.shouldShrinkTo1px) { - console.log('[PlayerPanel] Condition 2.5: Using saved modalStyle from expand'); + dlog('[PlayerPanel] Condition 2.5: Using saved modalStyle from expand'); const shrinkInfo = panelInfo.playerState?.shrinkInfo; // 저장된 modalStyle 사용 (top, left 포함) @@ -1767,7 +1782,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props if (typeof window !== 'undefined') { window.requestAnimationFrame(() => { window.requestAnimationFrame(() => { - console.log('[PlayerPanel] Condition 2.5: Removing skipFlag after DOM render'); + dlog('[PlayerPanel] Condition 2.5: Removing skipFlag after DOM render'); dispatch( updatePanel({ name: panel_names.PLAYER_PANEL, @@ -1788,7 +1803,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props panelInfo.modalContainerId && (lastPanelAction === 'previewPush' || lastPanelAction === 'previewUpdate') ) { - console.log('[PlayerPanel] Condition 1: Calculating modalStyle from DOM', { + dlog('[PlayerPanel] Condition 1: Calculating modalStyle from DOM', { lastPanelAction, }); const node = document.querySelector(`[data-spotlight-id="${panelInfo.modalContainerId}"]`); @@ -1815,17 +1830,17 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props }) ); } else { - console.log('[PlayerPanel] Condition 1: Node not found, using saved modalStyle'); + dlog('[PlayerPanel] Condition 1: Node not found, using saved modalStyle'); setModalStyle(panelInfo.modalStyle); setModalScale(panelInfo.modalScale); } } else if (panelInfo.shouldShrinkTo1px) { - console.log('[PlayerPanel] Condition 2: Shrinking - clearing modalStyle'); + dlog('[PlayerPanel] Condition 2: Shrinking - clearing modalStyle'); // 축소 상태: 인라인 스타일 제거 (CSS만 적용) setModalStyle({}); setModalScale(1); } else if (isOnTop && !panelInfo.modal && videoPlayer.current) { - console.log('[PlayerPanel] Condition 3: Playing fullscreen video'); + dlog('[PlayerPanel] Condition 3: Playing fullscreen video'); if (videoPlayer.current?.getMediaState()?.paused) { videoPlayer.current.play(); } @@ -1923,7 +1938,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props // 비디오가 새로 선택될 때 타이머 초기화 useEffect(() => { if (currentPlayingUrl) { - console.log('[PlayerPanel] 🎬 비디오 선택됨 - tabIndexV2 타이머 초기화'); + dlog('[PlayerPanel] 🎬 비디오 선택됨 - tabIndexV2 타이머 초기화'); resetTimerTabAutoAdvance(10000); } }, [selectedIndex, resetTimerTabAutoAdvance]); @@ -2322,10 +2337,10 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props const clearTimerV2 = useCallback(() => { if (timerIdV2.current) { - console.log('[clearTimerV2] 타이머 클리어됨'); + dlog('[clearTimerV2] 타이머 클리어됨'); const stack = new Error().stack; const lines = stack.split('\n').slice(1, 4).join(' → '); - console.log('[clearTimerV2] 호출 스택:', lines); + dlog('[clearTimerV2] 호출 스택:', lines); } clearTimeout(timerIdV2.current); timerIdV2.current = null; @@ -2333,19 +2348,19 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props const resetTimerV2 = useCallback( (timeout) => { - console.log('[TabContainerV2] resetTimerV2 호출', timeout); + dlog('[TabContainerV2] resetTimerV2 호출', timeout); if (timerIdV2.current) { - console.log('[TabContainerV2] 기존 타이머 클리어'); + dlog('[TabContainerV2] 기존 타이머 클리어'); clearTimerV2(); } if (initialEnterV2) { - console.log('[TabContainerV2] initialEnterV2 false로 변경'); + dlog('[TabContainerV2] initialEnterV2 false로 변경'); setInitialEnterV2(false); } timerIdV2.current = setTimeout(() => { - console.log('[TabContainerV2] 타이머 실행 - belowContentsVisible false로 변경 (30초 경과)'); + dlog('[TabContainerV2] 타이머 실행 - belowContentsVisible false로 변경 (30초 경과)'); setBelowContentsVisible(false); }, timeout); }, @@ -2373,9 +2388,9 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props // Redux로 오버레이 숨김 useEffect(() => { if (shouldHideOverlays) { - console.log('[PlayerPanel] shouldHideOverlays true - 오버레이 숨김'); + dlog('[PlayerPanel] shouldHideOverlays true - 오버레이 숨김'); setSideContentsVisible(false); - console.log('[setBelowContentsVisible] Redux로 오버레이 숨김 - false로 변경'); + dlog('[setBelowContentsVisible] Redux로 오버레이 숨김 - false로 변경'); setBelowContentsVisible(false); if (videoPlayer.current?.hideControls) { @@ -2400,9 +2415,9 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props // Redux로 오버레이 표시 useEffect(() => { if (shouldShowOverlays) { - console.log('[PlayerPanel] shouldShowOverlays true - 오버레이 표시'); + dlog('[PlayerPanel] shouldShowOverlays true - 오버레이 표시'); setSideContentsVisible(true); - console.log('[setBelowContentsVisible] Redux로 오버레이 표시 - true로 변경'); + dlog('[setBelowContentsVisible] Redux로 오버레이 표시 - true로 변경'); setBelowContentsVisible(true); if (videoPlayer.current?.showControls) { @@ -2424,33 +2439,33 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props const isDetailPanelReturn = prevIsTopPanelDetailFromPlayerRef.current === true; if (isDetailPanelReturn) { - console.log('[PlayerPanel] ✅ PlayerPanel 내부 DetailPanel에서 복귀함! - 오버레이 표시'); + dlog('[PlayerPanel] ✅ PlayerPanel 내부 DetailPanel에서 복귀함! - 오버레이 표시'); } else if (isHomePanelReturn) { - console.log('[PlayerPanel] 📺 HomePanel에서 복귀함 - 오버레이 표시'); + dlog('[PlayerPanel] 📺 HomePanel에서 복귀함 - 오버레이 표시'); // HomePanel에서 복귀 시 콘텐츠 타입에 따라 tabIndex 설정 - console.log('[PlayerPanel] 🔄 HomePanel 복귀 - tabIndex를 콘텐츠 타입에 따라 설정'); + dlog('[PlayerPanel] 🔄 HomePanel 복귀 - tabIndex를 콘텐츠 타입에 따라 설정'); if (tabContainerVersion === 2) { if (panelInfoRef.current.shptmBanrTpNm === 'VOD') { setTabIndexV2(2); - console.log('[PlayerPanel] 📝 VOD 콘텐츠 - tabIndexV2를 2로 설정됨'); + dlog('[PlayerPanel] 📝 VOD 콘텐츠 - tabIndexV2를 2로 설정됨'); } else { setTabIndexV2(1); - console.log('[PlayerPanel] 📝 LIVE 콘텐츠 - tabIndexV2를 1로 설정됨'); + dlog('[PlayerPanel] 📝 LIVE 콘텐츠 - tabIndexV2를 1로 설정됨'); } } } else { - console.log('[PlayerPanel] 🔄 그 외 복귀 - 오버레이 표시'); + dlog('[PlayerPanel] 🔄 그 외 복귀 - 오버레이 표시'); } setSideContentsVisible(true); - console.log('[setBelowContentsVisible] 복귀 - true로 변경'); + dlog('[setBelowContentsVisible] 복귀 - true로 변경'); setBelowContentsVisible(true); // VideoPlayer가 belowContentsVisible prop을 감지해서 자동으로 controls 표시함 // PlayerPanel 내부 DetailPanel에서 복귀 시에만 포커스 복원 시도 if (isDetailPanelReturn) { const lastFocusedTargetId = panelInfo?.lastFocusedTargetId; - console.log( + dlog( '[PlayerPanel] 🎯 PlayerPanel DetailPanel 복귀 - lastFocusedTargetId:', lastFocusedTargetId ); @@ -2458,7 +2473,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props if (lastFocusedTargetId) { // ShopNowContents가 렌더링될 때까지 잠시 대기 후 포커스 복원 setTimeout(() => { - console.log('[PlayerPanel] 🔍 500ms 후 포커스 복원 시도:', lastFocusedTargetId); + dlog('[PlayerPanel] 🔍 500ms 후 포커스 복원 시도:', lastFocusedTargetId); Spotlight.focus(lastFocusedTargetId); }, 500); } @@ -2484,17 +2499,17 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props return; } - console.log( + dlog( '[PlayerPanel] 📺 Modal false 상태 - shptmBanrTpNm:', panelInfoRef.current?.shptmBanrTpNm ); if (panelInfoRef.current?.shptmBanrTpNm === 'VOD') { setTabIndexV2(2); - console.log('[PlayerPanel] 📝 VOD 콘텐츠 - tabIndexV2를 2로 설정됨'); + dlog('[PlayerPanel] 📝 VOD 콘텐츠 - tabIndexV2를 2로 설정됨'); } else { setTabIndexV2(1); - console.log('[PlayerPanel] 📝 LIVE 콘텐츠 - tabIndexV2를 1로 설정됨'); + dlog('[PlayerPanel] 📝 LIVE 콘텐츠 - tabIndexV2를 1로 설정됨'); } } }, [isOnTop, panelInfo.modal, videoVerticalVisible, tabContainerVersion]); @@ -2566,30 +2581,30 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props if (!isTransitionedTo2) { if (timerIdV2.current) { - console.log('[TabContainerV2] 타이머 클리어 - tabIndex가 2가 아님', tabIndexV2); + dlog('[TabContainerV2] 타이머 클리어 - tabIndex가 2가 아님', tabIndexV2); clearTimerV2(); } return; } - console.log('[TabContainerV2] tabIndex 1 → 2 감지, 타이머 시작'); + dlog('[TabContainerV2] tabIndex 1 → 2 감지, 타이머 시작'); if (!belowContentsVisible || videoVerticalVisible) { - console.log( + dlog( '[TabContainerV2] early return - belowContentsVisible 또는 videoVerticalVisible 조건 불만족' ); return; } // tabIndex 1 → 2로 변경된 정확한 시점에 30초 타이머 시작 - console.log('[TabContainerV2] 30초 타이머 시작'); + dlog('[TabContainerV2] 30초 타이머 시작'); resetTimerV2(REGULAR_TIMEOUT); return () => { // cleanup: tabIndex가 2가 아니거나 오버레이가 사라질 때만 타이머 클리어 if (!belowContentsVisible || videoVerticalVisible || tabIndexV2 !== 2) { if (timerIdV2.current) { - console.log('[TabContainerV2] cleanup - 타이머 클리어'); + dlog('[TabContainerV2] cleanup - 타이머 클리어'); clearTimerV2(); } }