레이아웃 시스템

VPE 2.0의 레이아웃 시스템은 JSON으로 컨트롤바, 버튼 배치, 영역 구성을 선언적으로 정의합니다. 화면 환경별(PC/모바일/전체화면)과 콘텐츠 유형별(VOD/Live)을 각각 분리해 관리할 수 있습니다.

레이아웃 타입

layout 옵션은 다음 3가지 형태로 전달할 수 있습니다.

// 1) 단일 레이아웃 — 단순 케이스 (라이브/VOD/디바이스 분기 없이 하나의 컨트롤바 구성)
type ControlBarLayout = {
    order?: string[];
    top?: Group[];
    upper?: Group[];
    center?: Group[];
    lower?: Group[];
    bottom?: Group[];
};

// 2) 라이브 / VOD 분기
type ControlBarLayoutVariant = {
    live: ControlBarLayout;
    vod: ControlBarLayout;
};

// 3) 반응형 (PC / Mobile / Fullscreen 분기)
type ControlBarLayoutResponsive = {
    pc: { live: ControlBarLayout; vod: ControlBarLayout };
    mobile: { live: ControlBarLayout; vod: ControlBarLayout };
    fullscreen: { live: ControlBarLayout; vod: ControlBarLayout };
    breakpoint?: number; // 기본 768
};

구성 구조

반응형 레이아웃의 최상위 키는 pc, mobile, fullscreen으로 나뉘며, 각 환경 내부에 vod live 레이아웃을 정의합니다.

layout.json
{
    "pc": {
        "vod": { ... },
        "live": { ... }
    },
    "mobile": {
        "vod": { ... },
        "live": { ... }
    },
    "fullscreen": {
        "vod": { ... },
        "live": { ... }
    }
}

섹션과 순서

order 배열이 렌더링 순서를 결정합니다. 기본 섹션은 top, upper, center, lower, bottom입니다.

pc.vod
{
    "order": ["top", "upper", "center", "lower", "bottom"],
    "top": [
        { "items": ["MetaDesc"] },
        { "wrapper": "Blank", "items": [], "align": "left" },
        { "items": ["ShareBtn"] }
    ],
    "center": [
        { "items": ["BigPlayBtn"], "align": "center" }
    ],
    "bottom": [
        { "items": ["ProgressBar"] },
        { "items": ["PlayBtn", "VolumeBtn", "TimeDisplay"], "align": "left" },
        { "items": ["SettingBtn", "PIPBtn", "FullscreenBtn"], "align": "right" }
    ]
}

행(Row) 구성

각 섹션은 여러 개의 Row(=Group)로 구성됩니다. Row는 items 배열을 통해 버튼이나 컴포넌트를 배치하고, align wrapper로 정렬과 묶음 방식을 조정합니다.

타입설명
keystringGroup 식별자. 런타임 player.layout() 머지 시 같은 key의 Group끼리 교체됩니다. (권장)
items(string | HTMLString)[]Row에 배치할 컨트롤 목록 (빌트인 키 또는 HTML 문자열)
alignleft | center | rightRow 내 컨텐츠 정렬 방식
wrapper"Group" | "Blank"Row에 적용할 래퍼 타입 (Group=묶음, Blank=빈 공간/간격)
capnumberRow 내 표시 아이템 수 제한
noPaddingbooleanGroup 좌우 패딩 제거

기본 레이아웃 예제

각 Group에 key를 지정하면 런타임 머지 시 특정 Group만 안전하게 교체할 수 있습니다.

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" },
    ],
};

const player = new ncplayer("video", {
    playlist: [{ file: "https://example.com/master.m3u8" }],
    layout,
});

사용 가능한 레이아웃 아이템

PlayBtnVolumeBtnTimeBtnSubtitleBtnFullscreenBtnSettingBtnPipBtnMetaDescBigPlayBtnSeekBarSettingModalDurationBtnSkipForwardBtnSkipBackBtnCurrentTimeBtnMuteBtnPrevBtnNextBtnNextPrevBtnShareBtnBlank

UMD / HTML 적용

UMD 환경에서는 레이아웃 JSON을 layout 옵션으로 전달합니다. 커스텀 HTML 요소를 items 배열에 문자열로 직접 삽입할 수 있습니다.

// 커스텀 HTML 요소 정의
const Logo = `
    <div style="padding:0 15px;">
        <a href="https://www.ncloud.com" target="_blank">
            <img src="https://player.vpe.naverncp.com/images/ncp-logo-white.webp" style="height:24px;"/>
        </a>
    </div>`;

const vpeLayout = {
    pc: {
        vod: {
            order: ["top", "upper", "center", "seekbar", "lower", "bottom"],
            top: [{ items: ["MetaDesc"] }],
            upper: [],
            center: [{ items: ["BigPlayBtn"], align: "center" }],
            lower: [],
            bottom: [
                { items: ["PlayBtn", "PrevBtn", "NextBtn"], wrapper: "Group" },
                { items: ["VolumeBtn"], wrapper: "Group" },
                { items: ["TimeBtn"], wrapper: "Group" },
                { wrapper: "Blank", items: [] },
                { items: [Logo], wrapper: "Group" },
                { items: ["SubtitleBtn", "PipBtn", "SettingBtn", "FullscreenBtn"], cap: 2, wrapper: "Group" },
            ],
        },
    },
};

