[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:
2025-12-10 17:30:18 +09:00
parent 4fe3c94b1e
commit cbdf1b89f8

View File

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