npm package: @sgrsoft/vpe-react-sdk
Description: <img src="https://tkmenfxu2702.edge.naverncp.com/profile/202602/76303c7f18eb7e2e70c0e1a152419545.png" style="max-width:600px">
Latest version: 2.0.1

# Video Player Enhancement React SDK

<img src="https://tkmenfxu2702.edge.naverncp.com/profile/202602/76303c7f18eb7e2e70c0e1a152419545.png" style="max-width:600px">

React 를 지원하는 Video Player Enhancement SDK입니다. `hls.js`/`dashjs`는 번들에 포함되지 않으며 외부에서 주입합니다.

## 지원 버전
- React 최소 버전: `18.0.0` (React 18.x/19.x 지원)
- Next.js 권장 버전:
- React 18 사용 시: `14.2+`
- React 19 사용 시: `15.x+`

## Modules 이용하기

### npm 이용한 설치
```bash
npm install @sgrsoft/vpe-react-sdk
```

### yarn 이용한 설치
```bash
yarn add @sgrsoft/vpe-react-sdk
```

### pnpm 이용한 설치
```bash
pnpm add @sgrsoft/vpe-react-sdk
```

### 스트리밍 라이브러리 설치(선택)
```bash
npm install hls.js dashjs
```

## 플레이어 생성

### React (ESM)
```tsx
import Hls from "hls.js";
import dashjs from "dashjs";
import { VpePlayer } from "@sgrsoft/vpe-react-sdk";

export function App() {
    return (
        <VpePlayer
            accessKey="YOUR_ACCESS_KEY"
            hls={Hls}
            dashjs={dashjs}
            options={{
                aspectRatio: "16/9",
                playlist: [
                    {
                        file: "https://CDN_DOMAIN/example_video_01.mp4",
                        poster: "https://CDN_DOMAIN/example_video_01.png",
                    },
                ],
            }}
        />
    );
}
```

## platform 안내
`platform`은 재생 환경을 구분하는 필수 설정입니다.
- `pub`: 민간 클라우드 환경
- `gov`: 공공 클라우드 환경

```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    platform="pub"
    hls={Hls}
    dashjs={dashjs}

    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
    }}
/>
```

## 옵션 레퍼런스
`options`는 `vpe-core-sdk`에서 검증/보정됩니다. 기본값은 코어에서 결정되는 항목이 많아, 명시되지 않은 경우는 “vpe-core-sdk 기본값”으로 표기합니다.

| 옵션 | 타입 | 기본값 | 필수 | 설명 |
| --- | --- | --- | --- | --- |
| autostart | boolean | vpe-core-sdk 기본값 | No | 자동 재생 여부 |
| muted | boolean | vpe-core-sdk 기본값 | No | 시작 시 음소거 |
| aspectRatio | string | vpe-core-sdk 기본값 | No | 비율 (`"16/9"`, `"9/16"` 등) |
| objectFit | `"contain" \| "cover" \| "fill" \| "scale-down" \| "stretch"` | vpe-core-sdk 기본값 | No | 비디오 fit 방식 |
| controls | boolean | vpe-core-sdk 기본값 | No | 기본 컨트롤 표시 |
| keyboardShortcut | boolean | vpe-core-sdk 기본값 | No | 키보드 단축키 |
| seekingPreview | boolean | vpe-core-sdk 기본값 | No | 시킹 프리뷰 표시 |
| touchGestures | boolean | vpe-core-sdk 기본값 | No | 터치 제스처 사용 |
| startMutedInfoNotVisible | boolean | vpe-core-sdk 기본값 | No | 시작 음소거 안내 표시 여부 |
| lang | string | vpe-core-sdk 기본값 | No | UI 언어 |
| ui | string | vpe-core-sdk 기본값 | No | UI 모드/스킨 키 |
| controlBtn | object | vpe-core-sdk 기본값 | No | 버튼 표시 on/off 설정 |
| progressBarColor | string | vpe-core-sdk 기본값 | No | 진행바 색상 |
| controlActiveTime | number | `3000` | No | 컨트롤 자동 숨김(ms) |
| playRateSetting | number[] | vpe-core-sdk 기본값 | No | 재생 속도 리스트 |
| autoPause | boolean | vpe-core-sdk 기본값 | No | 자동 정지 |
| repeat | boolean | vpe-core-sdk 기본값 | No | 반복 재생 |
| setStartTime | string \| null | vpe-core-sdk 기본값 | No | 시작 시간 (문자열) |
| lowLatencyMode | boolean | vpe-core-sdk 기본값 | No | 저지연 모드 |
| descriptionNotVisible | boolean | vpe-core-sdk 기본값 | No | 메타 설명 숨김 |
| iosFullscreenNativeMode | boolean | vpe-core-sdk 기본값 | No | iOS 네이티브 전체화면 |
| visibleWatermark | boolean | vpe-core-sdk 기본값 | No | 워터마크 표시 |
| watermarkText | string | vpe-core-sdk 기본값 | No | 워터마크 텍스트 |
| watermarkConfig | object | vpe-core-sdk 기본값 | No | 워터마크 상세 설정 |
| devTestAppId | string | vpe-core-sdk 기본값 | No | 개발 테스트용 AppId |
| token | string | vpe-core-sdk 기본값 | No | 스트림 URL 토큰 |
| override | object | vpe-core-sdk 기본값 | No | 이벤트/동작 오버라이드 |
| ads | AdOptions | - | No | Pre-roll 광고 설정 (Google IMA) |
| layout | ControlBarLayout \| Variant \| Responsive | SDK 기본 레이아웃 | No | 컨트롤바 레이아웃 |

