Files
shoptime/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx
optrader 89ff921aaa [251125] fix: VideoPlayer 메모리최적화 - 1
🕐 커밋 시간: 2025. 11. 25. 10:41:26

📊 변경 통계:
  • 총 파일: 3개
  • 추가: +101줄
  • 삭제: -76줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx
  ~ com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx

🔧 주요 변경 내용:
  • UI 컴포넌트 아키텍처 개선
  • 중간 규모 기능 개선
  • 코드 정리 및 최적화
2025-11-25 10:41:26 +09:00

173 lines
5.3 KiB
JavaScript

import React, { useCallback, useMemo, useRef, useEffect } from 'react';
import ReactPlayer from 'react-player';
import handle from '@enact/core/handle';
var handledMediaEventsMap = [
'onReady',
'onStart',
'onPlay',
'onProgress',
'onDuration',
'onPause',
'onBuffer',
'onBufferEnd',
'onSeek',
'onEnded',
'onError',
];
export default function TReactPlayer({
mediaEventsMap = handledMediaEventsMap,
videoRef,
url,
dispatch,
...rest
}) {
const playerRef = useRef(null);
// 🔽 [최적화] handleEvent의 주요 의존성 추출
const playing = rest?.playing;
const config = rest?.config;
const handleEvent = useCallback(
(type) => (ev) => {
if (type === 'onReady') {
if (videoRef) {
const videoNode = playerRef.current.getInternalPlayer();
videoRef(videoNode);
const iframeEl =
typeof playerRef.current?.getInternalPlayer === 'function'
? playerRef.current.getInternalPlayer('iframe')
: null;
if (iframeEl) {
iframeEl.setAttribute('tabIndex', '-1');
iframeEl.setAttribute('aria-hidden', 'true');
}
if (
videoNode.tagName &&
!Object.prototype.hasOwnProperty.call(videoNode, 'proportionPlayed')
) {
Object.defineProperties(videoNode, {
error: {
get: function () {
return videoNode.networkState === videoNode.NETWORK_NO_SOURCE;
},
},
loading: {
get: function () {
return videoNode.readyState < videoNode.HAVE_ENOUGH_DATA;
},
},
proportionLoaded: {
get: function () {
return (
videoNode.buffered.length &&
videoNode.buffered.end(videoNode.buffered.length - 1) / videoNode.duration
);
},
},
proportionPlayed: {
get: function () {
return videoNode.currentTime / videoNode.duration;
},
},
});
} else if (!Object.prototype.hasOwnProperty.call(videoNode, 'proportionPlayed')) {
videoNode.play = videoNode.playVideo;
videoNode.pause = videoNode.pauseVideo;
videoNode.seek = videoNode.seekTo;
Object.defineProperties(videoNode, {
currentTime: {
get: function () {
return videoNode.getCurrentTime();
},
set: function (time) {
videoNode.seekTo(time);
},
},
duration: {
get: function () {
return videoNode.getDuration();
},
},
paused: {
get: function () {
return videoNode.getPlayerState() !== 1;
},
},
error: {
get: function () {
return !!videoNode?.playerInfo?.videoData?.errorCode;
},
},
loading: {
get: function () {
return !videoNode?.playerInfo?.videoData?.isPlayable; //todo
},
},
proportionLoaded: {
get: function () {
return videoNode?.getVideoBytesLoaded() ?? 0;
},
},
proportionPlayed: {
get: function () {
const duration = videoNode.getDuration();
return duration ? videoNode.getCurrentTime() / duration : 0;
},
},
playbackRate: {
get: function () {
return videoNode?.playerInfo?.playbackRate;
},
set: function (playbackRate) {
if (videoNode && videoNode.setPlaybackRate) {
videoNode.setPlaybackRate(playbackRate);
}
},
},
});
}
}
handle.forward('onLoadStart', { type, ev }, rest);
}
if (type === 'onEnded' && rest?.isYoutube && rest?.type === 'VOD') {
// YouTube 재생 종료 시 iframe이 포커스를 가져가는 문제를 방지
const iframeEl =
typeof playerRef.current?.getInternalPlayer === 'function'
? playerRef.current.getInternalPlayer('iframe')
: null;
iframeEl?.blur();
}
handle.forward('onUpdate', { type, ev }, rest);
},
[videoRef, playing, config] // ✅ 주요 의존성 추가
);
const handledMediaEvents = useMemo(() => {
const events = {};
for (let i = 0; i < mediaEventsMap.length; i++) {
const eventName = mediaEventsMap[i];
events[eventName] = handleEvent(eventName);
}
return events;
}, [handleEvent, mediaEventsMap]);
// 메모리 정리는 VideoPlayer.js componentWillUnmount에서 수행
// TReactPlayer는 react-player 래퍼 역할만 수행
return (
<ReactPlayer
ref={playerRef}
url={url}
progressInterval={1000}
config={rest.config}
{...handledMediaEvents}
{...rest}
playsinline // Add playsinline attribute here
/>
);
}