[250930] feat: YouMayLikeData관련 수정 및 배경이미지 개선

This commit is contained in:
2025-09-30 10:37:59 +09:00
parent 29859514a8
commit 1f387f9327
9 changed files with 231 additions and 126 deletions

View File

@@ -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

View File

@@ -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,

View File

@@ -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 (
<div ref={containerRef}>
<div ref={containerRef} style={{ position: 'relative', width: '100%', height: '100%' }}>
{/* 배경 이미지 및 그라데이션 컴포넌트 - 모든 콘텐츠 뒤에 렌더링 */}
<DetailPanelBackground />
<TPanel
isTabActivated={false}
className={css.detailPanelWrap}
@@ -791,7 +759,6 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
themeProductInfo={themeProductInfo}
onReady={handleProductAllSectionReady}
isOnRender={renderStates.canRender}
youmaylikeData={youmaylikeData}
/>
);
}

View File

@@ -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;

View File

@@ -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({
</TButton>
)}
{/* YouMayLike 버튼 렌더링 상태 로그 */}
{(() => {
{/* {(() => {
console.log('[YouMayLike] 버튼 렌더링 체크:', {
hasYouMayAlsoLike,
shouldRenderButton: hasYouMayAlsoLike,
youmaylikeDataLength: youmaylikeProductData?.length || 0
youmaylikeDataLength: youmaylikeData?.length || 0
});
return null;
})()}
})()} */}
</Container>
{panelInfo && panelInfo && panelInfo.type === 'theme' && !openThemeItemOverlay && (
@@ -734,19 +735,20 @@ export default function ProductAllSection({
<div ref={youMayAlsoLikelRef}>
<div id="scroll-marker-you-may-also-like" className={css.scrollMarker}></div>
<div id="you-may-also-like-section">
{(() => {
{/* {(() => {
console.log('[YouMayLike] YouMayAlsoLike 컴포넌트 렌더링:', {
productInfo: productData,
panelInfo,
hasData: !!youmaylikeProductData
hasData: !!youmaylikeData
});
return null;
})()}
})()} */}
<YouMayAlsoLike
productInfo={productData}
panelInfo={panelInfo}
onFocus={youmaylikeFocus}
onBlur={_onBlur}
youmaylikeData={youmaylikeData}
/>
</div>
</div>

View File

@@ -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 (
<div>

View File

@@ -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 (
<div className={css.backgroundContainer}>
{/* 실제 배경 이미지 */}
<img
src={detailPanelBg}
alt=""
className={css.backgroundImage}
aria-hidden="true"
onLoad={() => console.log('[DetailPanelBackground] 이미지 로드 완료')}
onError={(e) => console.error('[DetailPanelBackground] 이미지 로드 실패:', e)}
/>
{/* 그라데이션 레이어들 - CSS의 linear-gradient를 div로 구현 */}
{/* 1. 270도 방향 그라데이션 (왼쪽→오른쪽, 투명→불투명) */}
<div className={css.gradientLayer1} aria-hidden="true" />
{/* 2. 180도 방향 그라데이션 (위→아래, 투명→불투명) */}
<div className={css.gradientLayer2} aria-hidden="true" />
{/* 3. 투명 그라데이션 */}
<div className={css.gradientLayer3} aria-hidden="true" />
</div>
);
}

View File

@@ -0,0 +1,67 @@
@import "../../../../style/CommonStyle.module.less";
@import "../../../../style/utils.module.less";
// 배경 컨테이너 - 전체 화면을 덮으며 최하단에 위치
.backgroundContainer {
position: absolute; // 부모 기준 절대 위치
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0; // z-index: -1 대신 0 사용
overflow: hidden;
pointer-events: none; // 클릭 이벤트가 아래로 통과하도록
}
// 실제 배경 이미지
.backgroundImage {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover; // 화면 크기에 맞춰 이미지 조정
object-position: center;
z-index: 1; // 맨 아래 레이어
}
// 그라데이션 레이어 1: 270도 방향 (왼쪽→오른쪽, 투명→불투명)
// linear-gradient(270deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.77) 70%, rgba(0, 0, 0, 1) 100%)
.gradientLayer1 {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
270deg,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0.77) 70%,
rgba(0, 0, 0, 1) 100%
);
z-index: 2;
}
// 그라데이션 레이어 2: 180도 방향 (위→아래, 투명→불투명)
// linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 100%)
.gradientLayer2 {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 100%);
z-index: 3;
}
// 그라데이션 레이어 3: 투명 그라데이션
// linear-gradient(0deg, rgba(0, 0, 0, 0))
.gradientLayer3 {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(0deg, rgba(0, 0, 0, 0));
z-index: 4;
}

View File

@@ -0,0 +1,2 @@
// Barrel export for DetailPanelBackground
export { default } from './DetailPanelBackground';