diff --git a/com.twin.app.shoptime/.docs/ProductVideoV2-YouTube-Video-Type-Issue-Analysis.md b/com.twin.app.shoptime/.docs/ProductVideoV2-YouTube-Video-Type-Issue-Analysis.md new file mode 100644 index 00000000..99462e29 --- /dev/null +++ b/com.twin.app.shoptime/.docs/ProductVideoV2-YouTube-Video-Type-Issue-Analysis.md @@ -0,0 +1,199 @@ +# ProductVideoV2 YouTube 비디오 타입 문제 분석 및 해결 방안 + +## 문제 개요 + +ProductVideoV2 컴포넌트에서 YouTube URL이 `application/mpegurl` (HLS) 타입으로 잘못 처리되어 webOS TV 환경에서 비디오 재생 문제가 발생하고 있습니다. + +## 현재 상황 분석 + +### 1. 문제 발생 위치 +- **파일**: `src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx` +- **문제 라인**: 161-247번 라인 (videoType 결정 로직) +- **영향 라인**: 1003-1004번 라인 (source 태그 생성) + +### 2. 문제 현상 + +#### 로그 예시 +``` +🎥 [VIDEO FORMAT] URL 구조 분석 +Object { + originalUrl: "https://www.youtube.com/watch?v=WDEanlx9zoI", + lowerUrl: "https://www.youtube.com/watch?v=wdeanlx9zoi", + urlParts: {…}, + extensionChecks: { + isMp4: false, + isMpd: false, + isM3u8: false, + isHls: false, + isDash: false + }, + timestamp: "2025-11-12T11:24:16.690Z" +} + +🎥 [VIDEO FORMAT] 최종 타입 결정 +Object { + determinedType: "application/mpegurl", + determinationReason: "No specific format detected, defaulting to HLS" +} +``` + +### 3. 근본 원인 + +#### 현재 videoType 결정 로직 (161-247번 라인) +```javascript +const videoType = useMemo(() => { + const url = productInfo?.prdtMediaUrl; + if (url) { + const lowerUrl = url.toLowerCase(); + const isMp4 = lowerUrl.endsWith('.mp4'); + const isMpd = lowerUrl.endsWith('.mpd'); + const isM3u8 = lowerUrl.endsWith('.m3u8'); + const isHls = lowerUrl.includes('.m3u8') || lowerUrl.includes('playlist.m3u8'); + const isDash = lowerUrl.includes('.mpd') || lowerUrl.includes('dash'); + + if (isMp4) return 'video/mp4'; + else if (isMpd) return 'application/dash+xml'; + else if (isM3u8) return 'application/mpegurl'; + else if (isHls) return 'application/mpegurl'; + else if (isDash) return 'application/dash+xml'; + else return 'application/mpegurl'; // 기본값 + } + return 'application/mpegurl'; +}, [productInfo?.prdtMediaUrl]); +``` + +#### YouTube URL 특성 +- YouTube URL은 파일 확장자 기반 체크로 감지되지 않음 +- 예: `https://www.youtube.com/watch?v=WDEanlx9zoI` +- 확장자 없는 URL이라 항상 기본값인 HLS 타입으로 결정됨 + +### 4. webOS TV 환경에서의 문제 + +#### VideoPlayer 컴포넌트 구조 +```javascript +// videoComponent 결정 (881-883번 라인) +videoComponent={ + (typeof window === 'object' && !window.PalmSystem) || isYoutube + ? TReactPlayer + : Media +} + +// source 태그 생성 (1003-1004번 라인) +{typeof window === 'object' && window.PalmSystem && ( + +)} +``` + +#### webOS TV에서의 동작 +1. `window.PalmSystem`이 존재하므로 항상 `Media` 컴포넌트 사용 +2. YouTube URL이 `` 태그로 전달됨 +3. 잘못된 `videoType` (`application/mpegurl`)으로 전달됨 +4. Media 컴포넌트가 YouTube URL을 HLS로 처리하려다 실패 + +## 해결 방안 + +### 방안 1: YouTube URL에 대한 videoType 처리 로직 추가 + +#### 해결 원리 +YouTube URL은 webOS TV의 Media 컴포넌트에서 직접 처리되어야 하므로, `` 태그에 잘못된 타입을 전달하지 않도록 함 + +#### 구현 코드 +```javascript +// 비디오 타입 결정 로직 수정 +const videoType = useMemo(() => { + const url = productInfo?.prdtMediaUrl; + + // YouTube URL은 별도 타입으로 처리하지 않음 (webOS TV Media 컴포넌트에서 직접 처리) + if (url && isYoutube) { + return null; // 또는 빈 문자열 + } + + if (url) { + const lowerUrl = url.toLowerCase(); + const isMp4 = lowerUrl.endsWith('.mp4'); + const isMpd = lowerUrl.endsWith('.mpd'); + const isM3u8 = lowerUrl.endsWith('.m3u8'); + const isHls = lowerUrl.includes('.m3u8') || lowerUrl.includes('playlist.m3u8'); + const isDash = lowerUrl.includes('.mpd') || lowerUrl.includes('dash'); + + if (isMp4) return 'video/mp4'; + else if (isMpd) return 'application/dash+xml'; + else if (isM3u8) return 'application/mpegurl'; + else if (isHls) return 'application/mpegurl'; + else if (isDash) return 'application/dash+xml'; + else return 'application/mpegurl'; + } + return 'application/mpegurl'; +}, [productInfo?.prdtMediaUrl, isYoutube]); + +// source 태그 생성 조건 수정 +{typeof window === 'object' && window.PalmSystem && videoType && ( + +)} +``` + +### 방안 2: YouTube URL 감지 로직 개선 + +#### 개선점 +YouTube URL 감지 로직을 더 명확하게 하고, 타입 결정에 반영 + +#### 구현 코드 +```javascript +// YouTube URL 감지 로직 개선 +const isYoutube = useMemo(() => { + const url = productInfo?.prdtMediaUrl; + if (!url) return false; + + return url.includes('youtube.com') || + url.includes('youtu.be') || + url.includes('youtu'); +}, [productInfo?.prdtMediaUrl]); +``` + +## 예상 효과 + +### 1. YouTube URL 처리 개선 +- webOS TV 환경에서 YouTube 비디오가 올바르게 처리됨 +- 잘못된 HLS 타입으로 인한 재생 실패 방지 + +### 2. 다른 비디오 포맷 유지 +- MP4, HLS, DASH 등 기존 비디오 포맷 처리 로직 유지 +- webOS TV가 아닌 환경에서의 YouTube 처리는 기존과 동일 + +### 3. 안정성 향상 +- VideoPlayer 컴포넌트에서 예기치 않은 타입 오류 방지 +- webOS TV 미디어 플레이어와의 호환성 증대 + +## 테스트 시나리오 + +### 1. YouTube URL 테스트 +- URL: `https://www.youtube.com/watch?v=WDEanlx9zoI` +- 예상 결과: webOS TV에서 정상 재생 + +### 2. 일반 비디오 포맷 테스트 +- MP4: `https://example.com/video.mp4` +- HLS: `https://example.com/playlist.m3u8` +- DASH: `https://example.com/video.mpd` +- 예상 결과: 기존과 동일하게 정상 재생 + +### 3. webOS TV 환경 테스트 +- `window.PalmSystem` 존재 여부 확인 +- `Media` 컴포넌트 사용 확인 +- `` 태그 생성 로직 확인 + +## 롤백 계획 + +### 문제 발생 시 롤백 방법 +1. `videoType` 결정 로직을 기존 코드로 복원 +2. `source` 태그 생성 조건을 기존대로 복원 +3. YouTube 감지 로직은 유지 (디버깅용) + +### 롤백 영향 범위 +- ProductVideoV2 컴포넌트의 videoType 결정 로직만 영향 +- 다른 컴포넌트나 전역 설정은 영향 없음 + +## 결론 + +ProductVideoV2 컴포넌트의 YouTube 비디오 타입 문제는 webOS TV 환경에서 Media 컴포넌트가 YouTube URL을 올바르게 처리하지 못하는 것이 근본 원인입니다. 제안된 해결 방안을 통해 YouTube URL에 대한 `videoType`을 `null`로 처리하고 `source` 태그 생성 조건을 조정하여 문제를 해결할 수 있습니다. + +이 수정은 최소한의 변경으로 YouTube 비디오 재생 문제를 해결하면서, 기존 다른 비디오 포맷 처리에는 영향을 주지 않는 안전한 방안입니다. \ No newline at end of file diff --git a/com.twin.app.shoptime/.docs/ProductVideoV2-YouTube-iframe-Event-Problem-Analysis.md b/com.twin.app.shoptime/.docs/ProductVideoV2-YouTube-iframe-Event-Problem-Analysis.md new file mode 100644 index 00000000..67440b89 --- /dev/null +++ b/com.twin.app.shoptime/.docs/ProductVideoV2-YouTube-iframe-Event-Problem-Analysis.md @@ -0,0 +1,232 @@ +# ProductVideoV2 YouTube iframe 이벤트 문제 분석 및 해결 방안 + +## 문제 현상 + +YouTube 비디오가 전체화면 모드로 전환되면 **iframe 내부의 YouTube 컨트롤 오버레이**가 나타나서 키보드/마우스 이벤트를 가로채서 일반 모드로 돌아올 수 없음 + +## 🔥 근본 원인 분석 + +### 1. YouTube iframe의 독립적 이벤트 처리 + +#### 문제점 +- YouTube iframe은 **독립적인 문서 컨텍스트**를 가짐 +- iframe 내부의 YouTube 플레이어 컨트롤이 **자체적인 이벤트 핸들링**을 함 +- 부모 문서의 `window.addEventListener('keydown', ...)`가 **iframe 내부까지 전파되지 않음** + +#### 증거 +- `window.addEventListener('keydown', handleFullscreenKeyDown, true)` (capture phase)로 설정했지만 **iframe 내부까지는 도달하지 못함** +- YouTube iframe의 **native event handling**이 더 높은 우선순위를 가짐 + +### 2. Spotlight 포커스 시스템의 한계 + +#### 문제점 +- 현재 Spotlight 시스템은 React 컴포넌트 DOM 요소에만 동작 +- YouTube iframe 내부의 요소는 Spotlight가 **제어할 수 없는 영역** +- `spotlightRestrict="self-only"`가 iframe 내부까지 적용되지 않음 + +### 3. TReactPlayer의 내부 동작 방식 + +#### 문제점 +- TReactPlayer는 react-player 라이브러리를 사용 +- YouTube iframe을 생성할 때 **내부적으로 설정을 덮어쓸 수 있음** +- YOUTUBECONFIG가 react-player에 **제대로 전달되지 않을 가능성** + +### 4. webOS 환경 특성 + +#### 문제점 +- webOS TV 환경에서는 **키코드가 다르게 동작** +- 리모컨 버튼의 키코드: Back(461), Return(10009), ArrowUp/Down(37/40) 등 +- 이벤트 처리 순서가 웹 브라우저와 다를 수 있음 + +## 🎯 구체적인 문제 시나리오 + +### 시나리오 1: ESC 키 문제 +1. 사용자가 ESC 키 누름 +2. YouTube iframe이 이벤트를 먼저 처리 +3. 부모 문서의 `handleFullscreenKeyDown`가 호출되지 않음 +4. **결과:** 일반 모드로 돌아갈 수 없음 + +### 시나리오 2: Back 버튼(리모컨) 문제 +1. 리모컨 Back 버튼 누름 (keyCode: 461) +2. YouTube iframe이 이벤트를 가로챔 +3. **결과:** 포커스를 벗어나지 못함 + +### 시나리오 3: Spotlight 포커스 문제 +1. Spotlight가 전체화면 컨테이너에 포커스 설정 +2. YouTube iframe이 포커스를 훔쳐감 +3. **결과:** Spotlight 제어 불가 + +### 시나리오 4: 클릭/터치 이벤트 문제 +1. 전체화면에서 사용자가 화면 클릭 +2. YouTube iframe이 클릭 이벤트를 처리 +3. **결과:** 전체화면 해제 불가 + +## 🛠️ 해결 방안 분석 + +### 방안 1: YouTube 컨트롤 완전 제거 (현재 시도 중) + +#### 구현 내용 +```javascript +const YOUTUBECONFIG = { + playerVars: { + controls: 0, // ✅ 플레이어 컨트롤 완전 숨김 + disablekb: 1, // ✅ 키보드 입력 완전 비활성화 (핵심) + fs: 0, // ✅ 전체화면 버튼 비활성화 + rel: 0, // ✅ 관련 동영상 비활성화 + // ... 기타 설정 + }, +}; +``` + +#### 예상 효과 +- YouTube iframe이 내부 이벤트를 처리하지 않음 +- 부모 문서가 완전히 이벤트 제어 +- Spotlight 포커스 시스템 정상 동작 + +#### 현재 문제점 +- YOUTUBECONFIG가 react-player에 제대로 전달되지 않을 수 있음 +- TReactPlayer가 내부적으로 설정을 덮어쓸 가능성 + +### 방안 2: YouTube PostMessage API 활용 + +#### 구현 방식 +```javascript +const sendYouTubeCommand = (command, args = []) => { + const iframe = document.querySelector('iframe[src*="youtube"]'); + if (iframe) { + iframe.contentWindow.postMessage({ + event: 'command', + func: command, + args: args + }, '*'); + } +}; + +// ESC 키 처리 +sendYouTubeCommand('pauseVideo'); +setTimeout(() => setIsFullscreen(false), 100); +``` + +#### 장점 +- YouTube iframe과 직접 통신 가능 +- 더 정교한 제어 가능 + +#### 단점 +- 복잡성 증가 +- iframe 로드 타이밍 이슈 + +### 방안 3: 강제 포커스 회수 + +#### 구현 방식 +```javascript +useEffect(() => { + if (isFullscreen && isYoutube) { + const interval = setInterval(() => { + Spotlight.focus('product-video-v2-fullscreen-portal'); + }, 1000); + return () => clearInterval(interval); + } +}, [isFullscreen, isYoutube]); +``` + +#### 장점 +- 포커스 유지 보장 +- 간단한 구현 + +#### 단점 +- 리소스 낭비 +- 근본적인 해결책 아님 + +### 방안 4: TReactPlayer 대신 직접 제어 + +#### 구현 방식 +- react-player 라이브러리 대신 직접 YouTube iframe 제어 +- iframe 생성과 제어를 완전히 직접 관리 + +#### 장점 +- 완벽한 제어 가능 +- 의도치 않은 동작 방지 + +#### 단점 +- 복잡성 급증 +- 유지보수 어려움 + +## 🔍 진단을 위한 확인 사항 + +### 1. 로그 확인 +```javascript +// reactPlayerSubtitleConfig 설정 확인 +console.log('🎥 [reactPlayerSubtitleConfig] 설정 생성', { + isYoutube: isYoutube, + hasSubtitle: !!subtitleUrl, + youtubeConfig: YOUTUBECONFIG, +}); +``` + +### 2. DOM 확인 +- YouTube iframe이 실제로 생성되는지 확인 +- TReactPlayer가 iframe을 제대로 감싸고 있는지 확인 +- iframe에 적용된 설정 확인 + +### 3. 이벤트 전파 확인 +```javascript +// 전체화면 키보드 이벤트 로깅 +console.log('🖥️ [Fullscreen Container] 키보드 이벤트 감지', { + key: e.key, + keyCode: e.keyCode, + isYoutube: isYoutube, +}); +``` + +## 🎯 추천 해결 순서 + +### 1단계: 현재 방안 1 완료 +- YOUTUBECONFIG가 react-player에 제대로 전달되는지 확인 +- YouTube iframe이 실제로 컨트롤이 비활성화되는지 확인 + +### 2단계: 강화된 이벤트 핸들링 +- 리모컨 버튼 키코드 확장 (461, 10009 등) +- Capture phase 이벤트 처리 강화 + +### 3단계: 방안 2 전환 (필요 시) +- PostMessage API로 직접 YouTube 제어 + +### 4단계: 방안 3 보조 +- 주기적 포커스 회수로 안정성 확보 + +## 🔄 롤백 계획 + +### 롤백 1: YOUTUBECONFIG 복원 +```javascript +const YOUTUBECONFIG = { + playerVars: { + controls: 0, + autoplay: 1, + disablekb: 0, // 키보드 활성화로 복원 + fs: 1, // 전체화면 버튼 활성화로 복원 + // ... 기존 설정 + }, +}; +``` + +### 롤백 2: 이벤트 핸들러 복원 +```javascript +// Back 버튼 처리 로직 제거 +// return toggleOverlayVisibility(); +``` + +### 롤백 3: reactPlayerSubtitleConfig 복원 +```javascript +// isYoutube 의존성 제거 +}, [productInfo?.prdtMediaSubtitlUrl]); +``` + +## 결론 + +가장 현실적인 해결책은 **방안 1 (YouTube 컨트롤 완전 제거)**과 **방안 2 (PostMessage API)**의 조합입니다: + +1. 일단 YOUTUBECONFIG를 통해 컨트롤 완전 비활성화 +2. 필요시 PostMessage API로 직접 YouTube 제어 +3. Spotlight 포커스 시스템 보강으로 안정성 확보 + +이렇게 하면 YouTube iframe이 이벤트를 가로채지 못하고, 기존의 키보드 핸들링 로직이 정상 동작할 것입니다. \ No newline at end of file