React-Player

taehyung·2025년 2월 19일

회사 프로젝트에 React-Player 를 적용하기 전 공식 문서에 있는 내용을 정리하고 속성과 메소드를 하나씩 테스트 해보았다.

통합 사용 예제

import { useState, useRef } from 'react'
import ReactPlayer from 'react-player/lazy' // 플레이어 레이지 로딩
import ReactPlayer from 'react-player/youtube'; // 특정 플랫폼 사용 번들 크기 최적화
import notFoundImage from '@/assets/images/not-found.png'

export const VideoPlayer = () => {
  const [play, setPlay] = useState(false)
  const [show, setShow] = useState(true)
  const playerRef = useRef<ReactPlayer | null>(null)

  const togglePip = async () => {
    if (playerRef.current) {
      try {
        const video = playerRef.current.getInternalPlayer() // 현재 페이지의 비디오 플레이어 인스턴스
        if (video && document.pictureInPictureEnabled) {
          // document.pictureInPictureEnabled 현재 브라우저가 pip 지원하는지 boolean
          if (document.pictureInPictureElement) {
            await document.exitPictureInPicture() // 현재 pip 모드로 실행중인 영상을 플레이어로 전환 ( pip 모드 종료 )
          } else {
            await video.requestPictureInPicture() // 비디오 플레이어 인스턴스를 pip 모드로 전환
          }
        }
      } catch (error) {
        console.error('PIP 모드 전환 실패:', error)
      }
    }
  }

  return (
    <article className="flex flex-col items-center gap-4 rounded-lg border p-4 shadow-lg">
      <h3 className="text-xl font-semibold">Video Player</h3>
      {show ? (
        <div className="aspect-video size-full">
          <ReactPlayer
            //==== 플레이어의 상태와 관련된 props =====
            ref={playerRef}
            url="/cook.mp4" // 배열 가능하나 배열 중 브라우저가 지원하는 첫번째 포맷 사용 용도.. 플레이 리스트 같은 기능은 따로 구현해야 함
            controls={true} // 플레이어 컨트롤러 사용 여부
            light={notFoundImage} // light 모드 설정 - true or false : 영상을 바로 로드하지 않고 플레이어를 클릭해야 로드하는 썸네일 단계를 만들어줌. 이미지 URL만 제공해도 됨
            previewTabIndex={0} // light 모드의 썸네일이 Tab 포커스를 받는지 여부 null 일 때 포커스를 받지 않음 ( 웹 접근성 )
            playIcon={<img src={notFoundImage}></img>} // light 모드의 플레이 아이콘을 컴포넌트 형태로 받음 
            playing={play} // true가 되면 영상을 재생, false가 되면 영상 일시 정지
            playbackRate={0.75} // 영상 재생 속도 설정
            loop={false} // 영상 반복 재생 여부 설정
            volume={0.5} // 플레이어의 상태가 변경 될 때 볼륨 설정
            muted={true} // 음소거 설정
            pip={true} // pip 모드를 사용할 지 여부
            stopOnUnmount={false} // pip 모드에서 플레이어가 언마운트될 때 pip 모드 종료하고 영상을 정지할지에 대한 설정
            progressInterval={2000} // progress 상태를 몇 초마다 반환할 지 설정 ( 밀리초 단위 )
            onProgress={(progress) => { // progressInterval 시간마다 진행 상황을 반환함
              console.log(progress)
            }}
            wrapper={(props) => <section {...props}></section>} // 플레이어 root 요소 설정 props 에는 플레이어가 들어있을 것으로 예상 반드시 사용
            style={{ background: 'grey' }} // root 요소에 인라인 스타일 추가
            fallback={<>플레이어 로딩중..</>} // 레이지 로딩 중 표시할 로딩 컴포넌트
            width="100%"
            height="100%"
            config={{ file: { forceVideo: true } }} // 플레이어 별 선택옵션 설정
            playsinline // 모바일 디바이스에서 영상 재생 시 기본값이 전체화면인데 그거 제어해주는 옵션
            //==========================================
            
            //==== 플레이어의 이벤트와 관련된 props =====
            onReady={(playerInstance) => {
            	console.log(playerInstance) // 기본 매개변수로 플레이어 인스턴스를 제공
            }} // 로드, 준비 완료 시 호출
            onStart={() => {}} // 재생 시작 시 호출
            onPlay={() => {}} // 재생 시작 or 일시정지 후 재개 시 호출
            onProgress={(progressData) => {
            	console.log(progressData) // 기본 매개변수로 재생 중 진행 정보를 제공
            }} // progressInterval 상태에 설정된 초마다 호출 기본값 1초 매개변수에 프로그레스 정보를 포함한다. ( 재생 비율, 로드 비율, 재생 초, 로드 초 등 )
            onDuration={(totalDuration) => {
            	console.log(totalDuration) // 기본 매개변수로 영상 총 길이를 제공
            }} // 총 길이를 포함하는 콜백
            onPause={() => {}} // 일시정지 시 호출
            onBuffer={() => {}} // 버퍼링 시작 시 호출
            onBufferEnd={() => {}} // 버퍼링 종료 시 호출
            onSeek={(seekTime) => {
            	console.log(seekTime) // 기본 매개변수로 이동 된 시간 제공
            }} // 특정 시간으로 이동할 때 호출
            onPlaybackRateChange={(newPlaybackRate) => {
            	console.log(newPlaybackRate) // 기본 매개변수로 변경된 재생 속도 제공
            }} // 재생속도 변경 시 호출
            onPlaybackQualityChange={(newQuality) => {
            	console.log(newQuality) // 기본 매개변수로 변경된 재생 품질 제공
            }} // 재생 품질 변경 시 호출
            onEnded={() => {}} // 재생 종료 시 호출 ( loop = true 면 호출 X )
            onError={(errorInfo) => {
              	console.log(errorInfo) // 기본 매개변수로 에러 정보 제공
            }} // 에러 발생 시 호출
            onClickPreview={() => {}} // light 모드 썸네일 클릭 시 호출
            onEnablePIP={() => {}} // pip 모드 활성 시 호출
            onDisablePIP={() => {}} // pip 모드 종료 시 호출
            //==========================================
          />
        </div>
      ) : null}

      <div className="flex gap-2">
        <button
          onClick={() => setPlay((prev) => !prev)}
          className="rounded-lg bg-blue-500 px-4 py-2 text-white transition hover:bg-blue-700"
        >
          {play ? '정지' : '재생'}
        </button>
        <button
          onClick={togglePip}
          className="rounded-lg bg-gray-500 px-4 py-2 text-white transition hover:bg-gray-700"
        >
          PIP 모드
        </button>
        <button
          onClick={() => {
            setShow((prev) => !prev)
          }}
          className="rounded-lg bg-gray-500 px-4 py-2 text-white transition hover:bg-gray-700"
        >
          플레이어 숨김
        </button>
      </div>
    </article>
  )
}

