[▲ 문대경 아이펀팩토리 대표]
인벤에서는 게임업계 1.5세대 인물로 안정적인 게임서버엔진인 아이펀 엔진을 개발한 아이펀팩토리의 문대경 대표님을 모시고 서버 관련 컬럼을 기고 받고 있습니다.

문대경 대표는 서울대 컴퓨터 공학과를 졸업하고 미국 UC Berkeley 에서 컴퓨터 공학 박사 학위를 수여하였고, 1999년 넥슨 입사 후 2005년까지 넥슨에서 출시되는 다수의 게임 개발 프로젝트에서 서버 프로그램을 책임졌습니다.

아이펀팩토리가 개발한 '아이펀 엔진'은 네트워크 처리, DB처리, 분산 처리 등 게임 서버 구현에 필요한 필수 기능을 제공하여 효율적인 게임 개발이 가능하게 하는 게임 서버엔진입니다.

그 여섯 번째 시간으로TCP와 UDP에 대한 이야기를 가지고 설명하도록 하겠습니다.


* 본 내용은 본지의 편집방향과 일치하지 않을 수도 있습니다.


벌써 여섯 번째 연재다. 처음에 이렇게 길게 연재할 수 있을까 생각했는데, 인내를 가지고 기회를 주신 인벤 측과 재미도 없는 글을 가끔 들러주는 독자분들께 깊은 감사를 드린다. 이번 칼럼에서는 TCP/IP의 계층화와 게임 구현에 사용되는 TCP 와 UDP 의 차이점에 대한 이야기를 해보겠다.


■ IP. 모든 것의 허리.


아마 많은 프로그래머들에게 익숙할 이름인 TCP/IP 라는 프로토콜 체계는 나중에 두 개로 분리된 것이 아니라, 인터넷 프로토콜 표준이 제정될 때부터 지금처럼 두 개로 분리되어 제안되었다. David Clark 교수님의 88년 논문은 TCP/IP 를 설계할 때 어떤 우선순위로 설계되었는지에 대해서 설명하고 있는데, 그중에서 “여러 종류의 데이터 전달 방식을 지원할 것 (multiple types of delivery services)” 이라는 목적을 달성하기 위해서 가장 기본이 되는 기능만을 IP 에 포함시키고, 그 위에 TCP 라는 새로운 계층을 얹는 형태가 된 것이다. (관련해서 설명이 더 필요한 분들은 블로그에 별도로 정리한 “인터넷은 어떤 기준으로 설계되었을까” 라는 글과 “모듈화, end-to-end 원칙, 그리고 fate-sharing” 이라는 글을 참고하길 바란다.) 그리고 이를 도식화 하면 TCP/IP의 관계는 다음 그림처럼 모래 시계 모양으로 표시한다.

