HLS와 WebRTC

HLS와 WebRTC, 실시간 CCTV 위젯에서는 무엇을 선택해야 할까?

대시보드에 실시간 CCTV 영상 위젯을 만드는 기능을 개발하면서 HLS를 사용한 적이 있다. 처음에는 CCTV 영상을 화면에 보여주는 작업이라 단순히 영상 URL을 받아 video 태그에 연결하면 될 것이라고 생각했다. 하지만 실제로 구현해보니 생각보다 고려할 부분이 많았다.

브라우저에서 HLS를 바로 재생할 수 있는지, CCTV URL이 바뀌었을 때 기존 플레이어를 어떻게 정리할지, 단일 영상과 여러 영상을 보여주는 화면을 어떻게 나눌지, 라이브 영상의 지연 시간과 안정성 사이에서 어떤 값을 선택할지 등을 함께 봐야 했다.

이 과정에서 자연스럽게 HLS와 WebRTC의 차이도 궁금해졌다. 둘 다 웹에서 영상을 다룰 때 자주 언급되는 기술이지만, 실제로는 목적이 다르다. HLS는 안정적으로 영상을 전달하는 데 강점이 있고, WebRTC는 지연 시간을 줄여 실시간으로 주고받는 데 강점이 있다.

이번 글에서는 HLS와 WebRTC가 각각 어떤 방식으로 동작하는지, 그리고 대시보드 CCTV 위젯 같은 상황에서는 어떤 기준으로 선택하면 좋을지 정리해보려고 한다.

HLS란?

HLS는 HTTP Live Streaming의 약자다. 이름 그대로 HTTP 기반으로 영상을 전달하는 스트리밍 방식이다.

HLS는 하나의 긴 영상을 그대로 보내는 것이 아니라, 영상을 작은 조각으로 나눈 뒤 클라이언트가 그 조각들을 순서대로 받아 재생한다. 이때 어떤 조각을 어떤 순서로 받아야 하는지 알려주는 파일이 .m3u8 플레이리스트다.

흐름을 단순하게 보면 아래와 같다.

원본 영상
  ↓
작은 세그먼트로 분할
  ↓
.m3u8 플레이리스트 생성
  ↓
브라우저에서 세그먼트를 순서대로 받아 재생

웹에서 HLS를 사용할 때는 보통 hls.js 같은 라이브러리를 사용한다. 내가 구현한 CCTV 위젯에서도 브라우저에서 HLS를 재생하기 위해 hls.js를 사용했다.

import Hls from 'hls.js';

const videoRef = ref<HTMLVideoElement | null>(null);
let hls: Hls | null = null;

const initPlayer = (url: string) => {
  if (!url) return;

  const video = videoRef.value as HTMLVideoElement;

  if (Hls.isSupported()) {
    if (hls) {
      hls.destroy();
      hls = null;
    }

    hls = new Hls({
      liveSyncDuration: 30,
      maxBufferLength: 60,
      maxMaxBufferLength: 120,
    });

    hls.attachMedia(video);
    hls.loadSource(url);

    hls.on(Hls.Events.MANIFEST_PARSED, () => {
      video.play();
    });
  } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
    video.src = url;
    video.addEventListener('loadedmetadata', () => {
      video.play();
    });
  }
};

코드만 보면 단순히 HLS 소스를 로드하고 재생하는 것처럼 보이지만, 실제로는 몇 가지를 신경 써야 했다.

첫 번째는 브라우저 지원 여부였다. 모든 브라우저가 HLS를 같은 방식으로 지원하지 않기 때문에, 먼저 Hls.isSupported()hls.js 사용 가능 여부를 확인했다. 지원되는 환경에서는 hls.js를 통해 .m3u8 스트림을 video 태그에 연결했고, 네이티브 HLS를 지원하는 환경에서는 video.canPlayType('application/vnd.apple.mpegurl') 조건으로 직접 재생할 수 있도록 분기했다.

두 번째는 CCTV URL이 바뀌는 상황이었다. 대시보드 위젯에서는 사용자가 다른 CCTV를 선택할 수 있기 때문에, 처음 한 번 연결한 스트림만 계속 재생하는 구조로는 부족했다. 그래서 props.cctvurl 변경을 감지하고, URL이 바뀔 때마다 플레이어를 다시 초기화하도록 구성했다.

