Files
shoptime/com.twin.app.shoptime/[251116]_video_state_management_design.md
optrader 2d93ee6ca4 [251116] feat: playeReducer , playActions.js에 videoState 추가
🕐 커밋 시간: 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()

🔧 주요 변경 내용:
  • 타입 시스템 안정성 강화
  • 핵심 비즈니스 로직 개선
  • 개발 문서 및 가이드 개선
2025-11-16 17:28:35 +09:00

6.2 KiB

[251116] 새로운 비디오 상태 관리 시스템 구현

개요

기존의 videoPlayReducer는 유지하되, PlayerPanel과 VideoPlayer.js를 위한 새로운 비디오 상태 관리 시스템을 playerReducer에 구현한다. 재생 상태와 화면 상태를 분리하여 더 정밀한 비디오 상태 제어를 가능하게 한다.

설계 원칙

  1. 기존 videoPlayReducer 유지: 다른 컴포넌트에서 사용 중일 수 있으므로 그대로 둔다
  2. playerReducer에 새로운 상태 시스템 구현: PlayerPanel과 VideoPlayer.js 전용
  3. 이중 상태 관리: 재생 상태(Playback Status) + 화면 상태(Display Status)
  4. 기존 패턴 따르기: 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;
};

구현 순서

  1. playerActions.js에 상수 및 액션 함수들 추가
  2. playerReducer.js에 초기 상태 및 핸들러들 추가
  3. PlayerPanel.jsx에서 새로운 상태 시스템으로 전환
  4. VideoPlayer.js에서 새로운 상태 시스템으로 전환
  5. 테스트 및 검증

장점

  1. 정밀한 상태 제어: 재생 상태와 화면 상태를 별도로 관리
  2. 명확한 상태 의미: 각 상태가 명확한 의미를 가짐
  3. 확장성: 새로운 상태 추가가 용이
  4. 유지보수성: 기존 코드 영향 최소화
  5. 재사용성: 다른 컴포넌트에서도 활용 가능

주의사항

  • 기존 videoPlayReducer와 충돌하지 않도록 주의
  • PlayerPanel과 VideoPlayer.js에만 집중하여 구현
  • 기존 비디오 재생 로직과 호환성 유지