▲ TCP/IP 모델은 IP 가 허리인 모래시계 형태로 표현된다. (출처: http://www.ifwe.co/code/posts/what-title-ii-means-for-tcp/)

이 그림에서 주의 깊게 바라볼 부분은 잘록한 허리 부분인 IP 다. 이건 후세의 누군가가 꿈보다 좋은 해몽으로 붙여놓은 것도 아니고, 우연의 결과물도 아닌 TCP/IP 의 의도된 디자인인데, 어떤 통신 관련 하드웨어 기술이든 IP 만 구현하면 되고, 어떤 응용 프로그램이든 IP 위에서만 동작하게 하면 된다는 실로 엄청난 의미를 내포한다.

유선 랜 (Ethernet 이라 불림)과 WiFi 모두 IP 주소를 쓰고, 우리가 잘 인식하지는 못하지만, Bluetooth, LTE 나 3G 도 통신을 할 때는 IP 주소를 부여받아 통신한다. 그 덕에 까페의 WiFi 에서나 길을 걸으면서 LTE 상에서도 똑같은 앱을 이용해 스마트폰에서 음악을 들을 수 있다. 즉 어떤 통신 방법이든 IP 를 구현하는 한, IP 위에서 동작하던 응용 프로그램을 지원하는 데 문제가 없으며, 어떤 응용 프로그램이든 IP 로 통신한다는 것을 전제만 하면 IP 를 지원하는 어떤 하드웨어 기술에서도 동작할 수 있다.

▲ 과거 KT 의 광고 중에 All IP 라는 타이틀로 광고가 나간 적이 있었다. 물론 당시 KT 의 의도는 모든 장비들을 IP 수준에서 연결할 수 있다라는 장비의 다양성과 커버리지에 대한 이야기였겠지만, 실제로 IP 는 어떤 하드웨어 기술이라도, 어떤 응용 프로그램이라도 IP 만 가정하면 상호 동작할 수 있는 것을 가정해서 디자인되었다. (출처: KT)

그래서 이게 뭣이 중허냐고, 이미 당연하게 잘 쓰고 있었다고 할지도 모르겠지만, 이 덕분에 응용 프로그램과 하드웨어 기술은 독립적으로 발전할 수 있게 되었다. 그리고 각각이 독립적으로 발전할 수 있다는 것은 둘이 얽혀서 같이 바뀌어야 하는 것보다 훨씬 더 빠른 기술적 진보를 이루게 해준다. 여러분이 3G 폰을 쓰다가 LTE 폰으로 바꾸면서 앱을 설치하는 방식이 바뀌거나 3G 용 앱, LTE 용 앱을 구분하면서 설치했는가? 당연히 3G, LTE 구분 없이 애플의 App Store 나 구글의 Google Play 를 그대로 이용해 왔을 것이다.

여러분이 새 앱을 깔기 위해서 스마트폰을 바꿔야 한 적이 있는가? 특정 통신사나 휴대전화 제조사에 속한 앱이 아닌 한, 정상적인 경우라면 당연히 그런 일이 없었을 것이다. 이처럼 IP를 모래 시계의 허리로 만들고 “뭐든 IP 만 지원하면 된다” 라는 것은 IP 위아래 기술을 독립적으로 급속도로 발전시킬 수 있는 신박한 디자인이었다. 이름부터 Internet Protocol (IP) 이라고 해서 “인터넷은 이것만 구현하면 된다” 는 의미를 내포한다.

인터넷이라는 것은 영어로 the Internet (대소문자 주의. 정관사 주의) 어로서, internet 이라는 단어는 ‘사이에’ 라는 뜻의 ‘inter-’ 라는 접두사와 ‘네트워크’를 의미하는 ‘net’ 이 합쳐진 말이다. 즉 각각 독립적으로 떨어져 있던 네트워크들을 서로 연결한 것이 인터넷이다. 그리고 이렇게 서로 독립적이던 네트워크들을 연결하기 위한 프로토콜이 ‘인터넷 프로토콜 (Internet Protocol)’, 줄여서 ‘IP’ 다. 그리고 IP 가 이렇게 네트워크들을 연동하는 프로토콜이기 때문에 IP를 “네트워크 수준 프로토콜” 이라고도 한다.

IP 가 서로 다른 네트워크를 연결한다고 했으니 각각의 네트워크는 독립된 관리자가 존재할 것이다. 적어도 카이스트 네트워크 관리자가 포항공대 관리자와 같을 거라고는 생각되지 않는 것처럼 말이다. 그리고 각 네트워크가 독립적으로 동작한다는 것은 여러 네트워크를 거쳐야 하는 IP 입장에서는 ‘반드시 통신 가능하다’ 라는 것을 보장해줄 수 없다는 것을 뜻한다. 카이스트 네트워크를 거쳐가야 되는데 카이스트가 연휴기간 동안 시스템 점검을 할 수도 있는 것 아닌가? 그래서 열심히 통신해보려고는 하는데 반드시 된다는 보장은 못 한다는 의미로, IP를 설명하는 중요한 특성으로 best effort 라는 표현을 쓴다.


■ TCP, 불안정성 위에 구축한 안정성


이처럼 기간(基幹) 프로토콜이 통신의 안정성을 보장해주지 못한다면 그걸 이용해서 뭔가를 한다는 것은 굉장히 괴로운 일이 된다. 이건 마치 꿀렁거리는 바닥 위에서 쩜 10원 고스톱을 치는 것과 같다. 그런데 꿀렁거리는 바닥이라고 하더라도 그 위에 딱딱한 매트리스를 깔면 찰지게 고스톱을 칠 수 있는 것과 마찬가지로, 우리는 통신의 안정성을 보장해주지 못하는 기간 프로토콜 위에 매트리스 같은 뭔가를 깔아서 안정적인 통신이 가능하게 할 수 있다. 통신에서 그 매트리스 같은 무언가가 TCP다.

TCP는 Transmission Control Protocol이라는 글자의 약자를 딴 이름인데, 전송 제어 프로토콜 정도로 번역될 것 같다. 그러니까 안정적으로 패킷이 가지 못하는 경우 이걸 캐치해서 안정적으로 갈 수 있게 전송을 제어하는 역할을 하는 프로토콜이라는 뜻이다. 그런데 어떻게 불안정한 것 위에 안정성이라는 것을 보장해주는 것이 가능할까? 그 비결은 ACK 와 타임아웃, 그리고 재전송이다.

ACK 는 받은 걸 받았다고 알려주는 것을 의미한다. Acknowledgment 라는 영어 단어의 앞 세 글자를 딴 것인데 영어 단어에서 의미하듯, “잘 받았습니다.”라는 뜻이다. TCP 데이터가 물줄기처럼 흐른다고 해서 stream이라는 표현을 쓰는데, 이것도 복잡하니까 그냥 다들 그러는 것처럼 패킷이라고 하자. TCP는 패킷을 받을 때마다 ACK 이라는 별도의 패킷을 만들어서 “잘 받았습니다, 제가 다음에 받아야 하는 것은 몇 번 패킷입니다.”를 보낸 쪽에 알려준다. 그리고 보낸 쪽에서는 이걸 이용해서 잘 가고 있는지를 판단한다.

ACK 은 굉장히 고분고분한 착한 학생과 같다고 생각하면 된다. 매일 껨방에 출근 도장 찍던 착한 학생이 어느 순간 오지 않는다고 해보자. 그럼 껨방 주인아저씨는 매출 걱정보다 먼저 학생에게 무슨 일이 생겼다고 생각할 것이다. 우리 사회는 아직 훈훈하니까. 그리고 일정 기간 학생이 오지 않으면 심각한 일이 발생했다고 생각해서 경찰서에 신고할 것이다. 같은 일이 TCP 에서도 일어난다. 패킷을 받은 쪽은 ACK 을 보내서 패킷을 보내는 쪽에게 잘 받고 있다는 것을 알려주는데, ACK 이 일정 시간 동안 오지 않으면 패킷을 보내는 쪽에서는 특단의 조치를 취한다. 바로 다시 보내기이다.

ACK 이 오지 않는 경우는 두 가지를 생각해볼 수 있다. 보낸 패킷이 받는 쪽에 도착을 못해서 받는 쪽이 ACK 을 보내야 하는 것을 아예 모르는 경우와 받는 쪽은 패킷을 받았고 ACK 도 보냈는데 ACK 이 불안정한 IP 때문에 중간에 배달 사고를 만난 경우이다. 전자는 패킷을 다시 보내는 것이 말이 되는데 후자는 ACK 만 보내면 될 것 같다. 하지만 서로 통신을 못하는 상황에서는 이 둘을 효율적으로 구분하면서까지 문제를 해결하지는 못한다. 그 때문에 어떤 경우든 일정 시간 동안 ACK 을 못 받으면 패킷을 다시 보내버린다. 그게 약간 더 비효율적일 수 있지만, 상당히 더 확실하고 속 편한 방법이다.

정리하자면, TCP는 패킷을 보낼 때 다음 그림과 같은 일을 겪는다. 1) 받은 패킷에 대해서 잘 받았다는 뜻으로 ACK 을 보내준다. 2) ACK 을 일정 시간 동안 못 받으면 원래 패킷을 재전송한다.

