docs: Add MediaPlayer.v2 required changes analysis

MediaPanel 실제 사용 컨텍스트 분석 및 필수 수정 사항 문서화

주요 발견사항:
- DetailPanel → ProductVideo → MediaPanel 사용 패턴 분석
- Modal 모드: 오버레이 없음 (정상)
- Fullscreen 모드: MediaSlider 필수 (누락됨)

필수 수정 사항:
- MediaSlider 추가 (리모컨으로 진행 위치 조정)
- Times 컴포넌트 추가 (현재/전체 시간 표시)
- proportionLoaded/Played 상태 추가
- Slider 이벤트 핸들러 구현

조건부 렌더링:
- Modal 모드 (modal=true): Slider 숨김
- Fullscreen 모드 (modal=false): Slider 표시

예상 작업 시간: 1.5시간
This commit is contained in:
Claude
2025-11-10 08:14:53 +00:00
parent 05e54583a5
commit 64d1e553ed

View File

@@ -0,0 +1,404 @@
# MediaPlayer.v2 필수 수정 사항
**작성일**: 2025-11-10
**발견 사항**: MediaPanel의 실제 사용 컨텍스트 분석
---
## 🔍 실제 사용 패턴 분석
### 사용 위치
```
DetailPanel
→ ProductAllSection
→ ProductVideo
→ startMediaPlayer()
→ MediaPanel
→ MediaPlayer (VideoPlayer)
```
### 동작 플로우
#### 1⃣ **Modal 모드 시작** (작은 화면)
```javascript
// ProductVideo.jsx:174-198
dispatch(startMediaPlayer({
modal: true, // 작은 화면 모드
modalContainerId: 'product-video-player',
showUrl: productInfo.prdtMediaUrl,
thumbnailUrl: productInfo.thumbnailUrl960,
// ...
}));
```
**Modal 모드 특징**:
- 화면 일부 영역에 fixed position으로 표시
- **오버레이 없음** (controls, slider 모두 숨김)
- 클릭만 가능 (전체화면으로 전환)
#### 2⃣ **Fullscreen 모드 전환** (최대화면)
```javascript
// ProductVideo.jsx:164-168
if (isCurrentlyPlayingModal) {
dispatch(switchMediaToFullscreen()); // modal: false로 변경
}
```
**Fullscreen 모드 특징**:
- 전체 화면 표시
- **리모컨 엔터 키 → 오버레이 표시 필수**
- ✅ Back 버튼
-**비디오 진행 바 (MediaSlider)** ← 필수!
- ✅ 현재 시간 / 전체 시간 (Times)
- ✅ Play/Pause 버튼 (MediaControls)
---
## 🚨 현재 MediaPlayer.v2의 문제점
### ❌ 제거된 필수 기능
```javascript
// MediaPlayer.v2.jsx - 현재 상태
{controlsVisible && !isModal && (
<div className={css.simpleControls}>
<button onClick={...}>{paused ? '▶' : '⏸'}</button> // Play/Pause만
<button onClick={onBackButton}> Back</button>
</div>
)}
```
**문제**:
1.**MediaSlider (seek bar) 없음** - 리모컨으로 진행 위치 조정 불가
2.**Times 컴포넌트 없음** - 현재 시간/전체 시간 표시 안 됨
3.**proportionLoaded, proportionPlayed 상태 없음**
---
## ✅ 기존 MediaPlayer.jsx의 올바른 구현
### Modal vs Fullscreen 조건부 렌더링
```javascript
// MediaPlayer.jsx:2415-2461
{noSlider ? null : (
<div className={css.sliderContainer}>
{/* Times - 전체 시간 */}
{this.state.mediaSliderVisible && type ? (
<Times
noCurrentTime
total={this.state.duration}
formatter={durFmt}
type={type}
/>
) : null}
{/* Times - 현재 시간 */}
{this.state.mediaSliderVisible && type ? (
<Times
noTotalTime
current={this.state.currentTime}
formatter={durFmt}
/>
) : null}
{/* MediaSlider - modal이 아닐 때만 표시 */}
{!panelInfo.modal && (
<MediaSlider
backgroundProgress={this.state.proportionLoaded}
disabled={disabled || this.state.sourceUnavailable}
value={this.state.proportionPlayed}
visible={this.state.mediaSliderVisible}
spotlightDisabled={
spotlightDisabled || !this.state.mediaControlsVisible
}
onChange={this.onSliderChange}
onKnobMove={this.handleKnobMove}
onKeyDown={this.handleSliderKeyDown}
// ...
/>
)}
</div>
)}
```
**핵심 조건**:
```javascript
!panelInfo.modal // Modal이 아닐 때만 MediaSlider 표시
```
---
## 📋 MediaPlayer.v2 수정 필요 사항
### 1. 상태 추가
```javascript
// 현재 (7개)
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
const [paused, setPaused] = useState(!autoPlay);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [controlsVisible, setControlsVisible] = useState(false);
const [sourceUnavailable, setSourceUnavailable] = useState(true);
// 추가 필요 (2개)
const [proportionLoaded, setProportionLoaded] = useState(0); // 로딩된 비율
const [proportionPlayed, setProportionPlayed] = useState(0); // 재생된 비율
```
### 2. Import 추가
```javascript
import { MediaSlider, Times, secondsToTime } from '../MediaPlayer';
import DurationFmt from 'ilib/lib/DurationFmt';
import { memoize } from '@enact/core/util';
```
### 3. DurationFmt 헬퍼 추가
```javascript
const memoGetDurFmt = memoize(
() => new DurationFmt({
length: 'medium',
style: 'clock',
useNative: false,
})
);
const getDurFmt = () => {
if (typeof window === 'undefined') return null;
return memoGetDurFmt();
};
```
### 4. handleUpdate 수정 (proportionLoaded/Played 계산)
```javascript
const handleUpdate = useCallback((ev) => {
const el = videoRef.current;
if (!el) return;
const newCurrentTime = el.currentTime || 0;
const newDuration = el.duration || 0;
setCurrentTime(newCurrentTime);
setDuration(newDuration);
setPaused(el.paused);
setLoading(el.loading || false);
setError(el.error || null);
setSourceUnavailable((el.loading && sourceUnavailable) || el.error);
// 추가: proportion 계산
setProportionLoaded(el.proportionLoaded || 0);
setProportionPlayed(newDuration > 0 ? newCurrentTime / newDuration : 0);
// 콜백 호출
if (ev.type === 'timeupdate' && onTimeUpdate) {
onTimeUpdate(ev);
}
// ...
}, [onTimeUpdate, sourceUnavailable]);
```
### 5. Slider 이벤트 핸들러 추가
```javascript
const handleSliderChange = useCallback(({ value }) => {
const time = value * duration;
seek(time);
}, [duration, seek]);
const handleKnobMove = useCallback((ev) => {
if (!videoRef.current) return;
const seconds = Math.floor(ev.proportion * videoRef.current.duration);
if (!isNaN(seconds)) {
// 스크럽 시 시간 표시 업데이트 등
// 필요시 onScrub 콜백 호출
}
}, []);
const handleSliderKeyDown = useCallback((ev) => {
// Spotlight 키 이벤트 처리
// 위/아래 키로 controls 이동 등
}, []);
```
### 6. Controls UI 수정
```javascript
{/* Modal이 아닐 때만 전체 controls 표시 */}
{controlsVisible && !isModal && (
<div className={css.controlsContainer}>
{/* Slider Section */}
<div className={css.sliderContainer}>
{/* Times - 전체 시간 */}
<Times
noCurrentTime
total={duration}
formatter={getDurFmt()}
type={type}
/>
{/* Times - 현재 시간 */}
<Times
noTotalTime
current={currentTime}
formatter={getDurFmt()}
/>
{/* MediaSlider */}
<MediaSlider
backgroundProgress={proportionLoaded}
disabled={disabled || sourceUnavailable}
value={proportionPlayed}
visible={controlsVisible}
spotlightDisabled={spotlightDisabled}
onChange={handleSliderChange}
onKnobMove={handleKnobMove}
onKeyDown={handleSliderKeyDown}
spotlightId="media-slider-v2"
/>
</div>
{/* Controls Section */}
<div className={css.controlsButtons}>
<button className={css.playPauseBtn} onClick={...}>
{paused ? '▶' : '⏸'}
</button>
{onBackButton && (
<button className={css.backBtn} onClick={onBackButton}>
Back
</button>
)}
</div>
</div>
)}
```
### 7. CSS 추가
```less
// VideoPlayer.module.less
.controlsContainer {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 20px;
background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent);
z-index: 10;
}
.sliderContainer {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
}
.controlsButtons {
display: flex;
gap: 20px;
justify-content: center;
}
```
---
## 📊 수정 전/후 비교
### 현재 MediaPlayer.v2 (문제)
```
Modal 모드 (modal=true):
✅ 오버레이 없음 (정상)
✅ 클릭으로 전환 (정상)
Fullscreen 모드 (modal=false):
❌ MediaSlider 없음 (문제!)
❌ Times 없음 (문제!)
✅ Play/Pause 버튼 (정상)
✅ Back 버튼 (정상)
```
### 수정 후 MediaPlayer.v2 (정상)
```
Modal 모드 (modal=true):
✅ 오버레이 없음
✅ 클릭으로 전환
Fullscreen 모드 (modal=false):
✅ MediaSlider (seek bar)
✅ Times (현재/전체 시간)
✅ Play/Pause 버튼
✅ Back 버튼
```
---
## 🎯 우선순위
### High Priority (필수)
1.**MediaSlider 추가** - 리모컨으로 진행 위치 조정
2.**Times 컴포넌트 추가** - 시간 표시
3.**proportionLoaded/Played 상태** - slider 동작
### Medium Priority (권장)
4. Slider 이벤트 핸들러 세부 구현
5. Spotlight 키 네비게이션 (위/아래로 slider ↔ buttons)
6. CSS 스타일 개선
### Low Priority (선택)
7. Scrub 시 썸네일 표시 (기존에도 없음)
8. 추가 피드백 UI
---
## 🔧 구현 순서
1. **Phase 1**: 상태 및 import 추가 (10분)
2. **Phase 2**: MediaSlider 렌더링 (20분)
3. **Phase 3**: Times 컴포넌트 추가 (10분)
4. **Phase 4**: 이벤트 핸들러 구현 (20분)
5. **Phase 5**: CSS 스타일 조정 (10분)
6. **Phase 6**: 테스트 및 디버깅 (30분)
**총 예상 시간**: 약 1.5시간
---
## ✅ 체크리스트
- [ ] proportionLoaded, proportionPlayed 상태 추가
- [ ] MediaSlider, Times import
- [ ] DurationFmt 헬퍼 추가
- [ ] handleUpdate에서 proportion 계산
- [ ] handleSliderChange 구현
- [ ] handleKnobMove 구현
- [ ] handleSliderKeyDown 구현
- [ ] Controls UI에 slider 추가
- [ ] Times 컴포넌트 추가
- [ ] CSS 스타일 추가
- [ ] Modal 모드에서 slider 숨김 확인
- [ ] Fullscreen 모드에서 slider 표시 확인
- [ ] 리모컨으로 seek 동작 테스트
---
## 📝 결론
MediaPlayer.v2는 **MediaSlider와 Times가 필수**입니다.
이유:
1. DetailPanel → ProductVideo에서만 사용
2. Fullscreen 모드에서 리모컨 사용자가 비디오 진행 위치를 조정해야 함
3. 현재/전체 시간 표시 필요
**→ "간소화"는 맞지만, "필수 기능 제거"는 아님**
**→ MediaSlider는 제거 불가, 단 Modal 모드에서만 조건부 숨김**