[251112] feat: PinkFong Iframe issies

🕐 커밋 시간: 2025. 11. 12. 20:57:22

📊 변경 통계:
  • 총 파일: 2개

📁 추가된 파일:
  + com.twin.app.shoptime/.docs/ProductVideoV2-YouTube-Video-Type-Issue-Analysis.md
  + com.twin.app.shoptime/.docs/ProductVideoV2-YouTube-iframe-Event-Problem-Analysis.md

🔧 함수 변경 내용:
  📄 com.twin.app.shoptime/.docs/ProductVideoV2-YouTube-Video-Type-Issue-Analysis.md (md파일):
     Added: useMemo(), toLowerCase()
  📄 com.twin.app.shoptime/.docs/ProductVideoV2-YouTube-iframe-Event-Problem-Analysis.md (md파일):
     Added: postMessage(), setTimeout(), useEffect(), setInterval()

🔧 주요 변경 내용:
  • 타입 시스템 안정성 강화
  • 개발 문서 및 가이드 개선
This commit is contained in:
2025-11-12 20:57:23 +09:00
parent 53b012db2e
commit b8cf24dc0e
2 changed files with 431 additions and 0 deletions

View File

@@ -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 && (
<source src={productInfo?.prdtMediaUrl} type={videoType} />
)}
```
#### webOS TV에서의 동작
1. `window.PalmSystem`이 존재하므로 항상 `Media` 컴포넌트 사용
2. YouTube URL이 `<source>` 태그로 전달됨
3. 잘못된 `videoType` (`application/mpegurl`)으로 전달됨
4. Media 컴포넌트가 YouTube URL을 HLS로 처리하려다 실패
## 해결 방안
### 방안 1: YouTube URL에 대한 videoType 처리 로직 추가
#### 해결 원리
YouTube URL은 webOS TV의 Media 컴포넌트에서 직접 처리되어야 하므로, `<source>` 태그에 잘못된 타입을 전달하지 않도록 함
#### 구현 코드
```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 && (
<source src={productInfo?.prdtMediaUrl} type={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` 컴포넌트 사용 확인
- `<source>` 태그 생성 로직 확인
## 롤백 계획
### 문제 발생 시 롤백 방법
1. `videoType` 결정 로직을 기존 코드로 복원
2. `source` 태그 생성 조건을 기존대로 복원
3. YouTube 감지 로직은 유지 (디버깅용)
### 롤백 영향 범위
- ProductVideoV2 컴포넌트의 videoType 결정 로직만 영향
- 다른 컴포넌트나 전역 설정은 영향 없음
## 결론
ProductVideoV2 컴포넌트의 YouTube 비디오 타입 문제는 webOS TV 환경에서 Media 컴포넌트가 YouTube URL을 올바르게 처리하지 못하는 것이 근본 원인입니다. 제안된 해결 방안을 통해 YouTube URL에 대한 `videoType``null`로 처리하고 `source` 태그 생성 조건을 조정하여 문제를 해결할 수 있습니다.
이 수정은 최소한의 변경으로 YouTube 비디오 재생 문제를 해결하면서, 기존 다른 비디오 포맷 처리에는 영향을 주지 않는 안전한 방안입니다.

View File

@@ -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이 이벤트를 가로채지 못하고, 기존의 키보드 핸들링 로직이 정상 동작할 것입니다.