▲ TCP는 ACK 이라는 특별한 패킷을 이용해서 받는 쪽에 패킷이 잘 도착했다는 것을 알려준다. 그런데 보내는 데이터가 유실되든, ACK 이 유실되든 할 때에는 이 ACK 이 전달될 수 없고, 보내는 쪽에서는 어떤 일이 발생했는지 알지도 못하게 된다. 그래서 보내는 쪽은 일정 시간이 지나는 동안 ACK 이 도착하지 않으면 바로 재전송한다.


■ TCP ACK 의 두 가지 방법: cumulative ACK, selective ACK


물론 TCP 가 이렇게 패킷을 하나씩만 전송하지는 않는다. Sliding window이라는 개념을 이용해서 보낼 수 있는 만큼을 연속으로 계속 보낸다. (우리가 쓰는 MS Windows가 아니라, 창문을 조금 열고 많이 열고 하는 것처럼 양을 조절한다는 뜻에서 window이다) 하지만 이처럼 ACK 을 안 받고 연속으로 쏘는 것은 물량에 제한이 있다. 그리고 ACK 을 못 받는 순간 결국 더는 아무것도 안 보내고 기다리는 상황이 발생한다. 재전송이 결정될 때까지 말이다.

그런데 한 번에 하나씩 보내고 받고 하는 것이 아니라 여러 개를 연속으로 보낸다면, 가운데 패킷이 유실된 경우는 어떻게 될까? 매우 좋은 질문이다. TCP의 기본 동작은 뭔가 유실되면 그 뒤에 아무리 정상적으로 와도 다 버리고 유실된 것부터 재전송하는 것이다. 이건 상당히 비효율적으로 보인다. 왜 유실 된 것만 다시 보내지 않고 이렇게 할까?

