[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:
@@ -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 비디오 재생 문제를 해결하면서, 기존 다른 비디오 포맷 처리에는 영향을 주지 않는 안전한 방안입니다.
|
||||
@@ -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이 이벤트를 가로채지 못하고, 기존의 키보드 핸들링 로직이 정상 동작할 것입니다.
|
||||
Reference in New Issue
Block a user