업데이트됨 1개월 전
네트워크에 관한 반직관적인 진실이 있습니다: TCP의 신뢰성이 오히려 상황을 악화시킬 수 있다는 것입니다.
영상 통화 중에 20밀리초 분량의 오디오가 담긴 패킷이 손실되면, TCP는 그것을 재전송합니다. 그 패킷은 200밀리초 후에 도착할 수 있습니다. 그때쯤이면 이미 세 마디를 더 말한 상태입니다. 그 낡은 오디오를 지금 재생하면 어색하고 혼란스럽고 앞뒤가 맞지 않습니다. "신뢰할 수 있는" 전송이 패킷이 그냥 사라졌을 때보다 대화를 더 나쁘게 만든 것입니다.
실시간에서 오래된 데이터는 늦은 데이터가 아닙니다—그것은 틀린 데이터입니다. 200밀리초 전의 내 위치는 지금 내가 어디 있는지에 대한 거짓말입니다.
바로 이 때문에 게임, VoIP, 영상 스트리밍은 모두 TCP의 보장을 거부합니다. 이들은 거의 아무것도 보장하지 않는 프로토콜인 UDP를 선택합니다—무엇을 잃어버릴지 스스로 결정할 자유가 필요하기 때문입니다.
실시간을 망치는 TCP의 세 가지 죄
TCP는 실시간 경험을 망치는 세 가지 문제를 일으킵니다:
재전송 지연. 패킷이 손실되면 TCP는 그것을 재전송합니다. 재전송된 데이터는 너무 늦게 도착해 아무 의미가 없지만, TCP는 이미 구식이 된 정보에 시간과 대역폭을 낭비하며 그것을 전송합니다.
헤드 오브 라인 블로킹. TCP는 데이터를 순서대로 전달합니다. 패킷 47이 손실되면, TCP는 47이 재전송될 때까지 48, 49, 50번 패킷을 붙들어 놓습니다—애플리케이션이 그 최신 데이터를 지금 당장 필요로 하더라도 말입니다. 손실된 패킷이 줄을 막아버립니다.
공격적인 혼잡 제어. TCP가 패킷 손실을 감지하면 혼잡을 가정하고 전송 속도를 대폭 줄입니다. 실시간 애플리케이션에서 이는 네트워크가 가장 힘든 순간에 품질이 급격히 떨어지게 만듭니다. 애플리케이션은 품질을 완만하게 낮출 수 없습니다; TCP가 절벽 아래로 밀어버립니다.
UDP는 이런 문제들을 전혀 일으키지 않습니다. 다중화를 위한 포트 번호, 오류 감지를 위한 체크섬, 그리고 그게 전부입니다. 패킷은 네트워크가 전달하는 순서대로 도착합니다. 손실된 패킷은 그냥 사라집니다. 겉보기엔 아무것도 없는 것처럼 보이지만, 이것이 바로 UDP의 선물입니다: 모든 제어권이 애플리케이션으로 되돌아오는 것입니다.
게임: 완벽함보다 예측
경쟁적인 1인칭 슈팅 게임에서 50밀리초가 내 총알이 상대방보다 먼저 등록되느냐를 결정할 수 있습니다. 게임은 초당 20번, 30번, 심지어 60번씩 위치 업데이트를 전송합니다. 업데이트 하나가 손실되어도 다음 업데이트가 이미 오고 있습니다. 48, 49, 50번 업데이트가 더 최신 정보를 담아 이미 도착한 상황에서 47번 위치 업데이트를 재전송하는 건 무의미합니다.
게임 엔진은 재전송 대신 예측을 통해 패킷 손실을 해결합니다.
클라이언트 측 예측은 입력에 즉각적으로 반응하게 해줍니다. 앞으로 이동 버튼을 누르면 캐릭터가 화면에서 즉시 움직입니다—클라이언트가 서버가 확인할 내용을 미리 예측하는 것입니다. 서버의 권위 있는 업데이트가 도착하면 게임은 차이를 조율하여 캐릭터를 실제 위치로 맞추거나 궤적을 부드럽게 보정합니다.
보간법은 누락된 데이터를 숨깁니다. 위치 업데이트가 손실되어도 게임이 멈추거나 끊기지 않습니다. 알려진 위치들 사이를 부드럽게 애니메이션하며, 100밀리초 전 위치에서 현재 위치까지 자연스럽게 이동합니다. 누락된 패킷이 보이지 않게 됩니다.
렉 보상은 적중 판정을 공평하게 느끼게 합니다. 움직이는 표적을 향해 쏠 때, 서버는 지금 그들이 어디 있는지 확인하지 않습니다—네트워크 지연을 고려해 여러분의 시점에서 그들이 있었던 위치로 되감습니다. 여러분이 보고 쏜 것이 서버가 검증하는 것입니다.
게임은 또한 데이터에 철저한 우선순위를 매깁니다. 발사, 플레이어 사망, 목표물 점령 같은 중요한 이벤트는 여러 번 전송되거나 애플리케이션 계층의 확인 응답을 사용합니다. 파티클 이펙트 같은 시각적 효과 업데이트는 한 번 전송하고 잊습니다. 도착하면 좋고, 안 도착해도 게임은 계속됩니다. 모든 데이터가 동등한 대우를 받을 필요는 없습니다.
VoIP: 지터 버퍼의 역설
음성 통화는 다른 도전에 직면합니다. 짧은 오디오 공백은 받아들일 수 있습니다—인간의 뇌는 누락된 음절을 놀랍도록 잘 채워 넣습니다. 하지만 지연은 대화를 망칩니다. 단방향 지연이 150밀리초를 초과하면1, 대화가 피곤해집니다. 서로 말이 겹치고, 어색하게 멈추고, 자연스러운 대화의 리듬을 잃게 됩니다.
VoIP 시스템은 오디오를 10~40밀리초 분량의 음성을 담은 패킷으로 인코딩합니다. 각 패킷은 독립적입니다. 패킷 47이 손실되어도 패킷 48에는 다음 시간 구간의 완벽한 오디오가 담겨 있습니다. TCP였다면 47이 재전송될 때까지 48을 붙들어 놓겠지만, UDP는 48을 즉시 전달합니다.
하지만 또 다른 문제가 있습니다: 지터. 20밀리초 간격으로 전송된 패킷이 5밀리초 간격으로 도착하거나, 50밀리초 간격으로, 또는 15밀리초 간격으로 도착할 수 있습니다. 이 변동은 끊임없는 버벅임을 일으킵니다—오디오가 무작위로 빨라졌다 느려졌다 합니다.
해결책은 아름답게도 역설적입니다: 지터 버퍼는 지연을 없애기 위해 의도적으로 지연을 추가합니다.
지터 버퍼는 들어오는 패킷을 재생하기 전에 20~80밀리초 동안 보관합니다. 일찍 도착한 패킷은 기다립니다. 늦게 도착한 패킷도 버퍼 창 안에 들어온다면 살아남습니다. 버퍼는 완벽하게 일정한 속도로 오디오를 내보내며, 네트워크 지연의 불균일함을 매끄럽게 펴줍니다. 작고 일정한 지연을 대가로 버벅임을 없애는 것입니다.
패킷이 진짜 손실되었을 때—지터 버퍼의 인내심이 다할 때까지도 도착하지 않으면—VoIP는 패킷 손실 은닉을 사용합니다. 간단한 방식은 마지막으로 수신한 좋은 패킷을 반복하거나 무음을 재생합니다. 정교한 알고리즘은 앞서 수신한 오디오를 분석해 누락된 내용을 합성하며, 음높이와 음색을 외삽하여 그럴듯한 소리를 만들어냅니다. Opus 같은 현대적인 코덱은 이것을 너무 잘해서 5% 미만의 손실은 종종 알아차리기 어렵습니다.
VoIP 시스템은 또한 지속적으로 적응합니다. 네트워크 상태를 모니터링하며 실시간으로 조정합니다—네트워크가 안정적일 때는 지터 버퍼를 줄이고, 지터가 증가하면 늘리며, 손실이 급증하면 비트레이트를 64kbps에서 32kbps로 낮춥니다. 이 지속적인 조정이 가능한 것은 UDP가 애플리케이션에게 자신의 운명을 스스로 결정할 수 있는 자유를 주기 때문입니다.
영상 스트리밍: 선택적 희생
라이브 영상 스트리밍은 VoIP의 지연 민감성과 엄청난 대역폭 수요를 결합합니다. 음성은 초당 20~100킬로비트가 필요합니다. 영상은 500킬로비트에서 10메가비트 이상이 필요합니다. 그리고 영상에는 음성에는 없는 복잡한 내부 의존 관계가 있습니다.
영상 코덱은 프레임 간의 차이를 인코딩하여 압축합니다. 키프레임(I-프레임)은 완전한 이미지를 담고 있습니다—용량이 크지만 다른 프레임에 의존하지 않습니다. 예측 프레임(P-프레임과 B-프레임)은 이전 프레임에서 변경된 것만 인코딩합니다—용량은 작지만 다른 프레임에 의존합니다. 키프레임을 잃으면 그것을 참조하는 모든 프레임이 손상됩니다. 예측 프레임을 잃으면 피해가 그 프레임에 국한됩니다.
이것이 중요도의 계층 구조를 만듭니다. 스트리밍 시스템은 불균등 오류 보호를 실시합니다: 키프레임은 추가 중복성, 더 잦은 전송, 때로는 선택적 재전송까지 받습니다. 예측 프레임은 더 쉽게 포기할 수 있습니다. 패킷을 잃어야 할 때 시스템은 어떤 손실이 가장 덜 해로운지 선택합니다.
적응형 비트레이트 스트리밍은 이 철학을 품질 수준으로 확장합니다. 애플리케이션은 패킷 손실과 가용 대역폭을 모니터링하며, 실시간으로 인코딩을 전환합니다. 상태가 나빠지면 스트림은 5메가비트의 1080p에서 2메가비트의 720p로, 혹은 1메가비트의 480p까지 떨어집니다. 사용자들은 끊임없는 버퍼링보다 해상도 저하를 선호합니다. 애플리케이션은 연속성을 유지하기 위해 의도적으로 품질을 희생합니다.
**RTP(실시간 전송 프로토콜)**는 이 모든 것을 위한 뼈대를 제공합니다. UDP 위에 위치하며, RTP는 신뢰성을 강요하지 않으면서 순서 번호와 타임스탬프를 추가합니다. 함께 쓰이는 RTCP는 품질과 동기화에 대한 피드백을 전달합니다. 두 프로토콜이 함께 스트리밍 애플리케이션에 지능적인 적응에 필요한 정보를 제공합니다.
일부 스트리밍 시스템은 선택적 재전송도 구현하지만—TCP보다 더 영리하게. 키프레임 패킷이 손실되고 50밀리초의 지연 예산이 남아 있으면, 애플리케이션은 그것을 구체적으로 요청합니다. 덜 중요한 패킷이거나 시간이 부족할 때는 그냥 넘어갑니다. 무엇을 기다릴 가치가 있는지 애플리케이션이 결정합니다.
네트워크의 역할
애플리케이션은 손실과 변동을 훌륭하게 처리하지만, 네트워크 수준에서 그런 문제를 줄이는 것도 여전히 도움이 됩니다. QoS(서비스 품질) 메커니즘은 대용량 전송보다 실시간 트래픽을 우선시할 수 있습니다.
DiffServ 마킹은 라우터에게 어떤 패킷이 가장 중요한지 알려줍니다. EF(Expedited Forwarding)로 표시된 VoIP 패킷은 우선 큐잉을 받아, 대용량 파일 전송 뒤에서 기다리는 시간이 줄어듭니다. 네트워크 전체에 걸쳐 설정될 때, 이는 실시간 품질을 크게 향상시킵니다.
하지만 현실적으로는, 엔드투엔드 QoS는 경로상의 모든 네트워크의 협력을 필요로 합니다. 완벽하게 설정된 홈 라우터도 ISP가 모든 트래픽을 동등하게 처리하면 도움이 되지 않습니다. 이것이 애플리케이션이 불완전한 전달을 스스로 처리해야 하는 이유입니다—네트워크가 친절하게 행동해 줄 것이라고 가정할 수 없습니다.
더 깊은 패턴
UDP는 애플리케이션에게 특별한 자유를 선물합니다: 자신의 특정 요구에 맞게 신뢰성이 무엇을 의미하는지 스스로 정의할 자유입니다.
게임에서 신뢰성은 모든 상태가 아닌 가장 최근의 상태를 의미합니다. VoIP에서 신뢰성은 완전한 데이터가 아닌 지속적인 흐름을 의미합니다. 스트리밍에서 신뢰성은 고정된 품질이 아닌 적응형 품질을 의미합니다.
TCP는 신뢰성에 대한 단일 정의를 강요합니다—순서대로, 보장된 모든 바이트. 그 정의는 파일 전송과 웹 페이지에는 완벽하게 맞습니다. 하지만 인간의 인식이 요구사항을 결정할 때, 50밀리초가 경험이 좋게 느껴지냐 아니냐를 결정할 때, TCP의 보장은 제약이 됩니다.
우리가 인터넷을 경험하는 데 있어 가장 중요한 애플리케이션들—우리가 즐기는 게임, 우리가 나누는 통화, 우리가 보는 스트림—모두 UDP 위에서 실행됩니다. 이들은 일부 데이터가 손실될 것을 받아들이고, 지능적으로 대응하기를 선택합니다. 그 선택과 그 위에 구축된 정교한 시스템이 바로 실시간이 가능한 이유입니다.
실시간 애플리케이션에서 UDP에 관한 자주 묻는 질문
게임은 왜 더 낮은 타임아웃으로 TCP를 사용하지 않나요?
TCP의 재전송은 운영 체제 수준에서 프로토콜에 내장되어 있습니다—애플리케이션이 이를 커스터마이징할 수 없습니다. 공격적인 타임아웃을 사용해도, TCP는 여전히 순서대로 전달(헤드 오브 라인 블로킹 유발)과 혼잡 제어(스트레스 상황에서 속도 붕괴 유발)를 강요합니다. 게임은 손실된 데이터를 늦게 받는 것이 아니라 완전히 건너뛰어야 하는데, TCP는 근본적으로 그렇게 할 수 없습니다.
실시간 애플리케이션은 얼마나 많은 패킷 손실을 감당할 수 있나요?
애플리케이션마다 다릅니다. 현대 코덱을 사용하는 VoIP는 5% 손실에서도 거의 알아차리기 어려운 수준으로 처리합니다. 게임은 보간법으로 1~2% 손실을 가릴 수 있지만 5% 이상에서는 "렉"이 느껴지기 시작합니다. 영상 스트리밍의 허용 범위는 어떤 패킷이 손실되느냐에 달려 있습니다—5% 무작위 손실은 괜찮을 수 있지만, 키프레임 손실은 다음 키프레임이 도착할 때까지 눈에 띄는 화질 저하를 일으킵니다.
UDP 트래픽이 TCP 트래픽과 경쟁하면 어떻게 되나요?
UDP에는 내장된 혼잡 제어가 없어, 네트워크가 혼잡할 때 자동으로 속도를 줄이지 않습니다. 이로 인해 UDP가 TCP 트래픽을 밀어낼 수 있습니다—또는 TCP가 공격적으로 물러나서 UDP에 더 많은 대역폭을 넘겨줄 수 있습니다. 잘 설계된 실시간 애플리케이션은 네트워크를 배려하는 좋은 시민이 되기 위해 자체적인 전송 속도 제한을 구현합니다.
신뢰할 수 있는 데이터가 일부 필요한 애플리케이션에 UDP를 사용할 수 있나요?
네. 많은 실시간 애플리케이션이 UDP 위에 선택적 신뢰성을 구현합니다. 게임은 위치 업데이트를 신뢰 없이 전송하면서 "플레이어 사망" 같은 중요한 이벤트는 애플리케이션 수준의 확인 응답과 재시도를 사용해 전송할 수 있습니다. ENet 같은 라이브러리와 QUIC 같은 프로토콜이 이 하이브리드 방식을 제공합니다.
빠른 연결에서도 게임이 끊기는 이유는 무엇인가요?
대역폭(속도)과 지연 시간(딜레이)은 다릅니다. 빠른 연결은 많은 양의 데이터를 전달하지만 반드시 빠르게 전달하지는 않습니다. 게임에서 주로 필요한 것은 낮은 지연 시간—서버까지의 짧은 왕복 시간입니다. 핑이 100ms인 1 Gbps 연결은 핑이 20ms인 50 Mbps 연결보다 게임에서 더 나쁘게 느껴집니다.
참고 자료
이 페이지가 도움이 되었나요?