Tech

우당탕탕~ 영상 서비스 개발기 2탄 : 인코더와 라이브 서비스

Tech HR 2023. 4. 4. 11:01

 

 

앞서 영상 파일 및 메타 관리 시스템 'VODKA'에 관한 내용을 다뤘는데요.

이번 편에서는 영상 서비스 인코더와 라이브 서비스에 대해 다뤄보고자 합니다.

 

아직 '우당탕탕~ 영상 서비스 개발기 1탄 : 영상 CMS'편을 보지 못하셨다면 아래 글을 클릭해 주세요!

 

▶️ 우당탕탕~ 영상 서비스 개발기 1탄 : 영상 CMS

 

우당탕탕~ 영상 서비스 개발기 1탄 : 영상 CMS

당신의 손에 소녀들의 운명이 달려있습니다. 카카오엔터테인먼트에서 새롭게 도전한 버추얼 걸그룹 데뷔 서바이벌 프로그램! '소녀 리버스(RE:VERSE)' 소녀 리버스는 30인의 미모와 개성이 넘치는

kakaoentertainment-tech.tistory.com

 

저희 친해요~ 영상서비스개발팀!

 

 


 

 

[Chapter 3] 인코더 & 채팅을 보여줘 패키지

영상 서비스의 핵심은 안정적인 동영상 스트리밍입니다. 저희는 안정적인 동영상 스트리밍을 위해 많은 고민을 했고, 여러 기술을 사용해 시청자들에게 안정적이고도 훌륭한 사용자 경험을 선사했습니다. 저희 카카오엔터테인먼트가 안정적인 사용자 경험을 위해 어떤 고민을 했고, 어떤 기술을 도입했고, 어떤 문제들을 해결했는지 알려드리려고 합니다.

 

1.  동영상 스트리밍은 어떻게 하는 건데요? 🤨

일단 간단한 동영상 스트리밍 방법론에 관해 서술하려 합니다.

동영상 스트리밍은 크게 다음 세 가지 과정을 거쳐서 여러분들께 서비스됩니다.

1️⃣ 트랜스 코딩 : 원본 영상 파일을 인코딩하여, 여러 해상도를 가진 영상 파일을 만든다.
2️⃣ 패키징 : 영상 파일을 몇 초 단위로 잘라 스트리밍에 용이한 구조를 가진 파일을 만든다.
3️⃣ CDN : 만들어진 영상 파일을 CDN(Content Delivery Network)에 올려 더욱 빠른 스트리밍이 가능하게 합니다.

 

인코딩 - 스트리밍 구성도

 

전체적인 흐름은 다음과 같습니다.

 

먼저 VODKA(CMS)에 업로드된 원본 파일을 360p, 480p, 720p, 1080p 화질로 트랜스코딩합니다. 트랜스코딩이란 원본 파일의 인코딩을 통해 여러 해상도를 가진 여러 벌의 영상 파일을 만들어내는 과정을 뜻합니다. 인코딩 과정에서 어떤 옵션값을 넣느냐에 따라 같은 해상도라 할지라도, 용량과 화질이 다르기 때문에, 트랜스코딩의 핵심은 적절한 인코딩 옵션값이라고 볼 수 있습니다.

 

그리고 나서 만들어진 4개의 파일을 각각 패키징합니다. 패키징이란 영상 파일을 몇 초 단위로 잘게 쪼개서 독립적으로 재생이 가능한 단위인 Segment화 한 후, Storage에 저장하는 과정입니다. 소녀 리버스의 경우 4초 단위(duration)로 잘라서 Storage에 저장합니다.

 

이렇게 되면 총 4벌의 패키징 된 파일이 Storage상에 저장됩니다. 즉, 사용자의 네트워크 품질에 따라 어떤 화질의 조각을 가져갈지 결정할 수 있게 되어 사용자 네트워크 품질에 맞는 적절한 서비스를 제공해줄 수 있습니다. 만약 Adaptive Bitrate Streaming이 불가능하다면, 네트워크 환경이 좋지 않은 유저들이 억지로 고화질 영상을 보며 지속적인 버퍼링에 그만 화면을 끄게 되고, 연이은 시청으로 이어지지 않는 최악의 유저 경험을 하게 되겠죠.

 

