Player Demo

VPE SDK를 활용한 다양한 플레이어 UI 데모를 확인하세요.

네이버클라우드 테스트 영상입니다

네이버클라우드

2026.01.30

추천 영상

YouTube 스타일 데모

메인 영상과 추천 영상 목록을 사이드바로 구성한 YouTube 스타일의 플레이어 데모입니다. 채널 정보, 좋아요/공유/저장 버튼 등 UGC 플랫폼에서 자주 사용하는 UI 패턴을 보여줍니다.

VpePlayerHLS.jsPlaylistResponsive Layout

예제 코드

React + TailwindCSS · UI 구성 예시
<div className="flex flex-col lg:flex-row gap-5">
  {/* Main player */}
  <div className="flex-1 min-w-0">
    <div className="rounded-2xl overflow-hidden">
      <VpePlayer
        key={`yt-${currentIdx}`}
        hls={Hls}
        accessKey="YOUR_ACCESS_KEY"
        options={{
          playlist,
          autostart: true,
          muted: true,
          aspectRatio: "16/9",
          controls: true,
        }}
        layout={youtubeLayout}
      />
    </div>

    {/* Video info card */}
    <div className="p-5 bg-neutral-900 rounded-2xl mt-5 flex justify-between">
      <div>
        <h2 className="text-white text-lg font-bold">
          {playlist[currentIdx].description.title}
        </h2>
        <div className="flex items-center gap-3 mt-3">
          <img
            src={playlist[currentIdx].description.profile_image}
            alt=""
            className="w-10 h-10 rounded-full object-cover"
          />
          <div>
            <p className="text-white text-sm font-semibold">
              {playlist[currentIdx].description.profile_name}
            </p>
            <p className="text-neutral-500 text-xs">
              {playlist[currentIdx].description.created_at}
            </p>
          </div>
        </div>
      </div>

      {/* Action buttons */}
      <div className="flex gap-2">
        {["좋아요", "공유", "저장"].map((label) => (
          <button
            key={label}
            type="button"
            className="px-4 py-2 bg-neutral-800 hover:bg-neutral-700 text-white text-xs font-semibold rounded-full transition-colors"
          >
            {label}
          </button>
        ))}
      </div>
    </div>
  </div>

  {/* Sidebar: recommended videos */}
  <aside className="lg:w-80 shrink-0 bg-neutral-900 overflow-y-auto rounded-2xl max-h-[645px]">
    <div className="p-4 border-b border-neutral-800">
      <h3 className="text-white font-bold text-sm">추천 영상</h3>
    </div>
    <div className="p-2 space-y-2">
      {playlist.map((v, i) => (
        <button
          key={i}
          type="button"
          onClick={() => setCurrentIdx(i)}
          className={`w-full flex items-start gap-2.5 p-2 rounded-lg text-left transition-colors cursor-pointer ${
            i === currentIdx ? "bg-neutral-800" : "hover:bg-neutral-800/50"
          }`}
        >
          <div className="relative w-40 shrink-0">
            <img src={v.poster} alt="" className="w-full rounded-lg aspect-video object-cover" />
            {i === currentIdx && (
              <span className="absolute inset-0 flex items-center justify-center bg-black/50 rounded-lg text-white text-xs font-bold">
                NOW
              </span>
            )}
          </div>
          <div className="min-w-0 flex-1 pt-0.5">
            <p className={`text-sm font-medium line-clamp-2 ${
              i === currentIdx ? "text-blue-400" : "text-neutral-200"
            }`}>
              {v.description.title}
            </p>
            <p className="text-xs text-neutral-500 mt-1">{v.description.profile_name}</p>
            <p className="text-xs text-neutral-500">{v.description.created_at}</p>
          </div>
        </button>
      ))}
    </div>
  </aside>
</div>