전자는 ACK 의 의미가 “앞의 것은 다 받았고 이걸 못 받았어. 여기부터 다 다시 보내줘” 라는 뜻이 되는 것이고, 후자는 ACK 의 의미가 “이것은 받았어. 앞에거 뒤에거를 받았는지는 따로 알려줄께.” 가 된다. 그래서 전자를 “앞의 것은 다 받았다” 라는 뜻으로 누적적이라고 해서 cumulative ACK 이라고 하고 후자를 받았는지 여부를 선택적으로 알려준다고 해서 selective ACK 이라고 한다.

Cumulative ACK 은 한 번에 주르륵 여러 개 패킷을 보냈다고 하더라도 문제가 되는 ACK 하나만 기억하면 된다. 반면 selective ACK 은 여러개 패킷에 대해서 각각 ACK 을 받았는지를 모두 기억해야 된다. 다시 말해 cumulative ACK 은 기억해야 되는 것도 적고, 구현도 간단한 반면 selective ACK 은 기억해야되는 것도 많고 구현도 복잡해지는 것을 의미한다. 이는 효율성과 복잡성에 대한 전형적인 트레이드오프 예라고 할 수 있겠다.

초기의 TCP 는 cumulative ACK 을 기반으로 했다. 그런데 뒤에 것을 다 버리다보니 비효율적이고, 이런 비효율성은 기간 프로토콜인 IP 가 패킷을 많이 까먹을수록 급속도로 비효율적으로 변하기 시작했다. 예를 들어 무선 통신에서의 IP 라면 까먹는 것도 많을테니 TCP 는 상당히 많은 양을 재전송 해야 된다. 그리고 사용자 입장에서 이건 매우매우매우매우 느리다고 느끼게 된다.

통신망이 10% 불안정해지면, 속도가 10% 를 까 먹는 것이 아니라 몇 배 이상 속도가 안 나게 된다. 그 때문에 나중에는 selective ACK 을 TCP의 옵션으로 채택했다. 그리고 보내는 쪽과 받는 쪽 모두 “나 selective ACK 할 줄 아는데…” 라고 해야만 동작한다. 마치 오락실에서 꼬마 아이가 애타는 눈빛으로 “나 이거 할 줄 아는데…” 라고 해도 마음씨 좋은 아저씨도 같은 마음이 아니라면 공짜 게임을 얻어 할 수 없는 것 처럼..

▲ TCP 는 초기에는 순서대로 앞의 것을 모두 받게끔 디자인되었다. 즉, 앞의 것이 유실되고 뒤에 것이 들어오더라도 뒤에것을 다 버리고 나중에 재전송받는다. 이 경우 1% 패킷 손실률이라고 해도, 어느 패킷이 유실되는지에 따라 전체 전송 효율에 큰 차이가 생긴다. 100개를 한 번에 보낼 수 있을 때, 맨 처음게 유실되면 정상적으로 도달한 99개를 모두 버리고 처음부터 다시 보내지만, 맨 마지막게 유실되면 맨 마지막 100번째 패킷만 다시 보내게 된다. 전자가 훨씬 느리게 느껴지며, 패킷 유실이 많은 무선 환경에서 문제는 더욱 심각해진다. 그 때문에 TCP에 누락된 것만 다시 보낼 수 있는 기능이 추가되었다. (출처: 양 끝 핸드폰, 서버 아이콘 (유료구매), 가운데 무선 공유기 아이콘 (By Chamelon Design, iconfinder.com, Creative Common License))



■ TCP 교통 체증과 서비스 붕괴 사건