이러한 과정들을 통해 총 4벌의 패키징된 파일이 Storage에 저장되면, 이제 동영상 스트리밍이 가능합니다. 하지만 Storage에 직접 동영상 스트리밍 요청이 들어오면 Origin의 부하가 심하게 걸려 다운로드 속도가 크게 저하되어 재생에 문제가 생길 수 있습니다. 보통은 이러한 영상 파일을 CDN에 올려 사용자가 직접 Origin에 접근하지 않고도 CDN에서 캐시된 파일을 다운로드할 수 있게 하여 Origin 부하를 해결합니다.

 

인코딩 서버 구성도

 

인코딩 서버는 크게 트랜스코딩 서버와 패키징서버 두 개로 나뉩니다. 그리고 두 서버를 제어하는 workflow platform인 airflow 서버가 모든 트랜스코딩/패키징 작업 프로세스를 제어합니다.

 

처음 서버를 설계할 때, 저희는 일련의 긴 작업들을 제어하고 데이터 입출력을 모니터링할 수 있는 도구가 필요했습니다. Airflow는 웹 서버로도 접근하여 조작할 수 있을뿐더러, 각 DAG(Directed Acyclic Graph)의 태스크별로 데이터를 재사용할 수 있고 태스크별로 로그들이 남기 때문에 인코딩, 패키징 등이 이루어지는 전체 과정을 모니터링하기에 적합하다고 판단하여 채택했습니다. Airflow는 GCP(Google Cloud Platform)의 Composer라는 기능을 사용하여 개발하였습니다. Composer는 Airflow 환경을 자동으로 구성해 주는 GCP의 제품인데요. 덕분에 복잡한 Airflow 설정을 하지 않고도 빠르게 개발할 수 있었습니다.

 

Airflow에 띄워져 있는 Encoder 관련 DAG 리스트

 

Airflow의 특정 DAG 내 태스크 리스트와 태스크 실행 이력

 

 

트랜스 코딩

동영상을 보다가, 터널에 진입하면 화질이 나빠지고, 다시 터널을 빠져나오면 좋아지는 경험을 한 번쯤 해보셨을 것입니다. 이러한 네트워크 품질에 따라 화질을 조절하는 기술을 Adaptive Bitrate Streaming이라고 부릅니다. 또한, 유튜브는 화질 선택이 가능해서 지원만 한다면 4K 동영상을 선택해서 볼 수 있습니다. 이 모든 게 다 여러 화질에 대한 영상이 미리 준비되어 있기 때문에 가능한 것입니다. 이러한 여러 화질의 동영상을 미리 인코딩해놓는 과정 트랜스코딩이라고 합니다.

 

영상의 용량과 화질은 꽤 비례관계에 있습니다. 그래서 용량이 높으면 화질이 좋아지겠지만, 사용자가 소비하는 네트워크 대역폭 또한 커지므로, 만약 나쁜 네트워크 환경에 있다면 좋은 사용자 경험을 하기 힘들어질 것입니다. 그래서 서비스 관점에서 인코딩의 핵심은 사용자가 최상의 경험을 할 수 있도록 용량을 최대한 낮추면서 화질은 최대한 유지하는 일입니다. 어떤 인코딩 옵션을 넣어서 성능개선을 하느냐가 핵심이라고 볼 수 있는데요, 보통 인코딩 옵션을 정할 때 용량/화질 그래프(bitrate-vmaf 혹은 bitrate-psnr)를 그려서 판단합니다. 저희는 여러 인코딩 옵션값을 통해 다음과 같은 bitrate-vmaf 그래프를 도출하였습니다.

 

bitrate - vmaf 점수 그래프

 

