Socket.IO 서버 벤치마킹

발행: (2026년 1월 20일 오전 12:59 GMT+9)
9 min read
원문: Dev.to

Source: Dev.to

코드만 조금만 바꾸면 가지 다른 Socket.IO 서버를 만들 수 있습니다 – 그리고 기본 서버를 사용하는 것은 절대 원하지 않을 겁니다.

저는 런타임(Bun, Node)과 WebSocket 서버(ws, uWebSockets.js, bun‑engine)의 조합을 비교해서 부하가 걸렸을 때 어떻게 동작하는지 살펴볼 것입니다.
이 서버들을 Socket.IO와 함께 사용하는 방법은 공식 문서를 참고하세요.

설정

경쟁자

라벨런타임WebSocket 서버
node‑wsNode.js 24.11.1ws
node‑uwsNode.js 24.11.1uWebSockets.js v20.52.0
bun‑wsBun 1.3.6ws
bun‑nativeBun 1.3.6@socket.io/bun-engine 0.1.0

*ws*는 기본값입니다. 순수 JS이며 신뢰할 수 있지만 빠르지는 않다 (스포일러: 그렇습니다).

테스트 서버는 최근 프로젝트 **Versus Type**의 백엔드를 약간 수정한 버전이며, 실시간 PvP 타이핑 게임입니다. 인증, 속도 제한, DB 호출을 제거했습니다.

부하 생성기에는 **Artillery**와 artillery-engine-socketio-v3 플러그인을 사용해 수천 명의 동시 클라이언트가 WebSocket으로 연결하고 게임을 플레이하도록 시뮬레이션합니다.

하드웨어

역할인스턴스사양
ServerAWS Standard B2als v22 vCPU, 4 GB RAM, Ubuntu 22.04 LTS
AttackerAWS Standard B4als v24 vCPU, 8 GB RAM, Ubuntu 22.04 LTS

공격 흐름

  1. Artillery는 초당 4개의 가상 사용자를 생성합니다.
  2. 각 사용자는 **/api/pvp/matchmake**를 호출합니다.
  3. 서버는 매치메이킹 알고리즘을 실행하고 룸 ID를 반환합니다 (룸당 최대 6명).
  4. 사용자는 WebSocket을 통해 연결하고, 룸에 참여하여 게임 상태(텍스트)를 받습니다.
  5. 서버는 카운트다운을 방송하고, 플레이어는 0이 될 때까지 기다립니다.
  6. 사용자는 분당 60타 (60 WPM) 속도로 키 입력 이벤트를 전송합니다 (≈ 1 이벤트 / 200 ms).
  7. 키 입력마다 서버는 입력을 검증하고, 상태를 업데이트하며, 새로운 상태를 룸 내 모든 사람에게 방송합니다.
  8. 사용자는 지연 시간을 추적하기 위해 매초 ping 이벤트를 보냅니다.

텍스트는 충분히 길어 벤치마크가 끝나기 전에 게임이 종료되지 않도록 합니다.
(실제 서버는 또한 매초 시스템 메시지와 WPM 업데이트를 방송합니다.)

GitHub 저장소: (서버, 클라이언트 및 결과 데이터를 포함합니다).

Source:

결과

전체 승자

Node + uWS (파란선)은 메모리 사용량을 제외한 모든 지표에서 다른 모든 구성보다 뛰어났으며, 메모리 사용량에서는 Bun이 1위를 차지했습니다.

0 – 800 클라이언트

0‑800 Graph

  • Bun 서버는 이벤트‑루프 지연~0 msNode 서버에 비해 현저히 낮습니다.
  • node‑uws 가 가장 안정적입니다.
  • ws 서버(​Bun 및 Node 모두) 는 p95 지연이 15‑20 ms 로 상승합니다.
  • 나머지 두 구성은 ≈ 5 ms 수준을 유지하며 견고합니다.

800 – 1 500 클라이언트

800‑1500 Graph

  • node‑ws 가 급격히 폭발— 1 k 클라이언트 부근에서 지연이 급증합니다.
  • bun‑wsbun‑native 도 뒤따르지만, 발생 시점이 늦습니다.
  • 이벤트‑루프 지연도 동일한 패턴을 보입니다.
  • CPU 사용량은 node‑ws 가 ~1 k 클라이언트에서 100 %, bun‑ws 가 ~1.2 k, bun‑native 가 ~1.3 k 에서 **100 %**에 도달합니다.
  • node‑uws≈ 80 % CPU 를 유지하며, 1.5 k 클라이언트에서도 다른 구성과 비슷한 상승률을 보입니다.
  • 처리량은 node‑uws 를 제외한 모든 구성에서 불안정해집니다.
  • 메모리: node‑uws 가 이상하게 감소했다가 다시 상승하는 모습을 보이며, Bun 서버는 전체적으로 메모리 사용량이 적음 – Bun 의 메모리 관리가 인상적입니다.

