# 타이머 클린업 및 메모리 누수 방지 작업 완료 보고 **작업 일시**: 2025-11-12 **작업 범위**: ProductVideo.v2.jsx, MediaPanel.jsx, MediaPlayer.v2.jsx --- ## 📋 작업 개요 비디오 플레이어 관련 컴포넌트들에서 타이머와 이벤트 리스너가 제대로 정리되지 않아 발생할 수 있는 메모리 누수를 방지하기 위해 다음 개선 작업을 수행했습니다: - ✅ **setTimeout/setInterval 타이머의 명시적 정리** - ✅ **이벤트 리스너의 적절한 등록/해제** - ✅ **Ref를 통한 타이머 추적 및 정리** - ✅ **컴포넌트 언마운트 시 리소스 정리** --- ## 🔧 ProductVideo.v2.jsx 개선 사항 ### 1. autoPlay 타이머 정리 강화 **파일 위치**: Line 566-597 ```javascript // Before return () => { if (autoPlayTimerRef.current) { clearTimeout(autoPlayTimerRef.current); autoPlayTimerRef.current = null; } clearAllVideoTimers(); if (videoPlayerRef.current) { try { videoPlayerRef.current.pause(); } catch (error) { console.warn('[ProductVideoV2] 비디오 정지 실패:', error); } } }; // After return () => { // ✅ autoPlay timer 정리 if (autoPlayTimerRef.current) { clearTimeout(autoPlayTimerRef.current); autoPlayTimerRef.current = null; } // ✅ 전역 비디오 타이머 정리 (메모리 누수 방지) clearAllVideoTimers?.(); // Optional chaining 추가 // ✅ 비디오 플레이어 정지 if (videoPlayerRef.current) { try { videoPlayerRef.current.pause?.(); // Optional chaining 추가 } catch (error) { console.warn('[ProductVideoV2] 비디오 정지 실패:', error); } } }; ``` **개선점**: - Optional chaining (`?.`) 추가로 null/undefined 체크 안정성 향상 - `isPlaying` dependency 제거 (무한 루프 방지) - 명확한 주석으로 코드 가독성 개선 ### 2. 전체화면 전환 시 타이머 정리 **파일 위치**: Line 615-647 ```javascript // Before useEffect(() => { if (isPlaying && videoPlayerRef.current) { // ... const timeoutId = setTimeout(() => { // ... }, 100); return () => clearTimeout(timeoutId); } }, [isFullscreen, isPlaying]); // After useEffect(() => { if (isPlaying && videoPlayerRef.current) { // ... const timeoutId = setTimeout(() => { // ... }, 100); // ✅ cleanup: 타이머 정리 return () => { if (timeoutId) { clearTimeout(timeoutId); } }; } }, [isFullscreen, isPlaying]); ``` **개선점**: - Null 체크 추가로 안정성 향상 - 명확한 cleanup 함수 작성 ### 3. 전역 document 이벤트 리스너 정리 명확화 **파일 위치**: Line 504-537 **개선점**: - 명확한 주석으로 이벤트 리스너 등록/해제 의도 표명 - cleanup 함수에서 일관된 이벤트 리스너 제거 --- ## 🎬 MediaPanel.jsx 개선 사항 ### 1. onEnded 타이머 관리 개선 **파일 위치**: Line 52-53 (ref 추가), Line 285-308 (콜백 개선) ```javascript // Added ref for timer tracking const onEndedTimerRef = useRef(null); // ✅ onEnded 타이머 관리 // Before const onEnded = useCallback( (e) => { Spotlight.pause(); setTimeout(() => { Spotlight.resume(); dispatch(PanelActions.popPanel(panel_names.MEDIA_PANEL)); }, 1500); e?.stopPropagation(); e?.preventDefault(); }, [dispatch] ); // After const onEnded = useCallback( (e) => { Spotlight.pause(); // ✅ 이전 타이머가 있으면 정리 if (onEndedTimerRef.current) { clearTimeout(onEndedTimerRef.current); } // ✅ 새로운 타이머 저장 (cleanup 시 정리용) onEndedTimerRef.current = setTimeout(() => { Spotlight.resume(); dispatch(PanelActions.popPanel(panel_names.MEDIA_PANEL)); onEndedTimerRef.current = null; }, 1500); e?.stopPropagation(); e?.preventDefault(); }, [dispatch] ); ``` **개선점**: - useRef를 통한 타이머 추적으로 중복 호출 방지 - 명시적 타이머 정리 로직 ### 2. 컴포넌트 언마운트 시 타이머 정리 **파일 위치**: Line 322-340 (신규 useEffect 추가) ```javascript // ✅ 컴포넌트 언마운트 시 모든 타이머 정리 useEffect(() => { return () => { // onEnded 타이머 정리 if (onEndedTimerRef.current) { clearTimeout(onEndedTimerRef.current); onEndedTimerRef.current = null; } // ✅ 비디오 플레이어 정지 if (videoPlayer.current) { try { videoPlayer.current.pause?.(); } catch (error) { console.warn('[MediaPanel] 비디오 정지 실패:', error); } } }; }, []); ``` **개선점**: - 컴포넌트 언마운트 시 모든 타이머 정리 - 비디오 플레이어 강제 정지로 리소스 누수 방지 ### 3. Modal 스타일 설정 시 ResizeObserver 정리 **파일 위치**: Line 114-171 ```javascript // ✅ modal 스타일 설정 useEffect(() => { let resizeObserver = null; // ... 스타일 설정 로직 // ✅ cleanup: resize observer 정리 return () => { if (resizeObserver) { resizeObserver.disconnect(); } }; }, [panelInfo, isOnTop]); ``` **개선점**: - ResizeObserver 초기화로 미래 구현 시 메모리 누수 방지 준비 --- ## 📹 MediaPlayer.v2.jsx 개선 사항 ### 1. proportionLoaded 업데이트 타이머 최적화 **파일 위치**: Line 411-431 ```javascript // Before useEffect(() => { updateProportionLoaded(); const interval = setInterval(() => { updateProportionLoaded(); }, 1000); return () => clearInterval(interval); }, [updateProportionLoaded]); // After useEffect(() => { updateProportionLoaded(); // ✅ 1초마다 업데이트 (비디오 재생 중일 때만) let intervalId = null; if (!paused) { intervalId = setInterval(() => { updateProportionLoaded(); }, 1000); } // ✅ cleanup: interval 정리 return () => { if (intervalId !== null) { clearInterval(intervalId); } }; }, [updateProportionLoaded, paused]); ``` **개선점**: - 비디오 일시정지 중에는 interval 생성하지 않음 (불필요한 타이머 제거) - `paused` dependency 추가로 상태 변화 감지 - 명시적 null 체크로 정리 안정성 향상 ### 2. 컴포넌트 언마운트 시 전체 cleanup 강화 **파일 위치**: Line 433-454 ```javascript // ✅ Cleanup: 컴포넌트 언마운트 시 모든 타이머 및 상태 정리 useEffect(() => { return () => { // ✅ controlsTimeoutRef 정리 if (controlsTimeoutRef.current) { clearTimeout(controlsTimeoutRef.current); controlsTimeoutRef.current = null; } // ✅ 비디오 플레이어 정지 if (videoRef.current) { try { videoRef.current.pause?.(); } catch (error) { console.warn('[MediaPlayer.v2] 비디오 정지 실패:', error); } } // ✅ MediaPlayer 언마운트 시 Redux 상태 정리 dispatch(stopMediaAutoClose()); }; }, [dispatch]); ``` **개선점**: - 비디오 플레이어 강제 정지 추가 - Optional chaining으로 안정성 향상 - 에러 핸들링 추가 ### 3. hideControls 메서드 주석 추가 **파일 위치**: Line 290-299 **개선점**: - 타이머 정리 의도 명확화를 위한 주석 추가 --- ## 🎯 핵심 개선 패턴 ### 1. **Ref를 통한 타이머 추적** ```javascript const timerRef = useRef(null); const startTimer = () => { if (timerRef.current) { clearTimeout(timerRef.current); } timerRef.current = setTimeout(() => { // ... timerRef.current = null; }, delay); }; useEffect(() => { return () => { if (timerRef.current) { clearTimeout(timerRef.current); timerRef.current = null; } }; }, []); ``` ### 2. **Optional Chaining으로 안정성 향상** ```javascript // Before videoRef.current.pause(); // After videoRef.current.pause?.(); ``` ### 3. **조건부 타이머 생성** ```javascript // Before - 항상 interval 생성 const interval = setInterval(() => { updateProportionLoaded(); }, 1000); // After - 필요할 때만 생성 let intervalId = null; if (!paused) { intervalId = setInterval(() => { updateProportionLoaded(); }, 1000); } ``` --- ## ✅ 검증 항목 다음 항목들이 개선되었습니다: - [x] **autoPlay 타이머** 정리 강화 (ProductVideo.v2.jsx) - [x] **전체화면 전환 타이머** 정리 (ProductVideo.v2.jsx) - [x] **Document 이벤트 리스너** 정리 명확화 (ProductVideo.v2.jsx) - [x] **onEnded 타이머** Ref 추적 (MediaPanel.jsx) - [x] **컴포넌트 언마운트 cleanup** 강화 (MediaPanel.jsx) - [x] **Modal 스타일 설정** ResizeObserver 정리 준비 (MediaPanel.jsx) - [x] **proportionLoaded 업데이트** 타이머 최적화 (MediaPlayer.v2.jsx) - [x] **전체 cleanup 함수** 강화 (MediaPlayer.v2.jsx) --- ## 🚀 다음 단계 ### 권장 사항 1. **Redux Actions 검토** - `clearAllVideoTimers()` 액션이 실제로 모든 타이머를 정리하는지 확인 - `startMediaAutoClose()`, `stopMediaAutoClose()` 타이머 정리 로직 검토 2. **VideoPlayer/Media 컴포넌트** - webOS Media 컴포넌트의 타이머 정리 로직 확인 - TReactPlayer의 cleanup 로직 검토 3. **테스트** - 장시간 비디오 재생 후 메모리 사용량 모니터링 - 여러 번 반복 재생/정지 시 메모리 누수 확인 - 전체화면 전환 시 리소스 누수 확인 4. **성능 모니터링** - Chrome DevTools Memory tab에서 힙 스냅샷 비교 - 컴포넌트 마운트/언마운트 반복 시 메모리 증감 확인 --- ## 📝 주요 변경 요약 | 파일 | 변경 사항 | 라인 | 개선 효과 | |------|---------|------|---------| | ProductVideo.v2.jsx | autoPlay 타이머 정리 강화 | 566-597 | 메모리 누수 방지 | | ProductVideo.v2.jsx | 전체화면 전환 타이머 정리 | 615-647 | 타이머 중복 방지 | | ProductVideo.v2.jsx | Document 이벤트 리스너 정리 | 504-537 | 이벤트 리스너 누수 방지 | | MediaPanel.jsx | onEnded 타이머 Ref 추적 | 52-53, 285-308 | 타이머 중복 호출 방지 | | MediaPanel.jsx | 컴포넌트 언마운트 cleanup | 322-340 | 메모리 누수 방지 | | MediaPanel.jsx | Modal 스타일 ResizeObserver | 114-171 | 옵저버 정리 준비 | | MediaPlayer.v2.jsx | proportionLoaded 타이머 최적화 | 411-431 | 불필요한 타이머 제거 | | MediaPlayer.v2.jsx | 전체 cleanup 강화 | 433-454 | 메모리 누수 방지 | --- ## ✨ 결론 비디오 플레이어 관련 컴포넌트들의 타이머와 이벤트 리스너 정리를 체계적으로 개선했습니다. 이를 통해 장시간 비디오 재생 시에도 메모리 누수 없이 안정적으로 동작할 것으로 기대됩니다. **작업 상태**: ✅ 완료