비동기 런타임 지형도
지금까지 우리는 Python의 눈으로 동시성을 보았습니다. 하지만 C10K 문제는 모든 언어가 마주한 도전이었고, 언어마다 나름의 답을 내놓았습니다. 이 장에서 Python을 넘어 Go, Rust, Node.js, Java가 이 문제를 어떻게 풀었는지 지형도를 그려 봅니다. 이 큰 그림을 보면, 우리가 배운 epoll과 이벤트 루프가 사실 모든 현대 비동기 시스템의 공통된 뿌리임을 알게 됩니다.
모두가 같은 뿌리에서 출발했다
먼저 강조할 것이 있습니다. 어떤 언어든, 가장 아래에는 우리가 배운 그 도구들이 있습니다. 리눅스라면 epoll, macOS라면 kqueue입니다. Go도, Rust도, Node.js도 결국은 운영체제의 이 기능 위에 자기만의 옷을 입힌 것입니다. 그래서 우리가 epoll을 손으로 짜 본 경험은, 이 모든 언어의 비동기 시스템을 이해하는 열쇠가 됩니다. 언어가 달라도 심장은 같습니다.
차이는 그 위에 어떤 옷을 입혔는가, 곧 개발자에게 동시성을 어떤 모습으로 보여 주는가에 있습니다.
Node.js, 이벤트 루프를 전면에 내세우다
Node.js는 이벤트 루프를 언어의 정체성으로 삼았습니다. 자바스크립트는 본래 한 흐름으로 도는 언어인데, Node.js는 그 위에 강력한 이벤트 루프를 얹어 동시성을 구현했습니다. 그 엔진이 libuv라는 라이브러리이며, 내부적으로 epoll과 kqueue를 씁니다. 우리가 PART 04와 06에서 만든 그 이벤트 루프가, Node.js에서는 언어의 기본 동작 방식인 셈입니다. 개발자는 콜백이나 async와 await로 이 루프 위에서 일합니다. Python의 asyncio와 사고방식이 매우 닮았습니다.
Go, 가벼운 흐름을 무수히 만들다
Go는 전혀 다른 길을 택했습니다. PART 04에서 스레드는 무거워서 수만 개를 만들 수 없다고 했는데, Go는 고루틴이라는 아주 가벼운 흐름을 만들어 이 한계를 정면으로 돌파했습니다. 고루틴은 일반 스레드보다 훨씬 가벼워, 수십만 개를 만들어도 부담이 적습니다.
Go의 매력은 여기에 있습니다. 개발자는 epoll이나 이벤트 루프를 전혀 신경 쓰지 않습니다. 그냥 손님마다 고루틴을 하나씩 만들면 됩니다. PART 04의 스레드 방식처럼 단순하게 짜는데, 성능은 epoll 수준으로 나옵니다. 비결은 Go의 런타임이 뒤에서 epoll을 돌리며 수많은 고루틴을 똑똑하게 배치해 주는 데 있습니다. 단순함과 성능을 동시에 잡은 것입니다.
Rust, 성능과 안전을 모두
Rust는 성능을 한 치도 양보하지 않으면서 안전까지 잡으려는 언어입니다. 비동기도 그 철학을 따릅니다. Rust의 async와 await는 Python과 문법은 비슷해 보이지만, 그 뒤에 tokio 같은 런타임이 붙어 epoll 위에서 돕니다. Rust는 실행 비용을 극도로 낮게 유지하도록 설계되어, 가장 빠른 축에 드는 네트워크 시스템을 만들 수 있습니다. 대신 배우기가 까다로운 편이라, 극한의 성능과 안전이 동시에 필요한 곳에서 주로 선택됩니다.
Java, 뒤늦게 가벼운 흐름으로
오랫동안 Java는 한 요청에 한 스레드를 붙이는 방식과, 그 한계를 넘기 위한 복잡한 비동기 라이브러리들 사이에서 고민했습니다. 그러다 최근 가상 스레드라는 것을 도입했습니다. Go의 고루틴과 비슷한 발상으로, 아주 가벼운 흐름을 무수히 만들 수 있게 한 것입니다. 덕분에 Java도 단순한 코드로 높은 동시성을 얻는 길에 합류했습니다. 거대한 생태계를 가진 언어가 같은 방향으로 움직였다는 점이 의미심장합니다.
두 갈래의 길, 하나의 목적지
지형도를 멀리서 보면 두 갈래의 길이 보입니다.
한 길은 이벤트 루프를 개발자에게 직접 보여 주는 방식입니다. async와 await로 흐름을 양보하며 한 루프 위에서 일합니다. Python의 asyncio, Node.js, Rust의 tokio가 이 길입니다. 다른 길은 가벼운 흐름을 무수히 만들어, 개발자는 동시성을 거의 의식하지 않게 하는 방식입니다. Go의 고루틴, Java의 가상 스레드가 이 길입니다.
두 길은 겉모습이 다르지만 목적지는 같습니다. 적은 자원으로 많은 연결을 감당하는 것입니다. 그리고 두 길 모두, 가장 아래에서는 우리가 배운 epoll과 kqueue가 묵묵히 돌고 있습니다. 어떤 언어를 쓰든, 어떤 멋진 추상화를 만나든, 그 바닥에는 우리가 이 PART에서 손으로 만져 본 그 도구들이 있습니다.
이것이 저수준을 배운 사람이 얻는 가장 큰 선물입니다. 화려한 추상화에 현혹되지 않고, 그 아래에서 무슨 일이 벌어지는지 꿰뚫어 보는 눈입니다. 이 눈을 가지면 새로운 언어와 도구를 만나도 두렵지 않습니다. 이름과 문법만 다를 뿐, 본질은 이미 우리 손안에 있기 때문입니다.
고성능 동시성의 세계를 한 바퀴 돌았습니다. 다음 PART에서는 또 다른 심화 주제, 통신을 안전하게 지키는 보안과 무너지지 않는 견고한 서버를 다룹니다.