해당 그래프에서 vmaf 점수가 높다면 시청자가 원본과 느끼는 화질 차이가 적기 때문에 기본적으로 높으면 높을수록 좋지만, 무작정 vmaf 점수만 높이는 것을 기준으로 삼으면 필요한 평균 bitrate 값도 높아지게 됩니다. 그렇게 되면 CDN에 들어가는 비용도 늘고, 대역폭이 낮은 환경에서 적용이 힘들어지는 등의 문제가 생깁니다. 그러므로 각 vmaf 점수별로 효율성에 따른 가중치를 부여하여 최적화된 점수를 기준으로 삼아야 합니다. 가중치를 산정하는 방법은 여러 가지가 있는데, 저희는 bitrate 값이 낮을수록 효율성이 높다고 판단하여 이에 따른 가중치를 부여했고, 최종적인 점수는 다음 식과 같습니다.

최적화 점수 = vmaf(x) * ({기준 bitrate} - x), x = vmaf 점수를 측정한 평균 bitrate

 

이처럼 시각적인 품질을 측정하는 vmaf 점수에 bitrate 값이 낮을수록 값이 커져서 가중치가 높아지는 ({기준 bitrate} - x) 항을 곱하여 최적화 점수를 계산했습니다. 이때 기준이 되는 bitrate는 서비스되는 환경에 따라 달라집니다. (일반적으로는 해상도별 권장 bitrate를 기준 bitrate로 인식) 이를 그래프로 도식화하면 다음과 같습니다.

 

vmaf 측정 그래프에서 최적화 점수에 대한 시각적인 그래프

 

위 그래프에서 빗금 면적이 곧 최적화 점수이기 때문에 면적이 크면 클수록 최적화가 잘 된 것으로 판단할 수 있으며, 이에 따라 포인트 몇 곳을 선정하여 해당 bitrate를 기준으로 인코딩을 진행하였습니다.

 

 

패키징

패키징은 인코딩 된 중간 데이터를 새로운 포맷으로 엮어내는 작업이라고 할 수 있습니다. 원본 영상을 여러 곳에 서비스하려면 그 서비스에 걸맞은 포맷으로 제공해야 하는데, 이것을 패키징 단계에서 수행하는 것이죠. 중간 데이터를 크게 분류했을 때 video 스트림, audio 스트림, 자막 스트림이 있는데, 이 데이터들을 요청받은 포맷별로 패키징을 수행하며, 이와 동시에 패키징 된 데이터를 요청받은 경로로 업로드하는 작업도 겸하고 있습니다.

 

현재 지원하는 포맷은 hls/dash/clean mp4이며, hls/dash는 스트리밍 용도로, clean mp4는 내부에서만 접근 가능한 영상의 인코딩 상태 검증 및 편집 용도로 사용됩니다. 또한, 단순 영상 패키징뿐만 아니라, 각종 썸네일 또한 추출하여 제공합니다. seek 썸네일의 경우, HTTP 오버헤드를 줄이기 위해 해상도를 대폭 낮추어, 여러 사진을 병합하여 제공합니다.

 

seek 썸네일 예시

 

패키징이 끝나면, 아래 사진과 같은 구조로 결과물들이 나오게 됩니다.

 

패키징 결과물

 

 

채팅 패키징

소통이 활발한 방송에서는 사용자들 간의 교류도 무척 중요합니다. 그리고 그러한 사용자들 간의 교류를 나타내는 대표적인 활동이 곧 채팅이라고 할 수 있습니다. 그렇기에 방송 영상과 마찬가지로 채팅 또한 반드시 같이 기록하고 같이 제공을 해야 합니다.

 

소녀 리버스를 다시 보기 하면, 그 당시 사람들이 쳤던 채팅이 다시 올라오는 모습을 볼 수 있습니다. 이를 통해 나는 다시 보기를 하고 있어도 사람들과 다 같이 본편 방송을 보고 있는 것 같은 특별한 경험을 하게 되죠. 이는 채팅 다시보기라는 기능으로 저희가 최상의 사용자 경험을 위해 준비한 특별한 기능입니다.

 

