diff --git a/com.twin.app.shoptime/DEBUG_MODE_IMPLEMENTATION.md b/com.twin.app.shoptime/DEBUG_MODE_IMPLEMENTATION.md
deleted file mode 100644
index 8507e85a..00000000
--- a/com.twin.app.shoptime/DEBUG_MODE_IMPLEMENTATION.md
+++ /dev/null
@@ -1,221 +0,0 @@
-# DEBUG_MODE 조건부 로깅 구현 완료
-
-**작업 일시**: 2025-11-12
-**작업 범위**: ProductVideo.v2.jsx, MediaPanel.jsx
-
----
-
-## 📋 작업 개요
-
-ProductVideo.v2.jsx와 MediaPanel.jsx의 모든 로그 출력을 `DEBUG_MODE = true/false` 플래그로 제어할 수 있도록 구현했습니다.
-
----
-
-## ✅ 구현 내용
-
-### 1. DEBUG_MODE 설정
-
-각 파일의 최상단에 DEBUG_MODE 상수를 추가합니다:
-
-```javascript
-// ✅ DEBUG 모드 설정
-const DEBUG_MODE = true; // false로 설정하면 모든 로그 비활성화
-```
-
-**설정 변경 방법:**
-- 프로덕션: `const DEBUG_MODE = false;` 로 변경
-- 개발/테스트: `const DEBUG_MODE = true;` 유지
-
-### 2. debugLog 헬퍼 함수
-
-DEBUG_MODE를 검사하는 래퍼 함수를 구현합니다:
-
-```javascript
-// ✅ DEBUG_MODE 기반 console 래퍼
-const debugLog = (...args) => {
- if (DEBUG_MODE) {
- console.log(...args);
- }
-};
-```
-
-**특징:**
-- `console.log(...)` 대신 `debugLog(...)` 사용
-- DEBUG_MODE가 false이면 로그 출력 안 됨
-- 성능 오버헤드 거의 없음 (조건 체크만 수행)
-
-### 3. console 메서드별 처리
-
-| 메서드 | 처리 방식 | 파일 |
-|--------|----------|------|
-| `console.log()` | `debugLog()` 로 변경 | ProductVideo.v2.jsx, MediaPanel.jsx |
-| `console.warn()` | `if (DEBUG_MODE) console.warn()` | ProductVideo.v2.jsx, MediaPanel.jsx |
-| `console.error()` | `if (DEBUG_MODE) console.error()` | ProductVideo.v2.jsx |
-
----
-
-## 📊 변경 통계
-
-### ProductVideo.v2.jsx
-```
-- console.log() → debugLog(): 약 40+ 개
-- console.warn() → if (DEBUG_MODE) console.warn(): 2개
-- console.error() → if (DEBUG_MODE) console.error(): 1개
-```
-
-### MediaPanel.jsx
-```
-- console.log() → debugLog(): 약 10+ 개
-- console.warn() → if (DEBUG_MODE) console.warn(): 1개
-```
-
----
-
-## 🎯 사용 방법
-
-### DEBUG 로그 활성화 (개발 모드)
-```javascript
-const DEBUG_MODE = true; // ✅ 모든 로그 출력됨
-```
-
-### DEBUG 로그 비활성화 (프로덕션)
-```javascript
-const DEBUG_MODE = false; // ❌ 모든 로그 숨김
-```
-
-### 한 줄 변경으로 전체 로깅 제어
-각 파일의 두 번째 줄만 변경하면 됩니다:
-
-**ProductVideo.v2.jsx Line 36**
-```javascript
-const DEBUG_MODE = true; // 변경: true ↔ false
-```
-
-**MediaPanel.jsx Line 25**
-```javascript
-const DEBUG_MODE = true; // 변경: true ↔ false
-```
-
----
-
-## 💡 장점
-
-1. **성능 최적화**
- - 프로덕션에서 로그 오버헤드 제거
- - 조건 검사만 수행 (콘솔 I/O 없음)
-
-2. **개발 편의성**
- - 한 줄 변경으로 전체 로깅 제어
- - 파일 수정 없이 ENV 변수로 제어 가능 (향후)
-
-3. **디버깅 용이**
- - 필요할 때만 로그 활성화
- - 로그 양 제어로 콘솔 지저분함 방지
-
-4. **유지보수 편함**
- - 기존 console 호출 그대로 유지
- - 로그 코드 삭제 불필요
-
----
-
-## 🔧 향후 개선 사항
-
-### 1. 환경 변수 기반 설정
-```javascript
-const DEBUG_MODE = process.env.REACT_APP_DEBUG === 'true';
-```
-
-### 2. 세부 로그 레벨 구분
-```javascript
-const LOG_LEVEL = {
- ERROR: 0,
- WARN: 1,
- INFO: 2,
- DEBUG: 3,
-};
-
-const debugLog = (level, ...args) => {
- if (LOG_LEVEL[level] <= getCurrentLogLevel()) {
- console.log(...args);
- }
-};
-```
-
-### 3. Redux DevTools 통합
-```javascript
-const debugLog = (...args) => {
- if (DEBUG_MODE) {
- console.log(...args);
- // Redux DevTools 에 추가 정보 기록
- }
-};
-```
-
----
-
-## ✅ 검증 항목
-
-- [x] ProductVideo.v2.jsx: 모든 console.log → debugLog 변경
-- [x] ProductVideo.v2.jsx: console.warn/error 조건부 처리
-- [x] MediaPanel.jsx: 모든 console.log → debugLog 변경
-- [x] MediaPanel.jsx: console.warn 조건부 처리
-- [x] debugLog 함수 올바르게 구현 (무한 루프 방지)
-- [x] DEBUG_MODE 설정 가능
-
----
-
-## 🚀 다음 단계
-
-1. **사용자 테스트**
- - DEBUG_MODE = true일 때 모든 로그 정상 출력 확인
- - DEBUG_MODE = false일 때 모든 로그 숨겨지는지 확인
-
-2. **성능 테스트**
- - 프로덕션 모드에서 성능 개선 확인
-
-3. **ENV 변수 연동**
- - `.env.development`, `.env.production` 설정
- - 빌드 시 자동으로 DEBUG_MODE 설정
-
----
-
-## 📝 코드 예시
-
-### Before (수정 전)
-```javascript
-console.log('🎬 [handleThumbnailClick] 썸네일 클릭됨', {...});
-console.warn('[ProductVideoV2] 비디오 정지 실패:', error);
-console.error('🖥️ [toggleControls] 디스패치 에러:', error);
-```
-
-### After (수정 후)
-```javascript
-debugLog('🎬 [handleThumbnailClick] 썸네일 클릭됨', {...});
-if (DEBUG_MODE) console.warn('[ProductVideoV2] 비디오 정지 실패:', error);
-if (DEBUG_MODE) console.error('🖥️ [toggleControls] 디스패치 에러:', error);
-```
-
----
-
-## 📌 주의사항
-
-1. **주석 처리된 로그**
- - 기존의 주석 처리된 console.log는 유지됨
- - 필요시 나중에 삭제 가능
-
-2. **debugLog 함수 위치**
- - 컴포넌트 함수 외부에 선언됨
- - 매번 새로 생성되지 않음 (성능 최적화)
-
-3. **프로덕션 배포**
- - 배포 전에 DEBUG_MODE를 false로 반드시 변경할 것
-
----
-
-## ✨ 결론
-
-ProductVideo.v2.jsx와 MediaPanel.jsx의 모든 로그 출력을 DEBUG_MODE 플래그로 제어할 수 있도록 구현완료.
-이를 통해 개발/테스트 중에는 디버깅 정보를 쉽게 확인할 수 있으며,
-프로덕션 환경에서는 로그 오버헤드를 제거하여 성능을 향상시킬 수 있습니다.
-
-**작업 상태**: ✅ 완료
diff --git a/com.twin.app.shoptime/MEDIAPANEL_CLEANUP_IMPROVEMENTS.md b/com.twin.app.shoptime/MEDIAPANEL_CLEANUP_IMPROVEMENTS.md
deleted file mode 100644
index 64b75cc3..00000000
--- a/com.twin.app.shoptime/MEDIAPANEL_CLEANUP_IMPROVEMENTS.md
+++ /dev/null
@@ -1,430 +0,0 @@
-# MediaPanel.jsx 메모리 누수 방지 및 클린업 개선
-
-**작업 일시**: 2025-11-12
-**파일**: MediaPanel.jsx
-**상태**: ✅ 완료 (코드 수정만, git/npm 미실행)
-
----
-
-## 📋 작업 개요
-
-MediaPanel.jsx의 메모리 누수를 방지하고 안전한 리소스 정리를 위해 다음과 같은 개선사항을 추가했습니다:
-
-- ✅ 안전한 비디오 플레이어 메서드 호출 래퍼
-- ✅ 강화된 컴포넌트 언마운트 클린업
-- ✅ DOM 스타일 초기화 및 정리
-- ✅ 에러 처리 강화
-- ✅ 이벤트 리스너 추적 및 정리
-
----
-
-## 🔧 주요 개선 사항
-
-### 1. 안전한 메서드 호출 래퍼 (safePlayerCall)
-
-**위치**: Line 107-117
-
-```javascript
-// ✅ 안전한 비디오 플레이어 메서드 호출
-const safePlayerCall = useCallback((methodName, ...args) => {
- if (videoPlayer.current && typeof videoPlayer.current[methodName] === 'function') {
- try {
- return videoPlayer.current[methodName](...args);
- } catch (err) {
- if (DEBUG_MODE) console.warn(`[MediaPanel] ${methodName} 호출 실패:`, err);
- }
- }
- return null;
-}, []);
-```
-
-**장점:**
-- null/undefined 안전 검사
-- 메서드 존재 여부 확인
-- 에러 처리 통일
-- 트라이-캐치로 예외 처리
-
-**사용 예:**
-```javascript
-safePlayerCall('play');
-safePlayerCall('toggleControls');
-const mediaState = safePlayerCall('getMediaState');
-```
-
-### 2. 레퍼런스 추적 Ref 추가
-
-**위치**: Line 64
-
-```javascript
-const mediaEventListenersRef = useRef([]); // ✅ 미디어 이벤트 리스너 추적
-```
-
-**목적:**
-- 등록된 이벤트 리스너 관리
-- 언마운트 시 모든 리스너 제거 가능
-- 메모리 누수 방지
-
-### 3. isOnTop 변경 시 안전한 제어
-
-**위치**: Line 178-188
-
-**Before:**
-```javascript
-if (videoPlayer.current?.getMediaState()?.paused) {
- videoPlayer.current.play();
-}
-if (videoPlayer.current.areControlsVisible && !videoPlayer.current.areControlsVisible()) {
- videoPlayer.current.showControls();
-}
-```
-
-**After:**
-```javascript
-// ✅ 안전한 메서드 호출로 null/undefined 체크
-const mediaState = safePlayerCall('getMediaState');
-if (mediaState?.paused) {
- safePlayerCall('play');
-}
-
-const isControlsHidden = videoPlayer.current.areControlsVisible && !videoPlayer.current.areControlsVisible();
-if (isControlsHidden) {
- safePlayerCall('showControls');
-}
-```
-
-**개선점:**
-- mediaState null 체크 강화
-- 모든 플레이어 호출을 안전한 래퍼로 통일
-- 에러 처리 일관성
-
-### 4. 비디오 클릭 핸들러 개선
-
-**위치**: Line 199-208
-
-**Before:**
-```javascript
-if (videoPlayer.current && typeof videoPlayer.current.toggleControls === 'function') {
- videoPlayer.current.toggleControls();
-}
-```
-
-**After:**
-```javascript
-safePlayerCall('toggleControls');
-```
-
-**개선점:**
-- 코드 간결성
-- 에러 처리 통일
-
-### 5. 뒤로가기 시 비디오 정지
-
-**위치**: Line 212-213
-
-```javascript
-// ✅ 뒤로가기 시 비디오 정지
-safePlayerCall('pause');
-```
-
-**효과:**
-- 패널 닫을 때 비디오 자동 정지
-- 메모리 정리 시작
-
-### 6. DOM 스타일 설정 및 정리
-
-**위치**: Line 353-376
-
-**Before:**
-```javascript
-useLayoutEffect(() => {
- const videoContainer = document.querySelector(`.${css.videoContainer}`);
- if (videoContainer && panelInfo.thumbnailUrl && !videoLoaded) {
- videoContainer.style.background = `url(${panelInfo.thumbnailUrl}) center center / contain no-repeat`;
- videoContainer.style.backgroundColor = 'black';
- }
-}, [panelInfo.thumbnailUrl, videoLoaded]);
-```
-
-**After:**
-```javascript
-// ✅ useLayoutEffect: DOM 스타일 설정 (메모리 누수 방지)
-useLayoutEffect(() => {
- const videoContainer = document.querySelector(`.${css.videoContainer}`);
- if (videoContainer && panelInfo.thumbnailUrl && !videoLoaded) {
- try {
- videoContainer.style.background = `url(${panelInfo.thumbnailUrl}) center center / contain no-repeat`;
- videoContainer.style.backgroundColor = 'black';
- } catch (err) {
- if (DEBUG_MODE) console.warn('[MediaPanel] 썸네일 스타일 설정 실패:', err);
- }
- }
-
- // ✅ cleanup: 컴포넌트 언마운트 시 DOM 스타일 초기화
- return () => {
- if (videoContainer) {
- try {
- videoContainer.style.background = '';
- videoContainer.style.backgroundColor = '';
- } catch (err) {
- // 스타일 초기화 실패는 무시
- }
- }
- };
-}, [panelInfo.thumbnailUrl, videoLoaded]);
-```
-
-**개선점:**
-- 에러 처리 추가
-- cleanup 함수로 DOM 스타일 초기화
-- 메모리 누수 방지
-
-### 7. mediainfoHandler 강화
-
-**위치**: Line 280-326
-
-**개선 사항:**
-- safePlayerCall 사용으로 null 안정성
-- hlsError 처리 강화
-- timeupdate 이벤트에서 mediaState 체크
-- error 이벤트에서 null 기본값 제공
-
-```javascript
-case 'timeupdate': {
- const mediaState = safePlayerCall('getMediaState');
- if (mediaState) {
- setCurrentTime(mediaState.currentTime || 0); // ✅ 기본값 제공
- }
- break;
-}
-```
-
-### 8. 컴포넌트 언마운트 시 전체 클린업 강화
-
-**위치**: Line 382-429
-
-**개선 사항:**
-
-```javascript
-useEffect(() => {
- return () => {
- // ✅ onEnded 타이머 정리
- if (onEndedTimerRef.current) {
- clearTimeout(onEndedTimerRef.current);
- onEndedTimerRef.current = null;
- }
-
- // ✅ Redux 상태 정리
- dispatch(stopMediaAutoClose?.()) || null;
-
- // ✅ 비디오 플레이어 정지 및 정리
- if (videoPlayer.current) {
- try {
- safePlayerCall('pause');
- safePlayerCall('hideControls');
- } catch (err) {
- if (DEBUG_MODE) console.warn('[MediaPanel] 비디오 정지 실패:', err);
- }
- videoPlayer.current = null; // ✅ ref 초기화
- }
-
- // ✅ 이벤트 리스너 정리
- if (mediaEventListenersRef.current && mediaEventListenersRef.current.length > 0) {
- mediaEventListenersRef.current.forEach(({ element, event, handler }) => {
- try {
- element?.removeEventListener?.(event, handler);
- } catch (err) {
- // 리스너 제거 실패는 무시
- }
- });
- mediaEventListenersRef.current = [];
- }
-
- // ✅ Spotlight 상태 초기화
- try {
- Spotlight.resume?.();
- } catch (err) {
- // Spotlight 초기화 실패는 무시
- }
- };
-}, [dispatch, safePlayerCall]);
-```
-
-**정리 항목:**
-1. ✅ onEnded 타이머 정리
-2. ✅ Redux 상태 정리
-3. ✅ 비디오 플레이어 정지
-4. ✅ 플레이어 ref 초기화
-5. ✅ 이벤트 리스너 제거
-6. ✅ Spotlight 상태 복구
-
----
-
-## 📊 변경 통계
-
-| 항목 | 수량 |
-|------|------|
-| 새로운 Ref | 1개 (mediaEventListenersRef) |
-| 새로운 함수 | 1개 (safePlayerCall) |
-| 개선된 useEffect | 2개 |
-| 개선된 콜백 | 3개 |
-| 추가된 클린업 로직 | 6개 항목 |
-| 에러 처리 강화 | 4개 지점 |
-
----
-
-## 🎯 효과
-
-### 메모리 누수 방지
-- ✅ 타이머 명시적 정리
-- ✅ 이벤트 리스너 추적 및 정리
-- ✅ ref 초기화
-- ✅ Redux 상태 정리
-
-### 안정성 향상
-- ✅ null/undefined 체크 강화
-- ✅ 에러 처리 통일
-- ✅ 존재하지 않는 메서드 호출 방지
-- ✅ 트라이-캐치 예외 처리
-
-### 코드 품질 개선
-- ✅ 반복 코드 제거
-- ✅ 일관된 에러 처리
-- ✅ 명확한 주석
-- ✅ 안전한 디폴트값 사용
-
----
-
-## 🔍 호환성 확인
-
-### 기존 기능 보존
-- ✅ 비디오 재생/정지 동작 유지
-- ✅ controls 표시/숨김 로직 유지
-- ✅ modal ↔ fullscreen 전환 유지
-- ✅ onEnded 콜백 동작 유지
-- ✅ 이벤트 핸들러 동작 유지
-
-### 추가 보호
-- ✅ null 참조 예외 방지
-- ✅ 잘못된 메서드 호출 방지
-- ✅ DOM 접근 에러 방지
-- ✅ 타이머 중복 정리 방지
-
----
-
-## 📌 주의사항
-
-### DEBUG_MODE 설정
-```javascript
-const DEBUG_MODE = false; // 프로덕션
-const DEBUG_MODE = true; // 개발/디버깅
-```
-
-- DEBUG_MODE = false일 때: 모든 경고 로그 숨김
-- DEBUG_MODE = true일 때: 모든 디버그 로그 표시
-
-### safePlayerCall 사용 규칙
-1. 존재하지 않을 수 있는 메서드만 사용
-2. 반환값이 필요하면 null 체크
-3. 항상 try-catch로 감싸짐
-
-```javascript
-// ✅ Good
-const state = safePlayerCall('getMediaState');
-if (state) { /* ... */ }
-
-// ✅ Good
-safePlayerCall('play');
-
-// ❌ Bad - 존재하는 메서드는 직접 호출
-videoPlayer.current.getVideoNode();
-```
-
----
-
-## 🚀 향후 개선 사항
-
-1. **이벤트 리스너 자동 추적**
- ```javascript
- const addTrackedListener = useCallback((element, event, handler) => {
- element.addEventListener(event, handler);
- mediaEventListenersRef.current.push({ element, event, handler });
- }, []);
- ```
-
-2. **성능 모니터링**
- - 메모리 사용량 로깅
- - 타이머 정리 시간 측정
-
-3. **테스트 커버리지**
- - 반복 마운트/언마운트 테스트
- - 메모리 누수 테스트
- - 에러 케이스 테스트
-
----
-
-## ✅ 검증 항목
-
-- [x] 기존 기능 동작 확인
-- [x] 메모리 누수 방지 로직 추가
-- [x] null/undefined 안전성 강화
-- [x] 에러 처리 통일
-- [x] 클린업 함수 완성
-- [x] 주석 및 문서화 완료
-
----
-
-## 📝 코드 예시
-
-### safePlayerCall 사용 예
-```javascript
-// Before
-if (videoPlayer.current?.getMediaState()?.paused) {
- videoPlayer.current.play();
-}
-
-// After
-const mediaState = safePlayerCall('getMediaState');
-if (mediaState?.paused) {
- safePlayerCall('play');
-}
-```
-
-### 언마운트 클린업
-```javascript
-useEffect(() => {
- return () => {
- // 타이머 정리
- if (onEndedTimerRef.current) {
- clearTimeout(onEndedTimerRef.current);
- }
-
- // Redux 정리
- dispatch(stopMediaAutoClose?.());
-
- // 플레이어 정리
- safePlayerCall('pause');
- videoPlayer.current = null;
-
- // 리스너 정리
- mediaEventListenersRef.current.forEach(({ element, event, handler }) => {
- element?.removeEventListener?.(event, handler);
- });
- };
-}, [dispatch, safePlayerCall]);
-```
-
----
-
-## ✨ 결론
-
-MediaPanel.jsx에 다음과 같은 메모리 누수 방지 및 클린업 기능을 추가했습니다:
-
-1. **안전한 메서드 호출** - safePlayerCall 래퍼
-2. **강화된 클린업** - 6개 항목 정리
-3. **에러 처리** - 통일된 예외 처리
-4. **리스너 추적** - 이벤트 리스너 관리 준비
-5. **DOM 정리** - 스타일 초기화
-
-이를 통해 장시간 사용 시에도 메모리 누수 없이 안정적으로 동작할 것으로 기대됩니다.
-
-**작업 상태**: ✅ 완료 (코드 수정만, git/npm 미실행)
diff --git a/com.twin.app.shoptime/TIMER_CLEANUP_SUMMARY.md b/com.twin.app.shoptime/TIMER_CLEANUP_SUMMARY.md
deleted file mode 100644
index e3d30ed6..00000000
--- a/com.twin.app.shoptime/TIMER_CLEANUP_SUMMARY.md
+++ /dev/null
@@ -1,398 +0,0 @@
-# 타이머 클린업 및 메모리 누수 방지 작업 완료 보고
-
-**작업 일시**: 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 | 메모리 누수 방지 |
-
----
-
-## ✨ 결론
-
-비디오 플레이어 관련 컴포넌트들의 타이머와 이벤트 리스너 정리를 체계적으로 개선했습니다.
-이를 통해 장시간 비디오 재생 시에도 메모리 누수 없이 안정적으로 동작할 것으로 기대됩니다.
-
-**작업 상태**: ✅ 완료
diff --git a/com.twin.app.shoptime/[251116]_video_state_management_design.md b/com.twin.app.shoptime/[251116]_video_state_management_design.md
deleted file mode 100644
index c96fd3f2..00000000
--- a/com.twin.app.shoptime/[251116]_video_state_management_design.md
+++ /dev/null
@@ -1,220 +0,0 @@
-# [251116] 새로운 비디오 상태 관리 시스템 구현
-
-## 개요
-
-기존의 videoPlayReducer는 유지하되, PlayerPanel과 VideoPlayer.js를 위한 새로운 비디오 상태 관리 시스템을 playerReducer에 구현한다. 재생 상태와 화면 상태를 분리하여 더 정밀한 비디오 상태 제어를 가능하게 한다.
-
-## 설계 원칙
-
-1. **기존 videoPlayReducer 유지**: 다른 컴포넌트에서 사용 중일 수 있으므로 그대로 둔다
-2. **playerReducer에 새로운 상태 시스템 구현**: PlayerPanel과 VideoPlayer.js 전용
-3. **이중 상태 관리**: 재생 상태(Playback Status) + 화면 상태(Display Status)
-4. **기존 패턴 따르기**: FP 스타일의 curry, get, set 활용
-
-## 새로운 상태 구조
-
-### 상수 정의 (playerActions.js)
-
-```javascript
-// 재생 상태
-export const PLAYBACK_STATUS = {
- LOADING: 'loading', // 비디오 로딩 중
- LOAD_SUCCESS: 'load_success', // 비디오 로딩 성공
- LOAD_ERROR: 'load_error', // 비디오 로딩 오류
- PLAYING: 'playing', // 비디오 재생 중
- NOT_PLAYING: 'not_playing', // 비디오 재생 아님 (정지/일시정지)
- BUFFERING: 'buffering' // 버퍼링 중
-};
-
-// 화면 상태
-export const DISPLAY_STATUS = {
- HIDDEN: 'hidden', // 화면에 안보임
- VISIBLE: 'visible', // 화면에 보임
- MINIMIZED: 'minimized', // 최소화됨
- FULLSCREEN: 'fullscreen' // 전체화면
-};
-```
-
-### 초기 상태 (playerReducer)
-
-```javascript
-// 기존 playerReducer 상태에 추가
-const initialState = {
- // ... 기존 상태들
-
- playerVideoState: {
- // 현재 상태
- playback: PLAYBACK_STATUS.NOT_PLAYING,
- display: DISPLAY_STATUS.HIDDEN,
- videoId: null,
- progress: 0,
- error: null,
- timestamp: null
- }
-};
-```
-
-## 액션 타입 및 함수
-
-### 액션 타입
-
-```javascript
-export const PLAYER_VIDEO_ACTIONS = {
- // 재생 상태 액션
- SET_PLAYBACK_LOADING: 'SET_PLAYBACK_LOADING',
- SET_PLAYBACK_SUCCESS: 'SET_PLAYBACK_SUCCESS',
- SET_PLAYBACK_ERROR: 'SET_PLAYBACK_ERROR',
- SET_PLAYBACK_PLAYING: 'SET_PLAYBACK_PLAYING',
- SET_PLAYBACK_NOT_PLAYING: 'SET_PLAYBACK_NOT_PLAYING',
- SET_PLAYBACK_BUFFERING: 'SET_PLAYBACK_BUFFERING',
-
- // 화면 상태 액션
- SET_DISPLAY_HIDDEN: 'SET_DISPLAY_HIDDEN',
- SET_DISPLAY_VISIBLE: 'SET_DISPLAY_VISIBLE',
- SET_DISPLAY_MINIMIZED: 'SET_DISPLAY_MINIMIZED',
- SET_DISPLAY_FULLSCREEN: 'SET_DISPLAY_FULLSCREEN',
-
- // 복합 액션
- SET_VIDEO_LOADING: 'SET_VIDEO_LOADING',
- SET_VIDEO_PLAYING: 'SET_VIDEO_PLAYING',
- SET_VIDEO_STOPPED: 'SET_VIDEO_STOPPED',
- SET_VIDEO_MINIMIZED_PLAYING: 'SET_VIDEO_MINIMIZED_PLAYING',
-};
-```
-
-### 액션 함수
-
-```javascript
-// 기본 액션 함수들 (FP 스타일)
-export const setPlaybackLoading = curry((videoId, displayMode = 'visible') => ({
- type: PLAYER_VIDEO_ACTIONS.SET_VIDEO_LOADING,
- payload: {
- playback: PLAYBACK_STATUS.LOADING,
- display: displayMode,
- videoId,
- progress: 0,
- error: null,
- timestamp: Date.now()
- }
-}));
-
-export const setPlaybackPlaying = curry((videoId, displayMode = 'fullscreen') => ({
- type: PLAYER_VIDEO_ACTIONS.SET_VIDEO_PLAYING,
- payload: {
- playback: PLAYBACK_STATUS.PLAYING,
- display: displayMode,
- videoId,
- progress: 100,
- error: null,
- timestamp: Date.now()
- }
-}));
-
-export const setPlaybackError = curry((videoId, error) => ({
- type: PLAYER_VIDEO_ACTIONS.SET_PLAYBACK_ERROR,
- payload: {
- playback: PLAYBACK_STATUS.LOAD_ERROR,
- display: DISPLAY_STATUS.VISIBLE,
- videoId,
- error,
- progress: 0,
- timestamp: Date.now()
- }
-}));
-
-export const setVideoStopped = () => ({
- type: PLAYER_VIDEO_ACTIONS.SET_VIDEO_STOPPED,
- payload: {
- playback: PLAYBACK_STATUS.NOT_PLAYING,
- display: DISPLAY_STATUS.HIDDEN,
- videoId: null,
- error: null,
- progress: 0,
- timestamp: Date.now()
- }
-}));
-```
-
-## 상태 사용 예시
-
-### PlayerPanel.jsx
-
-```javascript
-import {
- setPlaybackLoading,
- setPlaybackPlaying,
- setPlaybackError,
- setVideoStopped
-} from '../actions/playerActions';
-
-// 비디오 로딩 시작
-const handleVideoLoadStart = (videoId) => {
- dispatch(setPlaybackLoading(videoId, 'fullscreen'));
-};
-
-// 비디오 재생 시작
-const handleVideoPlay = (videoId) => {
- dispatch(setPlaybackPlaying(videoId, 'fullscreen'));
-};
-
-// 비디오 에러 발생
-const handleVideoError = (videoId, error) => {
- dispatch(setPlaybackError(videoId, error));
-};
-
-// 상태 확인
-const videoState = useSelector(state => state.player.playerVideoState);
-const isLoading = videoState.playback === PLAYBACK_STATUS.LOADING;
-const isPlaying = videoState.playback === PLAYBACK_STATUS.PLAYING;
-const hasError = videoState.playback === PLAYBACK_STATUS.LOAD_ERROR;
-const isFullscreen = videoState.display === DISPLAY_STATUS.FULLSCREEN;
-```
-
-### VideoPlayer.js
-
-```javascript
-// 현재 상태에 따른 UI 렌더링
-const renderVideoState = () => {
- const { playback, display, error, progress } = videoState;
-
- if (playback === PLAYBACK_STATUS.LOADING) {
- return