새소식

반응형
BACKEND/기법

디스코드가 WebSocket 트래픽을 절반으로 줄인 비밀

  • -
반응형

"WebSocket 트래픽을 절반으로 줄인 비밀" text img

실시간 최적화의 혁신 전략

디스코드의 기술 페이지를 읽으며 디스코드가 어떻게 WebSocket 트래픽을 거의 절반으로 줄였는지 알아보았습니다. 디스코드가 사용한 스마트한 기법들과 우리가 개발자로서 배울 수 있는 교훈들을 알아봅시다.


📌 문제점

디스코드의 사용자 수가 증가함에 따라 WebSocket 대역폭 사용량도 급증했으며, 이는 특히 모바일 기기느린 네트워크를 사용하는 사용자들에게 영향을 미쳤습니다.

주요 병목 지점:

  • 높은 대역폭 사용: JSON 데이터 형식이 불필요한 오버헤드 추가
  • 중복 데이터 전송: 작은 변경 사항에도 전체 데이터 스냅샷 전송
  • 제한된 모바일 성능: 느린 네트워크 환경에서 지연 및 높은 데이터 비용 발생

이러한 문제를 해결하기 위해 디스코드의 엔지니어들은 고품질 사용자 경험을 유지하면서 WebSocket 트래픽을 최적화하는 데 집중했습니다.


🚀 디스코드의 접근 방식

디스코드는 다음과 같은 4가지 전략으로 비효율성을 해결했습니다:

  1. 이진 데이터 형식으로 전환
  2. 메시지 압축 구현
  3. 데이터 동기화 최적화
  4. 델타 업데이트 활용

각 전략을 자세히 살펴보겠습니다.


1️⃣ 이진 데이터 형식으로 전환

  • 문제점: JSON은 사람이 읽기 쉽지만 메시지 크기가 큼
  • 해결책: 오버헤드를 줄이기 위해 MessagePack 도입

✅ 왜 MessagePack인가?

Format of MessagePack

  • JSON 대비 작은 메시지 크기
  • 기존 기술 스택에 손쉬운 통합
  • 직렬화/역직렬화 시 성능 저하 최소화

🔧 구현 방법

  • 서버: msgpack-lite로 메시지 인코딩
  • 클라이언트: MessagePack 호환 라이브러리로 디코딩

📊 결과

  • 메시지 크기 35% 감소
  • 직렬화/역직렬화 효율성 향상, 지연 시간 거의 없음
반응형

2️⃣ 메시지 압축 구현

  • 문제점: 반복적인 데이터로 인해 여전히 큰 메시지 존재
  • 해결책: per-message Deflate 압축 적용

https://github.com/faye/permessage-deflate-node

 

GitHub - faye/permessage-deflate-node: Per-message DEFLATE compression extension for WebSocket connections

Per-message DEFLATE compression extension for WebSocket connections - faye/permessage-deflate-node

github.com

 

✅ 왜 per-message Deflate인가?

  • 개별 WebSocket 메시지 압축 가능
  • 대부분의 브라우저와 라이브러리에서 지원

📊 결과

  • 대역폭 사용량 추가로 25% 감소
  • 약간의 CPU 사용 증가, 그러나 대역폭 절감 효과가 더 큼

3️⃣ 데이터 동기화 최적화

  • 문제점: 변경되지 않은 데이터까지 전송하여 낭비 발생
  • 해결책: 선택적 동기화 전략 적용
    • 클라이언트가 로컬 캐시 유지
    • 서버는 변경된 데이터만 전송

✍️ 예시

  • 최적화 전:
{
  "id": "ef23r24grewg",
  "name": "Siyeon",
  "status": "waiting",
  "lastActive": "2024-2-12T05:04:10Z"
}
  • 최적화 후:
{
  "id": "ef23r24grewg",
  "status": "ongoing"
}

📊 결과

  • 메시지 빈도 및 크기 감소
  • 불필요한 데이터 전송 없이 최신 상태 유지

4️⃣ 델타 업데이트 활용

  • 문제점: 선택적 업데이트로도 큰 데이터 구조 존재 (예: 목록)
  • 해결책: 델타 업데이트로 이전 상태 대비 변경 사항만 전송

✍️ 예시 (JSON Patch)

  • 원본: { "tasks": ["todo", "in-progress", "done"] }
  • 업데이트: { "tasks": ["todo", "review", "done", "archived"] }
  • 델타:
[
  { "op": "replace", "path": "/tasks/1", "value": "review" },
  { "op": "add", "path": "/tasks/3", "value": "archived" }
]

📊 결과

  • 데이터 전송 최소화
  • 빠른 클라이언트 업데이트 지원

⚠️ 도전 과제 및 해결 방법

  1. 복잡성 증가
    • 문제: 코드 복잡성 증가
    • 해결: 직렬화/역직렬화 로직을 모듈화하여 관리, 철저한 테스트 수행
  2. 구형 클라이언트 호환성
    • 문제: 일부 클라이언트는 새로운 기능 미지원
    • 해결: WebSocket 핸드셰이크에서 버전 협상 도입
  3. 이진 데이터 디버깅 어려움
    • 문제: 사람이 읽기 어려움
    • 해결: 내부 디버깅 도구 개발, 개발 단계에서는 JSON 사용

🌟 최적화 결과

  • WebSocket 트래픽 50% 감소
  • 모바일 사용자 경험 개선: 더 빠른 업데이트, 낮은 데이터 사용량
  • 비용 절감: 대역폭 비용 및 서버 부하 감소

💡 실시간 애플리케이션을 위한 교훈

  • 효율적인 데이터 형식 사용: MessagePack, Protobuf 활용
  • 압축의 중요성: 높은 트래픽 환경에서 큰 효과
  • 선택적 업데이트: 불필요한 데이터 전송 방지
  • 하위 호환성 고려: 모든 프로토콜 변경 시 필수
  • 트래픽 분석과 테스트: 병목 지점 식별로 효율성 향상

📢 결론

디스코드의 최적화 사례는 실시간 시스템 개발에 있어 강력한 인사이트를 제공합니다.

"작은 최적화가 모여, 큰 변화를 만듭니다."

여러분이 비슷한 문제에 직면해 있다면, 디스코드의 접근 방식을 참고해 보세요. 작은 개선들이 쌓여 여러분의 애플리케이션을 더 빠르고 효율적으로 성장시킬 수 있습니다.

끝까지 읽어주셔서 감사합니다!

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.