핵심: node‑ws 는 부하를 감당하지 못하고, node‑uws 는 평탄한 지연과 낮은 이벤트‑루프 지연을 유지합니다.

1 500 – 2 100 클라이언트

1500‑2100 Graph

  • node‑ws, bun‑ws, bun‑native 는 사실상 죽은 상태이며 지연이 천정부지로 상승합니다.
  • node‑uws 는 여전히 ≈ 80 % CPU 를 유지하면서 낮은 지연을 보입니다.
  • 참고: node‑ws 의 p95 지연이 잠시 평탄해 보이는 이유는 메트릭 기록이 중단돼 Artillery 의 pushgateway 가 새로운 값이 들어올 때까지 마지막 값을 그대로 보여주기 때문입니다.

2 100 – 3 300 클라이언트

2100‑3300 Graph

  • node‑uws꾸준히 버텨냅니다 – CPU 가 80 % 수준을 유지하고, 지연도 낮으며 서버가 응답성을 유지합니다.
  • 다른 모든 구성은 압도당해 CPU 가 100 % 로 치솟고, 지연이 크게 폭발하며 빈번히 충돌합니다.

주요 내용

구성강점약점
node‑uws전반적으로 최고의 성능, 안정적인 지연 시간, 낮은 이벤트‑루프 지연, ~80 % CPU로 3 k 이상 클라이언트를 처리Bun보다 약간 높은 메모리 사용량
bun‑native뛰어난 메모리 사용량, 낮은 클라이언트 수에서 낮은 이벤트‑루프 지연높은 클라이언트 수를 유지할 수 없으며, ~1.3 k 클라이언트 이후 지연 시간이 급격히 상승
bun‑ws초기 단계에서 낮은 이벤트‑루프 지연, 좋은 메모리 사용량부하가 걸리면 bun‑native보다 빨리 실패
node‑ws간단하고 기본 옵션약 1 k 클라이언트에서 충돌, 높은 CPU 사용량 및 지연 시간

결론: 고트래픽 실시간 애플리케이션에 원시 성능이 필요하다면 Node + uWebSockets.js가 명확한 승자입니다. 메모리가 주요 고려 사항이고 Bun에 익숙하다면 Bun 네이티브 엔진이 견고한 2차 선택이 될 수 있지만, 확장성 한계에 주의해야 합니다.

![Benchmark Graph](https://media2.dev.to/dynamic/image/width=800,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj46ux807ur388inzxs51.png)

`node-uws` is the only one still standing. It's at ~90‑100 % CPU now.

Throughput starts to become less stable, and latency slowly creeps up. It goes dead after ~3 250 clients.  
We can say it could handle a solid 3 000‑3 100 concurrent clients just fine—more than double the next best (`bun-native`).

전체 그래프

Full Graph

CSV 파일은 GitHub에서 여기 확인할 수 있습니다.

Bun, 무슨 일이 있었나요?

bun-native가 여기서 완전히 망가지는 것을 보는 것은 놀라운 일입니다, 왜냐하면 Bun WebSocket 서버가 내부적으로 uWebSockets를 사용하기 때문입니다.

정확한 이유는 잘 모르겠지만, @socket.io/bun-engine이 아직 매우 새롭고 (v0.1.0) 비효율성 및 오버헤드를 추가하는 추상화 레이어가 있을 수 있기 때문일지도 모릅니다.

Back to Blog

관련 글

더 보기 »

초보자를 위한 Pusher vs Socket.io 설명

실시간 기능에 대해 이야기할 때 “그냥 WebSockets를 사용해” 혹은 “Pusher가 더 쉽다” 같은 말을 들어본 적이 있을 겁니다. 처음에는 이 주제가 종종 …

WebSocket과 Socket.IO

이 게시물에는 깜박이는 gif가 포함되어 있습니다. HTTP 요청은 나를 꽤 멀리 데려다 주었지만, 이제 그 한계에 부딪히고 있습니다. 클라이언트에게 서버 업데이트를 어떻게 알려야 할까요?