웹에서 HEVC with Alpha 영상을 사용할 때 고려해야 할 것들

웹 페이지에 투명 배경을 가진 3D 애니메이션을 넣어야 하는 상황이 있었다.

영상은 HEVC with Alpha로 만들어졌고, 약 20초 길이에 4MB, 8MB, 10MB 세 가지 용량으로 제공됐다. 모바일용 영상의 해상도는 가로 1280px 정도였다.

처음에는 단순히 이런 고민이었다.

4MB, 8MB, 10MB 중 웹에서 사용하기 적절한 용량은 어느 정도일까?

그런데 조금 파고들어 보니 영상 파일의 용량만 비교해서 결정할 문제는 아니었다.

네트워크, 비트레이트, 브라우저의 Range Request, 디코딩, 메모리, GPU 사용량까지 서로 다른 문제를 구분해서 볼 필요가 있었다.

파일 용량보다 먼저 봐야 하는 비트레이트

20초짜리 영상의 파일 크기가 각각 다음과 같다고 해보자.

  • 4MB
  • 8MB
  • 10MB

대략적인 평균 비트레이트는 다음 공식으로 구할 수 있다.

파일 크기(MB) × 8 ÷ 영상 길이(초)

따라서 20초 영상이라면:

파일 크기평균 비트레이트
4MB약 1.6 Mbps
8MB약 3.2 Mbps
10MB약 4 Mbps

여기서 Mbps는 Megabits per second, 즉 초당 몇 메가비트의 데이터를 소비하는지를 의미한다.

예를 들어 10MB 영상의 평균 비트레이트가 4Mbps라면, 영상을 실시간으로 끊김 없이 공급하려면 평균적으로 초당 약 4Mb 이상의 데이터를 받아올 수 있어야 한다.

물론 실제 스트리밍은 버퍼가 있기 때문에 정확히 4Mbps를 항상 유지해야 한다는 뜻은 아니다.

예를 들어 인터넷 다운로드 속도가 안정적으로 9Mbps라면 10MB 영상 전체의 데이터 크기는 약 80Mb이므로:

80Mb ÷ 9Mbps ≈ 8.9초

정도면 전체 데이터를 받을 수 있다.

영상은 20초이기 때문에 네트워크가 재생 속도보다 상당히 빠른 셈이다.

따라서 CDN까지 사용한다면 20초짜리 10MB 영상 자체가 지나치게 큰 파일이라고 보기는 어렵다.

하지만 여기서 중요한 것은 평균 인터넷 속도만 보고 판단하면 안 된다는 것이다.

모바일 환경에서는 이동 중이거나 지하철, 건물 내부, 혼잡한 기지국 환경 등에서 순간적인 네트워크 속도가 크게 떨어질 수 있다.

그래서 글로벌 웹 서비스를 대상으로 한다면 4MB나 8MB처럼 조금 더 보수적인 선택이 안정성 측면에서는 유리하다.

CDN이 모든 네트워크 문제를 해결해주지는 않는다

영상 파일을 AWS S3에 올리고 CloudFront 같은 CDN을 연결하면 사용자가 한국에 있든 미국에 있든 가까운 CDN Edge에서 데이터를 받을 수 있다.

이는 상당히 중요한 최적화다.

사용자가 한국에 있는데 미국 서버까지 요청하거나, 반대로 미국 사용자가 한국 서버까지 요청하는 것보다 RTT와 TTFB를 크게 줄일 수 있다.

하지만 CDN이 사용자의 마지막 네트워크 구간까지 빠르게 만들어주는 것은 아니다.

사용자가 실제로 3Mbps LTE 환경에 있다면 CDN을 사용한다고 해서 그 연결이 갑자기 30Mbps가 되지는 않는다.

즉 CDN은:

  • 물리적 서버 거리
  • 국제망 구간
  • 서버 응답 지연
  • 캐시 적중률

같은 문제를 개선하지만,