const player = new ncplayer("video", {
    playlist: [
        {
            file: "https://CDN_DOMAIN/example.m3u8",
            poster: "https://CDN_DOMAIN/poster.jpg",
        },
    ],
    layout: vpeLayout,
});

런타임 레이아웃 변경

player.layout(nextLayout, merge?) 메서드를 사용하면 재생 중에도 레이아웃을 동적으로 변경할 수 있습니다. 두 번째 인자 merge의 기본값은 true입니다.

머지 규칙 (merge: true)

  1. top/upper/center/lower/bottom/order 중 next에 정의되지 않은 section은 base 유지
  2. section 내 Group 머지는 key 기반:
    • base와 next에 같은 key의 Group → next로 교체
    • base에만 있는 key → base 유지
    • next에만 있는 key → 결과 끝에 append
    • next에 key 없는 Group → 결과 끝에 append (신규 추가로 간주)
  3. 양쪽 Group 모두 key가 없으면 → section 통째 교체 (하위 호환)

예시 — 특정 Group만 교체

// base
// bottom: [
//   { key: "play", items: ["PlayBtn"] },
//   { key: "volume", items: ["VolumeBtn"] },
//   { key: "time", items: ["TimeBtn"] },
//   { key: "blank", wrapper: "Blank", items: [] },
//   { key: "right", items: ["SubtitleBtn", "FullscreenBtn"] },
// ]

player.layout({
    bottom: [{ key: "right", items: ["SubtitleBtn", "PipBtn", "SettingBtn", "FullscreenBtn"] }],
});
// 결과: play/volume/time/blank 유지, right만 교체

예시 — 새 Group 추가

player.layout({
    bottom: [{ key: "extra", items: ["ShareBtn"] }],
});
// 결과: 기존 5개 Group + 끝에 extra(ShareBtn) append

예시 — section 전체 교체 (merge: false)

player.layout(
    { bottom: [{ items: ["PlayBtn"] }] },
    false, // 통째로 교체
);

전체 HTML 예제

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>VPE Player - 레이아웃 시스템</title>
    <!-- hls.js / dash.js 를 VPE 스크립트보다 먼저 로드 -->
    <script src="https://player.vpe.naverncp.com/lib/js/hls.min.js"></script>
    <script src="https://player.vpe.naverncp.com/lib/js/dash.all.min.js"></script>
    <script src="https://player.vpe.naverncp.com/v2/ncplayer.js?access_key=YOUR_ACCESS_KEY"></script>
</head>
<body>
    <div id="video" style="max-width: 800px; margin: 0 auto;"></div>
    <button id="btn-layout1">Layout 1</button>
    <button id="btn-layout2">Layout 2</button>

    <script>
        var Logo = '<div style="padding:0 15px;"><a href="https://www.ncloud.com" target="_blank"><img src="https://player.vpe.naverncp.com/images/ncp-logo-white.webp" style="height:24px;"/></a></div>';

        var layout1 = {
            pc: {
                vod: {
                    order: ["top", "center", "bottom"],
                    top: [{ items: ["MetaDesc"] }],
                    center: [{ items: ["BigPlayBtn"], align: "center" }],
                    bottom: [
                        { items: ["PlayBtn", "VolumeBtn", "TimeBtn"], wrapper: "Group" },
                        { wrapper: "Blank", items: [] },
                        { items: [Logo], wrapper: "Group" },
                        { items: ["SettingBtn", "FullscreenBtn"], wrapper: "Group" },
                    ],
                },
            },
        };

        var layout2 = {
            pc: {
                vod: {
                    order: ["center", "bottom"],
                    center: [{ items: ["BigPlayBtn"], align: "center" }],
                    bottom: [
                        { items: ["PlayBtn", "VolumeBtn"], wrapper: "Group" },
                        { wrapper: "Blank", items: [] },
                        { items: ["FullscreenBtn"], wrapper: "Group" },
                    ],
                },
            },
        };

        var player = new ncplayer("video", {
            playlist: [
                {
                    file: "https://CDN_DOMAIN/example.m3u8",
                    poster: "https://CDN_DOMAIN/poster.jpg",
                },
            ],
            autostart: true,
            muted: true,
            layout: layout1,
        });

        document.getElementById("btn-layout1").addEventListener("click", function() {
            player.layout(layout1);
        });
        document.getElementById("btn-layout2").addEventListener("click", function() {
            player.layout(layout2);
        });
    </script>
</body>
</html>

UI Editor 활용

레이아웃 JSON은 UI Editor에서 시각적으로 편집하고 결과 JSON을 그대로 적용할 수 있습니다.

UMD