pile·
아키텍처·카카오 엔터테인먼트 FE카카오 엔터테인먼트 FE·

라이브채팅 플랫폼 구현기 2탄 : 아키텍처 및 성능 테스트

카카오엔터테인먼트가 MMA2022용으로 만든 라이브채팅 플랫폼의 아키텍처 설계와 성능 테스트 과정을 다룬다. 대용량 메시지를 1초 안에 전달한다는 목표 아래 동기/비동기 처리, 룸 배치 방식, 부하 테스트 도구를 하나씩 비교해 골랐다. 최종 구조는 Kafka 기반 비동기 처리이고, k6 분산 부하 테스트를 반복하며 목표치를 맞췄다.

핵심 포인트
  • 메시지 처리는 비동기로 결정했다. 후처리 구간의 부하를 고립시켜 전면 장애를 피하고, 기능 추가 시 side effect 를 줄이는 게 이유였다.
  • 비동기 구현은 Java Thread/Future 대신 Kotlin Coroutine 을 골랐다. 팀에 경험자는 없었지만 구현 난이도가 낮고 학습 기회가 된다고 봤다.
  • 룸을 단일 서버에 고정하지 않고 분산된 N대 서버에 흩어 배치했고, 서버 간 메시지 전달은 Kafka 가 맡는다.
  • 성능 목표는 Pod(cpu 4, memory 8GiB) 하나당 동시접속 2,000명, 메시지 송수신 1,000ms 이내. 코드나 플랫폼을 바꿀 때마다 부하 테스트를 돌리는 Performance Driven Development 를 택했다.
  • 테스트 러너는 오픈소스 k6, 플랫폼은 유료 k6 Cloud 대신 k8s 위에 직접 구축했다.
상세 정리
  • 요구사항: 대용량 메시지 트래픽, 인증 사용자만 접속, 1,000ms 이내 수신, 실시간 지표 모니터링, 전 메시지 영구 저장, 룸 자동 분할/병합, 실시간 금칙어 체크, 관리자의 실시간 제재.
  • 룸 분할/병합이 필요한 이유: 접속자 전원이 한 룸에 있으면 메시지가 너무 빨리 흘러 사용자가 사실상 읽지 못한다. 설정된 인원수 기준으로 쪼갠다.
  • 동기 방식은 설계가 단순하고 관리할 Eco system 이 적지만, 수신 후 후처리가 서버 부하를 가중시키고 한 구간 부하가 전면 장애로 번진다고 판단해 버렸다.
  • 룸을 동일 서버에 배치하는 안은 브로커 없이 바로 전달해 빠르지만, 서버별 룸 정보를 따로 관리해야 하고 룸별 트래픽 편차로 리소스 사용이 쏠린다.
  • 룸 분산 배치를 고른 근거: 리소스 사용이 고르고 룸 정보 관리가 필요 없다. 채팅 서버는 WebSocket 연결 유지로 이미 CPU 사용이 높아 금칙어/도배 후처리까지 맡기면 성능 저하가 예상됐다.
  • 기술 구성: Kafka 는 서버 간 메시지와 관리자 Command/Event 전달, Kafka Streams 는 금칙어와 도배 판정을 stream processing 으로 처리, Redis 는 룸 메타정보와 접속 정보, 최근 메시지 캐시.
  • 영구 저장은 Sink Processor 가 담당한다. Kafka 는 보관 주기가 짧아 메시지를 비동기로 MongoDB 에 저장하고, 지표 측정용으로 Elasticsearch 에도 적재한다.
  • 테스트 도구 선정 기준은 WebSocket 지원, 단일 인스턴스 부하 가중 능력, 분산 로드 테스팅, 비용, 시나리오 작성 편의, 의미 있는 결과 확인 여부였다.
  • 러너 비교: JMeter 와 Grinder 는 쓰레드 블로킹 기반이고 Grinder 는 WebSocket 네이티브 지원이 없다. Gatling 은 논블로킹이지만 오픈소스판의 분산 부하 지원이 약하다. k6 는 논블로킹에 오픈소스에서도 k8s 분산 테스트를 지원하고 리포트 연동 대상이 많아 선택됐다.
  • 플랫폼 비교: nGrinder 는 WebSocket 이 취약, Taurus 는 분산 기능 없음, AWS Distributed Load Testing 은 JMeter 전용, Gatling/k6 SaaS 는 비용 부담이라 자체 구축으로 갔다. 초기엔 AWS EKS + CloudWatch, 막바지 대량 테스트 때는 Google Cloud GKE 로 옮겼다.
  • 이슈 1: 대규모 부하 테스트에서 Redis Pub/Sub 성능 문제를 발견해 Message Broker 를 스트림 방식으로 전환했다.
  • 이슈 2: 스트림 도구 첫 선택이던 Google Cloud Pub/Sub 이 자체적으로 소모하는 시간이 컸다. Kafka 와 latency 를 비교한 뒤 Kafka 로 갔다.
  • 이슈 3: Pod 여러 개에 부하를 줬을 때 GCP 기본 로드밸런서의 Round Robin 이 분배를 제대로 못 했다. WebSocket 은 즉시 응답 없이 연결을 물고 있기 때문이다. Consistent Hashing 으로 바꿔 인스턴스별 부하를 고르게 맞췄다.
  • 남은 과제 1: 도배 감지가 "같은 문구를 일정 시간 내 N회 입력" 수준이라 감탄사 구분이나 조금씩 변형된 문구 판정을 못 한다. 자연어 처리 기반 고도화가 필요하다.
  • 남은 과제 2: 해싱이 클라이언트가 준 정보에 의존해 항상 같은 인스턴스로 간다. zookeeper 등으로 서버 상황을 반영해 직접 고르는 방식을 원한다.
  • 남은 과제 3: 성능 테스트를 전부 수동으로 돌렸고, GCP 로 옮긴 뒤에는 결과 시각화를 다시 구축하지 못했다. CI 단계 자동 수행과 리포팅 연동이 과제다.