Props

Prop설명기본값
url재생할 영상/오디오의 URL (배열 가능)-
playingtrue면 재생, false면 일시 정지false
loop반복 재생 여부false
controls영상 컨트롤러 표시 여부
* Vimeo 비디오의 경우 비디오 소유자가 컨트롤 숨기기를 활성화해야 합니다.
false
lighttrue시 썸네일 제공
◦ 미리보기 이미지를 재정의하기 위해 이미지 URL을 전달합니다.
false
volume플레이어의 상태가 변하면 볼륨이 0 or 100으로 설정됨null
muted플레이어 음소거 여부false
playbackRate영상 재생 속도 설정
youtube, wistia 및 파일 경로에서만 지원
1
width플레이어 너비640px
height플레이어 높이360px
style루트 요소에 인라인 스타일 추가{}
progressIntervalonProgress 콜백 함수 사이의 시간 (밀리초)1000
playsinlineplaysinline이 지원되는 경우 속성을 적용false
pippip모드 사용여부
true여도 따로 pip 관련 로직을 작성해야만 작동한다.
false
stopOnUnmountpip 모드로 작동중인 플레이어가 언마운트 되었을 때 pip를 종료 시킬 것인지 여부
pip로 계속 재생 시키고 싶으면 false
true
fallback지연 로딩을 사용하는 경우 폴백으로 사용할 요소 또는 구성 요소null
wrapper컨테이너로 사용할 요소 지정div
playIconlight 모드에서 사용할 재생 아이콘 설정-
previewTabIndexlight 모드에 썸네일에 탭 포커스 이동 가능 여부
null이면 탭 포커스 안됨, 나머지 숫자는 다 가능
0
config플레이어에 대한 다양한 재정의 옵션-

Config ( 플레이어에 대한 다양한 재정의 옵션 )

ReactPlayer 플레이어별 옵션 정리

