From 44d3c05678b601a043a6101c22d991c1c03ecc4f Mon Sep 17 00:00:00 2001 From: "opacity@t-win.kr" Date: Thu, 28 Aug 2025 10:45:16 +0900 Subject: [PATCH] =?UTF-8?q?deeplink=20=EB=94=94=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=9E=AC=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- com.twin.app.shoptime/src/App/App.js | 51 ++++++----- .../src/App/deepLinkHandler.js | 2 +- .../src/actions/actionTypes.js | 10 ++- .../src/actions/commonActions.js | 78 +++++++++-------- .../src/reducers/commonReducer.js | 61 +++++++++---- .../src/utils/helperMethods.js | 54 +++++++++++- .../src/views/MainView/MainView.jsx | 86 +++++++++++++++++-- 7 files changed, 257 insertions(+), 85 deletions(-) diff --git a/com.twin.app.shoptime/src/App/App.js b/com.twin.app.shoptime/src/App/App.js index cbeceb2c..535effed 100644 --- a/com.twin.app.shoptime/src/App/App.js +++ b/com.twin.app.shoptime/src/App/App.js @@ -71,8 +71,6 @@ import { sendLogTotalRecommend } from "../actions/logActions"; // } from "../utils/focus-monitor"; // import { PanelHoc } from "../components/TPanel/TPanel"; - - let foreGroundChangeTimer = null; // 기존 콘솔 메서드를 백업 @@ -149,15 +147,15 @@ function AppBase(props) { (state) => state.common.appStatus.cursorVisible ); const introTermsAgree = useSelector((state) => state.common.introTermsAgree); - const deviceRegistered = useSelector((state) => state.common.deviceRegistered); + const deviceRegistered = useSelector( + (state) => state.common.deviceRegistered + ); // const optionalTermsAgree = useSelector((state) => state.common.optionalTermsAgree); const termsLoading = useSelector((state) => state.common.termsLoading); // termsFlag 전체 상태 확인 // const termsFlag = useSelector((state) => state.common.termsFlag); const termsData = useSelector((state) => state.home.termsData); - - useEffect(() => { // Chromium68 호환성을 위해 Optional Chaining 제거 if (termsData && termsData.data && termsData.data.terms) { @@ -173,7 +171,8 @@ function AppBase(props) { // const macAddress = useSelector((state) => state.common.macAddress); // Chromium68 호환성을 위해 Optional Chaining 제거 - const deviceCountryCode = httpHeader && httpHeader["X-Device-Country"] || ""; + const deviceCountryCode = + (httpHeader && httpHeader["X-Device-Country"]) || ""; useEffect(() => { if (!cursorVisible && !Spotlight.getCurrent()) { @@ -221,9 +220,7 @@ function AppBase(props) { // called by [receive httpHeader, launch, relaunch] const initService = useCallback( - (haveyInit = true) => { - // console.log( // "<<<<<<<<<<<<< appinfo >>>>>>>>>>>>{heavyInit, appinfo} ", // haveyInit, @@ -240,19 +237,22 @@ function AppBase(props) { if (haveyInit) { dispatch(changeAppStatus({ connectionFailed: false })); if (typeof window === "object" && window.PalmSystem) { - dispatch( - changeAppStatus({ - // Chromium68 호환성을 위해 Optional Chaining 제거 - cursorVisible: window.PalmSystem && window.PalmSystem.cursor && window.PalmSystem.cursor.visibility, - }) - ); + dispatch( + changeAppStatus({ + // Chromium68 호환성을 위해 Optional Chaining 제거 + cursorVisible: + window.PalmSystem && + window.PalmSystem.cursor && + window.PalmSystem.cursor.visibility, + }) + ); } dispatch(getHomeMenu()); dispatch(getMyRecommandedKeyword()); dispatch(getMyUpcomingAlertShow()); } - const launchParams = getLaunchParams(); + const launchParams = getLaunchParams(dispatch); console.log( "initService...{haveyInit, launchParams}", @@ -311,11 +311,15 @@ function AppBase(props) { // set foreground flag using delay time. clearTimeout(foreGroundChangeTimer); foreGroundChangeTimer = setTimeout(() => { - console.log( - "visibility changed !!! ==> set to foreground cursorVisible", - // Chromium68 호환성을 위해 Optional Chaining 제거 - JSON.stringify(window.PalmSystem && window.PalmSystem.cursor && window.PalmSystem.cursor.visibility) - ); // eslint-disable-line no-console + console.log( + "visibility changed !!! ==> set to foreground cursorVisible", + // Chromium68 호환성을 위해 Optional Chaining 제거 + JSON.stringify( + window.PalmSystem && + window.PalmSystem.cursor && + window.PalmSystem.cursor.visibility + ) + ); // eslint-disable-line no-console if (platform.platformName !== "webos") { //for debug dispatch( @@ -329,7 +333,10 @@ function AppBase(props) { changeAppStatus({ isAppForeground: true, // Chromium68 호환성을 위해 Optional Chaining 제거 - cursorVisible: window.PalmSystem && window.PalmSystem.cursor && window.PalmSystem.cursor.visibility, + cursorVisible: + window.PalmSystem && + window.PalmSystem.cursor && + window.PalmSystem.cursor.visibility, }) ); } @@ -450,7 +457,7 @@ function AppBase(props) { }, [introTermsAgree, deviceRegistered, dispatch, initService, termsLoading]); useEffect(() => { - const launchParmas = getLaunchParams(); + const launchParmas = getLaunchParams(dispatch); const linkTpNm = launchParmas.contentTarget ? launchParmas.contentTarget.split("_")[2] || "" : Config.LOG_MENU.APP; diff --git a/com.twin.app.shoptime/src/App/deepLinkHandler.js b/com.twin.app.shoptime/src/App/deepLinkHandler.js index 8c7eaa94..7d8fdf63 100644 --- a/com.twin.app.shoptime/src/App/deepLinkHandler.js +++ b/com.twin.app.shoptime/src/App/deepLinkHandler.js @@ -68,7 +68,7 @@ export const handleDeepLink = (contentTarget) => (dispatch, getState) => { expsOrd = tokens[6]; // 노출순번 curationId = tokens[7]; // 큐레이션아이디 panelName = panel_names.DETAIL_PANEL; - deeplinkPanel = "Product Detaoil"; + deeplinkPanel = "Product Detail"; panelInfo = { patnrId: patnrId, prdtId: prdtId, diff --git a/com.twin.app.shoptime/src/actions/actionTypes.js b/com.twin.app.shoptime/src/actions/actionTypes.js index a9b026e3..e440312f 100644 --- a/com.twin.app.shoptime/src/actions/actionTypes.js +++ b/com.twin.app.shoptime/src/actions/actionTypes.js @@ -20,7 +20,8 @@ export const types = { CHANGE_APP_STATUS: "CHANGE_APP_STATUS", SEND_BROADCAST: "SEND_BROADCAST", CHANGE_LOCAL_SETTINGS: "CHANGE_LOCAL_SETTINGS", - GNB_OPENED: "GNB_OPENED", SET_SHOW_POPUP: "SET_SHOW_POPUP", + GNB_OPENED: "GNB_OPENED", + SET_SHOW_POPUP: "SET_SHOW_POPUP", SET_SHOW_SECONDARY_POPUP: "SET_SHOW_SECONDARY_POPUP", SET_HIDE_POPUP: "SET_HIDE_POPUP", SET_HIDE_SECONDARY_POPUP: "SET_HIDE_SECONDARY_POPUP", @@ -43,6 +44,7 @@ export const types = { SET_SYSTEM_NOTICE: "SET_SYSTEM_NOTICE", SET_SYSTEM_TERMINATION: "SET_SYSTEM_TERMINATION", SET_DEEP_LINK: "SET_DEEP_LINK", + SET_DEEP_LINK_DEBUG: "SET_DEEP_LINK_DEBUG", SET_SECOND_LAYER_INFO: "SET_SECOND_LAYER_INFO", SET_ERROR_MESSAGE: "SET_ERROR_MESSAGE", CLEAR_ERROR_MESSAGE: "CLEAR_ERROR_MESSAGE", @@ -65,7 +67,7 @@ export const types = { // home actions GET_HOME_TERMS: "GET_HOME_TERMS", - SET_TERMS_ID_MAP: "SET_TERMS_ID_MAP", + SET_TERMS_ID_MAP: "SET_TERMS_ID_MAP", SET_OPTIONAL_TERMS_AVAILABILITY: "SET_OPTIONAL_TERMS_AVAILABILITY", GET_HOME_MENU: "GET_HOME_MENU", GET_HOME_LAYOUT: "GET_HOME_LAYOUT", @@ -235,9 +237,9 @@ export const types = { // new actions CANCEL_FOCUS_ELEMENT: "CANCEL_FOCUS_ELEMENT", - // 약관동의 여부 확인 상태 + // 약관동의 여부 확인 상태 GET_TERMS_AGREE_YN_START: "GET_TERMS_AGREE_YN_START", - GET_TERMS_AGREE_YN_SUCCESS: "GET_TERMS_AGREE_YN_SUCCESS", + GET_TERMS_AGREE_YN_SUCCESS: "GET_TERMS_AGREE_YN_SUCCESS", GET_TERMS_AGREE_YN_FAILURE: "GET_TERMS_AGREE_YN_FAILURE", // device diff --git a/com.twin.app.shoptime/src/actions/commonActions.js b/com.twin.app.shoptime/src/actions/commonActions.js index 84f1f9b1..3807db4b 100644 --- a/com.twin.app.shoptime/src/actions/commonActions.js +++ b/com.twin.app.shoptime/src/actions/commonActions.js @@ -32,7 +32,7 @@ export const gnbOpened = (status) => ({ }); export const setShowPopup = (config) => { - const payload = typeof config === 'string' ? { activePopup: config } : config; + const payload = typeof config === "string" ? { activePopup: config } : config; return { type: types.SET_SHOW_POPUP, payload, @@ -289,25 +289,28 @@ export const getDeviceId = (onComplete) => (dispatch, getState) => { export const getTermsAgreeYn = () => (dispatch, getState) => { dispatch({ type: types.GET_TERMS_AGREE_YN_START }); - + try { const { terms } = getState().home.termsData.data; - console.log("getTermsAgreeYn", terms.map(term => ({ - trmsId: term.trmsId, - trmsTpCd: term.trmsTpCd, - trmsAgrFlag: term.trmsAgrFlag, - trmsPopFlag: term.trmsPopFlag, - }))); + console.log( + "getTermsAgreeYn", + terms.map((term) => ({ + trmsId: term.trmsId, + trmsTpCd: term.trmsTpCd, + trmsAgrFlag: term.trmsAgrFlag, + trmsPopFlag: term.trmsPopFlag, + })) + ); // MST00405 선택약관 정보만 따로 출력 - const optionalTerm = terms.find(term => term.trmsTpCd === 'MST00405'); + const optionalTerm = terms.find((term) => term.trmsTpCd === "MST00405"); if (optionalTerm) { console.log("getTermsAgreeYn MST00405 선택약관:", { trmsId: optionalTerm.trmsId, trmsTpCd: optionalTerm.trmsTpCd, trmsAgrFlag: optionalTerm.trmsAgrFlag, - trmsPopFlag: optionalTerm.trmsPopFlag + trmsPopFlag: optionalTerm.trmsPopFlag, }); } else { console.log("getTermsAgreeYn MST00405 선택약관을 찾을 수 없습니다."); @@ -334,7 +337,7 @@ export const getTermsAgreeYn = () => (dispatch, getState) => { break; } return acc; - }, {}); + }, {}); dispatch({ type: types.GET_TERMS_AGREE_YN_SUCCESS, @@ -561,6 +564,11 @@ export const setDeepLink = (deepLinkInfo) => ({ payload: deepLinkInfo, }); +export const setDeepLinkDebug = (debugInfo) => ({ + type: types.SET_DEEP_LINK_DEBUG, + payload: debugInfo, +}); + export const setSecondLayerInfo = (secondLayerInfo) => ({ type: types.SET_SECOND_LAYER_INFO, payload: secondLayerInfo, @@ -758,35 +766,37 @@ export const resetOptionalTermsSession = () => ({ // 선택약관 동의 처리를 위한 헬퍼 함수 export const handleOptionalTermsAgree = () => (dispatch) => { - console.log('[CommonActions] 선택약관 동의 처리'); - dispatch(setOptionalTermsUserDecision('agreed')); + console.log("[CommonActions] 선택약관 동의 처리"); + dispatch(setOptionalTermsUserDecision("agreed")); dispatch(setOptionalTermsPopupShown(true)); }; // 선택약관 거절 처리를 위한 헬퍼 함수 export const handleOptionalTermsDecline = () => (dispatch) => { - console.log('[CommonActions] 선택약관 거절 처리'); - dispatch(setOptionalTermsUserDecision('declined')); + console.log("[CommonActions] 선택약관 거절 처리"); + dispatch(setOptionalTermsUserDecision("declined")); dispatch(setOptionalTermsPopupShown(true)); }; // 선택약관 상태 통합 업데이트 (TV 환경 최적화 - API 호출 없이 즉시 반영) -export const updateOptionalTermsAgreement = (agreed = true) => (dispatch) => { - console.log(`[CommonActions] 선택약관 통합 상태 업데이트: ${agreed}`); - - // 1. optionalTermsPopupFlow 업데이트 (TV 환경용) - dispatch(setOptionalTermsUserDecision(agreed ? 'agreed' : 'declined')); - dispatch(setOptionalTermsPopupShown(true)); - - // 2. 기본 optionalTermsAgree 상태 직접 업데이트 (API 호출 없이) - dispatch({ - type: types.UPDATE_OPTIONAL_TERMS_AGREE_DIRECT, - payload: agreed - }); - - // 3. termsAgreementStatus도 동기화 - dispatch({ - type: types.UPDATE_TERMS_AGREEMENT_STATUS_DIRECT, - payload: { MST00405: agreed } - }); -}; +export const updateOptionalTermsAgreement = + (agreed = true) => + (dispatch) => { + console.log(`[CommonActions] 선택약관 통합 상태 업데이트: ${agreed}`); + + // 1. optionalTermsPopupFlow 업데이트 (TV 환경용) + dispatch(setOptionalTermsUserDecision(agreed ? "agreed" : "declined")); + dispatch(setOptionalTermsPopupShown(true)); + + // 2. 기본 optionalTermsAgree 상태 직접 업데이트 (API 호출 없이) + dispatch({ + type: types.UPDATE_OPTIONAL_TERMS_AGREE_DIRECT, + payload: agreed, + }); + + // 3. termsAgreementStatus도 동기화 + dispatch({ + type: types.UPDATE_TERMS_AGREEMENT_STATUS_DIRECT, + payload: { MST00405: agreed }, + }); + }; diff --git a/com.twin.app.shoptime/src/reducers/commonReducer.js b/com.twin.app.shoptime/src/reducers/commonReducer.js index 59fabe75..799f2f5f 100644 --- a/com.twin.app.shoptime/src/reducers/commonReducer.js +++ b/com.twin.app.shoptime/src/reducers/commonReducer.js @@ -11,7 +11,7 @@ const initialState = { serverHOST: "", //"US.nextlgsdp.com", mbr_no: "", //X-User-Number : "US2401051532595" deviceId: "", //d87cedca-84e7-c05e-613d-39739bb7941f - cursorVisible: false, + cursorVisible: false, loginUserData: {}, toast: false, toastText: null, @@ -20,7 +20,8 @@ const initialState = { }, broadcast: {}, httpHeader: null, - isGnbOpened: false, popup: { + isGnbOpened: false, + popup: { popupVisible: false, activePopup: null, secondaryPopup: null, @@ -32,7 +33,7 @@ const initialState = { optionalTermsConfirmSelected: false, }, termsFlag: null, - termsLoading: false, // 25.06.16 추가 + termsLoading: false, // 25.06.16 추가 introTermsAgree: undefined, // Y, N checkoutTermsAgree: undefined, useLog: true, @@ -71,6 +72,15 @@ const initialState = { isDeepLink: false, }, + deepLinkDebug: { + rawLaunchParams: "", + parsedParams: null, + finalParams: null, + containerLaunch: false, + parseError: null, + timestamp: null, + }, + secondLayerInfo: {}, macAddress: { wifi: "", wired: "", p2p: "" }, connectionFailed: false, @@ -85,9 +95,9 @@ const initialState = { // 선택약관 팝업 상태 관리 (TV 환경 최적화) optionalTermsPopupFlow: { - popupShown: false, // 팝업 표시 여부 - userDecision: null, // 'agreed' | 'declined' | null - agreedInSession: false, // 세션 내 동의 여부 (로컬 상태 기반) + popupShown: false, // 팝업 표시 여부 + userDecision: null, // 'agreed' | 'declined' | null + agreedInSession: false, // 세션 내 동의 여부 (로컬 상태 기반) }, }; @@ -184,7 +194,8 @@ export const commonReducer = (state = initialState, action) => { secondaryPopupVisible: false, secondaryPopup: null, }, - }; case types.SET_HIDE_SECONDARY_POPUP: + }; + case types.SET_HIDE_SECONDARY_POPUP: return { ...state, popup: { @@ -233,8 +244,13 @@ export const commonReducer = (state = initialState, action) => { } case types.GET_TERMS_AGREE_YN_SUCCESS: { - const { privacyTerms, serviceTerms, purchaseTerms, paymentTerms, optionalTerms } = - action.payload; + const { + privacyTerms, + serviceTerms, + purchaseTerms, + paymentTerms, + optionalTerms, + } = action.payload; const introTermsAgree = privacyTerms === "Y" && serviceTerms === "Y"; const checkoutTermsAgree = purchaseTerms === "Y" && paymentTerms === "Y"; @@ -262,9 +278,11 @@ export const commonReducer = (state = initialState, action) => { case types.GET_HOME_TERMS: { const newTermsStatus = { ...state.termsAgreementStatus }; if (action.payload?.data?.terms) { - action.payload.data.terms.forEach(term => { - if (Object.prototype.hasOwnProperty.call(newTermsStatus, term.trmsTpCd)) { - newTermsStatus[term.trmsTpCd] = term.trmsAgrFlag === 'Y'; + action.payload.data.terms.forEach((term) => { + if ( + Object.prototype.hasOwnProperty.call(newTermsStatus, term.trmsTpCd) + ) { + newTermsStatus[term.trmsTpCd] = term.trmsAgrFlag === "Y"; } }); } @@ -279,7 +297,7 @@ export const commonReducer = (state = initialState, action) => { const newTermsStatus = { ...state.termsAgreementStatus }; // action payload에 담겨온 동의한 약관 코드 리스트를 기반으로 상태 업데이트 if (action.payload?.agreedTermCodes) { - action.payload.agreedTermCodes.forEach(termCode => { + action.payload.agreedTermCodes.forEach((termCode) => { if (Object.prototype.hasOwnProperty.call(newTermsStatus, termCode)) { newTermsStatus[termCode] = true; } @@ -288,7 +306,7 @@ export const commonReducer = (state = initialState, action) => { return { ...state, termsLoading: false, - termsAgreementStatus: newTermsStatus + termsAgreementStatus: newTermsStatus, }; } case types.SET_MYPAGE_TERMS_AGREE_FAIL: @@ -310,7 +328,7 @@ export const commonReducer = (state = initialState, action) => { ...state.termsAgreementStatus, MST00401: true, MST00402: true, - } + }, }; } else { return state; @@ -364,6 +382,17 @@ export const commonReducer = (state = initialState, action) => { }; } + case types.SET_DEEP_LINK_DEBUG: { + return { + ...state, + deepLinkDebug: { + ...state.deepLinkDebug, + ...action.payload, + timestamp: new Date().toISOString(), + }, + }; + } + case types.SET_SECOND_LAYER_INFO: { return { ...state, @@ -398,7 +427,7 @@ export const commonReducer = (state = initialState, action) => { optionalTermsPopupFlow: { ...state.optionalTermsPopupFlow, userDecision: action.payload, - agreedInSession: action.payload === 'agreed', + agreedInSession: action.payload === "agreed", }, }; } diff --git a/com.twin.app.shoptime/src/utils/helperMethods.js b/com.twin.app.shoptime/src/utils/helperMethods.js index 704fe362..047d633a 100644 --- a/com.twin.app.shoptime/src/utils/helperMethods.js +++ b/com.twin.app.shoptime/src/utils/helperMethods.js @@ -147,8 +147,15 @@ let localLaunchParams = { // contentTarget: "V3_2001_HOMEBANNER:1240712_TM_10", }; -export const getLaunchParams = () => { +export const getLaunchParams = (dispatch = null) => { let params = {}; + let debugInfo = { + rawLaunchParams: "", + parsedParams: null, + finalParams: null, + containerLaunch: false, + parseError: null, + }; if ( typeof window === "object" && @@ -156,15 +163,58 @@ export const getLaunchParams = () => { window.PalmSystem.launchParams ) { try { + debugInfo.rawLaunchParams = window.PalmSystem.launchParams; + console.log("[DEBUG] Raw launchParams:", window.PalmSystem.launchParams); + params = JSON.parse(window.PalmSystem.launchParams); + debugInfo.parsedParams = JSON.parse(JSON.stringify(params)); + + console.log("[DEBUG] Parsed params:", JSON.stringify(params, null, 2)); + if (params["x-webos-app-container-launch"] === true) { - params = params.details; + debugInfo.containerLaunch = true; + console.log( + "[DEBUG] Container launch detected, extracting details:", + params.details + ); + params = params.details || {}; } + + debugInfo.finalParams = JSON.parse(JSON.stringify(params)); + console.log("[DEBUG] Final params:", JSON.stringify(params, null, 2)); } catch (e) { + console.log("[DEBUG] LaunchParams parsing error:", e); + debugInfo.parseError = e.message; params = {}; } + + // Redux dispatch가 전달된 경우 디버그 정보 저장 + if (dispatch && typeof dispatch === "function") { + try { + const { setDeepLinkDebug } = require("../actions/commonActions"); + dispatch(setDeepLinkDebug(debugInfo)); + } catch (importError) { + console.log("[DEBUG] Failed to dispatch debug info:", importError); + } + } + return params; } else { + debugInfo.rawLaunchParams = "No PalmSystem or launchParams available"; + debugInfo.finalParams = localLaunchParams; + + console.log("[DEBUG] No PalmSystem or launchParams, using local params"); + + // Redux dispatch가 전달된 경우 디버그 정보 저장 + if (dispatch && typeof dispatch === "function") { + try { + const { setDeepLinkDebug } = require("../actions/commonActions"); + dispatch(setDeepLinkDebug(debugInfo)); + } catch (importError) { + console.log("[DEBUG] Failed to dispatch debug info:", importError); + } + } + return localLaunchParams; } }; diff --git a/com.twin.app.shoptime/src/views/MainView/MainView.jsx b/com.twin.app.shoptime/src/views/MainView/MainView.jsx index ee1abb42..1e33aadb 100644 --- a/com.twin.app.shoptime/src/views/MainView/MainView.jsx +++ b/com.twin.app.shoptime/src/views/MainView/MainView.jsx @@ -676,6 +676,7 @@ export default function MainView({ className, initService }) { const contentTarget = useSelector( (state) => state.common.deepLinkInfo.contentTarget ); + const deepLinkDebug = useSelector((state) => state.common.deepLinkDebug); return (
- deepLinkInfo -

{contentTarget}

+
+ 🔍 DEEPLINK DEBUG INFO +
+ +
+ ContentTarget: +
+ {contentTarget || "없음"} +
+
+ +
+ Raw LaunchParams: +
+ {deepLinkDebug.rawLaunchParams || "없음"} +
+
+ +
+ Parsed Params: +
+ {deepLinkDebug.parsedParams + ? JSON.stringify(deepLinkDebug.parsedParams, null, 1) + : "없음"} +
+
+ +
+ Final Params: +
+ {deepLinkDebug.finalParams + ? JSON.stringify(deepLinkDebug.finalParams, null, 1) + : "없음"} +
+
+ +
+ Container Launch: + + {deepLinkDebug.containerLaunch ? "YES" : "NO"} + +
+ + {deepLinkDebug.parseError && ( +
+ Parse Error: +
+ {deepLinkDebug.parseError} +
+
+ )} + +
+ Last Update: {deepLinkDebug.timestamp || "없음"} +
);