이렇게 ACK 을 보내는 방식으로 TCP는 불안정한 IP 위에서 안정성을 보장해줄 수 있었다. 그런데 모든 것이 해결된 것처럼 생각했는데, 1986년 10월 일대 사건이 일어난다. 당시 최첨단 32 kbps 회선이 40 bps 밖에 전송을 못 하는 일이 발생한 것이다. 아마 어떤 분들은 32 kbps 라는 속도에서 이미 “읭? -_-a” 라는 반응을 보인 분도 있겠지만, 어쨌거나 당시 최첨단이었고, 그 네트워크 연결이 무려 1,000분의 1의 속도밖에 못 내는 일이 발생한 것이다.

연구자들이 확인해보니 중간에 패킷을 전달하는 장비인 라우터가 문제였다. 그런데 그 장비가 고장이라는 뜻이 아니라 인터넷이 점차 여러 연구 기관에서 쓰다 보니 그 라우터가 과부하가 걸려서 상당히 많은 패킷을 놓치고 있던 것이다. 그런데 TCP는 패킷이 유실되는 확률이 올라가면 cumulative ACK 과 재전송 때문에 그 속도가 드라마틱하게 느려진다고 앞에서 설명했다. 그리고 모두 재전송을 시도하면 상황이 더 악화한다. 더 많은 패킷이 들어오니 라우터는 더 부하가 걸리고 더 많은 패킷을 놓친다. 그렇게 인터넷은 종말을 맞는 듯했으나, 이때 인터넷을 구한 히어로 반 제이콥슨 (Van Jacobson) 이 등장한다.

▲ Van Jacobson (1950~): 인터넷을 구한 불세출의 영웅으로 추앙받는 분이시다. tcpdump 와 traceroute 도 이분이 만드셨다. UC Berkeley 대학의 뒷산에 있는 Lawrence Berkeley National Lab (LBNL) 에서 오랫동안 연구자로 있었다. 참고로 이 연구소의 Lawrence 라는 이름은 1961년 입자 가속기를 만든 로렌스의 이름을 땄다. 그리고 헐크의 배경으로도 유명한 곳이다. 굳이 버클리에 있는 연구소에 로렌스라는 이름을 붙인 이유는 로렌스가 UC Berkeley 에서 입자 가속기를 만들었기 때문이다. (사진 출처: wikipedia)


그는 패킷이 유실되면 무식하게 바로 쏴대지 말고, 이게 중간에 장비가 과부하 걸린 것일 수도 있으니 양을 조절하자고 제안한다. 양을 얼마로 제한하느냐면 일단 1개만 보내는 걸로…. 앞에서 여러 패킷을 주르륵 보낼 수 있다고 한 것을 기억하는가? 이전까지는 유실되면 유실된 것부터 그 주르륵의 남은 개수만큼을 보냈었다. 이걸 1개로 제한하는 획기적인 아이디어였던 것이다. 그리고 그 방법은 기가 막히게 적중했다. (물론 1개 보낸 패킷이 성공적으로 도달하면 빠른 속도로 동시에 보내는 패킷 개수를 회복해 나간다.)

이처럼 중간의 장비가 과부하 걸려서 패킷들을 놓치는 것을 길에 차들이 몰려서 꽉 막히는 것에 빗대서 체증 (congestion) 이라고 한다. (장비 체증이 아니라 네트워크 체증 (network congestion) 이라고 부른다.) 그리고 라우터 장비 등은 교통 교차로에 비유된다. 이 사건 이후로 TCP에는 이런 교통 체증을 벗어나기 위한 반 제이콥슨의 알고리즘이 도입되었다. TCP Tahoe의 등장이다. Tahoe 는 Android 의 버전들을 마시멜로우, 킷캣 이런 이름을 붙여서 하는 것처럼 TCP 버전을 구분하는 이름이다.

그리고 반 제이콥슨의 알고리즘을 개량하기 시작해서 TCP Reno, TCP Vegas 등이 등장하기 시작했다. (사실, Tahoe, Reno, Vegas 는 모두 지명이다. Tahoe는 반 제이콥슨이 근무한 로렌스 버클리 연구소에서 레저를 즐기러 가는, 캘리포니아와 네바다의 경계에 걸친 북쪽의 거대 호수의 이름이고, Reno 는 그 호수 옆 도박으로 유명한 네바다의 도시이다. 그리고 도박으로 유명한 도시하면 Las Vegas 다보니, 그 다음 버전으로 Vegas 가 나온 것이다. Tahoe는 미국 인디언 말로 큰 호수라는 뜻이라고 한다. 우리 나라말로 타호 라고 했을 때도 큰 호수가 연상되는 것으로 보아 두 언어 사이에 상관 관계가 있다는 추측도 있다.)

