좌충우돌 실시간 영상 통화 구현기
VMONSTER TEAM
Apr 28, 2025
안녕하세요! 브이몬스터의 백엔드 개발자 Thunder(신지원)입니다. 자연스럽게 소통 가능한 AI Avatar를 안정적으로 제공하는 기술 개발을 담당하고 있습니다. 이 과정에서 실시간 비디오/오디오 송출 기술이 필요했는데요. 개발하면서 겪었던 여정을 공유하려고 합니다. 일종의 개발 일기처럼 봐주시면 좋겠습니다. 😊
1. Daily에서 WebRTC-rs를 거쳐 LiveKit까지
실시간 영상 통신의 필요성과 WebRTC
AI Avatar 서비스를 만들기 위해서는 사용자와 아바타 간의 자연스러운 소통이 핵심입니다. 이를 위해 AI Avatar가 생성한 영상과 음성을 실시간으로 사용자에게 전달해야 했습니다. 즉, 실시간 미디어 스트리밍 기술이 필요했습니다.
이런 실시간 통신에 가장 적합한 기술이 바로 WebRTC(Web Real-Time Communication)입니다. WebRTC는 웹 브라우저나 모바일 앱에서 별도의 플러그인 없이 P2P 방식으로 오디오, 비디오, 데이터를 실시간 교환할 수 있게 해주는 오픈 소스 기술입니다. 특히 다음과 같은 장점이 있습니다:
낮은 지연 시간: 실시간 대화를 위해 필수적인 요소
미디어 품질 자동 최적화: 네트워크 상황에 따라 적응적으로 품질 조절
크로스 플랫폼 지원: 웹, 모바일 등 다양한 환경에서 작동
저희 팀은 WebRTC 서버 운영 경험이 없었기도 했고, WebRTC를 기반으로 AI Avatar까지 만들어야 했던 상황이라 실시간 미디어 스트리밍 기술은 Daily.co에서 제공하는 솔루션을 사용하기로 했습니다.
처음에는 Daily.co로 시작했어요
약 1년 전, WebRTC Python SDK를 제공하는 Daily.co를 선택했습니다. CTO께서 혼자서 개발을 시작하셨는데, 사용해보니 몇 가지 문제가 있더라고요.
Daily SDK는 저희 사용 사례에 완전히 부합하지는 않았습니다. 특히, Thread leak 관련 이슈가 간헐적으로 발생하면서 서버 재시작이 필요한 상황도 있었는데요, 이는 Github와 Discord에서도 논의된 바 있고 Daily 개발팀에서도 인지하고 있는 문제였습니다. 다만, 저희가 확인한 시점 기준으로는 해당 이슈가 여전히 해결되지 않은 상태였습니다.
그리고 생각보다 비용이 많이 들었습니다. 당시 저희의 서비스는 영상합성 관련한 많은 연산량이 요구되었고, 사용자 1명당 4코어 cpu, 서버 비용으로 월 160달러가 필요했습니다.
“우리가 직접 만들어보는건 어때요?” — Webrtc-rs로의 모험
비용 문제를 해결하려면 결국 더 low-level 언어로 WebRTC 서버를 직접 구현하는 게 답이라고 판단했습니다. 그래서 Rust로 작성된 WebRTC 순수 구현체인 Webrtc-rs를 선택했어요.
선택 이유는 간단했습니다:
Rust는 Python보다 훨씬 빨라서 유저당 4 core CPU까지 필요 없을 거라 생각했어요
WebRTC 서버를 직접 구현하면 Daily같은 서비스 비용을 지불할 필요가 없고요
WebRTC-rs는 WebRTC 오픈소스 중 가장 유명한 Pion 프로젝트(Golang)를 Rust로 포팅 중인 활발한 프로젝트였거든요