참고
- `playlist` 등 재생 관련 옵션은 `vpe-core-sdk`에서 확장 처리됩니다. 예시는 위 “플레이어 생성” 섹션을 참고하세요.

## 옵션 사용 예시
아래 예시는 각 옵션을 단독으로 적용하는 형태입니다. 공통으로 `accessKey`와 `playlist`를 포함한 실행 코드를 제공합니다.

### autostart
- 자동 재생 여부를 설정합니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
                {
                    file: "https://CDN_DOMAIN/example_video_01.mp4"
                }
        ],
        autostart: true,
    }}
/>
```

### muted
- 시작 시 음소거를 적용합니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        muted: true,
    }}
/>
```

### aspectRatio
- 플레이어 비율을 지정합니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        aspectRatio: "16/9",
    }}
/>
```

### objectFit
- 비디오 표시 방식을 설정합니다.
- cover | contain | fill | stretch
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        objectFit: "cover",
    }}
/>
```

### controls
- 기본 컨트롤 표시 여부를 설정합니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        controls: true
    }}
/>
```

### keyboardShortcut
- 키보드 단축키를 활성화합니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        keyboardShortcut: true,
    }}
/>
```

### seekingPreview
- 시킹 프리뷰 표시 여부를 설정합니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        seekingPreview: true,
    }}
/>
```

### touchGestures
- 터치 제스처 사용 여부를 설정합니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        touchGestures: true,
    }}
/>
```

### startMutedInfoNotVisible
- 시작 음소거 안내 문구 표시 여부를 설정합니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        startMutedInfoNotVisible: true,
    }}
/>
```

### lang
- UI 언어를 설정합니다.
- 설정하지 않거나 'auto'로 설정되면 브라우저 or 기기설정을 따라갑니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        lang: "ko",
    }}
/>
```

### ui
- UI 모드/스킨 키를 설정합니다.
- auto | pc | mobile
- 설정하지 않거나 'auto'로 설정되면 브라우저 가로폭이나 기기를 따라갑니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        ui: "auto",
    }}
/>
```

### controlBtn
- 컨트롤 버튼 표시 여부를 설정합니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        controlBtn: {
            play: true,
            fullscreen: true,
            progressBar: true,
            volume: true,
            times: true,
            pictureInPicture: true,
            setting: true,
            subtitle: true,
        },
    }}
/>
```

### progressBarColor
- 진행바 색상을 설정합니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        progressBarColor: "#00E0FF",
    }}
/>
```

### controlActiveTime
- 컨트롤 자동 숨김 시간을 설정합니다(ms).
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        controlActiveTime: 3000,
    }}
/>
```

### playRateSetting
- 재생 속도 옵션을 설정합니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        playRateSetting: [0.5, 1, 1.5, 2],
    }}
/>
```

### autoPause
- 탭 전환/창 최소화 시 자동 정지를 설정합니다.
- 인앱의 웹뷰 상황에서는 정상동작하지 않습니다. (앱의 백그라운드 상황은 지원하지 않습니다.)
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        autoPause: true,
    }}
/>
```

