회사 프로젝트에 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>
)
}
| Prop | 설명 | 기본값 |
|---|---|---|
| url | 재생할 영상/오디오의 URL (배열 가능) | - |
| playing | true면 재생, false면 일시 정지 | false |
| loop | 반복 재생 여부 | false |
| controls | 영상 컨트롤러 표시 여부 * Vimeo 비디오의 경우 비디오 소유자가 컨트롤 숨기기를 활성화해야 합니다. | false |
| light | true시 썸네일 제공 ◦ 미리보기 이미지를 재정의하기 위해 이미지 URL을 전달합니다. | false |
| volume | 플레이어의 상태가 변하면 볼륨이 0 or 100으로 설정됨 | null |
| muted | 플레이어 음소거 여부 | false |
| playbackRate | 영상 재생 속도 설정 youtube, wistia 및 파일 경로에서만 지원 | 1 |
| width | 플레이어 너비 | 640px |
| height | 플레이어 높이 | 360px |
| style | 루트 요소에 인라인 스타일 추가 | {} |
| progressInterval | onProgress 콜백 함수 사이의 시간 (밀리초) | 1000 |
| playsinline | playsinline이 지원되는 경우 속성을 적용 | false |
| pip | pip모드 사용여부 true여도 따로 pip 관련 로직을 작성해야만 작동한다. | false |
| stopOnUnmount | pip 모드로 작동중인 플레이어가 언마운트 되었을 때 pip를 종료 시킬 것인지 여부 pip로 계속 재생 시키고 싶으면 false | true |
| fallback | 지연 로딩을 사용하는 경우 폴백으로 사용할 요소 또는 구성 요소 | null |
| wrapper | 컨테이너로 사용할 요소 지정 | div |
| playIcon | light 모드에서 사용할 재생 아이콘 설정 | - |
| previewTabIndex | light 모드에 썸네일에 탭 포커스 이동 가능 여부 null이면 탭 포커스 안됨, 나머지 숫자는 다 가능 | 0 |
| config | 플레이어에 대한 다양한 재정의 옵션 | - |
| 플랫폼 | 옵션 | 설명 |
|---|---|---|
| YouTube | playerVars | 기본 플레이어 변수를 재정의 |
embedOptions | 기본 임베드 옵션을 재정의 | |
onUnstarted | 상태가 "unstarted"로 변경될 때 호출 (자동 재생 실패 시 주로 발생) | |
appId | Facebook 앱 ID 설정 | |
version | 사용할 Facebook SDK 버전 | |
playerId | 서버 사이드 렌더링 시 일관된 ID를 유지 (react-uid와 함께 사용) | |
attributes | fb-video 요소에 추가 속성 전달 | |
| SoundCloud | options | 기본 플레이어 옵션 재정의 |
| Vimeo | playerOptions | 기본 플레이어 옵션 재정의 |
title | <iframe> 태그의 title 속성 설정 | |
| Mux | attributes | HTML 요소 속성 적용 |
version | 사용할 Mux 플레이어 버전 | |
| Wistia | options | 기본 플레이어 옵션 재정의 |
playerId | 서버 사이드 렌더링 시 일관된 ID를 유지 (react-uid와 함께 사용) | |
| Mixcloud | options | 기본 플레이어 옵션 재정의 |
| Dailymotion | params | 기본 플레이어 변수를 재정의 |
| Twitch | options | 기본 플레이어 옵션 재정의 |
playerId | 서버 사이드 렌더링 시 일관된 ID를 유지 (react-uid와 함께 사용) | |
| 파일 (로컬/직접 링크) | attributes | HTML 요소 속성 적용 |
forceVideo | <video> 요소를 강제로 렌더링 | |
forceAudio | <audio> 요소를 강제로 렌더링 | |
forceHLS | hls.js를 사용하여 HLS 스트림을 강제 실행 | |
forceSafariHLS | Safari에서도 hls.js를 사용하여 HLS 스트림 실행 | |
forceDisableHLS | hls.js 사용을 강제로 비활성화 | |
forceDASH | dash.js를 사용하여 DASH 스트림 강제 실행 | |
forceFLV | flv.js를 사용하여 FLV 스트림 강제 실행 | |
hlsOptions | 기본 hls.js 옵션 재정의 | |
hlsVersion | hls.js 버전을 jsdelivr에서 가져옴 (기본값: 0.13.1) | |
dashVersion | dash.js 버전을 cdnjs에서 가져옴 (기본값: 2.9.2) | |
flvVersion | flv.js 버전을 jsdelivr에서 가져옴 (기본값: 1.5.0) |
| 메서드 | 설명 |
|---|---|
ReactPlayer.canPlay(url) | 주어진 URL이 재생 가능한지 확인. (단, 개인정보 보호 설정, 스트리밍 권한 등의 이유로 재생이 불가능한 경우 감지하지 못함. 그런 경우 onError 이벤트가 호출됨.) 패턴에 맞지 않는 URL은 기본 HTML5 플레이어로 재생됨. |
ReactPlayer.canEnablePiP(url) | 주어진 URL이 Picture-in-Picture(PiP) 모드에서 재생 가능한지 확인. |
ReactPlayer.addCustomPlayer(CustomPlayer) | 사용자 정의 플레이어 추가. (자세한 내용은 Adding custom players 참고) |
ReactPlayer.removeCustomPlayers() | addCustomPlayer()를 사용하여 추가한 모든 사용자 정의 플레이어를 제거. |
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>
);
}
플레이어의 이벤트로 사용가능한 콜백함수 리스트
| Prop | 설명 |
|---|---|
onReady | 미디어가 로드되고 재생 준비가 완료되었을 때 호출. playing이 true로 설정되어 있으면 즉시 재생 시작. |
onStart | 미디어가 재생을 시작할 때 호출. |
onPlay | 미디어가 시작되거나 일시정지 후 재개될 때 호출. |
onProgress | 미디어의 재생 및 로드 진행 상황을 포함하는 콜백. 재생된 비율, 로드된 비율, 재생된 초, 로드된 초 등의 값이 포함됨. |
onDuration | 미디어의 총 길이(초 단위)를 포함하는 콜백. |
onPause | 미디어가 일시정지되었을 때 호출. |
onBuffer | 미디어가 버퍼링을 시작할 때 호출. |
onBufferEnd | 미디어의 버퍼링이 끝났을 때 호출. YouTube, Facebook, 파일 형식에 대해 작동. |
onSeek | 미디어가 특정 시간으로 이동할 때 호출. (초 단위) |
onPlaybackRateChange | 미디어의 재생 속도가 변경되었을 때 호출. YouTube, Vimeo(활성화된 경우), Wistia, 파일 경로에서만 지원. |
onPlaybackQualityChange | 미디어의 재생 품질이 변경되었을 때 호출. YouTube에서만 지원. |
onEnded | 미디어 재생이 끝났을 때 호출. (반복 설정이 true일 경우 호출되지 않음) |
onError | 미디어 재생 중 오류가 발생했을 때 호출. |
onClickPreview | 사용자에게 라이트 모드 프리뷰를 클릭했을 때 호출. |
onEnablePIP | PIP(그림 속 그림) 모드가 활성화되었을 때 호출. |
onDisablePIP | PIP(그림 속 그림) 모드가 비활성화되었을 때 호출. |
이 표는 ReactPlayer에서 제공하는 각 콜백 프로퍼티가 어떤 이벤트에서 호출되는지 설명하고 있습니다.
<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 스트리밍 지원 |
Vimeo Pro 사용 (.mp4 직접 제공)
.mp4 URL을 제공하여 <video> 태그에서 재생 가능.HLS(m3u8) / DASH(mpd) 스트리밍 지원
.m3u8(HLS) 또는 .mpd(DASH) 포맷을 제공하는 경우 hls.js 또는 dash.js 라이브러리를 사용하여 직접 재생할 수 있음.CDN이나 직접 서버에서 .mp4 호스팅
<video controls>
<source src="https://yourserver.com/video.mp4" type="video/mp4" />
</video>
.mp4 파일을 호스팅하면 기본 <video> 태그에서 재생 가능.<video> 태그에서 직접 재생 ❌ (API 또는 iframe 필요). .mp4 또는 .m3u8을 제공하므로 기본 비디오 플레이어에서 ✅ 가능. .mp4 파일을 서버에 업로드하거나 CDN에서 제공하면 기본 플레이어에서 문제없이 재생 가능! 🎬