플랫폼옵션설명
YouTubeplayerVars기본 플레이어 변수를 재정의
embedOptions기본 임베드 옵션을 재정의
onUnstarted상태가 "unstarted"로 변경될 때 호출 (자동 재생 실패 시 주로 발생)
FacebookappIdFacebook 앱 ID 설정
version사용할 Facebook SDK 버전
playerId서버 사이드 렌더링 시 일관된 ID를 유지 (react-uid와 함께 사용)
attributesfb-video 요소에 추가 속성 전달
SoundCloudoptions기본 플레이어 옵션 재정의
VimeoplayerOptions기본 플레이어 옵션 재정의
title<iframe> 태그의 title 속성 설정
MuxattributesHTML 요소 속성 적용
version사용할 Mux 플레이어 버전
Wistiaoptions기본 플레이어 옵션 재정의
playerId서버 사이드 렌더링 시 일관된 ID를 유지 (react-uid와 함께 사용)
Mixcloudoptions기본 플레이어 옵션 재정의
Dailymotionparams기본 플레이어 변수를 재정의
Twitchoptions기본 플레이어 옵션 재정의
playerId서버 사이드 렌더링 시 일관된 ID를 유지 (react-uid와 함께 사용)
파일 (로컬/직접 링크)attributesHTML 요소 속성 적용
forceVideo<video> 요소를 강제로 렌더링
forceAudio<audio> 요소를 강제로 렌더링
forceHLShls.js를 사용하여 HLS 스트림을 강제 실행
forceSafariHLSSafari에서도 hls.js를 사용하여 HLS 스트림 실행
forceDisableHLShls.js 사용을 강제로 비활성화
forceDASHdash.js를 사용하여 DASH 스트림 강제 실행
forceFLVflv.js를 사용하여 FLV 스트림 강제 실행
hlsOptions기본 hls.js 옵션 재정의
hlsVersionhls.js 버전을 jsdelivr에서 가져옴 (기본값: 0.13.1)
dashVersiondash.js 버전을 cdnjs에서 가져옴 (기본값: 2.9.2)
flvVersionflv.js 버전을 jsdelivr에서 가져옴 (기본값: 1.5.0)

ReactPlayer 메서드 정리

Static Methods (정적 메서드)

메서드설명
ReactPlayer.canPlay(url)주어진 URL이 재생 가능한지 확인. (단, 개인정보 보호 설정, 스트리밍 권한 등의 이유로 재생이 불가능한 경우 감지하지 못함. 그런 경우 onError 이벤트가 호출됨.)
패턴에 맞지 않는 URL은 기본 HTML5 플레이어로 재생됨.
ReactPlayer.canEnablePiP(url)주어진 URL이 Picture-in-Picture(PiP) 모드에서 재생 가능한지 확인.
ReactPlayer.addCustomPlayer(CustomPlayer)사용자 정의 플레이어 추가. (자세한 내용은 Adding custom players 참고)
ReactPlayer.removeCustomPlayers()addCustomPlayer()를 사용하여 추가한 모든 사용자 정의 플레이어를 제거.

Instance Methods (인스턴스 메서드)

useRef를 사용하여 ReactPlayer 인스턴스의 메서드를 호출할 수 있음.

메서드설명
seekTo(amount, type)특정 위치로 이동 (seek)
- amount: 이동할 시간 (초 단위) 또는 비율 (0~1 사이)
- type: 'seconds' 또는 'fraction'으로 지정 가능 (기본값: 비율)
getCurrentTime()현재 재생 중인 시간(초 단위) 반환.
사용 불가능한 경우 null 반환.
getSecondsLoaded()현재 로드된 영상 시간(초 단위) 반환.
지원되지 않거나 불가능한 경우 null 반환.
getDuration()전체 영상 길이(초 단위) 반환.
길이를 알 수 없는 경우 null 반환.
getInternalPlayer(type?)현재 사용 중인 내부 플레이어 반환.
- 예) YouTube 플레이어 인스턴스, <video> 태그 등
- type을 지정하면 특정 플레이어 가져올 수 있음
'hls': hls.js 플레이어
'dash': dash.js 플레이어
- 내부 플레이어가 없는 경우 null 반환
showPreview()Light 모드에서 미리보기 화면으로 돌아감.

예제 코드

import React, { useRef } from "react";
import ReactPlayer from "react-player";

export default function VideoPlayer() {
  const playerRef = useRef<ReactPlayer>(null);

  const handleSeek = () => {
    playerRef.current?.seekTo(10, "seconds"); // 10초 앞으로 이동
  };

  return (
    <div>
      <ReactPlayer
        ref={playerRef}
        url="https://www.example.com/video.mp4"
        controls
      />
      <button onClick={handleSeek}>10초 앞으로</button>
    </div>
  );
}

Callback Props

플레이어의 이벤트로 사용가능한 콜백함수 리스트