### repeat
- 반복 재생을 설정합니다.
- 여러 영상의 플레이리스트 상태라면 모든 영상이 재생 후 맨처음 영상으로 이동하여 반복 재생됩니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        repeat: true,
    }}
/>
```

### setStartTime
- 시작 시간을 지정합니다(문자열).
- 기존적으로 UTC 시간을 지원하지만, 로컬포멧도 지원합니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        setStartTime: "2026-02-03T09:00:00Z",
    }}
/>
```

### lowLatencyMode
- 저지연 모드를 활성화합니다.
- LL-HLS를 제대로 재생하려면 해당 옵션이 필수입니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        lowLatencyMode: true,
    }}
/>
```

### descriptionNotVisible
- 메타 설명 표시 여부를 설정합니다.
- 기본값은 false 이며 ture 설정시 화면에 표시되지 않습니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        descriptionNotVisible: true,
    }}
/>
```

### iosFullscreenNativeMode
- iOS 네이티브 전체화면 모드를 사용합니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        iosFullscreenNativeMode: true,
    }}
/>
```

### visibleWatermark
- 워터마크 표시 여부를 설정합니다.
- 이 옵션은 Video Player Enhancement 콘솔 옵션에서만 적용 가능합니다.
- 코드사에서 이 옵션을 수정할 수 없습니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        visibleWatermark: true,
    }}
/>
```

### watermarkText
- 워터마크 텍스트를 지정합니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        watermarkText: "SAMPLE@example.com",
    }}
/>
```

### watermarkConfig
- 워터마크 상세 설정을 지정합니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        watermarkConfig: {
            randPosition: true, //위치 랜덤 여부
            randPositionInterVal: 5000, //랜덤 주기 (ms)
            x: 10, //randPosition 이 false 라면 워터마크 위치 고정시 X값 (단위 %)
            y: 10, //randPosition 이 false 라면 워터마크 위치 고정시 Y값 (단위 %)
            opacity: 0.4, //워터마크 투명도
        },
    }}
/>
```


### token
- 스트림 URL에 자동으로 붙일 토큰을 설정합니다.
- 글로벌엣지 CDN 보안 토큰을 설정합니다. hls/dash 적용시 하위 ts 파일까지 함께 적용됩니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        token: "token=...",
    }}
/>
```

### override
- 내부 동작을 오버라이드합니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        override: {
            error: (error) => console.log(error), //에러 발생시 실행
            nextSource: () => {}, //다음 동영상 이동시 실행
            prevSource: () => {}, //이전 동영상 이동시 실행
            pip: { //pip 오버라이드, pip를 직접 구현할때 사용
                on: () => {},
                off: () => {},
            },
        },

    }}
/>
```

### ads
- Pre-roll 광고를 설정합니다. Google IMA SDK 기반으로 동작합니다.
- `tagUrl`에 VAST 태그 URL을 전달하면 콘텐츠 재생 전에 광고가 자동 재생됩니다.
- 광고 재생 중 콘텐츠 비디오는 자동으로 일시정지되고, 컨트롤바가 숨겨집니다.
- 광고 완료/스킵/에러 후 콘텐츠가 자동 재생됩니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    hls={Hls}
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example.m3u8"
            }
        ],
        autostart: true,
        muted: true,
        ads: {
            tagUrl: "https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/single_preroll_skippable&sz=640x480&ciu_szs=300x250%2C728x90&gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&impl=s&correlator=",
            enabled: true, // 기본값: true. false로 설정하면 광고 비활성화
        },
    }}
/>
```

**AdOptions 타입**

| 옵션 | 타입 | 기본값 | 필수 | 설명 |
| --- | --- | --- | --- | --- |
| tagUrl | string | - | Yes | VAST/VMAP 광고 태그 URL |
| enabled | boolean | `true` | No | 광고 활성화 여부 |

---

### layout
- 레이아웃을 옵션으로 전달해 컨트롤바를 변경합니다.
- 레이아웃 Video Player Enhancement 시스템은 콘솔에 UI 편집툴을 이용하여 작성할 수 있습니다.
- PC | Mobile | FullScreen 상태에 vod | live 별로 레이아웃 구성 지원합니다.
- 전체 레이아웃이 아닌 일부만 입력한다면 기존 레이아웃과 머지되어 동작합니다.
- 자세한건 하위 레이아웃 시스템에 기술되어 있습니다.
```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
        layout: {
            pc:{
                vod: {
                    bottom: [{ key: "play", items: ["PlayBtn"] }],
                },
                live:{
                    bottom: [{ key: "play", items: ["PlayBtn"] }],
                }
            }
        },
    }}
