docs: Add video player analysis and optimization documentation
- 비디오 플레이어 구조 분석 및 최적화 계획 문서 작성 - Modal 전환 기능 상세 분석 문서 작성 - MediaPlayer.v2.jsx 설계를 위한 필수 기능 정의
This commit is contained in:
437
.docs/modal-transition-analysis.md
Normal file
437
.docs/modal-transition-analysis.md
Normal file
@@ -0,0 +1,437 @@
|
|||||||
|
# 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
|
||||||
|
<VideoPlayer
|
||||||
|
disabled={panelInfo.modal} // modal에서는 controls 비활성
|
||||||
|
spotlightDisabled={panelInfo.modal} // modal에서는 spotlight 비활성
|
||||||
|
style={panelInfo.modal ? modalStyle : {}}
|
||||||
|
modalScale={panelInfo.modal ? modalScale : 1}
|
||||||
|
modalClassName={panelInfo.modal && panelInfo.modalClassName}
|
||||||
|
onClick={onVideoClick} // 클릭 시 전환
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 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 변경
|
||||||
|
<VideoPlayer
|
||||||
|
disabled={false} // controls 활성화
|
||||||
|
spotlightDisabled={false} // spotlight 활성화
|
||||||
|
style={{}} // fixed position 제거 → 전체화면
|
||||||
|
modalScale={1}
|
||||||
|
modalClassName={undefined}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 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에 있음
|
||||||
|
|
||||||
|
**→ 여전히 대폭 간소화 가능!**
|
||||||
214
.docs/video-player-analysis-and-optimization-plan.md
Normal file
214
.docs/video-player-analysis-and-optimization-plan.md
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
# 비디오 플레이어 분석 및 최적화 계획
|
||||||
|
|
||||||
|
**작성일**: 2025-11-10
|
||||||
|
**대상**: MediaPlayer.v2.jsx 설계
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 현재 구조 분석
|
||||||
|
|
||||||
|
### 1. 발견된 파일들
|
||||||
|
|
||||||
|
| 파일 | 경로 | 라인 수 | 타입 |
|
||||||
|
|------|------|---------|------|
|
||||||
|
| VideoPlayer.js | `src/components/VideoPlayer/VideoPlayer.js` | 2,658 | Class Component |
|
||||||
|
| MediaPlayer.jsx | `src/components/VideoPlayer/MediaPlayer.jsx` | 2,595 | Class Component |
|
||||||
|
| MediaPanel.jsx | `src/views/MediaPanel/MediaPanel.jsx` | 415 | Function Component |
|
||||||
|
| PlayerPanel.jsx | `src/views/PlayerPanel/PlayerPanel.jsx` | 25,146+ | (파일 읽기 실패) |
|
||||||
|
|
||||||
|
### 2. 주요 문제점
|
||||||
|
|
||||||
|
#### 🔴 심각한 코드 비대화
|
||||||
|
```
|
||||||
|
VideoPlayer.js: 2,658 라인 (클래스 컴포넌트)
|
||||||
|
MediaPlayer.jsx: 2,595 라인 (거의 동일한 복사본)
|
||||||
|
PlayerPanel.jsx: 25,146+ 라인
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 🔴 과도한 Enact 프레임워크 의존성
|
||||||
|
```javascript
|
||||||
|
// 7개 이상의 Decorator 래핑
|
||||||
|
ApiDecorator
|
||||||
|
I18nContextDecorator
|
||||||
|
Slottable
|
||||||
|
FloatingLayerDecorator
|
||||||
|
Skinnable
|
||||||
|
SpotlightContainerDecorator
|
||||||
|
Spottable, Touchable
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 🔴 복잡한 상태 관리 (20+ 상태 변수)
|
||||||
|
```javascript
|
||||||
|
state = {
|
||||||
|
// 미디어 상태
|
||||||
|
currentTime, duration, paused, loading, error,
|
||||||
|
playbackRate, proportionLoaded, proportionPlayed,
|
||||||
|
|
||||||
|
// UI 상태
|
||||||
|
announce, feedbackVisible, feedbackAction,
|
||||||
|
mediaControlsVisible, mediaSliderVisible, miniFeedbackVisible,
|
||||||
|
titleVisible, infoVisible, bottomControlsRendered,
|
||||||
|
|
||||||
|
// 기타
|
||||||
|
sourceUnavailable, titleOffsetHeight, bottomOffsetHeight,
|
||||||
|
lastFocusedTarget, slider5WayPressed, thumbnailUrl
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 🔴 메모리 점유 과다
|
||||||
|
|
||||||
|
**8개의 Job 인스턴스**:
|
||||||
|
- `autoCloseJob` - 자동 controls 숨김
|
||||||
|
- `hideTitleJob` - 타이틀 숨김
|
||||||
|
- `hideFeedbackJob` - 피드백 숨김
|
||||||
|
- `hideMiniFeedbackJob` - 미니 피드백 숨김
|
||||||
|
- `rewindJob` - 되감기 처리
|
||||||
|
- `announceJob` - 접근성 알림
|
||||||
|
- `renderBottomControl` - 하단 컨트롤 렌더링
|
||||||
|
- `slider5WayPressJob` - 슬라이더 5-way 입력
|
||||||
|
|
||||||
|
**다수의 이벤트 리스너**:
|
||||||
|
- `mousemove`, `touchmove`, `keydown`, `wheel`
|
||||||
|
- 복잡한 Spotlight 포커스 시스템
|
||||||
|
|
||||||
|
#### 🔴 불필요한 기능들 (MediaPanel에서 미사용)
|
||||||
|
```javascript
|
||||||
|
// PlayerOverlayQRCode (QR코드 표시)
|
||||||
|
// VideoOverlayWithPhoneNumber (전화번호 오버레이)
|
||||||
|
// ThemeIndicatorArrow (테마 인디케이터)
|
||||||
|
// FeedbackTooltip, MediaTitle (주석 처리됨)
|
||||||
|
// 복잡한 TabContainerV2 동기화
|
||||||
|
// Redux 통합 (updateVideoPlayState)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 webOS 특정 기능 분석
|
||||||
|
|
||||||
|
### 필수 기능
|
||||||
|
|
||||||
|
#### 1. Spotlight 포커스 관리
|
||||||
|
```javascript
|
||||||
|
// 리모컨 5-way 네비게이션
|
||||||
|
SpotlightContainerDecorator
|
||||||
|
Spottable, Touchable
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Media 컴포넌트 (webOS 전용)
|
||||||
|
```javascript
|
||||||
|
videoComponent: window.PalmSystem ? Media : TReactPlayer
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. playbackRate 네거티브 지원
|
||||||
|
```javascript
|
||||||
|
if (platform.webos) {
|
||||||
|
this.video.playbackRate = pbNumber; // 음수 지원 (되감기)
|
||||||
|
} else {
|
||||||
|
// 브라우저: 수동 되감기 구현
|
||||||
|
this.beginRewind();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 제거 가능한 기능
|
||||||
|
|
||||||
|
- FloatingLayer 시스템
|
||||||
|
- 복잡한 announce/accessibility 시스템
|
||||||
|
- Marquee 애니메이션
|
||||||
|
- 다중 오버레이 시스템
|
||||||
|
- Job 기반 타이머 → `setTimeout`으로 대체 가능
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📐 MediaPlayer.v2.jsx 초기 설계 (수정 전)
|
||||||
|
|
||||||
|
### 설계 원칙
|
||||||
|
```
|
||||||
|
1. 함수 컴포넌트 + React Hooks 사용
|
||||||
|
2. 상태 최소화 (5~7개만)
|
||||||
|
3. Enact 의존성 최소화 (Spotlight 기본만)
|
||||||
|
4. 직접 video element 제어
|
||||||
|
5. props 최소화 (15개 이하)
|
||||||
|
6. 단순한 controls UI
|
||||||
|
7. 메모리 효율성 우선
|
||||||
|
```
|
||||||
|
|
||||||
|
### 최소 상태 (6개)
|
||||||
|
```javascript
|
||||||
|
const [currentTime, setCurrentTime] = useState(0);
|
||||||
|
const [duration, setDuration] = useState(0);
|
||||||
|
const [paused, setPaused] = useState(true);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [controlsVisible, setControlsVisible] = useState(false);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 필수 Props (~12개)
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
src, // 비디오 URL
|
||||||
|
type, // 비디오 타입
|
||||||
|
autoPlay, // 자동 재생
|
||||||
|
loop, // 반복 재생
|
||||||
|
disabled, // modal 상태
|
||||||
|
onEnded, // 종료 콜백
|
||||||
|
onError, // 에러 콜백
|
||||||
|
onBackButton, // 뒤로가기
|
||||||
|
thumbnailUrl, // 썸네일
|
||||||
|
panelInfo, // 패널 정보
|
||||||
|
spotlightId, // spotlight ID
|
||||||
|
videoComponent // Media or TReactPlayer
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 제거할 기능들
|
||||||
|
```
|
||||||
|
❌ QR코드 오버레이
|
||||||
|
❌ 전화번호 오버레이
|
||||||
|
❌ 테마 인디케이터
|
||||||
|
❌ 복잡한 피드백 시스템
|
||||||
|
❌ MediaSlider (seek bar)
|
||||||
|
❌ 자동 숨김/보임 Job 시스템
|
||||||
|
❌ Announce/Accessibility 복잡계
|
||||||
|
❌ FloatingLayer
|
||||||
|
❌ Redux 통합
|
||||||
|
❌ TabContainer 동기화
|
||||||
|
❌ 다중 overlay 시스템
|
||||||
|
❌ MediaTitle, infoComponents
|
||||||
|
❌ jumpBy, fastForward, rewind
|
||||||
|
❌ playbackRate 조정
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📈 예상 개선 효과
|
||||||
|
|
||||||
|
| 항목 | 현재 | 개선 후 | 개선율 |
|
||||||
|
|------|------|---------|--------|
|
||||||
|
| **코드 라인** | 2,595 | ~500 | **80% 감소** |
|
||||||
|
| **상태 변수** | 20+ | 5~7 | **65% 감소** |
|
||||||
|
| **Props** | 70+ | ~12 | **83% 감소** |
|
||||||
|
| **타이머/Job** | 8 | 2~3 | **70% 감소** |
|
||||||
|
| **메모리 점유** | 높음 | 낮음 | **예상 50%+ 감소** |
|
||||||
|
| **렌더링 속도** | 느림 | 빠름 | **예상 2~3배 향상** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚨 중요 요구사항 추가
|
||||||
|
|
||||||
|
### Modal 모드 전환 기능 (필수)
|
||||||
|
|
||||||
|
사용자 피드백:
|
||||||
|
> "비디오 플레이어가 이렇게 복잡하게 된 데에는 다 이유가 있다.
|
||||||
|
> modal=true 모드에서 화면의 일부 크기로 재생이 되다가
|
||||||
|
> 그 화면 그대로 키워서 modal=false로 전체화면으로 비디오를 재생하는 부분이 있어야 한다."
|
||||||
|
|
||||||
|
**→ 이 기능은 반드시 유지되어야 함**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 다음 단계
|
||||||
|
|
||||||
|
1. Modal 전환 기능 상세 분석
|
||||||
|
2. 필수 기능 재정의
|
||||||
|
3. MediaPlayer.v2.jsx 재설계
|
||||||
|
4. 구현 우선순위 결정
|
||||||
Reference in New Issue
Block a user