사용자의 실제 모바일 네트워크 품질 자체까지 해결하는 것은 아니다.

웹 브라우저는 8MB 파일을 처음부터 전부 받을까?

웹의 <video> 요소는 서버가 HTTP Range Request를 지원한다면 영상 파일의 특정 구간만 요청할 수 있다.

브라우저 요청에는 다음과 같은 헤더가 들어갈 수 있다.

1
Range: bytes=0-1048575

서버나 CDN이 이를 지원하면:

1
2
206 Partial Content
Content-Range: bytes 0-1048575/8388608

같은 응답을 받을 수 있다.

AWS S3와 CloudFront는 이러한 Range 요청을 지원하기 때문에 일반적인 구성에서는 별도의 스트리밍 서버 없이도 영상 데이터를 필요한 범위만 전달할 수 있다.

다만 206 Partial Content라는 응답 코드만 보고 브라우저가 항상 작은 조각만 다운로드한다고 단정해서는 안 된다.

예를 들어 다음 요청도 가능하다.

1
Range: bytes=0-

이는 0바이트부터 파일 끝까지 요청한다는 의미이므로 결과적으로 전체 파일을 전달할 수도 있다.

따라서 실제 동작을 확인하려면 Chrome DevTools의 Network 탭에서 다음을 확인하는 것이 좋다.

  • Request의 Range
  • Response의 Content-Range
  • Transferred 크기
  • 추가 Range 요청 발생 여부

다운로드된 영상은 네트워크가 끊겨도 재생될까?

가능하다.

비트레이트는 네트워크가 반드시 계속 연결되어 있어야 한다는 의미가 아니다.

브라우저가 이미 영상 데이터를 충분히 받아두었다면 네트워크가 끊겨도 해당 버퍼 범위까지는 계속 재생할 수 있다.

개념적으로 보면 구조는 다음과 같다.

Network → Media Buffer → Decoder → Frame → 화면

네트워크는 버퍼를 채우고, 디코더는 그 버퍼의 데이터를 계속 소비한다.

따라서 8MB 전체가 이미 다운로드되어 있다면 인터넷 연결이 끊겨도 20초 영상을 끝까지 재생할 수 있다.

반대로 4MB 정도만 받아둔 상태에서 인터넷이 끊기면 해당 데이터로 재생 가능한 구간까지만 재생되고 이후에는 멈출 수 있다.

8MB 영상은 메모리에서도 8MB일까?

여기서 가장 많이 헷갈릴 수 있는 부분이 있다.

영상 파일 크기와 재생 중 메모리 사용량은 전혀 다른 값이다.

HEVC 영상의 8MB는 강하게 압축된 데이터 크기다.

브라우저가 이를 화면에 보여주려면 압축된 데이터를 실제 픽셀 정보로 복원해야 한다.

이 과정을 디코딩(decoding) 이라고 한다.

예를 들어 1280×720 RGBA 한 프레임을 단순 계산하면:

1280 × 720 × 4 bytes ≈ 3.5MB

정도다.

즉 8MB짜리 영상 전체보다 디코딩된 몇 장의 프레임이 더 많은 메모리를 사용할 수도 있다.

물론 브라우저가 20초 영상 전체를 한 번에 프레임으로 변환해서 메모리에 올려두는 것은 아니다.

대략 다음과 같이 동작한다.

압축 데이터 읽기 → 필요한 프레임 디코딩 → 몇 개 프레임 버퍼 유지 → 화면 표시 → 오래된 프레임 정리

HEVC는 프레임 간 압축을 적극적으로 사용하기 때문에 특정 프레임을 만들기 위해 이전이나 이후의 Reference Frame을 함께 보관해야 할 수도 있다.

따라서 실제 메모리에는:

  • 압축 데이터 버퍼
  • 디코딩된 프레임
  • Reference Frame
  • Decoder 내부 버퍼
  • GPU Texture