/>
```

## 레이아웃 시스템
`layout` props로 컨트롤바를 커스터마이즈할 수 있습니다.

### 기본 레이아웃
```tsx
const layout = {
    top: [],
    center: [{ key: "bigPlayBtn", items: ["BigPlayBtn"] }],
    bottom: [
        { key: "play", items: ["PlayBtn"], wrapper: "Group", noPadding: true },
        { key: "volume", items: ["VolumeBtn"], wrapper: "Group" },
        { key: "time", items: ["TimeBtn"], wrapper: "Group" },
        { key: "blank", wrapper: "Blank", items: [] },
        { key: "right", items: ["SubtitleBtn", "FullscreenBtn"], cap: 2, wrapper: "Group" },
    ],
};

<VpePlayer layout={layout} />;
```

### 라이브/다시보기 분기
```tsx
const layoutVariant = {
    live: { /* live 전용 레이아웃 */ },
    vod: { /* vod 전용 레이아웃 */ },
};

<VpePlayer layout={layoutVariant} />;
```

### 반응형 레이아웃
```tsx
const responsiveLayout = {
    pc: {
        live: { /* pc live */ },
        vod: { /* pc vod */ },
    },
    mobile: {
        live: { /* mobile live */ },
        vod: { /* mobile vod */ },
    },
    fullscreen: {
        live: { /* fullscreen live */ },
        vod: { /* fullscreen vod */ },
    },
    breakpoint: 768,
};

<VpePlayer layout={responsiveLayout} />;
```

### 레이아웃 실시간 변경
Ref 인스턴스에서 `layout(nextLayout, merge?)`를 호출하면 즉시 반영됩니다.

```ts
import { setup } from "@sgrsoft/vpe-react-sdk";

const instance = setup("#video", "YOUR_ACCESS_KEY", {
    options: {
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
    },
});

instance.layout({
    bottom: [{ key: "custom", items: ["SettingBtn"] }],
});
```

## 플레이어 메소드 (Ref)
`ref`로 전달되는 `PlayerHandle`에서 아래 메소드를 사용할 수 있습니다.

```tsx
import { useRef } from "react";
import { VpePlayer, type PlayerHandle } from "@sgrsoft/vpe-react-sdk";

export function App() {
    const playerRef = useRef<PlayerHandle>(null);

    return (
        <>
            <VpePlayer
                ref={playerRef}
                accessKey="YOUR_ACCESS_KEY"
                options={{
                    playlist: [
                        {
                            file: "https://CDN_DOMAIN/example_video_01.mp4"
                        }
                    ],
                }}
            />
            <button onClick={() => playerRef.current?.play()}>Play</button>
            <button onClick={() => playerRef.current?.pause()}>Pause</button>
        </>
    );
}
```

지원 메소드

| 메소드 | 시그니처 | 설명 |
| --- | --- | --- |
| play | `play()` | 재생 시작 |
| pause | `pause()` | 재생 일시정지 |
| mute | `mute()` | 음소거 |
| prev | `prev()` | 이전 플레이리스트 아이템 |
| next | `next()` | 다음 플레이리스트 아이템 |
| fullscreen | `fullscreen()` | 전체화면 토글 |
| fullscreenOn | `fullscreenOn()` | 전체화면 진입 |
| fullScreenOff | `fullScreenOff()` | 전체화면 종료 |
| pip | `pip()` | PIP 토글 |
| volume | `volume(vol?)` | 볼륨 설정 |
| uiHidden | `uiHidden()` | UI 숨김 |
| uiVisible | `uiVisible()` | UI 표시 |
| currentTime | `currentTime(time?)` | 현재 재생 시간 설정 |
| controlBarActive | `controlBarActive()` | 컨트롤바 활성화 |
| controlBarDeactive | `controlBarDeactive()` | 컨트롤바 비활성화 |
| tokenChange | `tokenChange(token)` | 토큰 변경 |
| layout | `layout(nextLayout, merge?)` | 레이아웃 변경 |
| changeUiMode | `changeUiMode(mode)` | UI 모드 변경 |
| changePlayMode | `changePlayMode(mode)` | 재생 모드 변경 |
| addNextSource | `addNextSource(source)` | 다음 영상 플레이리스트에 추가 |
| addPrevSource | `addPrevSource(source)` | 이전 영상 플레이리스트에 추가 |

### addNextSource / addPrevSource
현재 재생 중인 영상의 다음/이전 위치에 새로운 영상을 동적으로 추가합니다.

```tsx
// 단일 영상 추가
playerRef.current?.addNextSource({
    file: "https://CDN_DOMAIN/new_video.mp4",
    poster: "https://CDN_DOMAIN/new_poster.jpg",
});