Prop설명
onReady미디어가 로드되고 재생 준비가 완료되었을 때 호출. playingtrue로 설정되어 있으면 즉시 재생 시작.
onStart미디어가 재생을 시작할 때 호출.
onPlay미디어가 시작되거나 일시정지 후 재개될 때 호출.
onProgress미디어의 재생 및 로드 진행 상황을 포함하는 콜백. 재생된 비율, 로드된 비율, 재생된 초, 로드된 초 등의 값이 포함됨.
onDuration미디어의 총 길이(초 단위)를 포함하는 콜백.
onPause미디어가 일시정지되었을 때 호출.
onBuffer미디어가 버퍼링을 시작할 때 호출.
onBufferEnd미디어의 버퍼링이 끝났을 때 호출. YouTube, Facebook, 파일 형식에 대해 작동.
onSeek미디어가 특정 시간으로 이동할 때 호출. (초 단위)
onPlaybackRateChange미디어의 재생 속도가 변경되었을 때 호출. YouTube, Vimeo(활성화된 경우), Wistia, 파일 경로에서만 지원.
onPlaybackQualityChange미디어의 재생 품질이 변경되었을 때 호출. YouTube에서만 지원.
onEnded미디어 재생이 끝났을 때 호출. (반복 설정이 true일 경우 호출되지 않음)
onError미디어 재생 중 오류가 발생했을 때 호출.
onClickPreview사용자에게 라이트 모드 프리뷰를 클릭했을 때 호출.
onEnablePIPPIP(그림 속 그림) 모드가 활성화되었을 때 호출.
onDisablePIPPIP(그림 속 그림) 모드가 비활성화되었을 때 호출.

이 표는 ReactPlayer에서 제공하는 각 콜백 프로퍼티가 어떤 이벤트에서 호출되는지 설명하고 있습니다.


기본 HTML5 비디오 플레이어(<video>)에서 재생 가능한 플랫폼

YouTube는 보안 정책 때문에 <video> 태그에서 직접 재생할 수 없지만, 다른 플랫폼은 경우에 따라 가능함.

플랫폼<video> 태그에서 직접 재생 가능 여부비고
로컬 파일 (.mp4, .webm, .ogg)✅ 가능파일을 직접 제공하는 경우
서버에 업로드된 동영상 (CDN, S3 등)✅ 가능mp4, webm 등의 포맷 필요
Vimeo Pro (유료 플랜)✅ 가능직접 .mp4 URL 제공
Dailymotion❌ 불가능iframe 또는 API 필요
Facebook Video❌ 불가능iframe 필요
Twitch❌ 불가능iframe 필요
SoundCloud (오디오)❌ 불가능<audio> 지원
Mux (유료 스트리밍 서비스)✅ 가능.m3u8, .mp4 지원
Cloudflare Stream (유료)✅ 가능.mp4 스트리밍 지원

기본 비디오 플레이어에서 사용 가능한 대체 방법

  1. Vimeo Pro 사용 (.mp4 직접 제공)

    • Vimeo 무료 버전은 불가능하지만, Vimeo Pro 이상 플랜은 .mp4 URL을 제공하여 <video> 태그에서 재생 가능.
  2. HLS(m3u8) / DASH(mpd) 스트리밍 지원

    • Mux, Cloudflare Stream 같은 스트리밍 서비스는 HTML5 플레이어에서 직접 재생 가능.
    • .m3u8(HLS) 또는 .mpd(DASH) 포맷을 제공하는 경우 hls.js 또는 dash.js 라이브러리를 사용하여 직접 재생할 수 있음.
  3. CDN이나 직접 서버에서 .mp4 호스팅

    <video controls>
      <source src="https://yourserver.com/video.mp4" type="video/mp4" />
    </video>
    • 직접 .mp4 파일을 호스팅하면 기본 <video> 태그에서 재생 가능.

결론

  • YouTube, Dailymotion, Facebook, Twitch 등은 <video> 태그에서 직접 재생 ❌ (API 또는 iframe 필요).
  • Vimeo Pro, Mux, Cloudflare Stream 같은 서비스는 .mp4 또는 .m3u8을 제공하므로 기본 비디오 플레이어에서 ✅ 가능.
  • 직접 .mp4 파일을 서버에 업로드하거나 CDN에서 제공하면 기본 플레이어에서 문제없이 재생 가능! 🎬

알아두면 좋은 것

  • light 모드는 단순히 썸네일 제공의 목적 보다는 사용자가 썸네일을 클릭했을 때 영상을 로드하므로 필요한 영상만 로드 할 수 있다는 최적화로써의 장점도 있음.
  • light 속성은 이미지 url만 제공하면 알아서 설정됨. 그러나, Tab 포커스가 가능하기 때문에 img 태그를 사용하여 alt로 접근성을 향상시키는게 좋음 .
  • 음소거 ( muted ) 영상만 자동 재생을 사용할 수 있음.
  • 특정 모바일 브라우저(예: Chrome, Safari)의 HTML5 <video>요소는 사용자 상호작용(예: 플레이어 탭)을 통해 시작된 경우에만 재생을 허용함
  • 유튜브 영상은 url={['url','url','url']} 으로 제목없는 재생 목록을 만들어서 연속적으로 볼 수 있다. 앞 뒤 영상 이동 가능

profile
Front End

0개의 댓글