왜 읽나WebSocket 기반 대규모 실시간 채팅을 설계하는 백엔드/인프라 엔지니어에게 구조 선택지와 부하 테스트 도구 비교를 한 번에 훑게 해주는 글.
카카오 엔터테인먼트 FE
카카오 엔터테인먼트 FE 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 아키텍처·aws-architectureAWS Architecture·

    일관성이 새로운 지연이다: 데이터 레이어에서의 AI

    AI 에이전트가 데이터베이스 레이어에서 겪는 데이터 정합성 문제를 다룬다. 비동기 복제 지연(replication lag)으로 stale 데이터를 읽은 에이전트가 잘못된 판단을 내리는 "조용한 독"을 AWS 서비스 기반 세 가지 아키텍처 패턴으로 해결하는 방법을 제시한다. "빠르지만 틀린 답이 약간 느리지만 맞는 답보다 비싸다"는 주장 아래, 데이터베이스 아키텍처를 AI 신뢰성의 기반으로 재정의한다.

    요약 이어보기
    #ai-agent#data-consistency#aurora+2
  2. 아키텍처·flexflex·

    사람도 에이전트도, 덜 읽을수록 더 잘 고칩니다

    flex 팀이 코드베이스의 모듈 경계 설계가 AI 에이전트의 코드 탐색 효율에 미치는 영향을 실측한 글이다. 193개 에이전트 작업 로그를 분석하고, 잘 나뉜 모듈과 뭉친 모듈을 비교한 결과 — 도메인이 물리 모듈로 분리된 저장소에서 에이전트가 불필요한 파일을 덜 읽고 목표 코드에 더 집중했다. 사람도 에이전트도 컨텍스트 한도가 있어, 무관 코드를 덜 읽을수록 수리 성공률이 높다는 것이 핵심 결론이다.

    요약 이어보기
    #llm-agent#context-window#modular-architecture+1
  3. 아키텍처·aws-architectureAWS Architecture·

    데이터 거주 요건을 충족하는 재해 복구 전략

    데이터 거주(data residency) 규정을 지키면서 재해 복구(DR)를 구현해야 하는 조직을 위해 AWS가 세 가지 전략 프레임워크를 제시한다. 금융, 의료, 공공 부문처럼 지리적 데이터 보관 의무가 있는 환경에서도 복구 목표(RTO/RPO)를 달성할 수 있는 아키텍처 선택지를 비교한다.

    요약 이어보기
    #multi-cloud#compliance#disaster-recovery+2