타사 채팅 다시보기 API 예시

 

위 사진은 통상적인 채팅 다시 보기 API 예시입니다. 보통 메시지들을 DB에 저장하고 특정 구간을 꺼내쓰는 방식을 사용합니다. 이런 방식은 서버 개발을 추가로 해야할 뿐 더러, 플레이어 구현에 부담이 가해질 수 밖에 없습니다. 왜냐하면 어느 재생 시간대에서 어느 API를 호출해서 데이터를 들고올 것이고, 그리고 해당 데이터의 어느 부분을 화면에 랜더링 할 것인가 등 재생 시간대와 관련된 실제 플레이어가 해주는 많은 부분을 직접 구현해야 하기 때문입니다.

 

소녀 리버스 11화 채팅 다시보기

 

그래서 저희 채팅 다시보기 기능 구현 시에 관련 서버와 프론트 개발 부담을 덜기 위해서 자막 파일에 채팅을 실어서 보내는 방식을 선택했습니다. 해당 방식의 장점은 자막 관련 로직이 이미 플레이어에 다 구현이 되어 있어서 플레이어 개발 시 어느 시간대에 어느 채팅을 출력할지와 같은 고민을 하지 않아도 된다는 것입니다. 플레이어 개발자는 이런 고민 없이 그저 플레이어가 내려준 데이터를 그대로 랜더링 하면 되는 것이죠. 이러한 자막 파일 기반 스트리밍 방식은 이미 검증된 플레이어의 자막 재생 방식을 그대로 쓰는 것이라 플레이어 구현에 부담을 많이 덜 수 있었고, 또한 파일 기반의 방식이라 CDN에 캐시 시키기도 용이했습니다.

 

방송이 끝난 후, 그날 방송의 채팅 데이터들을 취합한 JSON 파일을 영상 파일과 같이 채팅 패키징을 시작합니다. 채팅 패키징은 다음과 같은 순서로 작업이 이루어집니다.

1️⃣ JSON 파일에서 비즈니스 로직에 따라 메시지들은 필터링 및 편집합니다.
2️⃣ 편집된 JSON 파일을 자막 표준 파일인 vtt 파일로 변환하여, 플레이어에서 자막처럼 다룰 수 있게 만듭니다.
3️⃣ 변환된 vtt파일을 스트리밍에 적합하도록 적절한 길이로 잘라 segment화 한 후 Storage에 저장하고, 해당 자막에 대한 플레이리스트를 생성하여 master 플레이리스트에 자막의 한 종류로 등록합니다.

 

이렇게 하여 영상과 똑같은 타이밍에, 똑같은 채팅 메시지를 사용자들에게 안정적으로 제공할 수 있게 되었습니다.

실제 서비스되고 있는 소녀 리버스의 m3u8 파일을 보면 CHAT 자막이란 이름으로, 채팅 다시 보기가 서비스되고 있음을 확인할 수 있습니다.

#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="chat_kor",DEFAULT=NO,AUTOSELECT=YES,LANGUAGE="kor",URI="5/hdntl=exp=1679102576~acl=%2f*~data=hdntl~hmac=b5820f03d6e0dd8901bc9df072dde8212b6b5fdc5bf23b9b15f27e89a91a744e/playlist.m3u8"

 

 


 

2.  개선이 필요한 사항 

현재 kubernetes의 자동 확장 및 축소기능을 사용한 유연한 서버 구성을 갖추고 있습니다. 하지만 리소스 점유율 기준으로 자동 확장 및 축소를 수행하기 때문에 정확히 저희가 원하는 타이밍에 확장되고 축소되고 있지 않아 서버 자원을 효율적으로 쓰고 있지 않다는 문제를 가지고 있습니다. 이러한 문제를 해결하기 위해 Custom Matrix Server를 개발하여 정확히 저희가 원하는 타이밍에 서버가 동적으로 확장되고 축소할 수 있도록 개선할 예정입니다.

 

 

 

 

 