Webrtc-rs로 구현하기 위한 기본적인 아키텍처입니다. 이 정도까지만 고려하고 구현하면 끝날 줄 알았어요
그런데… 생각보다 험난했어요
Rust로 개발하면서 여러 어려움이 있었습니다.
첫째로, Rust는 정말 러닝커브가 가파른 언어예요. C++만큼은 아니지만, 소유권 개념이나 라이프타임 같은 특유의 문법을 이해하는 데 시간이 꽤 걸렸어요. 일반적인 API 서버를 만드는거면 좋은 사례들을 참고할 수 있었겠지만, 제가 개발하는 영역이 메이저한 영역이 아니다 보니 Rust으로 WebRTC 및 미디어 처리에 대한 사용 사례를 찾기도 어려웠구요. GPT도 Rust에 대한 학습이 다른 언어보다 부족해서 도움을 얻기가 쉽지 않았고요.
둘째, 앞서 언급했듯 구현 난이도가 높다 보니 개발 일정 맞추기 위해 급하게 처리하고 넘어간 부분이 있었어요. 꼼꼼하게 구현하고 싶었지만 현실은 그렇게 녹록지 않았죠.
셋째, Rust 생태계에는 Python에 비해 미디어를 처리하는 라이브러리의 양도 적고 질도 낮다는 문제가 있었어요. 특히 저희는 VP8 코덱을 사용했기 때문에 VP8 encode/decode에 특화된 라이브러리가 필요했는데, VP8과 관련된 라이브러리는 오랫동안 관리를 안한 경우가 많아서 라이브러리를 어느 정도 수정해야만 적용 가능했습니다.
넷째, Rust에서 미디어 처리 속도가 Python에 비해 기대했던것 보다 성능이 안나올 때가 있었어요. 예를 들면 비디오를 load해서 재생하고, 필요할 때는 비디오를 각 프레임마다 합성 해야 했었는데요. OpenCV를 기준으로, 비디오에서 각 프레임을 load하는 속도는 오히려 Python이 빨랐습니다. 결론적으로는 Rust가 빨랐지만 제가 기대했던 것만큼의 성능 향상은 아니었죠.
어찌저찌해서 백엔드 구현을 마쳤는데, 그때서야 알게 된 충격적인 사실은..! Webrtc-rs에는 아직 구현되지 않은 기능들이 있다는 거였어요. 대표적으로 ABS(Adaptive Bitrate Streaming)라는 기능인데, 사용자의 네트워크 상황에 따라 화질을 능동적으로 조절해주는 중요한 기능이에요. 구글이 만든 Webrtc-js에는 있지만, Webrtc-rs에는 아직 없어서 제가 직접 구현해야 했어요. 눈물을 머금고.. Webrtc-rs를 직접 수정해가며 어떻게든 구현은 했지만, 안정적으로 동작할지는 확신이 없었습니다.

이 사진처럼, 네트워크 상황에 따라 실시간으로 화질이 변하도록 라이브러리를 수정하며 구현했답니다 (하하)
또 다른 문제는 미디어 처리였어요. 가공한 이미지를 VP8/VP9 코덱으로 변환해야 했는데, Rust에는 코덱 관련 라이브러리가 부족했어요. OpenCV Rust는 의외로 OpenCV Python보다 느렸고(같은 C/C++ 라이브러리를 쓰는데도 Python 쪽이 더 최적화된 것 같았어요), 다른 Rust 미디어 라이브러리들은 속도는 빠르지만 C 코드를 그대로 사용하다 보니 unsafe 구문을 써야 했고, 결국 메모리 누수가 발생하기도 했어요.

끝나지 않는 WebRTC 최적화의 길..
WebRTC-rs 마이그레이션 후기
그래도 비용은 확실히 줄었어요! Daily Python SDK를 쓸 때는 4 core CPU, 8GB RAM을 사용했는데, Rust 버전으로 바꾸니 2 core CPU, 2GB RAM으로도 안정적으로 운영할 수 있었습니다.
WebRTC 라이브러리와 미디어 코덱 관련 라이브러리를 직접 수정하고 개발하면서 동시에 AI 아바타 기능까지 추가하는 건, 혼자 하기에는 쉽지 않은 일이었습니다. 다음과 같은 기능들을 모두 신경 써야 했으니까요:
WebRTC 기능: 안정적인 영상/음성 통신, 네트워크 상황에 대응되는 실시간 화질 변경(ABS) 등
미디어 처리 기능: 실시간 영상 합성, 배경화면 변경, 아바타 위치 조정, 아바타 확대/축소 등
AI 아바타 기능: 대화 처리, 대화 중단 처리 등
게다가 제 주요 역할은 사용자에게 사람처럼 느껴지는 AI 아바타를 제공하는 것이지, WebRTC 서버 / 미디어 처리 기술을 만드는 게 아니었거든요. WebRTC 서버 개발과 미디어 처리 성능 개선에 너무 많은 시간을 쏟다 보니 정작 AI 아바타 기능 개발에 집중하기 어려웠어요.
일정도 계속 밀리다 보니.. 결국 WebRTC 순수 구현체를 활용한 서버 운영은 현실적으로 어렵다는 판단이 들었고, WebRTC와 미디어 처리 과정까지 고성능으로 제공하는 서비스를 찾기 시작했습니다.
2. 세 번째 시도: LiveKit Python으로 마이그레이션
여러 대안을 검토한 결과, LiveKit Python을 선택했습니다. 그 이유는:
LiveKit은 여러 언어로 SDK를 지원하지만, 우선적으로 Python을 지원해요.
비용 절감의 핵심인 미디어 처리 속도가 Rust 라이브러리들보다 LiveKit Python이 더 빨랐어요. 원본 이미지를 VP8으로 변환하는 시간을 측정했을 때는 Opencv python보다도 빨랐거든요.
이러면서도 CPU 사용량은 Rust와 비슷했어요. 즉, CPU 사용량은 비슷한데 미디어 처리 속도는 더 빨랐던 거죠.
LiveKit 구독 비용이 생각보다 저렴했고요.
LiveKit에서 WebRTC 부분을 지속적으로 개선하고 있고, Webrtc-rs에 없던 네트워크 최적화 기능들도 이미 구현해 두어서 통신 관련해서는 신경 쓸 필요가 없었어요.
나중에 팀이 커질 때 Rust 개발자보다 Python 개발자를 구하기가 훨씬 쉬울 것 같았어요.
OpenAI, Heygen, Character.AI 같은 유명 기업들이 이미 사용하고 있어 검증된 솔루션이라는 확신도 들었고요.
특히 Livekit python sdk의 미디어 처리 성능이 Opencv python 보다 더 뛰어나다는걸 제 눈으로 확인하고 정말 감탄하고 놀라웠는데요. Livekit 팀이 미디어 처리 최적화를 위해 얼마나 노력했을지 느껴지며 엔지니어로서 경외감이 들기도 했습니다.