watch(
  () => props.cctvurl,
  async (newUrl) => {
    await nextTick();
    if (videoRef.value) initPlayer(newUrl);
  }
);

여기서 중요한 점은 기존 HLS 인스턴스를 정리하는 것이었다. 새로운 CCTV 스트림을 연결하기 전에 기존 hls 인스턴스가 남아 있으면 이전 스트림 연결이 유지되거나 불필요한 리소스를 계속 사용할 수 있다. 그래서 새 인스턴스를 만들기 전에 기존 인스턴스가 있다면 destroy()로 정리한 뒤 다시 생성하도록 했다.

세 번째는 라이브 버퍼 설정이었다.

hls = new Hls({
  liveSyncDuration: 30,
  maxBufferLength: 60,
  maxMaxBufferLength: 120,
});

실시간 CCTV라고 해서 무조건 지연 시간을 최소화하는 것만 중요한 것은 아니었다. 대시보드 관제 화면에서는 영상이 너무 자주 끊기지 않고 안정적으로 재생되는 것도 중요했다. 그래서 라이브 버퍼 목표 시간과 최대 버퍼 길이를 설정해, 실시간성과 재생 안정성 사이에서 균형을 맞추려고 했다.




WebRTC란?

HLS를 사용하면서 같이 비교해보게 된 기술이 WebRTC였다. 직접 프로젝트에 적용해본 것은 아니지만, 영상 스트리밍 방식을 찾아보다 보면 HLS와 함께 자주 언급되는 기술이라 차이를 정리해보고 싶었다. WebRT는 Web Real-Time Communication의 약자다. 브라우저에서 별도 플러그인 없이 오디오, 비디오, 데이터를 실시간으로 주고받기 위한 기술이다. HLS처럼 서버에 있는 영상 조각을 받아 재생하는 방식이라기보다, 연결을 맺은 뒤 미디어 스트림을 실시간으로 주고받는 방식에 가깝다.

그래서 WebRTC는 화상회의, 음성 통화, 원격 제어처럼 지연 시간이 짧아야 하는 서비스에서 많이 사용된다. 예를 들어 사용자의 카메라 영상을 가져오는 기본 코드는 아래처럼 작성할 수 있다.

const stream = await navigator.mediaDevices.getUserMedia({
  video: true,
  audio: true,
});

const video = document.querySelector('video') as HTMLVideoElement;
video.srcObject = stream;

다만 실제 서비스에서 WebRTC를 사용하려면 이 코드만으로 끝나지는 않는다. 서로 연결 정보를 주고받는 signaling 과정이 필요하고, 네트워크 환경에 따라 STUN, TURN 서버를 사용해야 할 수도 있다. 여러 사용자가 동시에 연결되는 구조라면 SFU 같은 미디어 서버도 고려해야 한다.

정리하면 WebRTC는 지연 시간을 줄이는 데 강점이 있지만, 그만큼 연결 관리나 서버 구성 측면에서 고려할 부분도 많아 보였다. 그래서 단순히 실시간 영상이면 WebRTC가 더 좋다라고 보기보다는, 서비스에서 어느 정도의 실시간성이 필요한지를 먼저 봐야 한다고 느꼈다.




HLS와 WebRTC 비교

두 기술을 비교할 때 가장 먼저 봐야 할 기준은 지연 시간이 얼마나 중요한지라고 생각했다.

HLS는 HTTP 기반으로 동작하고, 영상을 작은 조각으로 나누어 받아 재생하는 방식이다. 그래서 기존 웹 서버나 CDN과 잘 맞고, 여러 사용자가 안정적으로 영상을 보는 상황에 적합하다. 대신 구조적으로 어느 정도의 지연 시간은 발생할 수 있다.

반대로 WebRTC는 연결을 맺은 뒤 미디어 스트림을 실시간으로 주고받는 방식이라 지연 시간을 줄이는 데 강점이 있다. 다만 signaling, STUN/TURN, 미디어 서버 같은 요소를 함께 고려해야 해서 HLS보다 구현과 운영이 복잡할 수 있다.

