🕐 커밋 시간: 2025. 11. 16. 17:28:35 📊 변경 통계: • 총 파일: 5개 • 추가: +398줄 • 삭제: -8줄 📁 추가된 파일: + com.twin.app.shoptime/[251116]_video_state_management_design.md 📝 수정된 파일: ~ com.twin.app.shoptime/src/actions/actionTypes.js ~ com.twin.app.shoptime/src/actions/playActions.js ~ com.twin.app.shoptime/src/reducers/playReducer.js ~ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx 🔧 함수 변경 내용: 📄 com.twin.app.shoptime/src/actions/playActions.js (javascript): ✅ Added: returnToPreview() 🔄 Modified: finishModalVideoForce(), shrinkVideoTo1px(), resumePlayerControl() 📄 com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx (javascript): 🔄 Modified: SpotlightContainerDecorator() 📄 com.twin.app.shoptime/[251116]_video_state_management_design.md (md파일): ✅ Added: curry(), dispatch(), useSelector() 🔧 주요 변경 내용: • 타입 시스템 안정성 강화 • 핵심 비즈니스 로직 개선 • 개발 문서 및 가이드 개선
6.2 KiB
6.2 KiB
[251116] 새로운 비디오 상태 관리 시스템 구현
개요
기존의 videoPlayReducer는 유지하되, PlayerPanel과 VideoPlayer.js를 위한 새로운 비디오 상태 관리 시스템을 playerReducer에 구현한다. 재생 상태와 화면 상태를 분리하여 더 정밀한 비디오 상태 제어를 가능하게 한다.
설계 원칙
- 기존 videoPlayReducer 유지: 다른 컴포넌트에서 사용 중일 수 있으므로 그대로 둔다
- playerReducer에 새로운 상태 시스템 구현: PlayerPanel과 VideoPlayer.js 전용
- 이중 상태 관리: 재생 상태(Playback Status) + 화면 상태(Display Status)
- 기존 패턴 따르기: FP 스타일의 curry, get, set 활용
새로운 상태 구조
상수 정의 (playerActions.js)
// 재생 상태
export const PLAYBACK_STATUS = {
LOADING: 'loading', // 비디오 로딩 중
LOAD_SUCCESS: 'load_success', // 비디오 로딩 성공
LOAD_ERROR: 'load_error', // 비디오 로딩 오류
PLAYING: 'playing', // 비디오 재생 중
NOT_PLAYING: 'not_playing', // 비디오 재생 아님 (정지/일시정지)
BUFFERING: 'buffering' // 버퍼링 중
};
// 화면 상태
export const DISPLAY_STATUS = {
HIDDEN: 'hidden', // 화면에 안보임
VISIBLE: 'visible', // 화면에 보임
MINIMIZED: 'minimized', // 최소화됨
FULLSCREEN: 'fullscreen' // 전체화면
};
초기 상태 (playerReducer)
// 기존 playerReducer 상태에 추가
const initialState = {
// ... 기존 상태들
playerVideoState: {
// 현재 상태
playback: PLAYBACK_STATUS.NOT_PLAYING,
display: DISPLAY_STATUS.HIDDEN,
videoId: null,
progress: 0,
error: null,
timestamp: null
}
};
액션 타입 및 함수
액션 타입
export const PLAYER_VIDEO_ACTIONS = {
// 재생 상태 액션
SET_PLAYBACK_LOADING: 'SET_PLAYBACK_LOADING',
SET_PLAYBACK_SUCCESS: 'SET_PLAYBACK_SUCCESS',
SET_PLAYBACK_ERROR: 'SET_PLAYBACK_ERROR',
SET_PLAYBACK_PLAYING: 'SET_PLAYBACK_PLAYING',
SET_PLAYBACK_NOT_PLAYING: 'SET_PLAYBACK_NOT_PLAYING',
SET_PLAYBACK_BUFFERING: 'SET_PLAYBACK_BUFFERING',
// 화면 상태 액션
SET_DISPLAY_HIDDEN: 'SET_DISPLAY_HIDDEN',
SET_DISPLAY_VISIBLE: 'SET_DISPLAY_VISIBLE',
SET_DISPLAY_MINIMIZED: 'SET_DISPLAY_MINIMIZED',
SET_DISPLAY_FULLSCREEN: 'SET_DISPLAY_FULLSCREEN',
// 복합 액션
SET_VIDEO_LOADING: 'SET_VIDEO_LOADING',
SET_VIDEO_PLAYING: 'SET_VIDEO_PLAYING',
SET_VIDEO_STOPPED: 'SET_VIDEO_STOPPED',
SET_VIDEO_MINIMIZED_PLAYING: 'SET_VIDEO_MINIMIZED_PLAYING',
};
액션 함수
// 기본 액션 함수들 (FP 스타일)
export const setPlaybackLoading = curry((videoId, displayMode = 'visible') => ({
type: PLAYER_VIDEO_ACTIONS.SET_VIDEO_LOADING,
payload: {
playback: PLAYBACK_STATUS.LOADING,
display: displayMode,
videoId,
progress: 0,
error: null,
timestamp: Date.now()
}
}));
export const setPlaybackPlaying = curry((videoId, displayMode = 'fullscreen') => ({
type: PLAYER_VIDEO_ACTIONS.SET_VIDEO_PLAYING,
payload: {
playback: PLAYBACK_STATUS.PLAYING,
display: displayMode,
videoId,
progress: 100,
error: null,
timestamp: Date.now()
}
}));
export const setPlaybackError = curry((videoId, error) => ({
type: PLAYER_VIDEO_ACTIONS.SET_PLAYBACK_ERROR,
payload: {
playback: PLAYBACK_STATUS.LOAD_ERROR,
display: DISPLAY_STATUS.VISIBLE,
videoId,
error,
progress: 0,
timestamp: Date.now()
}
}));
export const setVideoStopped = () => ({
type: PLAYER_VIDEO_ACTIONS.SET_VIDEO_STOPPED,
payload: {
playback: PLAYBACK_STATUS.NOT_PLAYING,
display: DISPLAY_STATUS.HIDDEN,
videoId: null,
error: null,
progress: 0,
timestamp: Date.now()
}
}));
상태 사용 예시
PlayerPanel.jsx
import {
setPlaybackLoading,
setPlaybackPlaying,
setPlaybackError,
setVideoStopped
} from '../actions/playerActions';
// 비디오 로딩 시작
const handleVideoLoadStart = (videoId) => {
dispatch(setPlaybackLoading(videoId, 'fullscreen'));
};
// 비디오 재생 시작
const handleVideoPlay = (videoId) => {
dispatch(setPlaybackPlaying(videoId, 'fullscreen'));
};
// 비디오 에러 발생
const handleVideoError = (videoId, error) => {
dispatch(setPlaybackError(videoId, error));
};
// 상태 확인
const videoState = useSelector(state => state.player.playerVideoState);
const isLoading = videoState.playback === PLAYBACK_STATUS.LOADING;
const isPlaying = videoState.playback === PLAYBACK_STATUS.PLAYING;
const hasError = videoState.playback === PLAYBACK_STATUS.LOAD_ERROR;
const isFullscreen = videoState.display === DISPLAY_STATUS.FULLSCREEN;
VideoPlayer.js
// 현재 상태에 따른 UI 렌더링
const renderVideoState = () => {
const { playback, display, error, progress } = videoState;
if (playback === PLAYBACK_STATUS.LOADING) {
return <LoadingSpinner progress={progress} />;
}
if (playback === PLAYBACK_STATUS.LOAD_ERROR) {
return <ErrorMessage error={error} onRetry={handleRetry} />;
}
if (playback === PLAYBACK_STATUS.BUFFERING) {
return <BufferingIndicator />;
}
if (playback === PLAYBACK_STATUS.PLAYING && display === DISPLAY_STATUS.FULLSCREEN) {
return <VideoPlayer videoId={videoState.videoId} />;
}
return null;
};
구현 순서
- playerActions.js에 상수 및 액션 함수들 추가
- playerReducer.js에 초기 상태 및 핸들러들 추가
- PlayerPanel.jsx에서 새로운 상태 시스템으로 전환
- VideoPlayer.js에서 새로운 상태 시스템으로 전환
- 테스트 및 검증
장점
- 정밀한 상태 제어: 재생 상태와 화면 상태를 별도로 관리
- 명확한 상태 의미: 각 상태가 명확한 의미를 가짐
- 확장성: 새로운 상태 추가가 용이
- 유지보수성: 기존 코드 영향 최소화
- 재사용성: 다른 컴포넌트에서도 활용 가능
주의사항
- 기존 videoPlayReducer와 충돌하지 않도록 주의
- PlayerPanel과 VideoPlayer.js에만 집중하여 구현
- 기존 비디오 재생 로직과 호환성 유지