[251210] fix: DetailPanel,ProductAllSection API최적화-2
🕐 커밋 시간: 2025. 12. 10. 17:30:18 📊 변경 통계: • 총 파일: 1개 • 추가: +85줄 • 삭제: -52줄 📝 수정된 파일: ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx 🔧 주요 변경 내용: • 소규모 기능 개선 • 코드 정리 및 최적화
This commit is contained in:
@@ -274,7 +274,6 @@ export default function ProductAllSection({
|
|||||||
|
|
||||||
// const [currentHeight, setCurrentHeight] = useState(0);
|
// const [currentHeight, setCurrentHeight] = useState(0);
|
||||||
//하단부분까지 갔을때 체크용
|
//하단부분까지 갔을때 체크용
|
||||||
const [documentHeight, setDocumentHeight] = useState(0);
|
|
||||||
const [isBottom, setIsBottom] = useState(false);
|
const [isBottom, setIsBottom] = useState(false);
|
||||||
|
|
||||||
//qr코드 노출용
|
//qr코드 노출용
|
||||||
@@ -283,6 +282,8 @@ export default function ProductAllSection({
|
|||||||
|
|
||||||
// sendLogGNB용 entryMenu
|
// sendLogGNB용 entryMenu
|
||||||
const entryMenuRef = useRef(null);
|
const entryMenuRef = useRef(null);
|
||||||
|
const lastProductDetailLogKeyRef = useRef(null);
|
||||||
|
const lastGnbLogKeyRef = useRef(null);
|
||||||
|
|
||||||
// 출처 정보 통합 (향후 확장성 대비)
|
// 출처 정보 통합 (향후 확장성 대비)
|
||||||
// YouMayLike 상품이 아닐 경우 fromPanel을 초기화하여 오기 방지
|
// YouMayLike 상품이 아닐 경우 fromPanel을 초기화하여 오기 방지
|
||||||
@@ -298,6 +299,7 @@ export default function ProductAllSection({
|
|||||||
|
|
||||||
// 모든 timeout/timer를 추적하기 위한 ref
|
// 모든 timeout/timer를 추적하기 위한 ref
|
||||||
const timersRef = useRef([]);
|
const timersRef = useRef([]);
|
||||||
|
const contentHeightRef = useRef(0);
|
||||||
|
|
||||||
// handleScrollToImages의 timeout을 추적하기 위한 ref
|
// handleScrollToImages의 timeout을 추적하기 위한 ref
|
||||||
const scrollToImagesTimeoutRef = useRef(null);
|
const scrollToImagesTimeoutRef = useRef(null);
|
||||||
@@ -733,6 +735,7 @@ export default function ProductAllSection({
|
|||||||
|
|
||||||
// sendLogGNB 로깅 - Source의 DetailPanel 컴포넌트들과 동일한 패턴
|
// sendLogGNB 로깅 - Source의 DetailPanel 컴포넌트들과 동일한 패턴
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!productData?.prdtId) return;
|
||||||
if (!entryMenuRef.current) entryMenuRef.current = nowMenu;
|
if (!entryMenuRef.current) entryMenuRef.current = nowMenu;
|
||||||
|
|
||||||
// BUY NOW 버튼 활성화 상태에 따른 메뉴 결정 (Source SingleProduct vs UnableProduct 패턴)
|
// BUY NOW 버튼 활성화 상태에 따른 메뉴 결정 (Source SingleProduct vs UnableProduct 패턴)
|
||||||
@@ -754,8 +757,22 @@ export default function ProductAllSection({
|
|||||||
? `${baseMenu}/${Config.LOG_MENU.DETAIL_PAGE_YOU_MAY_LIKE}`
|
? `${baseMenu}/${Config.LOG_MENU.DETAIL_PAGE_YOU_MAY_LIKE}`
|
||||||
: baseMenu;
|
: baseMenu;
|
||||||
|
|
||||||
|
const logKey = `${productData?.patnrId || ''}-${productData?.prdtId || ''}`;
|
||||||
|
if (lastGnbLogKeyRef.current === logKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastGnbLogKeyRef.current = logKey;
|
||||||
|
|
||||||
dispatch(sendLogGNB(menu));
|
dispatch(sendLogGNB(menu));
|
||||||
}, [isBillingProductVisible, isGroupProductVisible, isTravelProductVisible, fromPanel?.fromYouMayLike]);
|
}, [
|
||||||
|
isBillingProductVisible,
|
||||||
|
isGroupProductVisible,
|
||||||
|
isTravelProductVisible,
|
||||||
|
fromPanel?.fromYouMayLike,
|
||||||
|
productData?.patnrId,
|
||||||
|
productData?.prdtId,
|
||||||
|
nowMenu,
|
||||||
|
]);
|
||||||
|
|
||||||
// sendLogGNB 전송 후 플래그 초기화 (1회 사용 후 비활성화)
|
// sendLogGNB 전송 후 플래그 초기화 (1회 사용 후 비활성화)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -774,37 +791,54 @@ export default function ProductAllSection({
|
|||||||
|
|
||||||
// sendLogProductDetail 로깅 - Source의 productData 변경 감지와 동일한 패턴
|
// sendLogProductDetail 로깅 - Source의 productData 변경 감지와 동일한 패턴
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (productData && Object.keys(productData).length > 0) {
|
if (!productData || Object.keys(productData).length === 0) {
|
||||||
const params = {
|
return;
|
||||||
befPrice: productData?.priceInfo?.split("|")[0],
|
|
||||||
curationId: productData?.curationId ?? "",
|
|
||||||
curationNm: productData?.curationNm ?? "",
|
|
||||||
entryMenu: entryMenuRef.current,
|
|
||||||
expsOrd: "1",
|
|
||||||
inDt: formatGMTString(new Date()),
|
|
||||||
lastPrice: productData?.priceInfo?.split("|")[1],
|
|
||||||
lgCatCd: productData?.catCd ?? "",
|
|
||||||
lgCatNm: productData?.catNm ?? "",
|
|
||||||
linkTpCd: panelInfo?.linkTpCd ?? "",
|
|
||||||
logTpNo: isTravelProductVisible
|
|
||||||
? Config.LOG_TP_NO.PRODUCT.TRAVEL_DETAIL
|
|
||||||
: isGroupProductVisible
|
|
||||||
? Config.LOG_TP_NO.PRODUCT.GROUP_DETAIL
|
|
||||||
: isBillingProductVisible
|
|
||||||
? Config.LOG_TP_NO.PRODUCT.BILLING_PRODUCT_DETAIL
|
|
||||||
: Config.LOG_TP_NO.PRODUCT.PRODUCT_DETAIL,
|
|
||||||
patncNm: productData?.patncNm ?? "",
|
|
||||||
patnrId: productData?.patnrId ?? "",
|
|
||||||
prdtId: productData?.prdtId ?? "",
|
|
||||||
prdtNm: productData?.prdtNm ?? "",
|
|
||||||
revwGrd: productData?.revwGrd ?? "",
|
|
||||||
rewdAplyFlag: productData.priceInfo?.split("|")[2],
|
|
||||||
tsvFlag: productData?.todaySpclFlag ?? "",
|
|
||||||
};
|
|
||||||
|
|
||||||
dispatch(sendLogProductDetail(params));
|
|
||||||
}
|
}
|
||||||
}, [productData, panelInfo?.linkTpCd, isBillingProductVisible, isGroupProductVisible, isTravelProductVisible, dispatch]); // productData 변경 시 재실행
|
|
||||||
|
const logTpNo = isTravelProductVisible
|
||||||
|
? Config.LOG_TP_NO.PRODUCT.TRAVEL_DETAIL
|
||||||
|
: isGroupProductVisible
|
||||||
|
? Config.LOG_TP_NO.PRODUCT.GROUP_DETAIL
|
||||||
|
: isBillingProductVisible
|
||||||
|
? Config.LOG_TP_NO.PRODUCT.BILLING_PRODUCT_DETAIL
|
||||||
|
: Config.LOG_TP_NO.PRODUCT.PRODUCT_DETAIL;
|
||||||
|
|
||||||
|
const logKey = `${productData?.patnrId || ''}-${productData?.prdtId || ''}-${panelInfo?.linkTpCd || ''}-${logTpNo}`;
|
||||||
|
if (lastProductDetailLogKeyRef.current === logKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastProductDetailLogKeyRef.current = logKey;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
befPrice: productData?.priceInfo?.split("|")[0],
|
||||||
|
curationId: productData?.curationId ?? "",
|
||||||
|
curationNm: productData?.curationNm ?? "",
|
||||||
|
entryMenu: entryMenuRef.current,
|
||||||
|
expsOrd: "1",
|
||||||
|
inDt: formatGMTString(new Date()),
|
||||||
|
lastPrice: productData?.priceInfo?.split("|")[1],
|
||||||
|
lgCatCd: productData?.catCd ?? "",
|
||||||
|
lgCatNm: productData?.catNm ?? "",
|
||||||
|
linkTpCd: panelInfo?.linkTpCd ?? "",
|
||||||
|
logTpNo,
|
||||||
|
patncNm: productData?.patncNm ?? "",
|
||||||
|
patnrId: productData?.patnrId ?? "",
|
||||||
|
prdtId: productData?.prdtId ?? "",
|
||||||
|
prdtNm: productData?.prdtNm ?? "",
|
||||||
|
revwGrd: productData?.revwGrd ?? "",
|
||||||
|
rewdAplyFlag: productData.priceInfo?.split("|")[2],
|
||||||
|
tsvFlag: productData?.todaySpclFlag ?? "",
|
||||||
|
};
|
||||||
|
|
||||||
|
dispatch(sendLogProductDetail(params));
|
||||||
|
}, [
|
||||||
|
productData,
|
||||||
|
panelInfo?.linkTpCd,
|
||||||
|
isBillingProductVisible,
|
||||||
|
isGroupProductVisible,
|
||||||
|
isTravelProductVisible,
|
||||||
|
dispatch,
|
||||||
|
]); // productData 변경 시 재실행
|
||||||
|
|
||||||
// [251115] 주석 처리: MediaPanel에서 이미 포커스 이동을 처리하므로
|
// [251115] 주석 처리: MediaPanel에서 이미 포커스 이동을 처리하므로
|
||||||
// ProductAllSection의 자동 포커스는 포커스 탈취를 일으킬 수 있음
|
// ProductAllSection의 자동 포커스는 포커스 탈취를 일으킬 수 있음
|
||||||
@@ -1246,6 +1280,13 @@ export default function ProductAllSection({
|
|||||||
const prevScrollTopRef = useRef(0); // HomePanel 스타일 스크롤 위치 추적
|
const prevScrollTopRef = useRef(0); // HomePanel 스타일 스크롤 위치 추적
|
||||||
const scrollExpandTimerRef = useRef(null); // 스크롤 확장 타이머
|
const scrollExpandTimerRef = useRef(null); // 스크롤 확장 타이머
|
||||||
const mediaMinimizedRef = useRef(false);
|
const mediaMinimizedRef = useRef(false);
|
||||||
|
const getTotalContentHeight = useCallback(() => {
|
||||||
|
const measuredHeight =
|
||||||
|
contentHeightRef.current ||
|
||||||
|
scrollContainerRef.current?.scrollHeight ||
|
||||||
|
0;
|
||||||
|
return measuredHeight + (youMayAlsoLikelRef.current?.scrollHeight || 0);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleArrowClickAlternative = useCallback(() => {
|
const handleArrowClickAlternative = useCallback(() => {
|
||||||
dispatch(minimizeModalMedia());
|
dispatch(minimizeModalMedia());
|
||||||
@@ -1258,14 +1299,14 @@ export default function ProductAllSection({
|
|||||||
animate: true,
|
animate: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// documentHeight를 활용하여 반복 계산 제거
|
// 캐시된 콘텐츠 높이를 활용하여 반복 계산 최소화
|
||||||
const totalHeight = documentHeight + (youMayAlsoLikelRef.current?.scrollHeight || 0);
|
const totalHeight = getTotalContentHeight();
|
||||||
const isAtBottom = scrollPositionRef.current + 1100 >= totalHeight;
|
const isAtBottom = scrollPositionRef.current + 1100 >= totalHeight;
|
||||||
|
|
||||||
if (isAtBottom) {
|
if (isAtBottom) {
|
||||||
setIsBottom(isAtBottom);
|
setIsBottom(isAtBottom);
|
||||||
}
|
}
|
||||||
}, [documentHeight, scrollTop, dispatch]);
|
}, [scrollTop, dispatch, getTotalContentHeight]);
|
||||||
|
|
||||||
const handleScroll = useCallback(
|
const handleScroll = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
@@ -1276,12 +1317,12 @@ export default function ProductAllSection({
|
|||||||
const prevScrollTop = prevScrollTopRef.current;
|
const prevScrollTop = prevScrollTopRef.current;
|
||||||
|
|
||||||
scrollPositionRef.current = currentScrollTop;
|
scrollPositionRef.current = currentScrollTop;
|
||||||
|
contentHeightRef.current = e?.scrollHeight || contentHeightRef.current || 0;
|
||||||
|
|
||||||
// 기존 bottom 체크 로직 유지
|
// 기존 bottom 체크 로직 유지
|
||||||
if (documentHeight) {
|
const totalHeight = getTotalContentHeight();
|
||||||
const isAtBottom =
|
if (totalHeight) {
|
||||||
scrollPositionRef.current + 944 >=
|
const isAtBottom = scrollPositionRef.current + 944 >= totalHeight;
|
||||||
documentHeight + (youMayAlsoLikelRef.current?.scrollHeight || 0);
|
|
||||||
if (isAtBottom !== isBottom) {
|
if (isAtBottom !== isBottom) {
|
||||||
setIsBottom(isAtBottom);
|
setIsBottom(isAtBottom);
|
||||||
}
|
}
|
||||||
@@ -1334,7 +1375,7 @@ export default function ProductAllSection({
|
|||||||
}
|
}
|
||||||
// v2: onScrollStop에서 처리 (기존 로직 유지)
|
// v2: onScrollStop에서 처리 (기존 로직 유지)
|
||||||
},
|
},
|
||||||
[documentHeight, isBottom, productVideoVersion, isVideoPlaying, dispatch]
|
[isBottom, productVideoVersion, isVideoPlaying, dispatch, getTotalContentHeight]
|
||||||
);
|
);
|
||||||
|
|
||||||
// 스크롤 멈추었을 때만 호출 (성능 최적화)
|
// 스크롤 멈추었을 때만 호출 (성능 최적화)
|
||||||
@@ -1342,10 +1383,10 @@ export default function ProductAllSection({
|
|||||||
(e) => {
|
(e) => {
|
||||||
const currentScrollTop = e.scrollTop;
|
const currentScrollTop = e.scrollTop;
|
||||||
scrollPositionRef.current = currentScrollTop;
|
scrollPositionRef.current = currentScrollTop;
|
||||||
if (documentHeight) {
|
contentHeightRef.current = e?.scrollHeight || contentHeightRef.current || 0;
|
||||||
const isAtBottom =
|
const totalHeight = getTotalContentHeight();
|
||||||
currentScrollTop + 944 >=
|
if (totalHeight) {
|
||||||
documentHeight + (youMayAlsoLikelRef.current?.scrollHeight || 0);
|
const isAtBottom = currentScrollTop + 944 >= totalHeight;
|
||||||
if (isAtBottom !== isBottom) {
|
if (isAtBottom !== isBottom) {
|
||||||
setIsBottom(isAtBottom);
|
setIsBottom(isAtBottom);
|
||||||
}
|
}
|
||||||
@@ -1360,7 +1401,7 @@ export default function ProductAllSection({
|
|||||||
return shouldMinimize;
|
return shouldMinimize;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[documentHeight]
|
[getTotalContentHeight]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -1385,14 +1426,6 @@ export default function ProductAllSection({
|
|||||||
setActiveButton(null);
|
setActiveButton(null);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setDocumentHeight(
|
|
||||||
(productDetailRef.current?.scrollHeight || 0) +
|
|
||||||
(descriptionRef.current?.scrollHeight || 0) +
|
|
||||||
(reviewRef.current?.scrollHeight || 0)
|
|
||||||
);
|
|
||||||
}, [productDetailRef.current, descriptionRef.current, reviewRef.current]);
|
|
||||||
|
|
||||||
// 스크롤 위치에 따른 MediaPanel 제어 (비디오 재생 중에는 자동 제어 안함 - unmount 시에만 정리)
|
// 스크롤 위치에 따른 MediaPanel 제어 (비디오 재생 중에는 자동 제어 안함 - unmount 시에만 정리)
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// console.log('📍 [ProductAllSection] useEffect 실행 - shouldMinimizeMedia:', shouldMinimizeMedia);
|
// console.log('📍 [ProductAllSection] useEffect 실행 - shouldMinimizeMedia:', shouldMinimizeMedia);
|
||||||
|
|||||||
Reference in New Issue
Block a user