어쨌거나 반 제이콥슨은 1986년의 써드 임팩트 수준의 인터넷 붕괴를 막아냈다. 그리고 이제부터 패킷이 유실되면 뜨거운 것에 닿았을 때 손을 움츠리는 것처럼 TCP는 보내는 쪽에서는 얼른 양을 줄여서 “조심조심” 보내게 되었다.


■ 단순한 껍데기인 UDP


앞에서 IP는 온 힘을 다해서 보내기는 하지만 보장은 못 한다고 했다. 그 때문에 IP의 패킷 전달 서비스는 불안정하다고 했다. 갈 때도 있고 안 갈 때도 있다. TCP 는 이런 불안정한 것 위에 안정적인 패킷 전달을 구현하였다. 대신 세상에 공짜가 없듯이, TCP 의 안정성은 ACK 라는 부가적인 패킷과 타임아웃, 재전송이라는 비용을 지불하고 만들어졌다. TCP 는 자신이 이런 복잡한 절차를 모두 마치고 패킷들이 모두 안정적으로 수신된 다음에야 비로소 프로그램에 패킷을 건네준다.

그 때문에 TCP 는 느리다. 패킷 유실이 없다면 마찬가지 아닐까 생각할 수도 있지만, TCP 는 네트워크 체증을 막기 위해서 한 번에 보낼 수 있는 패킷 개수가 정해져 있다고 했다. 그래서 처음에는 적은 숫자를 보내고 유실이 없으면 이 숫자를 늘려나간다. 그 때문에 TCP 는 유실이 없다고 하더라도 한 번에 보낼 수 있는 패킷 갯수 제한 때문에 느리다. (그리고 그것 말고도 바로 보내지 않고 Nagle 알고리즘이라는 버퍼링 기법 등 구현상의 이유 때문에도 느리다.)

그럼 이렇게 무거운 TCP 를 쓰고 싶지 않으면 어떻게 해야 되나? 앞에서 인터넷을 설계할 때 다양한 서비스들을 돌릴 수 있도록 설계되었다고 했다. 만일 내 서비스가 안정성 대신에 속도가 중요하다고 하면 어떻게 해야 될까? 가장 흔하게 드는 예가 인터넷 전화라고 불리는 VoIP 다. 인간의 언어는 대충 단어 몇 개 못 알아들어도 전체 문장을 이해하는 데 문제가 없는 경우가 많다. (이를 Context Sensitive Language 라는 표현을 쓴다.) 조금 화가 날 수는 있다.

오히려 상대가 한 말이 늦게 전달되면거나 단어 사이 간격이 들쭉날쭉하면 더 알아듣기 힘들다. 그러다가 딥빡이 오게 되고, 그러다가 애인과 싸우게 된다. 그때문에 VoIP 는 인류의 평화와 연인들의 행복을 위해 패킷 재전송은 필요 없이 가능한 한 빠르게 전달하는 것을 목표로 한다. 그럼 이런 경우는 어떻게 하나? UDP 가 그런 역할을 한다.

사실 UDP 는 하는 일이 없는 더미라고 생각하면 된다. “물은 셀프입니다" 도 아닌데, “TCP 를 안 쓸 거면 그냥 더 낮은 수준의 IP 를 그대로 쓰세요.” 라고 하기에 좀 뭣하니까 TCP 랑 구색을 맞추기 위해서 껍데기로 UDP 가 만들어졌다. UDP 가 단순 껍데기 역할이기 때문에 그 특성은 IP 자체의 특성과 마찬가지다. 불안 정적인 best effort 에, 그냥 바로 쏘고 만다.

IP 패킷이 어떤 방식으로 경로를 선택하는지는 내 IGC 발표 슬라이드를 참고해주기 바란다. 어쨌거나, IP 는 최적의 경로를 따라 가는 것이 아니다. 그때문에 IP의 꼭두각시인 UDP 역시 최적의 경로로 가지는 않는다. 보통 게임을 만들 때 “속도 때문에 UDP 를 쓴다.” 라고 하는데 이는 UDP 가 최적의 경로를 따라 간다는 뜻이 아니라 TCP 에 비해서 하는 일도 없고, 주변 신경도 안쓰기 때문에 상대적으로 더 빠르다는 뜻이다. TCP 와 달리 안정성 확보를 위해서 해야되는 일도 없고, 네트워크 체증 생각도 안하고 마구잡이로 보낼 수 있다.


