From 1f387f932704e2217cd0823bd39594de71d9e6b4 Mon Sep 17 00:00:00 2001 From: optrader Date: Tue, 30 Sep 2025 10:37:59 +0900 Subject: [PATCH] =?UTF-8?q?[250930]=20feat:=20YouMayLikeData=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EB=B0=B0=EA=B2=BD?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/actions/mainActions.js | 37 +++++-- .../src/reducers/mainReducer.js | 7 ++ .../src/views/DetailPanel/DetailPanel.jsx | 103 ++++++------------ .../views/DetailPanel/DetailPanel.module.less | 23 ++-- .../ProductAllSection/ProductAllSection.jsx | 40 +++---- .../YouMayAlsoLike/YouMayAlsoLike.jsx | 40 ++++--- .../DetailPanelBackground.jsx | 38 +++++++ .../DetailPanelBackground.module.less | 67 ++++++++++++ .../components/DetailPanelBackground/index.js | 2 + 9 files changed, 231 insertions(+), 126 deletions(-) create mode 100644 com.twin.app.shoptime/src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.jsx create mode 100644 com.twin.app.shoptime/src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.module.less create mode 100644 com.twin.app.shoptime/src/views/DetailPanel/components/DetailPanelBackground/index.js diff --git a/com.twin.app.shoptime/src/actions/mainActions.js b/com.twin.app.shoptime/src/actions/mainActions.js index 943ffba4..9029db27 100644 --- a/com.twin.app.shoptime/src/actions/mainActions.js +++ b/com.twin.app.shoptime/src/actions/mainActions.js @@ -308,24 +308,43 @@ export const getTop20Show = () => (dispatch, getState) => { // 유메이라이크 아이템 리스트 IF-LGSP-201 export const getMainYouMayLike = - ({ lgCatCd, exclCurationId, exclPatnrId, exclPrdtId }) => + ({ lgCatCd, exclCurationId, exclPatnrId, exclPrdtId, catDpTh3, catDpTh4 }) => (dispatch, getState) => { + // console.log('[YouMayLike] API 요청 시작:', { + // lgCatCd, + // exclCurationId, + // exclPatnrId, + // exclPrdtId, + // catDpTh3, + // catDpTh4 + // }); + const onSuccess = (response) => { - console.log("getMainYouMayLike onSuccess ", response.data); - console.log('[YouMayLike] API 응답 구조:', { - fullResponse: response.data, - dataField: response.data.data, - hasYoumaylike: !!(response.data.data && response.data.data.youmaylike), - youmaylikeLength: response.data.data?.youmaylike?.length || 0 - }); + // console.log('[YouMayLike] API 응답 성공 (onSuccess):', { + // fullResponse: response.data, + // dataField: response.data.data, + // hasYoumaylike: !!(response.data.data && response.data.data.youmaylike), + // youmaylikeLength: response.data.data?.youmaylike?.length || 0, + // youmaylikeData: response.data.data?.youmaylike + // }); + + // console.log('[YouMayLike] Redux dispatch 전 - payload:', response.data.data); dispatch({ type: types.GET_YOUMAYLIKE, payload: response.data.data, }); + + // console.log('[YouMayLike] Redux dispatch 완료'); }; const onFail = (error) => { + // console.error('[YouMayLike] API 요청 실패 (onFail):', { + // error: error, + // errorMessage: error?.message, + // errorResponse: error?.response, + // errorData: error?.response?.data + // }); console.error("getMainYouMayLike onFail", error); }; @@ -334,7 +353,7 @@ export const getMainYouMayLike = getState, "get", URLS.GET_YOUMAYLIKE, - { lgCatCd, exclCurationId, exclPatnrId, exclPrdtId }, + { lgCatCd, exclCurationId, exclPatnrId, exclPrdtId, catDpTh3, catDpTh4 }, {}, onSuccess, onFail diff --git a/com.twin.app.shoptime/src/reducers/mainReducer.js b/com.twin.app.shoptime/src/reducers/mainReducer.js index 8a6deccf..a129f2ea 100644 --- a/com.twin.app.shoptime/src/reducers/mainReducer.js +++ b/com.twin.app.shoptime/src/reducers/mainReducer.js @@ -137,6 +137,13 @@ export const mainReducer = (state = initialState, action) => { } case types.GET_YOUMAYLIKE: { const data = action.payload; + // console.log('[YouMayLike] Reducer - GET_YOUMAYLIKE 액션 처리:', { + // actionPayload: action.payload, + // youmaylikeFromPayload: data.youmaylike, + // youmaylikeLength: data.youmaylike?.length || 0, + // prevState: state.youmaylikeData, + // newState: data.youmaylike + // }); return { ...state, youmaylikeData: data.youmaylike, diff --git a/com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx b/com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx index f8ce6ac8..f89c52f3 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx @@ -18,8 +18,6 @@ import DetailPanelSkeleton from './DetailPanelSkeleton/DetailPanelSkeleton'; import Spotlight from '@enact/spotlight'; import { setContainerLastFocusedElement } from '@enact/spotlight/src/container'; -import detailPanelBg - from '../../../assets/images/detailpanel/detailpanel-bg-1.png'; import indicatorDefaultImage from '../../../assets/images/img-thumb-empty-144@3x.png'; import { getDeviceAdditionInfo } from '../../actions/deviceActions'; @@ -47,6 +45,7 @@ import { } from '../../utils/helperMethods'; import { SpotlightIds } from '../../utils/SpotlightIds'; import THeaderCustom from './components/THeaderCustom'; +import DetailPanelBackground from './components/DetailPanelBackground'; import css from './DetailPanel.module.less'; import ProductAllSection from './ProductAllSection/ProductAllSection'; import ThemeItemListOverlay from './ThemeItemListOverlay/ThemeItemListOverlay'; @@ -232,24 +231,8 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) { [scrollToSection] ); - // ===== 고정 배경 이미지 설정 (detailPanelBg만 사용) ===== - // 모든 DetailPanel에서 동일한 배경 이미지(detailpanel-bg-1.png) 사용 - useEffect(() => { - console.log("[PartnerId] Partner background info:", { - panelPatnrId: panelPatnrId, - productDataPatnrId: productData?.patnrId, - productDataPatncNm: productData?.patncNm, - productDataThumbnailUrl960: productData?.thumbnailUrl960, - imageUrl: imageUrl, - detailPanelBg: detailPanelBg, - }); - - // 고정 배경 이미지만 설정 (파트너사별 변경 없이) - document.documentElement.style.setProperty( - "--bg-url", - `url(${detailPanelBg})` - ); - }, [panelPatnrId, productData, imageUrl]); + // ===== 배경 이미지 설정 (컴포넌트로 구현되어 useEffect 불필요) ===== + // DetailPanelBackground 컴포넌트로 배경 렌더링 // FP 방식으로 pending scroll 처리 (메모리 누수 방지) useEffect(() => { @@ -338,43 +321,25 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) { useEffect(() => { const shouldLoadRecommendations = fp.pipe(() => lgCatCd, fp.isNotEmpty)(); - console.log('[YouMayLike] API 호출 체크:', { - lgCatCd, - shouldLoadRecommendations, - panelInfo_curationId: panelInfo?.curationId, - panelInfo_patnrId: panelInfo?.patnrId, - panelInfo_prdtId: panelInfo?.prdtId, - // 비교용 - 제거 예정 - old_panelCurationId: panelCurationId, - old_panelPatnrId: panelPatnrId, - old_panelPrdtId: panelPrdtId - }); - if (shouldLoadRecommendations) { - const apiParams = { + const youMayLikeParams = { lgCatCd: lgCatCd, exclCurationId: panelInfo?.curationId, exclPatnrId: panelInfo?.patnrId, exclPrdtId: panelInfo?.prdtId, + catDpTh3: productData?.catDpTh3, + catDpTh4: productData?.catDpTh4, }; - console.log('[YouMayLike] getMainYouMayLike API 호출 중...', { - apiParams, - currentProduct: { - patnrId: panelInfo?.patnrId, - prdtId: panelInfo?.prdtId, - curationId: panelInfo?.curationId - } - }); - dispatch(getMainYouMayLike(apiParams)); - } else { - console.log('[YouMayLike] API 호출하지 않음 - lgCatCd가 비어있음'); + + // console.log('[YouMayLike]-youmaylikeData 요청 파라미터:', youMayLikeParams); + dispatch(getMainYouMayLike(youMayLikeParams)); } }, [panelInfo?.curationId, panelInfo?.patnrId, panelInfo?.prdtId, lgCatCd]); const getlgCatCd = useCallback(() => { // DetailPanel.backup.jsx와 완전히 동일한 로직 if (productData && !panelInfo?.curationId) { - console.log('[YouMayLike] lgCatCd 설정 (일반상품):', productData.catCd); + // console.log('[YouMayLike] lgCatCd 설정 (일반상품):', productData.catCd); setLgCatCd(productData.catCd); } else if ( themeProductInfos && @@ -382,16 +347,16 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) { panelInfo?.curationId ) { const themeCatCd = themeProductInfos[selectedIndex]?.catCd; - console.log('[YouMayLike] lgCatCd 설정 (테마상품):', themeCatCd); + // console.log('[YouMayLike] lgCatCd 설정 (테마상품):', themeCatCd); setLgCatCd(themeCatCd); } else { - console.log('[YouMayLike] lgCatCd 설정 (빈값):', { - hasProductData: !!productData, - panelCurationId: panelInfo?.curationId, - hasThemeProductInfos: !!themeProductInfos, - selectedIndex, - themeProductPmtSuptYn: themeProductInfos?.[selectedIndex]?.pmtSuptYn - }); + // console.log('[YouMayLike] lgCatCd 설정 (빈값):', { + // hasProductData: !!productData, + // panelCurationId: panelInfo?.curationId, + // hasThemeProductInfos: !!themeProductInfos, + // selectedIndex, + // themeProductPmtSuptYn: themeProductInfos?.[selectedIndex]?.pmtSuptYn + // }); setLgCatCd(""); } }, [productData, themeProductInfos, selectedIndex, panelInfo?.curationId]); @@ -402,21 +367,21 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) { }, [themeProductInfos, productData, panelInfo, selectedIndex, getlgCatCd]); // lgCatCd 변경 추적 로그 - useEffect(() => { - console.log('[YouMayLike] lgCatCd 변경됨:', { - lgCatCd, - willTriggerYouMayLike: !!lgCatCd - }); - }, [lgCatCd]); + // useEffect(() => { + // console.log('[YouMayLike] lgCatCd 변경됨:', { + // lgCatCd, + // willTriggerYouMayLike: !!lgCatCd + // }); + // }, [lgCatCd]); // youmaylikeData 변경 추적 로그 - useEffect(() => { - console.log('[YouMayLike] DetailPanel - youmaylikeData 변경됨:', { - youmaylikeData, - hasData: !!(youmaylikeData && youmaylikeData.length > 0), - dataLength: youmaylikeData?.length || 0 - }); - }, [youmaylikeData]); + // useEffect(() => { + // console.log('[YouMayLike] DetailPanel - youmaylikeData 변경됨:', { + // youmaylikeData, + // hasData: !!(youmaylikeData && youmaylikeData.length > 0), + // dataLength: youmaylikeData?.length || 0 + // }); + // }, [youmaylikeData]); // 최근 본 상품 저장이 필요하면: // - 순수 유틸로 빌드/업서트 함수 작성 후, 적절한 useEffect에서 호출하세요. @@ -729,7 +694,10 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) { }, []); return ( -
+
+ {/* 배경 이미지 및 그라데이션 컴포넌트 - 모든 콘텐츠 뒤에 렌더링 */} + + ); } diff --git a/com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.module.less b/com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.module.less index 812c4022..a9e310ed 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.module.less +++ b/com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.module.less @@ -2,18 +2,16 @@ @import "../../style/utils.module.less"; .detailPanelWrap { - background-size: cover; - background-position: center; - background-image: - linear-gradient(0deg, rgba(0, 0, 0, 0)), - linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 100%), - linear-gradient( - 270deg, - rgba(0, 0, 0, 0) 0%, - rgba(0, 0, 0, 0.77) 70%, - rgba(0, 0, 0, 1) 100% - ), - var(--bg-url); + // 배경 이미지와 그라데이션은 DetailPanelBackground 컴포넌트로 구현 + // CSS 변수 대신 실제 DOM 요소 사용하여 webOS TV 호환성 확보 + position: relative; + z-index: 1; // 배경 컴포넌트(z-index: 0) 위에 표시 + background: transparent !important; // 투명 배경으로 설정하여 뒤의 배경 컴포넌트가 보이도록 + + // 하위 요소들도 투명 배경 (detailPanelWrap 스코프 내에서만 적용) + > * { + background: transparent !important; + } } .header { @@ -52,6 +50,7 @@ position: relative; display: flex; justify-content: space-between; + background: transparent; // 투명 배경으로 설정 // padding-left: 120px; diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx index 8b16fdde..6f1fc8ea 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx @@ -145,14 +145,16 @@ export default function ProductAllSection({ themeProductInfo, onReady, isOnRender, - youmaylikeData, }) { const dispatch = useDispatch(); - // Redux 상태 추가 - DetailPanel.backup.jsx와 동일하게 + // Redux 상태 const webOSVersion = useSelector((state) => state.common.appStatus.webOSVersion); const groupInfos = useSelector((state) => state.product.groupInfo); + // YouMayLike 데이터는 API 응답 시간이 걸리므로 직접 구독 + const youmaylikeData = useSelector((state) => state.main.youmaylikeData); + const [currentHeight, setCurrentHeight] = useState(0); //하단부분까지 갔을때 체크용 const [documentHeight, setDocumentHeight] = useState(0); @@ -212,19 +214,18 @@ export default function ProductAllSection({ hasReviews, // 리뷰 존재 여부 플래그 추가 } = useReviews(productData.prdtId, panelInfo && panelInfo.patnrId); - // YouMayAlsoLike 데이터 확인 - props로 받은 데이터 사용 - const youmaylikeProductData = youmaylikeData; - const hasYouMayAlsoLike = youmaylikeProductData && youmaylikeProductData.length > 0; + // YouMayAlsoLike 데이터 확인 + const hasYouMayAlsoLike = youmaylikeData && youmaylikeData.length > 0; // YouMayLike 데이터 디버깅 - useEffect(() => { - console.log('[YouMayLike] ProductAllSection - 데이터 상태 (Props):', { - youmaylikeProductData, - hasYouMayAlsoLike, - dataLength: youmaylikeProductData?.length || 0, - productDataPrdtId: productData?.prdtId - }); - }, [youmaylikeProductData, hasYouMayAlsoLike, productData]); + // useEffect(() => { + // console.log('[YouMayLike] ProductAllSection - 데이터 상태:', { + // youmaylikeData, + // hasYouMayAlsoLike, + // dataLength: youmaylikeData?.length || 0, + // productDataPrdtId: productData?.prdtId + // }); + // }, [youmaylikeData, hasYouMayAlsoLike, productData]); // ProductAllSection 마운트 시 showAllReviews 초기화 useEffect(() => { @@ -619,14 +620,14 @@ export default function ProductAllSection({ )} {/* YouMayLike 버튼 렌더링 상태 로그 */} - {(() => { + {/* {(() => { console.log('[YouMayLike] 버튼 렌더링 체크:', { hasYouMayAlsoLike, shouldRenderButton: hasYouMayAlsoLike, - youmaylikeDataLength: youmaylikeProductData?.length || 0 + youmaylikeDataLength: youmaylikeData?.length || 0 }); return null; - })()} + })()} */} {panelInfo && panelInfo && panelInfo.type === 'theme' && !openThemeItemOverlay && ( @@ -734,19 +735,20 @@ export default function ProductAllSection({
- {(() => { + {/* {(() => { console.log('[YouMayLike] YouMayAlsoLike 컴포넌트 렌더링:', { productInfo: productData, panelInfo, - hasData: !!youmaylikeProductData + hasData: !!youmaylikeData }); return null; - })()} + })()} */}
diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/YouMayAlsoLike/YouMayAlsoLike.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/YouMayAlsoLike/YouMayAlsoLike.jsx index 32ad0c6a..d54f4895 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/YouMayAlsoLike/YouMayAlsoLike.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/YouMayAlsoLike/YouMayAlsoLike.jsx @@ -31,32 +31,36 @@ const Container = SpotlightContainerDecorator( "div" ); -export default function YouMayAlsoLike({ productInfo, panelInfo, onFocus, onBlur }) { +export default function YouMayAlsoLike({ productInfo, panelInfo, onFocus, onBlur, youmaylikeData }) { const { getScrollTo, scrollLeft } = useScrollTo(); const [newYoumaylikeProductData, setNewYoumaylikeProductData] = useState([]); const dispatch = useDispatch(); const focusedContainerIdRef = useRef(null); - const youmaylikeProductData = useSelector( + // Redux에서 데이터 조회 + const reduxYoumaylikeData = useSelector( (state) => state.main.youmaylikeData ); + + // props로 받은 데이터 우선 사용, 없으면 Redux 데이터 사용 + const youmaylikeProductData = youmaylikeData || reduxYoumaylikeData; //노출 9개로 변경위한 처리건. useEffect(() => { - console.log('[YouMayLike] YouMayAlsoLike 컴포넌트 - 데이터 처리:', { - originalData: youmaylikeProductData, - originalLength: youmaylikeProductData?.length || 0, - hasData: !!(youmaylikeProductData && youmaylikeProductData.length > 0) - }); + // console.log('[YouMayLike] YouMayAlsoLike 컴포넌트 - 데이터 처리:', { + // originalData: youmaylikeProductData, + // originalLength: youmaylikeProductData?.length || 0, + // hasData: !!(youmaylikeProductData && youmaylikeProductData.length > 0) + // }); if (youmaylikeProductData && youmaylikeProductData.length > 0) { const processedData = youmaylikeProductData.slice(0, youmaylikeProductData.length - 1); - console.log('[YouMayLike] 처리된 데이터 설정:', { - processedLength: processedData.length, - processedData - }); + // console.log('[YouMayLike] 처리된 데이터 설정:', { + // processedLength: processedData.length, + // processedData + // }); setNewYoumaylikeProductData(processedData); } else { - console.log('[YouMayLike] 데이터 없음 - 빈 배열 설정'); + // console.log('[YouMayLike] 데이터 없음 - 빈 배열 설정'); setNewYoumaylikeProductData([]); } }, [youmaylikeProductData]); @@ -98,12 +102,12 @@ export default function YouMayAlsoLike({ productInfo, panelInfo, onFocus, onBlur }, [onBlur]); // 렌더링 시점 로그 - console.log('[YouMayLike] YouMayAlsoLike 렌더링:', { - newYoumaylikeProductData, - hasData: !!(newYoumaylikeProductData && newYoumaylikeProductData.length > 0), - dataLength: newYoumaylikeProductData?.length || 0, - willRender: !!(newYoumaylikeProductData && newYoumaylikeProductData.length > 0) - }); + // console.log('[YouMayLike] YouMayAlsoLike 렌더링:', { + // newYoumaylikeProductData, + // hasData: !!(newYoumaylikeProductData && newYoumaylikeProductData.length > 0), + // dataLength: newYoumaylikeProductData?.length || 0, + // willRender: !!(newYoumaylikeProductData && newYoumaylikeProductData.length > 0) + // }); return (
diff --git a/com.twin.app.shoptime/src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.jsx b/com.twin.app.shoptime/src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.jsx new file mode 100644 index 00000000..6fa4191f --- /dev/null +++ b/com.twin.app.shoptime/src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.jsx @@ -0,0 +1,38 @@ +// src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.jsx +import React, { useEffect } from 'react'; +import css from './DetailPanelBackground.module.less'; +import detailPanelBg from '../../../../../assets/images/detailpanel/detailpanel-bg-1.png'; + +/** + * DetailPanel의 배경 이미지와 그라데이션을 렌더링하는 컴포넌트 + * CSS 변수 대신 실제 DOM 요소로 구현하여 webOS TV 호환성 확보 + */ +export default function DetailPanelBackground() { + useEffect(() => { + console.log('[DetailPanelBackground] 배경 이미지 경로:', detailPanelBg); + }, []); + + return ( +
+ {/* 실제 배경 이미지 */} + console.log('[DetailPanelBackground] 이미지 로드 완료')} + onError={(e) => console.error('[DetailPanelBackground] 이미지 로드 실패:', e)} + /> + + {/* 그라데이션 레이어들 - CSS의 linear-gradient를 div로 구현 */} + {/* 1. 270도 방향 그라데이션 (왼쪽→오른쪽, 투명→불투명) */} +