// 여러 영상 동시 추가
playerRef.current?.addPrevSource([
    { file: "https://CDN_DOMAIN/video_1.mp4" },
    { file: "https://CDN_DOMAIN/video_2.mp4" },
]);
```

## 이벤트
`onEvent`로 이벤트를 수신합니다.

```tsx
<VpePlayer
    accessKey="YOUR_ACCESS_KEY"
    options={{
        playlist: [
            {
                file: "https://CDN_DOMAIN/example_video_01.mp4"
            }
        ],
    }}
    onEvent={(event) => {
        if (event.type === "error") {
            console.log(event.data);
        }
    }}
/>;
```

지원 이벤트

| 이벤트 | 설명 |
| --- | --- |
| stateChange | 플레이어 상태 스냅샷 변경 |
| ready | 초기화 완료 및 재생 준비 |
| play | 재생 시작 |
| pause | 재생 일시정지 |
| ended | 재생 종료 |
| fullscreen | 전체화면 진입 |
| fullscreenExit | 전체화면 종료 |
| loadingStart | 로딩 시작 |
| loadingEnd | 로딩 종료 |
| bufferingStart | 버퍼링 시작 |
| bufferingEnd | 버퍼링 종료 |
| seeking | 탐색 시작 |
| seeked | 탐색 종료 |
| waiting | 재생 대기 상태 |
| volumechange | 볼륨 변경 |
| timeupdate | 재생 시간 변경 |
| controlbarActive | 컨트롤바 활성화 |
| controlbarDeactive | 컨트롤바 비활성화 |
| next | 다음 아이템으로 이동 |
| prev | 이전 아이템으로 이동 |
| skipForward | 앞으로 건너뜀 |
| skipBack | 뒤로 건너뜀 |
| playlistChange | 플레이리스트 변경 |
| error | 오류 발생 |
| adStart | 광고 재생 시작 |
| adComplete | 광고 재생 완료 |
| adSkip | 사용자가 광고 스킵 |
| adError | 광고 로드/재생 에러 |

## One Click Multi DRM 예제
```ts
options={{
    aspectRatio: "16/9",
    autostart: true,
    muted: true,
    playlist: [
        {
            poster: "https://CDN_DOMAIN/example_poster.jpg",
            description: {
                title: "테스트 영상",
                created_at: "2024.07.13",
                profile_name: "VPE",
                profile_image: "https://CDN_DOMAIN/profile.png",
            },
            drm: {
                "com.widevine.alpha": {
                    src: "https://CDN_DOMAIN/manifest.mpd",
                    licenseUri: "https://multi-drm.apigw.ntruss.com/api/v1/license",
                    licenseRequestHeader: {
                        "x-ncp-region_code": "KR",
                        "x-ncp-iam-access-key": "YOUR_ACCESS_KEY",
                        "x-ncp-apigw-timestamp": 1770125949480,
                        "x-ncp-apigw-signature-v2": "YOUR_SIGNATURE",
                        "x-drm-token": "YOUR_DRM_TOKEN",
                    },
                },
                "com.microsoft.playready": {
                    src: "https://CDN_DOMAIN/manifest.mpd",
                    licenseUri: "https://multi-drm.apigw.ntruss.com/api/v1/license",
                    licenseRequestHeader: {
                        "x-ncp-region_code": "KR",
                        "x-ncp-iam-access-key": "YOUR_ACCESS_KEY",
                        "x-ncp-apigw-timestamp": 1770125949480,
                        "x-ncp-apigw-signature-v2": "YOUR_SIGNATURE",
                        "x-drm-token": "YOUR_DRM_TOKEN",
                    },
                },
                "com.apple.fps": {
                    src: "https://CDN_DOMAIN/index.m3u8",
                    certificateUri: "https://multi-drm.apigw.ntruss.com/api/v1/license/fairPlay",
                    certificateRequestHeader: {
                        "x-ncp-region_code": "KR",
                        "x-ncp-iam-access-key": "YOUR_ACCESS_KEY",
                        "x-ncp-apigw-timestamp": 1770125949480,
                        "x-ncp-apigw-signature-v2": "YOUR_SIGNATURE",
                        "x-drm-token": "YOUR_DRM_TOKEN",
                        "accept": "application/json",
                    },
                    licenseUri: "https://multi-drm.apigw.ntruss.com/api/v1/license",
                    licenseRequestHeader: {
                        "x-ncp-region_code": "KR",
                        "x-ncp-iam-access-key": "YOUR_ACCESS_KEY",
                        "x-ncp-apigw-timestamp": 1770125949481,
                        "x-ncp-apigw-signature-v2": "YOUR_SIGNATURE",
                        "x-drm-token": "YOUR_DRM_TOKEN",
                    },
                },
            },
        },
    ],
}}
```

## Pre-roll 광고 (Google IMA)

Google IMA SDK를 사용한 Pre-roll 영상 광고를 지원합니다.
`options.ads.tagUrl`에 VAST 태그 URL을 전달하면 콘텐츠 재생 전에 광고가 자동으로 재생됩니다.

- Google IMA SDK는 외부에서 자동 로드됩니다 (번들에 포함되지 않음)
- 광고 재생 중 콘텐츠 비디오는 자동으로 일시정지되고, 컨트롤바가 숨겨집니다
- 광고 완료/스킵/에러 후 콘텐츠가 자동 재생됩니다
- `ads` 옵션이 없으면 기존과 동일하게 동작합니다

### React (ESM) 예제

```tsx
"use client";