■ UDP vs. TCP


TCP 는 네트워크 체증에 한 번 크게 데였기 때문에 조심조심하는데, UDP 는 이걸 신경 쓰지 않고 마구잡이로 보낸다고 했다. 그렇다면 이 둘이 동시에 존재하면 어떻게 될까? 아주 재미있게도, TCP 는 네트워크 체증이 생긴 줄 알고 전송 속도를 줄이고, 그 줄인 속도를 아싸 조쿠나 하면서 UDP 가 잡아먹는다. 이는 TCP 가 100 개 있고 UDP 가 1개 있어도 마찬가지가 된다. TCP 는 모두 빌빌대게 되는데 UDP 혼자 살아남는다. 그래서 UDP 는 통신 프로토콜에서 배쓰 같은 존재라고 생각할 수 있다. 이 때문에 UDP 도 TCP 처럼 네트워크 체증을 고려하게 하자는 제안도 있었고 (TCP-friendly UDP), 통신사에서는 UDP 를 좋아하지 않는다는 말도 있다.

한 가지 재미있는 것은, 게임에 쓸 프로토콜을 선택하는 데 있어서 한국 사람들과 미국 사람들의 차이점이다. 한국에서는 뭘 만들든 TCP 를 먼저 생각한다. 제공되는 안정성과 명시적인 연결 끊김이라는 것이 프로그램 짜는데 엄청 편하기 때문이다. 반면, 미국 개발자들은 게임을 “반응성이 중요한 응용프로그램” 으로 생각한다. 그 때문에 TCP 대신 UDP 를 먼저 생각한다. 내가 유학 시절 학부생 네트워크 과목 조교를 하면서 머드 게임을 만드는 숙제를 내줬었는데, 거의 모든 학생들이 UDP 로 구현을 해온 것을 보고 나는 깜짝 놀랐었다.

그래서 TCP 의 편리함과 UDP 의 속도를 결합해보자고 시도되는 것이 Reliable UDP 라는 것이다. 그런데 나는 Reliable UDP 를 다소 이단으로 생각하고 그게 만능이라고 신봉되는 것이 다소 사이비 같은 느낌을 강하게 갖는다. 그 이유는 TCP 와 UDP 가 각각 안정성과 속도라는 상충되는 목표를 가지고 만들어졌는데, 그 둘의 단점을 제외하고 장점만을 취한다는 것은 불가능하기 때문이다. 그건 마치 몸은 순발력 있게 보통 키에 슬림하면서 200kg 역기를 들어올리는 근력을 갖는 것과 같다. TCP 와 UDP 가 이미 안정성과 속도라는 트레이드 오프의 결과인 만큼 트레이드 오프를 무시하는 먼치킨은 존재할 수 없다. (있었다면 그게 표준이 되었을 것이다.)

예를 들어, 만일 UDP 를 이용해 TCP 수준의 안정성을 구현한다면 결국 그건 TCP 내부에서 처리하는 전송 알고리즘을 다 구현하는 것을 의미하고 결국 TCP 만큼 느려진다. 그렇다면 결국 TCP 가 제공하는 기능 중 몇 가지를 제외하고 대신 속도를 얻는 것이 Reliable UDP 인데, 이건 응용 프로그램마다 어떤 걸 희생할 수 있는지에 따라 달라질 수 밖에 없기 때문에 어떤 경우든 요구사항을 가장 잘 만족하는 Reliable UDP 라는 것은 존재할 수 없기 때문이다.


■ 맺음말


이번 회에서는 모든 것의 허리 역할로 IP 가 존재하며, 그 위에 안정성을 위한 TCP, IP 그대로의 맛을 추구하는 UDP 에 대해서 장황하게 설명했다. 이 때문에 TCP 를 쓸지 UDP 를 쓸지는 무엇을 만드는지에 따라 적절하게 달라져야 된다는 것을 이야기했다. 이와 관련해서 내가 일반적으로 하는 조언은 오소독스한 내용이다. “안정성이 필요하시면 TCP, 속도가 필요하시면 UDP를 쓰세요. 대신 UDP 는 유실될 수 있으니, 유실돼도 스테이트 관리가 가능하게 내용을 실어 보내셔야 합니다.”