위에서 보여드렸던 사진보다 간결하죠? Livekit이 많은 부분을 래핑해줘서 사용자 간 연결과 부가 기능 사용을 코드 몇 줄로 끝낼 수 있습니다.
LiveKit 마이그레이션은 훨씬 수월했어요
Daily에서 Rust로 변경했던 과정에 비하면 정말 편했어요. Rust 버전에서 손수 만들었던 많은 기능들이 LiveKit Python SDK에서는 이미 함수로 제공되고 있었거든요. 덕분에 3주 정도에 마이그레이션을 완료할 수 있었습니다.
LiveKit을 사용하면서 아쉬웠던 점은 있나요?
음.. 글쎄요? 굳이 말하자면 아무래도 솔루션이다 보니 그들이 제공하는 사용법을 다시 익혀야한다는 점 정도가 있었어요. 처음에만 Livekit의 설계를 이해하는데 어려움을 조금 겪었고, 그 외엔 크게 없었던 것 같아요. Slack에 질문하면 바로 답해줘서 좋았고 문서도 이해하기 쉽게 작성되어 있어서 좋았습니다.
3. 결론: 세 가지 방식을 경험하고 배운 교훈들
저희가 경험한 세 가지 솔루션의 장단점을 아래 표로 정리해봤습니다:

이 표를 통해 솔루션별 특징을 비교해보면, 저희가 최종적으로 LiveKit을 선택한 이유가 더 명확해집니다.
Daily는 초기 개발 속도는 빨랐지만 Thread leak 문제와 높은 리소스 요구량이 큰 걸림돌이었습니다. WebRTC-rs는 자원 사용량과 비용 측면에서 우수했지만, 개발 난이도가 높고 직접 구현해야 하는 부담이 컸죠. LiveKit은 이 두 솔루션의 장점을 모두 가져오면서 단점을 최소화했습니다. Livekit은 Python의 개발 편의성과 충분한 성능, 그리고 합리적인 비용의 균형을 제공했습니다.
결국 저희 선택의 핵심은 ‘핵심 비즈니스 집중’이었습니다. 스타트업에서는 모든 기술 스택을 직접 구현하기보다 검증된 서비스를 활용해 핵심 가치 창출에 집중하는 것이 더 현명한 선택이었습니다. LiveKit으로 옮기고 나서 코드 구조도 훨씬 깔끔해졌고, 무엇보다 본 업무인 AI 아바타 개발에 집중할 수 있게 되었다는 점이 가장 큰 장점이었습니다.
물론 각 프로젝트마다 우선순위와 상황이 다르기 때문에, 이런 의사결정은 팀의 역량과 비즈니스 목표에 맞게 이루어져야 합니다. 비용 최적화가 가장 중요하다면 직접 구현이 좋을 수 있고, 빠른 시장 진입이 중요하다면 관리형 서비스가 유리할 수 있습니다.
개발자로서 가끔은 기술적 완벽함을 추구하다가 비즈니스 목표를 놓치는 경우가 있습니다. 저희 사례처럼, 때로는 직접 구현보다 검증된 서비스를 활용하는 게 더 현명한 선택일 수 있어요. 기술 선택 시 비즈니스 가치와 개발자 경험, 그리고 팀의 역량을 함께 고려하는 균형 잡힌 시각이 중요하다고 생각합니다.
여러분의 프로젝트에서도 이런 의사결정을 내릴 때 참고가 되면 좋겠네요.