[Chapter 4] 말도 많고 탈도 많았던 리얼 라이브 중계

제작 현장!

 

촬영 현장은 라이브 때마다 생각하지 못한 다양한 이슈가 발생하는 경이로운(🥲) 곳입니다. 여러 회차 라이브를 진행하면서 기존에 발생했던 문제점을 해결하고 대처하고 했지만, 그걸 비웃기라도 하듯이 매번 새로운 이슈가 발생하여 라이브 진행 시마다 공포와 긴장의 연속이었습니다. 하지만 좀 더 인터렉티브한 라이브 시청 경험을 제공하기 위해 적용한 다양한 기술들이 시청자들에게 좋은 경험과 함께 웃고 즐길 수 있는 장치가 되는 것 같아 한편으로는 뿌듯하고 즐거운 경험이었습니다.

 

본 챕터에서는 라이브 방송을 좀 더 인터렉티브 하게 만들어준 몇 가지 기술 및 개발 솔루션을 소개하겠습니다. 

 

 

1. Timed Metadata 라이브 투표를 부탁해

HLS는 사용자마다 약간의 딜레이(2~4초)가 존재합니다. 하지만 소녀 리버스에서는 아래 이미지와 같이 시청자들에게 동일한 장면에 라이브 투표가 보이길 원했습니다.

 

시청자 별 투표 노출 시점

 

위 그림에서 보면 A 시청자는 B 시청자보다 TS 파일을 재생하는 시점이 빨라 투표 팝업이 B 시청자보다 좀 더 빠르게 노출되는 형태입니다. 즉, 사용자별로 동일한 TS 파일 재생 시 투표가 노출되도록 운영 툴에서 투표를 생성하면 해당 장면의 TS 파일을 시작으로 투표 종료 시간 전까지의 모든 TS 파일에 ID3v2 Frame을 PES(Packet Elementary Stream, 패킷화 기본 스트림)형태로 변환하여 Injection 하는 형태로 구현했습니다.

 

HLS를 구성하는 MPEG TS는 아래와 같은 구성으로 이루어집니다.

1️⃣ 영상, 음성, 데이터 등 각각의 미디어 비트 스트림인 ES(Elementary Stream, 기본스트림)
2️⃣ ES에 PES 헤더를 추가하여 패킷화 시킨 PES 패킷
3️⃣ 각각의 PES 패킷을 188 바이트 고정길이 패킷들로 분할 구성한 TS Packet
4️⃣ TS Packet을 일정 길이(duration)로 구성한 MPEG-2 Transport Stream (.ts 확장자를 가지는 HLS 기본 미디어 콘텐츠 데이터 파일)

 

이 순서대로 Timed Metadata가 Injection 된 TS 파일을 생성하는 절차는 아래와 같습니다.

 

먼저 ID3v2 Frame을 생성합니다.

 

ID3v2 구조

 

생성된 ID3v2 Frame에 PES 헤더를 붙여 PES 패킷을 생성합니다.

 

ES -> PES Packet

 

PES 패킷을 TS Packet으로 분할 구성(188 Byte 고정)하여 TS Stream을 생성합니다.

 

PES Packet -> TS Packet

 

TS Packet은 188 Byte 고정 길이 형태로 구성해야 합니다. 위 그림에서 PES Packet을 184 Byte 마다(TS 헤더 4Byte 제외) 분할하여 TS Packet으로 생성되는 형태로 구성되어 있는데, 실제로는 대부분의 PES Packet의 길이는 184 Byte의 배수로 떨어지지 않습니다. 따라서 TS Packet 생성 시 상황에 따라 유효필드(padding)를 추가하여 188 Byte 길이를 맞추는 작업이 필요합니다.

 

전체 작업을 도식화하면 아래와 같습니다.

 

ID3v2 Frame Injection Process

 