import Hls from "hls.js";
import { VpePlayer } from "@sgrsoft/vpe-react-sdk";

export default function AdPlayer() {
    return (
        <VpePlayer
            accessKey="YOUR_ACCESS_KEY"
            hls={Hls}
            options={{
                playlist: [
                    {
                        file: "https://CDN_DOMAIN/example.m3u8",
                        poster: "https://CDN_DOMAIN/poster.jpg",
                    },
                ],
                autostart: true,
                muted: true,
                ads: {
                    tagUrl: "https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/single_preroll_skippable&sz=640x480&ciu_szs=300x250%2C728x90&gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&impl=s&correlator=",
                },
            }}
            onEvent={(event) => {
                switch (event.type) {
                    case "adStart":
                        console.log("광고 시작");
                        break;
                    case "adComplete":
                        console.log("광고 완료");
                        break;
                    case "adSkip":
                        console.log("광고 스킵");
                        break;
                    case "adError":
                        console.log("광고 에러", event.data?.message);
                        break;
                }
            }}
        />
    );
}
```

### UMD 예제

```html
<script src="https://player.vpe.naverncp.com/v2/ncplayer.js?access_key=YOUR_ACCESS_KEY"></script>
<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>

<div id="video"></div>

<script>
    const player = new ncplayer("video", {
        playlist: [
            { file: "https://CDN_DOMAIN/example.m3u8" },
        ],
        autostart: true,
        muted: true,
        ads: {
            tagUrl: "https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/single_preroll_skippable&sz=640x480&ciu_szs=300x250%2C728x90&gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&impl=s&correlator=",
        },
    });

    player.on("adStart", function(e) { console.log("광고 시작"); });
    player.on("adComplete", function(e) { console.log("광고 완료"); });
    player.on("adSkip", function(e) { console.log("광고 스킵"); });
    player.on("adError", function(e) { console.log("광고 에러", e.data?.message); });
</script>
```

### 광고 이벤트

| 이벤트 | 설명 |
| --- | --- |
| adStart | 광고 재생 시작 |
| adComplete | 광고 재생 완료 |
| adSkip | 사용자가 광고 스킵 |
| adError | 광고 로드/재생 에러 (`event.data.message`에 상세 메시지) |