[251112] feat: ProductVideroV2 Continuous Video Playing

🕐 커밋 시간: 2025. 11. 12. 15:28:31

📊 변경 통계:
  • 총 파일: 2개
  • 추가: +67줄
  • 삭제: -31줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.module.less
  ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx

🔧 주요 변경 내용:
  • 소규모 기능 개선
This commit is contained in:
2025-11-12 15:28:33 +09:00
parent 16718c4753
commit 1166311e4b
2 changed files with 236 additions and 163 deletions

View File

@@ -143,6 +143,16 @@
pointer-events: auto; pointer-events: auto;
} }
.hideWhileFullscreen {
visibility: hidden;
pointer-events: none;
}
.videoPortalHost {
width: 100%;
height: 100%;
}
// 전체화면 container (Portal, body에 항상 존재) // 전체화면 container (Portal, body에 항상 존재)
.fullscreenContainer { .fullscreenContainer {
position: fixed; position: fixed;

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react'; import React, { useCallback, useEffect, useLayoutEffect, useMemo, useState, useRef } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import Spottable from '@enact/spotlight/Spottable'; import Spottable from '@enact/spotlight/Spottable';
@@ -24,7 +24,11 @@ import {
stopMediaAutoClose, stopMediaAutoClose,
resetMediaAutoClose, resetMediaAutoClose,
} from '../../../../actions/mediaOverlayActions'; } from '../../../../actions/mediaOverlayActions';
import { pauseFullscreenVideo, resumeFullscreenVideo, clearAllVideoTimers } from '../../../../actions/playActions'; import {
pauseFullscreenVideo,
resumeFullscreenVideo,
clearAllVideoTimers,
} from '../../../../actions/playActions';
import css from './ProductVideo.module.less'; import css from './ProductVideo.module.less';
const SpottableComponent = Spottable('div'); const SpottableComponent = Spottable('div');
@@ -80,6 +84,16 @@ export function ProductVideoV2({
const videoPlayerWrapperRef = useRef(null); const videoPlayerWrapperRef = useRef(null);
const normalContainerRef = useRef(null); const normalContainerRef = useRef(null);
const fullscreenContainerRef = useRef(null); const fullscreenContainerRef = useRef(null);
const videoPortalHostRef = useRef(null);
const ensurePortalHost = useCallback(() => {
if (!videoPortalHostRef.current && typeof document !== 'undefined') {
const host = document.createElement('div');
host.className = css.videoPortalHost;
videoPortalHostRef.current = host;
}
return videoPortalHostRef.current;
}, []);
// 비디오 재생 가능 여부 체크 // 비디오 재생 가능 여부 체크
const canPlayVideo = useMemo(() => { const canPlayVideo = useMemo(() => {
@@ -149,33 +163,36 @@ export function ProductVideoV2({
}, [canPlayVideo, isPlaying]); }, [canPlayVideo, isPlaying]);
// 썸네일 클릭 핸들러 - 비디오 재생 시작 + Redux dispatch + MediaPlayer 메서드 호출 // 썸네일 클릭 핸들러 - 비디오 재생 시작 + Redux dispatch + MediaPlayer 메서드 호출
const handleThumbnailClick = useCallback((e) => { const handleThumbnailClick = useCallback(
console.log('🎬 [handleThumbnailClick] 썸네일 클릭됨', { (e) => {
canPlayVideo, console.log('🎬 [handleThumbnailClick] 썸네일 클릭됨', {
isPlaying, canPlayVideo,
eventType: e?.type, isPlaying,
target: e?.target?.className, eventType: e?.type,
bubbles: e?.bubbles, target: e?.target?.className,
timestamp: new Date().getTime(), bubbles: e?.bubbles,
}); timestamp: new Date().getTime(),
if (canPlayVideo && !isPlaying) { });
// console.log('[BgVideo] ProductVideoV2 - Starting video playback'); if (canPlayVideo && !isPlaying) {
console.log('🎬 [handleThumbnailClick] ✅ 비디오 재생 시작'); // console.log('[BgVideo] ProductVideoV2 - Starting video playback');
setIsPlaying(true); console.log('🎬 [handleThumbnailClick] ✅ 비디오 재생 시작');
setIsPlaying(true);
// 백그라운드 전체화면 비디오 일시정지 // 백그라운드 전체화면 비디오 일시정지
// console.log('[BgVideo] ProductVideoV2 - Pausing background fullscreen video'); // console.log('[BgVideo] ProductVideoV2 - Pausing background fullscreen video');
dispatch(pauseFullscreenVideo()); dispatch(pauseFullscreenVideo());
// Redux: mediaOverlay 상태 초기화 (MediaPlayer 전용) // Redux: mediaOverlay 상태 초기화 (MediaPlayer 전용)
dispatch(setMediaControlShow()); dispatch(setMediaControlShow());
dispatch(startMediaAutoClose(3000)); dispatch(startMediaAutoClose(3000));
// MediaPlayer 직접 제어: controls 표시 // MediaPlayer 직접 제어: controls 표시
setTimeout(() => { setTimeout(() => {
videoPlayerRef.current?.showControls?.(); videoPlayerRef.current?.showControls?.();
}, 100); }, 100);
} }
}, [canPlayVideo, isPlaying, dispatch]); },
[canPlayVideo, isPlaying, dispatch]
);
// 비디오 종료 핸들러 - 썸네일로 복귀 + Redux cleanup + MediaPlayer 메서드 호출 // 비디오 종료 핸들러 - 썸네일로 복귀 + Redux cleanup + MediaPlayer 메서드 호출
const handleVideoEnded = useCallback(() => { const handleVideoEnded = useCallback(() => {
@@ -372,10 +389,10 @@ export function ProductVideoV2({
const isCurrentlyVisible = videoPlayerRef.current?.areControlsVisible?.(); const isCurrentlyVisible = videoPlayerRef.current?.areControlsVisible?.();
if (isCurrentlyVisible) { if (isCurrentlyVisible) {
videoPlayerRef.current?.hideControls?.(); videoPlayerRef.current?.hideControls?.();
dispatch(setMediaControlHide()); // MediaOverlay Redux 상태 동기화 dispatch(setMediaControlHide()); // MediaOverlay Redux 상태 동기화
} else { } else {
videoPlayerRef.current?.showControls?.(); videoPlayerRef.current?.showControls?.();
dispatch(setMediaControlShow()); // MediaOverlay Redux 상태 동기화 dispatch(setMediaControlShow()); // MediaOverlay Redux 상태 동기화
} }
}, },
[isPlaying, isFullscreen, toggleFullscreen, dispatch, mediaOverlayState.controls?.visible] [isPlaying, isFullscreen, toggleFullscreen, dispatch, mediaOverlayState.controls?.visible]
@@ -394,34 +411,40 @@ export function ProductVideoV2({
}, [mediaOverlayState.controls?.visible, dispatch]); }, [mediaOverlayState.controls?.visible, dispatch]);
// 전체 화면 컨테이너용 마우스 다운 핸들러 (Capture Phase) // 전체 화면 컨테이너용 마우스 다운 핸들러 (Capture Phase)
const handleFullscreenContainerMouseDownCapture = useCallback((e) => { const handleFullscreenContainerMouseDownCapture = useCallback(
if (!isPlaying || !isFullscreen) return; (e) => {
if (!isPlaying || !isFullscreen) return;
e.preventDefault();
e.stopPropagation();
// toggleOverlayVisibility();
}, [isPlaying, isFullscreen, toggleOverlayVisibility]);
// 전체 화면 키보드 핸들러
const handleFullscreenKeyDown = useCallback((e) => {
if (!isPlaying || !isFullscreen) return;
// ESC 키만 오버레이 토글로 처리
if (e.key === 'Escape' || e.keyCode === 27) {
console.log('🖥️ [Fullscreen Container] ESC 키 - 오버레이 토글 실행');
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
toggleOverlayVisibility(); // toggleOverlayVisibility();
return; },
} [isPlaying, isFullscreen, toggleOverlayVisibility]
);
// Enter 키는 기본 동작 허용 (포커스된 요소의 동작 수행) // 전체 화면 키보드 핸들러
if (e.key === 'Enter' || e.keyCode === 13) { const handleFullscreenKeyDown = useCallback(
console.log('🖥️ [Fullscreen Container] Enter 키 - 포커스된 요소 동작 허용'); (e) => {
// Enter 키는 preventDefault하지 않고 기본 동작 허용 if (!isPlaying || !isFullscreen) return;
return;
} // ESC 키만 오버레이 토글로 처리
}, [isPlaying, isFullscreen, toggleOverlayVisibility]); if (e.key === 'Escape' || e.keyCode === 27) {
console.log('🖥️ [Fullscreen Container] ESC 키 - 오버레이 토글 실행');
e.preventDefault();
e.stopPropagation();
toggleOverlayVisibility();
return;
}
// Enter 키는 기본 동작 허용 (포커스된 요소의 동작 수행)
if (e.key === 'Enter' || e.keyCode === 13) {
console.log('🖥️ [Fullscreen Container] Enter 키 - 포커스된 요소 동작 허용');
// Enter 키는 preventDefault하지 않고 기본 동작 허용
return;
}
},
[isPlaying, isFullscreen, toggleOverlayVisibility]
);
// 마우스 다운 (클릭) 이벤트 - capture phase에서 처리 // 마우스 다운 (클릭) 이벤트 - capture phase에서 처리
const handleVideoPlayerMouseDown = useCallback( const handleVideoPlayerMouseDown = useCallback(
@@ -466,20 +489,24 @@ export function ProductVideoV2({
useEffect(() => { useEffect(() => {
const handleDocumentClick = (e) => { const handleDocumentClick = (e) => {
// ProductVideoV2 관련 요소인 경우만 로깅 // ProductVideoV2 관련 요소인 경우만 로깅
const isVideoElement = e.target?.closest('[class*="videoContainer"]') || const isVideoElement =
e.target?.closest('[class*="videoPlayer"]') || e.target?.closest('[class*="videoContainer"]') ||
e.target?.closest('[class*="videoThumbnail"]'); e.target?.closest('[class*="videoPlayer"]') ||
e.target?.closest('[class*="videoThumbnail"]');
if (isVideoElement) { if (isVideoElement) {
console.log('📄 [Document Level] 전역 클릭 감지됨', { console.log('📄 [Document Level] 전역 클릭 감지됨', {
eventPhase: e.eventPhase, eventPhase: e.eventPhase,
bubbles: e.bubbles, bubbles: e.bubbles,
target: e.target?.className, target: e.target?.className,
eventPath: e.composedPath?.().slice(0, 6).map(el => ({ eventPath: e
tag: el.tagName, .composedPath?.()
className: el.className, .slice(0, 6)
id: el.id .map((el) => ({
})) tag: el.tagName,
className: el.className,
id: el.id,
})),
}); });
} }
}; };
@@ -490,6 +517,36 @@ export function ProductVideoV2({
}; };
}, []); }, []);
useLayoutEffect(() => {
if (!isPlaying) return;
const host = ensurePortalHost();
const target = isFullscreen ? fullscreenContainerRef.current : normalContainerRef.current;
if (host && target && host.parentNode !== target) {
target.appendChild(host);
}
}, [isPlaying, isFullscreen, ensurePortalHost]);
useEffect(() => {
if (isPlaying) return;
const host = videoPortalHostRef.current;
if (host?.parentNode) {
host.parentNode.removeChild(host);
}
}, [isPlaying]);
useEffect(() => {
return () => {
const host = videoPortalHostRef.current;
if (host?.parentNode) {
host.parentNode.removeChild(host);
}
videoPortalHostRef.current = null;
};
}, []);
// autoPlay 기능: 컴포넌트 마운트 후 500ms 후 자동 재생 // autoPlay 기능: 컴포넌트 마운트 후 500ms 후 자동 재생
useEffect(() => { useEffect(() => {
if (autoPlay && canPlayVideo && !isPlaying) { if (autoPlay && canPlayVideo && !isPlaying) {
@@ -595,17 +652,10 @@ export function ProductVideoV2({
const containerProps = useMemo(() => { const containerProps = useMemo(() => {
const baseProps = { const baseProps = {
onClick: handleVideoContainerClick, // 모든 상태에서 동일하게 적용 onClick: handleVideoContainerClick,
}; };
if (isFullscreen) { if (isPlaying) {
return {
...baseProps,
spotlightRestrict: 'self-only', // 포커스가 밖으로 나가지 않도록
spotlightId: 'product-video-v2-fullscreen',
onSpotlightDown: handleSpotlightDown, // 전체화면에서도 Down 키 동작
};
} else if (isPlaying) {
return { return {
...baseProps, ...baseProps,
spotlightId: 'product-video-v2-playing', spotlightId: 'product-video-v2-playing',
@@ -615,70 +665,89 @@ export function ProductVideoV2({
} else { } else {
return baseProps; return baseProps;
} }
}, [isFullscreen, isPlaying, handleVideoContainerClick, handleSpotlightDown, handleContainerKeyDown]); }, [
isFullscreen,
isPlaying,
handleVideoContainerClick,
handleSpotlightDown,
handleContainerKeyDown,
]);
// ⚠️ 간단한 해결책: 비디오 영역 클릭 시 직접 동작 실행 // ⚠️ 간단한 해결책: 비디오 영역 클릭 시 직접 동작 실행
const handleContainerClickFallback = useCallback((e) => { const handleContainerClickFallback = useCallback(
const isThumbnailArea = e.target?.closest('[class*="videoThumbnail"]') || (e) => {
e.target?.closest('[class*="playButton"]'); const isThumbnailArea =
e.target?.closest('[class*="videoThumbnail"]') ||
e.target?.closest('[class*="playButton"]');
const isVideoPlayerArea = e.target?.closest('[class*="videoPlayer"]') || const isVideoPlayerArea =
e.target?.closest('[class*="VideoPlayer"]'); e.target?.closest('[class*="videoPlayer"]') || e.target?.closest('[class*="VideoPlayer"]');
// 썸네일 클릭: 비디오 재생 시작 // 썸네일 클릭: 비디오 재생 시작
if (isThumbnailArea && !isPlaying) { if (isThumbnailArea && !isPlaying) {
console.log('🎬 [handleContainerClickFallback] 썸네일 클릭 → 비디오 재생 시작'); console.log('🎬 [handleContainerClickFallback] 썸네일 클릭 → 비디오 재생 시작');
handleThumbnailClick(e); handleThumbnailClick(e);
} }
// 비디오 재생 중 클릭: 전체화면 토글 // 비디오 재생 중 클릭: 전체화면 토글
if (isVideoPlayerArea && isPlaying && !isFullscreen) { if (isVideoPlayerArea && isPlaying && !isFullscreen) {
console.log('🎬 [handleContainerClickFallback] 비디오 클릭 → 전체화면 토글'); console.log('🎬 [handleContainerClickFallback] 비디오 클릭 → 전체화면 토글');
toggleFullscreen(); toggleFullscreen();
} }
}, [isPlaying, isFullscreen, handleThumbnailClick, toggleFullscreen]); },
[isPlaying, isFullscreen, handleThumbnailClick, toggleFullscreen]
);
// ⚠️ 핵심: TScrollerDetail이 capture phase에서 이벤트를 막으므로, // ⚠️ 핵심: TScrollerDetail이 capture phase에서 이벤트를 막으므로,
// ProductVideoV2 컨테이너 전체에서 capture phase를 다시 열어줌 (Hooks는 early return 전에) // ProductVideoV2 컨테이너 전체에서 capture phase를 다시 열어줌 (Hooks는 early return 전에)
const handleContainerMouseDownCapture = useCallback((e) => { const handleContainerMouseDownCapture = useCallback(
// 비디오 영역인지 확인 (e) => {
const isVideoArea = e.currentTarget === containerRef.current; // 비디오 영역인지 확인
const isVideoArea = e.currentTarget === containerRef.current;
console.log('🔓 [ProductVideoV2 Container] onMouseDownCapture - TScrollerDetail 차단 우회', { console.log('🔓 [ProductVideoV2 Container] onMouseDownCapture - TScrollerDetail 차단 우회', {
isVideoArea, isVideoArea,
target: e.target?.className, target: e.target?.className,
isPlaying, isPlaying,
isFullscreen, isFullscreen,
}); });
// 전체 화면 모드가 아닐 때만 toggleFullscreen 호출 // 전체 화면 모드가 아닐 때만 toggleFullscreen 호출
if (!isFullscreen) { if (!isFullscreen) {
toggleFullscreen(); toggleFullscreen();
// TScrollerDetail의 onClick 실행을 막기 위해 preventDefault 호출 // TScrollerDetail의 onClick 실행을 막기 위해 preventDefault 호출
if (isPlaying) { if (isPlaying) {
console.log('🔓 [ProductVideoV2] TScrollerDetail onClick 실행 차단'); console.log('🔓 [ProductVideoV2] TScrollerDetail onClick 실행 차단');
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
}
} else {
// 전체 화면 모드에서는 오버레이(컨트롤)만 토글
console.log('🖥️ [ProductVideoV2.OVERLAY-Toggle] 전체 화면 모드 - 오버레이 토글 시작');
console.log(
'🖥️ [ProductVideoV2.OVERLAY-Toggle] 현재 오버레이 상태:',
overlayState.controls?.visible
);
try {
const result = dispatch(toggleControls());
console.log(
'🖥️ [ProductVideoV2.OVERLAY-Toggle] toggleControls 액션 디스패치 결과:',
result
);
// 디스패치 후 상태 변화 확인 (setTimeout으로 비동기 처리)
setTimeout(() => {
console.log('🖥️ [ProductVideoV2.OVERLAY-Toggle] 액션 디스패치 후 상태 확인 필요');
}, 10);
} catch (error) {
console.error('🖥️ [ProductVideoV2.OVERLAY-ERROR] toggleControls 디스패치 에러:', error);
}
} }
} else { },
// 전체 화면 모드에서는 오버레이(컨트롤)만 토글 [isPlaying, isFullscreen, dispatch, toggleControls]
console.log('🖥️ [ProductVideoV2.OVERLAY-Toggle] 전체 화면 모드 - 오버레이 토글 시작'); );
console.log('🖥️ [ProductVideoV2.OVERLAY-Toggle] 현재 오버레이 상태:', overlayState.controls?.visible);
try {
const result = dispatch(toggleControls());
console.log('🖥️ [ProductVideoV2.OVERLAY-Toggle] toggleControls 액션 디스패치 결과:', result);
// 디스패치 후 상태 변화 확인 (setTimeout으로 비동기 처리)
setTimeout(() => {
console.log('🖥️ [ProductVideoV2.OVERLAY-Toggle] 액션 디스패치 후 상태 확인 필요');
}, 10);
} catch (error) {
console.error('🖥️ [ProductVideoV2.OVERLAY-ERROR] toggleControls 디스패치 에러:', error);
}
}
}, [isPlaying, isFullscreen, dispatch, toggleControls]);
// Early return: 비디오 재생 불가능한 경우 // Early return: 비디오 재생 불가능한 경우
if (!canPlayVideo) return null; if (!canPlayVideo) return null;
@@ -687,17 +756,16 @@ export function ProductVideoV2({
// 전체화면: SpotlightContainer (self-only) // 전체화면: SpotlightContainer (self-only)
// 일반 재생: SpottableComponent (포커스 가능) // 일반 재생: SpottableComponent (포커스 가능)
// 썸네일: div // 썸네일: div
const ContainerComponent = isFullscreen const ContainerComponent = isPlaying ? SpottableComponent : 'div';
? SpotlightContainer
: isPlaying
? SpottableComponent
: 'div';
// VideoPlayer 컴포넌트 생성 함수 // VideoPlayer 컴포넌트 생성 함수
const renderVideoPlayer = () => { const renderVideoPlayer = () => {
if (!isPlaying) return null; if (!isPlaying) return null;
return ( const host = ensurePortalHost();
if (!host) return null;
return ReactDOM.createPortal(
<div <div
ref={videoPlayerWrapperRef} ref={videoPlayerWrapperRef}
className={`${css.videoPlayerWrapper} ${isFullscreen ? css.fullscreenPlayer : ''}`} className={`${css.videoPlayerWrapper} ${isFullscreen ? css.fullscreenPlayer : ''}`}
@@ -717,9 +785,10 @@ export function ProductVideoV2({
onMouseDownCapture={(e) => { onMouseDownCapture={(e) => {
// ⚠️ 핵심: VideoPlayer 내부(실제 비디오 영역)에서만 이벤트를 살리고, // ⚠️ 핵심: VideoPlayer 내부(실제 비디오 영역)에서만 이벤트를 살리고,
// 그 외 wrapper 영역에서만 preventDefault // 그 외 wrapper 영역에서만 preventDefault
const isVideoElement = e.target?.closest('[class*="videoPlayer"]') || const isVideoElement =
e.target?.closest('video') || e.target?.closest('[class*="videoPlayer"]') ||
e.target?.closest('[class*="react-player"]'); e.target?.closest('video') ||
e.target?.closest('[class*="react-player"]');
console.log('🎬 [videoPlayerWrapper] onMouseDownCapture 실행됨', { console.log('🎬 [videoPlayerWrapper] onMouseDownCapture 실행됨', {
isFullscreen, isFullscreen,
@@ -777,7 +846,8 @@ export function ProductVideoV2({
<track kind="subtitles" src={productInfo?.prdtMediaSubtitlUrl} default /> <track kind="subtitles" src={productInfo?.prdtMediaSubtitlUrl} default />
)} )}
</VideoPlayer> </VideoPlayer>
</div> </div>,
host
); );
}; };
@@ -787,7 +857,7 @@ export function ProductVideoV2({
<ContainerComponent <ContainerComponent
{...containerProps} {...containerProps}
ref={containerRef} ref={containerRef}
className={`${css.videoContainer} ${isFullscreen ? css.fullscreen : ''}`} className={`${css.videoContainer} ${isFullscreen ? css.hideWhileFullscreen : ''}`}
onMouseDownCapture={handleContainerMouseDownCapture} onMouseDownCapture={handleContainerMouseDownCapture}
onClick={handleContainerClickFallback} onClick={handleContainerClickFallback}
> >
@@ -818,8 +888,7 @@ export function ProductVideoV2({
</div> </div>
</div> </div>
</SpottableComponent> </SpottableComponent>
) : !isFullscreen ? ( ) : (
// 일반 재생 모드: VideoPlayer를 직접 렌더링
<div <div
ref={normalContainerRef} ref={normalContainerRef}
className={css.normalPlayerContainer} className={css.normalPlayerContainer}
@@ -833,10 +902,9 @@ export function ProductVideoV2({
handleVideoPlayerClick(e); handleVideoPlayerClick(e);
}} }}
onMouseDownCapture={(e) => { onMouseDownCapture={(e) => {
// ⚠️ 핵심: VideoPlayer 영역 내에서만 이벤트를 살리고, const isVideoPlayerArea =
// 그 외 영역(스크롤 영역)에서만 preventDefault e.target?.closest('[class*="videoPlayer"]') ||
const isVideoPlayerArea = e.target?.closest('[class*="videoPlayer"]') || e.target?.closest('[class*="VideoPlayer"]');
e.target?.closest('[class*="VideoPlayer"]');
console.log('🎬 [normalContainerRef] onMouseDownCapture 실행됨', { console.log('🎬 [normalContainerRef] onMouseDownCapture 실행됨', {
isPlaying, isPlaying,
@@ -845,39 +913,34 @@ export function ProductVideoV2({
target: e.target?.className, target: e.target?.className,
}); });
// VideoPlayer가 아닌 영역(스크롤 영역)에서만 preventDefault
if (!isVideoPlayerArea) { if (!isVideoPlayerArea) {
e.preventDefault(); e.preventDefault();
console.log('🛑 [normalContainerRef] preventDefault - 스크롤 영역에서의 클릭'); console.log('🛑 [normalContainerRef] preventDefault - 스크롤 영역에서의 클릭');
} else { } else {
// VideoPlayer 영역이면 이벤트를 전파시켜 click이 정상 발생하도록
console.log('✅ [normalContainerRef] 이벤트 전파 허용 - VideoPlayer 영역'); console.log('✅ [normalContainerRef] 이벤트 전파 허용 - VideoPlayer 영역');
} }
}} }}
> />
{renderVideoPlayer()} )}
</div>
) : null}
</ContainerComponent> </ContainerComponent>
{/* 전체화면 container (Portal) */} {/* 전체화면 container (Portal) */}
{isPlaying && isFullscreen && ReactDOM.createPortal( {isPlaying &&
<SpotlightContainer isFullscreen &&
spotlightRestrict="self-only" ReactDOM.createPortal(
spotlightId="product-video-v2-fullscreen-portal" <SpotlightContainer
onMouseDownCapture={handleFullscreenContainerMouseDownCapture} spotlightRestrict="self-only"
onKeyDown={handleFullscreenKeyDown} spotlightId="product-video-v2-fullscreen-portal"
// onClick={handleFullscreenClick} onMouseDownCapture={handleFullscreenContainerMouseDownCapture}
> onKeyDown={handleFullscreenKeyDown}
<div // onClick={handleFullscreenClick}
ref={fullscreenContainerRef}
className={css.fullscreenContainer}
> >
{renderVideoPlayer()} <div ref={fullscreenContainerRef} className={css.fullscreenContainer} />
</div> </SpotlightContainer>,
</SpotlightContainer>, document.body
document.body )}
)}
{renderVideoPlayer()}
</> </>
); );
} }