운영 툴에서 실시간 투표 생성 요청이 들어오면 패키징 단계에서 위 프로세스를 거쳐 TS 파일에 ID3v2 기반 Timed metadata를 Injection 하여 전송하고 있습니다. 참고로 플레이어에서는 TS decoding 시 PES Stream ID Type이 Private Steam일 경우 ES에 있는 ID3v2 Frame Data(json)를 가져와 투표 서버와 연동하고, 화면 노출 등의 처리를 하는 방식으로 구현되어 있습니다.

 

 

 

2. 급할 땐 Flutter

Flutter는 하나의 코드베이스로 모바일, 웹, 데스크톱에서 네이티브로 컴파일되는 구글의 아름다운(?) UI 툴킷입니다. 주로 모바일 애플리케이션 개발에 사용되며, iOS와 Android 플랫폼에서 동작하는 고품질의 네이티브 앱을 개발할 수 있도록 지원합니다.

 

🔍 Flutter
Dart라는 프로그래밍 언어로 개발되어 있으며, iOS와 Android 애플리케이션을 모두 개발할 수 있어서 개발자는 하나의 코드베이스로 두 플랫폼에서 동작하는 애플리케이션을 만들 수 있습니다. Flutter는 UI 디자인을 위한 위젯(Widget) 시스템을 제공하며, 이를 통해 재사용 가능한 UI 요소를 쉽게 만들 수 있습니다. 또한, 빠른 개발을 위한 다양한 툴과 기능을 제공합니다. 

 

예를 들어, 핫 리로드(Hot Reload) 기능을 사용하면 코드를 수정하고 앱을 다시 빌드하고 실행하는 시간을 대폭 줄일 수 있습니다. 또한, 다양한 라이브러리와 패키지를 포함하고 있어서 개발자들이 쉽게 추가 기능을 구현할 수 있습니다. 또한, 웹 애플리케이션과 데스크톱 애플리케이션 개발을 위한 프로젝트인 Flutter for Web과 Flutter for Desktop도 지원하고 있습니다. 이를 통해 개발자들은 하나의 코드베이스로 다양한 플랫폼에서 동작하는 애플리케이션을 만들 수 있습니다.

 

서비스를 운영하다 보면 필요한 툴들이 생기기 마련인데, 이를 빠르게 개발하여 결과물을 낼 수 있는 프레임워크로 Flutter를 선택하게 되었습니다. Dart 언어의 특성상 Java와 유사한 점이 있어서 백엔드 개발자가 배우고 개발하기에 그리 어렵지 않았던 것 같습니다.

 

 

채팅을 모아줘~ 릴레이 라이브

 

페이지 채팅까지 한눈에!!!

 

소녀들의 릴레이 라이브는 유튜브와 트위치 그리고 카카오페이지 3개의 플랫폼에서 동시 송출을 진행했습니다. 플랫폼마다 라이브 채팅 기능이 존재했고, 소녀들은 3개의 창을 띄워놓고 각 플랫폼별 채팅 메시지를 번갈아 보며 방송을 진행하는 형태였습니다. 저희는 빠르게 3개의 플랫폼에서 진행되는 채팅 메시지를 하나의 웹에서 볼 수 있도록 Flutter를 이용하여 개발하기 시작했습니다. 단! 이틀만에 위와 같은 채팅 통합 웹 페이지를 만들 수 있었고, 소녀들과 방송 제작진에게 좀 더 나은 환경을 제공해 드릴 수 있게 되었습니다.

 

깨알같은 사용자 반응에 뿌듯~

 

 

몇 대 몇? (스코어 보드)

라이브 방송 중에 중요한 모니터링 요소 중 하나는 현재 접속 중인 동접자 수, 채팅 메시지 수, 응원 수 등 현재 사용자 접속에 대한 카운팅 정보입니다. 초기 회차 때에는 데이터 수집 담당자(일해라 휴먼..!)가 10분마다 DB를 조회해서 모니터링 채팅방에 타이핑하는 방식으로 현재 접속자에 대한 정보를 공유했습니다. 이런 부분이 담당자로서는 불편하기도 하고, 혹여나 오타나 잘못 조회된 데이터가 공유될 수 있는 문제점 등이 있어 간단한 형태로 아래와 같은 스코어 보드 애플리케이션을 개발하게 되었습니다. (이건 무려 3 일만에!!🫢) 

 