두 방식의 차이를 간단히 정리하면 아래처럼 볼 수 있다.

기준HLSWebRTC
목적안정적인 영상 전달지연 시간을 줄인 실시간 통신
방식영상을 조각으로 나누어 HTTP로 재생연결을 맺고 미디어 스트림을 실시간 전송
지연 시간상대적으로 있음낮음
구현 난이도비교적 단순상대적으로 복잡
확장성CDN 활용이 쉬움대규모 확장 시 별도 미디어 서버 고려 필요
적합한 사례라이브 방송, VOD, CCTV 모니터링화상회의, 원격 제어, 양방향 영상 통신



CCTV 위젯에서는 왜 HLS를 사용했을까?

내가 개발했던 CCTV 위젯은 사용자가 대시보드에서 영상을 확인하는 용도에 가까웠다. 영상을 보면서 카메라와 양방향으로 통신하거나, 화면에서 즉시 장비를 제어하는 구조는 아니었다. 여러 위젯이 함께 있는 대시보드 안에서 CCTV 영상을 안정적으로 보여주는 것이 더 중요한 상황이었다.

이 기준으로 보면 HLS가 더 잘 맞는 방식이라고 생각했다. 지연 시간을 최대한 줄이는 것보다, 브라우저에서 안정적으로 재생되고 기존 웹 구조 안에서 비교적 단순하게 다룰 수 있는 점이 더 중요했기 때문이다.

실제 구현에서도 단순히 영상 URL을 연결하는 것에서 끝나지는 않았다. CCTV URL이 바뀌면 새로운 스트림을 다시 연결해야 했고, 단일 화면과 멀티 화면도 나누어 보여줘야 했다. 영상이 없는 경우에는 빈 상태도 필요했다. 이런 흐름을 처리할 때 hls.js를 사용하면 스트림 연결, 재초기화, 브라우저 지원 분기를 비교적 일관된 방식으로 가져갈 수 있었다.

물론 CCTV라고 해서 항상 HLS가 맞다고 생각하지는 않는다. 만약 영상을 보면서 장비를 바로 제어해야 하거나, 현장 상황이 거의 즉시 반영되어야 하는 서비스라면 WebRTC를 검토해야 할 것 같다. 몇 초의 지연도 문제가 되는 상황이라면 안정적인 재생보다 낮은 지연 시간이 더 중요한 기준이 될 수 있기 때문이다.

결론적으로 단순히 현재 상황을 모니터링하는 정도라면 HLS로도 충분할 수 있고, 즉각적인 상호작용이 필요하다면 WebRTC가 더 적합해 보인다고 생각했다.




실무에서 선택할 때 보는 기준

이번에 HLS를 사용하고 WebRTC와 비교해보면서, 영상 스트리밍 방식을 선택할 때는 몇 가지 기준을 먼저 봐야겠다고 생각했다.

1️⃣ 지연시간이 얼마나 중요한가?

몇 초 정도의 지연이 허용되는 화면이다? HLS
사용자의 행동이나 현장 상황이 거의 바로 반영되어야 한다? WebRTC

2️⃣ 양방향 통신이 필요한가?

사용자가 영상을 보기만 하는 구조이다? HLS
서로 영상을 주고받거나, 사용자의 입력이 영상 흐름에 바로 영향을 주는 구조이다? WebRTC

3️⃣ 같은 영상을 얼마나 많은 사용자가 보는가?

여러 사용자가 같은 영상을 안정적으로 봐야 한다? HLS
왜냐하면 HTTP 기반으로 동작하고 CDN을 활용하기 쉬워 확장성 측면에서 유리하기 때문이다. 반면 WebRTC는 낮은 지연 시간을 얻을 수 있지만, 사용자가 많아질수록 미디어 서버나 연결 구조를 더 신경 써야 할 것 같다.

4️⃣ 운영 복잡도

HLS는 상대적으로 단순하게 구성 가능.
WebRTC는 signaling, STUN/TURN, 연결 상태 관리 같은 요소를 함께 봐야 하기 때문에, 초저지연이 꼭 필요한 서비스인지 먼저 판단하는 것이 중요하다고 느꼈다.

0개의 댓글