등이 동시에 존재할 수 있다.

그렇다면 8MB를 4MB로 줄이면 메모리도 절반이 될까?

대부분 그렇지 않다.

예를 들어 다음 두 영상이 있다고 하자.

  • 1280×720 / 30fps / 4MB
  • 1280×720 / 30fps / 8MB

파일 크기는 두 배 차이가 난다.

하지만 디코딩한 뒤에는 둘 다 결국 1280×720 크기의 프레임이 만들어진다.

따라서 재생 중 디코딩 프레임이나 GPU Texture가 사용하는 메모리는 큰 차이가 나지 않을 수 있다.

4MB 영상의 주요 장점은 오히려:

  • 다운로드 시간 감소
  • 네트워크 트래픽 감소
  • 버퍼를 더 빠르게 확보
  • 데이터 사용량 감소

쪽에 있다.

메모리를 줄이는 것이 목적이라면 파일 압축률보다 다음 요소가 더 중요하다.

동시 재생 영상 수 > 해상도 > FPS > 파일 용량

HEVC with Alpha는 조금 더 까다롭다

이번 영상은 일반적인 불투명 영상이 아니라 배경이 투명한 HEVC with Alpha 영상이었다.

일반 영상은 디코딩된 프레임을 그대로 화면에 표시하면 되지만, Alpha 영상은 각 픽셀의 투명도를 적용해서 뒤에 있는 웹 페이지와 합성해야 한다.

즉:

HEVC Decode → Alpha 처리 → 배경과 Compositing → GPU Rendering

이라는 추가 과정이 들어간다.

특히 3D 애니메이션처럼:

  • 반투명 영역
  • 그림자
  • 글로우
  • 복잡한 움직임

등이 많은 경우 GPU 합성 부담도 고려해야 한다.

따라서 모바일에서는 일반적인 영상보다 조금 더 보수적으로 운영하는 것이 좋다.

캐러셀에서 영상이 여러 개라면?

실제 페이지에는 이런 영상이 약 10개 있었고 캐러셀 형태로 제공됐다.

다행히 동시에 DOM에 존재하는 <video>는 2개뿐이었다.

이 구조는 영상 10개를 모두 mount해두는 것보다 훨씬 좋다.

예를 들어:

  • 현재 슬라이드 영상
  • 다음 슬라이드 영상

만 유지하고 나머지 영상은 DOM에서 제거한다면 브라우저가 불필요한 Decoder와 GPU Resource를 계속 들고 있을 가능성을 크게 줄일 수 있다.

다만 video element가 2개 존재하는 것과 video 2개가 동시에 재생되는 것은 다른 문제다.

모바일에서는 가능하면:

현재 영상 → Play

다음 영상 → Preload만

구조가 더 안정적이다.

예를 들어:

1
2
3
4
5
6
7
8
9
10
11
12
13
<video
  src="current.mov"
  autoplay
  muted
  playsinline
></video>

<video
  src="next.mov"
  preload="metadata"
  muted
  playsinline
></video>

처럼 운용할 수 있다.

다음 영상의 초기 재생 지연이 실제로 문제가 된다면 preload="auto"도 테스트해볼 수 있다.

DOM에서 제거하면 메모리는 바로 해제될까?

반드시 즉시 해제되는 것은 아니다.

React에서 <video>를 unmount하면 브라우저 입장에서는 해당 미디어 리소스를 정리할 수 있는 상태가 된다.

하지만:

  • 브라우저 캐시
  • Decoder Resource
  • GPU Resource
  • Garbage Collection

등의 이유로 DevTools에서 보는 메모리가 즉시 내려오지 않을 수 있다.

따라서 메모리가 순간적으로 증가한다고 해서 곧바로 Memory Leak이라고 판단하면 안 된다.

중요한 것은 반복했을 때 메모리가 안정화되는가다.

정상적인 경우라면:

180MB → 230MB → 270MB → 240MB → 270MB