응원 100만이 넘는 순간!

 

 


 

3. 1분이 1초 같이 느껴졌던 라이브 방송 장애..

 장애는 항상 언제나 새롭고 짜릿한 것 같습니다. 특히 생방송 중에는 말이죠... 라이브 방송 중 발생한 몇 가지 이슈 중 1월 24일, 팀전 라이브 방송에서 발생한 플레이어 장애에 대한 원인과 처리 방법에 대해 간략하게 공유하고자 합니다. 

(이 자리를 빌려 라이브 방송 시 원활한 서비스를 제공하지 못한 점 다시 한번 송구의 말씀을 전합니다..🙇🏻‍♀️🙇🏻)

 

 1월 24일, 라이브 방송이 시작되고 5~10분이 지난 시점부터 커뮤니티에 재생 관련 장애 글들이 올라오기 시작했습니다. 저희는 곧바로 함께 모니터링 중인 QA 분들께 해당 증상에 관해 확인 요청을 드렸고, 개발자들도 브라우저 디버깅 모드 등을 이용해 원인을 파악하기 시작했습니다. 우선은 커뮤니티에 올라온 글의 내용을 기반으로 채팅 관련 모듈 점검, 스트리밍 서버 점검, CDN 점검 등 동시다발적으로 관련 모듈들을 빠르게 점검해 나갔습니다. 하지만 저희 내부 QA에서는 장애 증상이 재현되지 않았고, 모니터링에 참여한 10여 명의 인원중 1~2명 정도만 해당 증상 가끔 발현되는 것으로 확인되었습니다.

 

 이미 라이브는 시작되어 돌이킬 수 없는 상황이고, 만약에 원인을 찾아 수정한다고 해도 사용자의 재접속 또는 새로고침 등의 행위가 있어야 수정 사항이 반영될 가능성이 높은 상황이라 최대한 채팅 안내 메시지를 통해 시청자들에게 필요한 가이드를 하면서 상황을 지켜보고 원인 파악은 라이브 종료 후에 진행하는 것으로 내부 논의하였습니다. 시청자들은 짧게 느껴지셨겠지만.. 저희에게는 길고 길었던 라이브 방송이 종료된 후, 원인 분석을 위해 문제가 발생했던 증상을 재현해 보기 시작했고 바로 그 문제점을 찾을 수 있었습니다.

 

문제의 원인은 바로 HTTP 1.1의 고질적인 문제점이었던 HOL(Head of Line) 블로킹 문제였습니다.

 

HOL 블로킹 문제란, HTTP/1.1의 요청-응답 쌍은 항상 순서를 유지하고 동기적으로 수행되어야 합니다.

예를 들어 1개의 TCP 커넥션 상에서 3개의 이미지 (a.png, b.png, c.png)를 받는 경우, HTTP 리퀘스트는 다음과 같이 됩니다.

 

|---a.png---|
            |---b.png---|
                        |---c.png---|

 

하나의 요청이 처리되고 응답을 받은 후에 다음 요청을 보내게 되는데, 이전의 요청이 처리되지 않았다면 그다음 요청은 보낼 수 없다는 것입니다. 만약 a.png의 요청이 막혀버리게 되면 b,c가 아무리 빨리 처리될 수 있더라도 전체적으로 느려지게 됩니다.

 

|------------a.png------------|
                              |-b.png-|
                                      |---c.png---|

 

HTTP/1.1의 pipelining이라는 사양은(조건부로) 요청만 먼저 보내버리는 것으로, 이 문제를 회피하는 것처럼 보이지만 응답을 보낸 순서대로 무조건 받아야 하므로 a.png가 막혔을 때 생각보다 큰 효과를 보기 어렵습니다.

 

