From b8cf24dc0ec816c361a0caa29c046cdcbba09e96 Mon Sep 17 00:00:00 2001 From: optrader Date: Wed, 12 Nov 2025 20:57:23 +0900 Subject: [PATCH] [251112] feat: PinkFong Iframe issies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ• ์ปค๋ฐ‹ ์‹œ๊ฐ„: 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() ๐Ÿ”ง ์ฃผ์š” ๋ณ€๊ฒฝ ๋‚ด์šฉ: โ€ข ํƒ€์ž… ์‹œ์Šคํ…œ ์•ˆ์ •์„ฑ ๊ฐ•ํ™” โ€ข ๊ฐœ๋ฐœ ๋ฌธ์„œ ๋ฐ ๊ฐ€์ด๋“œ ๊ฐœ์„  --- ...deoV2-YouTube-Video-Type-Issue-Analysis.md | 199 +++++++++++++++ ...2-YouTube-iframe-Event-Problem-Analysis.md | 232 ++++++++++++++++++ 2 files changed, 431 insertions(+) create mode 100644 com.twin.app.shoptime/.docs/ProductVideoV2-YouTube-Video-Type-Issue-Analysis.md create mode 100644 com.twin.app.shoptime/.docs/ProductVideoV2-YouTube-iframe-Event-Problem-Analysis.md 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