처럼 어느 정도 범위에서 올라갔다 내려갔다 할 수 있다.

반대로:

180MB → 250MB → 320MB → 410MB → 500MB → 600MB

처럼 캐러셀을 반복할 때마다 계속 증가한다면 리소스가 제대로 해제되지 않는지 확인해야 한다.

필요하다면 unmount 전에 좀 더 적극적으로:

1
2
3
video.pause();
video.removeAttribute('src');
video.load();

를 호출하는 방법도 있다.

모바일에서는 어떤 문제가 생길 수 있을까?

영상 처리 때문에 메모리와 GPU 사용량이 지나치게 올라가면 모바일에서는 다음과 같은 현상이 나타날 수 있다.

  • 캐러셀 애니메이션 버벅임
  • 스크롤 프레임 드랍
  • 영상 재생 지연
  • 검은 화면
  • 발열 증가
  • 배터리 소비 증가
  • 브라우저의 적극적인 리소스 정리
  • 심한 경우 Safari 탭 리로드

특히 모바일 Safari에서는 페이지가 사용할 수 있는 리소스가 무한하지 않기 때문에 단순히 “최신 iPhone에서는 잘 되니까 괜찮다”고 판단하기보다 실제 기기에서 반복 테스트하는 것이 중요하다.

결국 무엇을 최적화해야 할까?

처음에는 4MB와 8MB 중 어떤 파일을 선택할지가 가장 중요한 문제라고 생각했다.

하지만 정리해보니 최적화 대상은 서로 분리해서 봐야 했다.

네트워크를 줄이고 싶다면

  • 파일 용량
  • 비트레이트
  • CDN
  • HTTP Range
  • preload 전략

을 봐야 한다.

메모리와 렌더링 부하를 줄이고 싶다면

  • 동시에 재생되는 video 개수
  • 해상도
  • FPS
  • Alpha 합성
  • video mount/unmount 전략

이 더 중요하다.

예를 들어 같은 1280px 해상도라면 8MB 영상을 4MB로 줄이는 것보다 동시에 재생되는 영상 2개를 1개로 줄이는 것이 모바일 부하에는 훨씬 큰 효과가 있을 가능성이 높다.

이번 케이스에서 선택한다면

현재 조건은 다음과 같다.

  • HEVC with Alpha
  • 약 20초
  • 모바일 1280px
  • 4MB / 8MB / 10MB 버전
  • 약 10개의 영상
  • 캐러셀
  • 동시에 최대 2개의 <video> 존재
  • S3 + CDN 사용

이 조건이라면 다음 전략을 먼저 적용할 것 같다.

  1. 현재 화면의 영상 하나만 실제로 재생한다.
  2. 다음 영상은 preload만 한다.
  3. 그 외 영상은 DOM에서 제거한다.
  4. 모바일에서는 1280px 버전을 사용한다.
  5. 60fps라면 30fps로 낮췄을 때 체감 차이가 있는지 확인한다.
  6. 4MB와 8MB의 화질을 실제 표시 크기에서 비교한다.
  7. 화질 차이가 거의 없다면 4MB를 사용한다.
  8. 차이가 분명하다면 8MB도 충분히 현실적인 선택이다.
  9. DevTools와 실제 모바일 기기에서 캐러셀을 반복 실행하며 메모리가 안정화되는지 확인한다.

결국 4MB냐 8MB냐에는 하나의 정답이 없다.

파일 크기는 네트워크 문제이고, 영상 재생 중 발생하는 메모리와 성능 문제는 대부분 디코딩과 렌더링의 문제다.

이 둘을 분리해서 생각하면 웹 영상 최적화에서 무엇을 먼저 손봐야 하는지가 훨씬 명확해진다.

압축된 영상의 크기보다 중요한 것은, 브라우저가 그 영상을 재생하기 위해 실제로 어떤 일을 하고 있는지를 이해하는 것이다.