요즘 많은 서비스가 HTTP/2로 전환되어 서비스되고 있고, 저희 서비스에도 HTTP/2로 서빙하는 서버들이 존재하고 있지만, 프로필 이미지를 서빙하는 서버가 HTTP/1.1로 서비스 중인 것으로 확인되었습니다. 또한, 프로필 이미지는 사용자가 업로드한 이미지 사이즈에 따라 다양한 크기의 이미지가 존재하는데, 해당 이미지를 별도의 최적화(resize 등) 없이 그대로 채팅창에서 이미지를 불러오다 보니 순간적으로 채팅이 급증하는 상황에서 HOL 블로킹 현상이 발생하는 것을 확인할 수 있었습니다. 

 

1월 24일에 진행된 팀전 라이브 방송은 팀별 베네핏 획득을 위해 치열하게 경쟁하는 형태의 라이브였고, 이를 위해 시청자들과의 인터렉션을 엄청나게 유도하는 형태로 진행되었습니다. 방송 당시 채팅량은 기존 라이브 방송의 최대 5배를 웃도는 수준으로 발생하였고, 사용자 프로필 이미지를 가져오는 부분에서 사용자에 따라 빈번하게 HOL 블로킹 현상을 일으키게 되면서, 결국엔 영상 파일 스트리밍까지 영향을 받아 영상 재생이 원활하지 못한 상황까지 발생하게 되었습니다. 문제를 확인한 후 바로 프로필 이미지 서버 앞에 이미지 최적화 및 HTTP/2를 지원하는 이미지 캐시 서버를 설치하고, 플레이어에서 이미지 URL을 해당 서버에서 가져오는 방식으로 변경처리 하는 작업을 통해 문제를 해결하였습니다.

 

이번 경험을 바탕으로 앞으로 진행되는 서비스에서는 좀 더 쾌적하게 라이브를 즐기실 수 있도록 꼼꼼하게 테스트하고 철저히 준비해야겠다고 마음을 다졌습니다. 

 

 


 

4. 아쉬웠던 점

라이브 방송 시 아쉬웠던 점은  End to End 지연시간이 대략 25초 정도 가량 발생한 점입니다. 이로 인해 소녀들과 러버스 여러분과의 소통이 원활하게 진행되지 못했던 부분이 있었습니다. HTTP 기반 스트리밍 기술은 평균 30 ~ 60초가량의 딜레이가 발생합니다. 하지만 출연자와 시청자 간의 원활한 소통을 하려면 지연시간을 최대한 줄이는 게 라이브 스트리밍 서비스를 하는 업체에서는 최대의 숙원이 아닐까 싶습니다. 저희도 현재 이러한 경험과 니즈를 바탕으로 HTTP 기반 저지연(5~7초 지연) 스트리밍 방법을 연구 & 개발 중에 있습니다. (기회가 된다면 이 기술에 대해서도 테크블로그에 소개해볼 예정이니 많은 관심 부탁 드립니다.)

 

 

 


 

 

 

이번 편에서는 영상서비스 인코더와 라이브 서비스에 대해 설명드렸는데요

마지막 편인 3탄에서는 플레이어 백엔드 서버와 데이터 수집 개발기에 대해 소개하도록 하겠습니다.

 

감사합니다.

 

 

▶️ 우당탕탕~ 영상 서비스 개발기 3탄 : 플레이어 백엔드 서버와 데이터 수집 

 

우당탕탕~ 영상 서비스 개발기 3탄 : 플레이어 백엔드 서버와 데이터 수집

앞서 영상서비스 인코더와 라이브 서비스에 관한 내용을 다뤘는데요. 이번 편에서는 플레이어 백엔드 서버와 로그 수집 분석에 대해 다뤄보고자 합니다. 아직 '우당탕탕~ 영상 서비스 개발기 2

kakaoentertainment-tech.tistory.com

 

📷 photo by. Selian