# Modal 전환 기능 상세 분석 **작성일**: 2025-11-10 **목적**: MediaPlayer.v2.jsx 설계를 위한 필수 기능 분석 --- ## 📋 Modal 모드 전환 플로우 ### 1. 시작: Modal 모드로 비디오 재생 ```javascript // actions/mediaActions.js - startMediaPlayer() dispatch(startMediaPlayer({ modal: true, modalContainerId: 'some-product-id', showUrl: 'video-url.mp4', thumbnailUrl: 'thumb.jpg', // ... })); ``` **MediaPanel에서의 처리 (MediaPanel.jsx:114-161)**: ```javascript useEffect(() => { if (panelInfo.modal && panelInfo.modalContainerId) { // 1. DOM 노드 찾기 const node = document.querySelector( `[data-spotlight-id="${panelInfo.modalContainerId}"]` ); // 2. 위치와 크기 계산 const { width, height, top, left } = node.getBoundingClientRect(); // 3. padding/margin 조정 const totalOffset = 24; // 6*2 + 6*2 const adjustedWidth = width - totalOffset; const adjustedHeight = height - totalOffset; // 4. Fixed 위치 스타일 생성 const style = { width: adjustedWidth + 'px', height: adjustedHeight + 'px', top: (top + totalOffset/2) + 'px', left: (left + totalOffset/2) + 'px', position: 'fixed', overflow: 'hidden' }; setModalStyle(style); setModalScale(adjustedWidth / window.innerWidth); } }, [panelInfo, isOnTop]); ``` **VideoPlayer에 전달**: ```javascript ``` --- ### 2. 전환: Modal → Fullscreen **사용자 액션**: modal 비디오 클릭 ```javascript // MediaPanel.jsx:164-174 const onVideoClick = useCallback(() => { if (panelInfo.modal) { dispatch(switchMediaToFullscreen()); } }, [dispatch, panelInfo.modal]); ``` **Redux Action (mediaActions.js:164-208)**: ```javascript export const switchMediaToFullscreen = () => (dispatch, getState) => { const modalMediaPanel = panels.find( (panel) => panel.name === panel_names.MEDIA_PANEL && panel.panelInfo?.modal ); if (modalMediaPanel) { dispatch(updatePanel({ name: panel_names.MEDIA_PANEL, panelInfo: { ...modalMediaPanel.panelInfo, modal: false // 🔑 핵심: modal만 false로 변경 } })); } }; ``` **MediaPanel 재렌더링**: ```javascript // panelInfo.modal이 false가 되면 useEffect 재실행 useEffect(() => { // modal이 false이면 else if 분기 실행 else if (isOnTop && !panelInfo.modal && !panelInfo.isMinimized && videoPlayer.current) { // 재생 상태 복원 if (videoPlayer.current?.getMediaState()?.paused) { videoPlayer.current.play(); } // controls 표시 if (!videoPlayer.current.areControlsVisible()) { videoPlayer.current.showControls(); } } }, [panelInfo, isOnTop]); // VideoPlayer에 전달되는 props 변경 ``` --- ### 3. 복귀: Fullscreen → Modal (Back 버튼) ```javascript // MediaPanel.jsx:176-194 const onClickBack = useCallback((ev) => { // modalContainerId가 있으면 modal에서 왔던 것 if (panelInfo.modalContainerId && !panelInfo.modal) { dispatch(PanelActions.popPanel()); ev?.stopPropagation(); return; } // 일반 fullscreen이면 그냥 닫기 if (!panelInfo.modal) { dispatch(PanelActions.popPanel()); ev?.stopPropagation(); } }, [dispatch, panelInfo]); ``` --- ## 🔑 핵심 메커니즘 ### 1. 같은 MediaPanel 재사용 - modal → fullscreen 전환 시 패널을 새로 만들지 않음 - **updatePanel**로 `panelInfo.modal`만 변경 - **비디오 재생 상태 유지** (같은 컴포넌트 인스턴스) ### 2. 스타일 동적 계산 ```javascript // modal=true style={{ position: 'fixed', top: '100px', left: '200px', width: '400px', height: '300px' }} // modal=false style={{}} // 전체화면 (기본 CSS) ``` ### 3. Pause/Resume 관리 ```javascript // modal에서 다른 패널이 위로 올라오면 useEffect(() => { if (panelInfo?.modal) { if (!isOnTop) { dispatch(pauseModalMedia()); // isPaused: true } else if (isOnTop && panelInfo.isPaused) { dispatch(resumeModalMedia()); // isPaused: false } } }, [isOnTop, panelInfo, dispatch]); // VideoPlayer에서 isPaused 감지하여 play/pause 제어 useEffect(() => { if (panelInfo?.modal && videoPlayer.current) { if (panelInfo.isPaused) { videoPlayer.current.pause(); } else if (panelInfo.isPaused === false) { videoPlayer.current.play(); } } }, [panelInfo?.isPaused, panelInfo?.modal]); ``` --- ## 📐 MediaPlayer.v2.jsx가 지원해야 할 기능 ### ✅ 필수 Props (추가) ```javascript { // 기존 src, autoPlay, loop, onEnded, onError, thumbnailUrl, videoComponent, // Modal 전환 관련 (필수) disabled, // modal=true일 때 true spotlightDisabled, // modal=true일 때 true onClick, // modal일 때 클릭 → switchMediaToFullscreen style, // modal일 때 fixed position style modalClassName, // modal일 때 추가 className modalScale, // modal일 때 scale 값 (QR코드 등에 사용) // 패널 정보 panelInfo: { modal, // modal 모드 여부 modalContainerId, // modal 기준 컨테이너 ID isPaused, // 일시정지 여부 (다른 패널 위로 올라옴) showUrl, // 비디오 URL thumbnailUrl, // 썸네일 URL }, // 콜백 onBackButton, // Back 버튼 핸들러 // Spotlight spotlightId, } ``` ### ✅ 필수 기능 #### 1. Modal 모드 스타일 적용 ```javascript const containerStyle = useMemo(() => { if (panelInfo?.modal && style) { return style; // MediaPanel에서 계산한 fixed position } return {}; // 전체화면 }, [panelInfo?.modal, style]); ``` #### 2. Modal 클릭 처리 ```javascript const handleVideoClick = useCallback(() => { if (panelInfo?.modal && onClick) { onClick(); // switchMediaToFullscreen 호출 return; } // fullscreen이면 controls 토글 toggleControls(); }, [panelInfo?.modal, onClick]); ``` #### 3. isPaused 상태 동기화 ```javascript useEffect(() => { if (panelInfo?.modal && videoRef.current) { if (panelInfo.isPaused) { videoRef.current.pause(); } else if (panelInfo.isPaused === false) { videoRef.current.play(); } } }, [panelInfo?.isPaused, panelInfo?.modal]); ``` #### 4. Modal → Fullscreen 전환 시 재생 복원 ```javascript useEffect(() => { // modal에서 fullscreen으로 전환되었을 때 if (prevPanelInfo?.modal && !panelInfo?.modal) { if (videoRef.current?.paused) { videoRef.current.play(); } setControlsVisible(true); } }, [panelInfo?.modal]); ``` #### 5. Controls/Spotlight 비활성화 ```javascript const shouldDisableControls = panelInfo?.modal || disabled; const shouldDisableSpotlight = panelInfo?.modal || spotlightDisabled; ``` --- ## 🚫 여전히 제거 가능한 기능 Modal 전환과 무관한 기능들: ``` ❌ QR코드 오버레이 (PlayerPanel 전용) ❌ 전화번호 오버레이 (PlayerPanel 전용) ❌ 테마 인디케이터 (PlayerPanel 전용) ❌ MediaSlider (seek bar) - 단순 재생만 ❌ 복잡한 피드백 시스템 (miniFeedback, 8개 Job) ❌ Announce/Accessibility 복잡계 ❌ FloatingLayer ❌ Redux 통합 (updateVideoPlayState) ❌ TabContainer 동기화 (PlayerPanel 전용) ❌ MediaTitle, infoComponents ❌ jumpBy, fastForward, rewind ❌ playbackRate 조정 ``` --- ## 📊 최종 상태 변수 (9개) ```javascript const [currentTime, setCurrentTime] = useState(0); const [duration, setDuration] = useState(0); const [paused, setPaused] = useState(true); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [controlsVisible, setControlsVisible] = useState(false); // Modal 관련 (MediaPanel에서 계산하므로 state 불필요) // modalStyle, modalScale → props로 받음 ``` --- ## 📊 최종 Props 목록 (~18개) ```javascript MediaPlayerV2.propTypes = { // 비디오 소스 src: PropTypes.string.isRequired, type: PropTypes.string, thumbnailUrl: PropTypes.string, // 재생 제어 autoPlay: PropTypes.bool, loop: PropTypes.bool, // Modal 전환 disabled: PropTypes.bool, spotlightDisabled: PropTypes.bool, onClick: PropTypes.func, style: PropTypes.object, modalClassName: PropTypes.string, modalScale: PropTypes.number, // 패널 정보 panelInfo: PropTypes.shape({ modal: PropTypes.bool, modalContainerId: PropTypes.string, isPaused: PropTypes.bool, showUrl: PropTypes.string, thumbnailUrl: PropTypes.string, }), // 콜백 onEnded: PropTypes.func, onError: PropTypes.func, onBackButton: PropTypes.func, // Spotlight spotlightId: PropTypes.string, // 비디오 컴포넌트 videoComponent: PropTypes.elementType, }; ``` --- ## 🎯 구현 우선순위 ### Phase 1: 기본 재생 (1일) - [ ] 비디오 element 렌더링 (Media / TReactPlayer) - [ ] 기본 play/pause 제어 - [ ] 로딩 상태 및 썸네일 표시 - [ ] API 제공 (getMediaState, play, pause) ### Phase 2: Modal 전환 (1일) - [ ] Modal 스타일 적용 (props.style) - [ ] Modal 클릭 → Fullscreen 전환 - [ ] isPaused 상태 동기화 - [ ] disabled/spotlightDisabled 처리 ### Phase 3: Controls (1일) - [ ] 최소한의 controls UI (재생/일시정지만) - [ ] Controls 자동 숨김/보임 - [ ] Spotlight 포커스 관리 (기본만) ### Phase 4: 테스트 및 최적화 (1일) - [ ] 메모리 프로파일링 - [ ] 전환 애니메이션 부드럽게 - [ ] Edge case 처리 --- ## 💡 예상 개선 효과 (수정) | 항목 | 현재 | 개선 후 | 개선율 | |------|------|---------|--------| | **코드 라인** | 2,595 | ~700 | **73% 감소** | | **상태 변수** | 20+ | 6~9 | **60% 감소** | | **Props** | 70+ | ~18 | **74% 감소** | | **타이머/Job** | 8 | 1~2 | **80% 감소** | | **필수 기능** | 100% | 100% | **유지** | | **메모리 점유** | 높음 | 낮음 | **예상 40%+ 감소** | | **렌더링 속도** | 느림 | 빠름 | **예상 2배 향상** | --- ## ✅ 결론 Modal 전환 기능은 복잡해 보이지만, 실제로는: 1. **MediaPanel**에서 스타일 계산 (modalStyle, modalScale) 2. **MediaPlayer**는 받은 style을 그대로 적용 3. **modal 플래그**에 따라 controls/spotlight 활성화 여부만 제어 따라서 MediaPlayer.v2.jsx는: - Modal 전환 로직 구현 필요 없음 - Props 받아서 적용만 하면 됨 - 핵심 복잡도는 MediaPanel에 있음 **→ 여전히 대폭 간소화 가능!**