WebSocket 채팅 안정화 개선기: Redis Pub/Sub 재설계와 운영 지표 검증
개요
먼저 저희 서비스는 고객이 업무 요청서를 통해 이루미와 매칭된 뒤, 서비스 내 채팅을 중심으로 업무를 진행, 관리하는 구조입니다.
하지만 운영 과정에서 다음 문의가 반복적으로 발생했습니다.
- 이루미 응답 지연 시 대응 방법 문의 및 환불 요청
- 결과물 확인 경로 문의
- 업무 시간 연장 방법 문의
즉, 채팅이 단순 메시지 확인 역할에 머물러 있었고, 실제 업무 관리와 실시간 소통을 안정적으로 지원하지 못하는 한계가 있었습니다.
이 문제를 해결하기 위해 채팅 기능 개선을 시작했습니다. 화면 요구사항에 맞춰 API를 정리하는 동시에, 기존 실시간 메시징 구조(WebSocket + Redis Pub/Sub)의 불안정성도 함께 개선했습니다.
이번 작업의 목표는 “더 빠른 시스템”이 아니라, 트래픽 증가/장애 상황에서도 버티는 안정적인 실시간 채팅 구조를 만드는 것이었습니다.
문제 정의
기존 구조는 기본적인 송수신은 가능했지만, 운영 환경에서 다음 문제가 반복되었습니다.
- 단일 Listener 병목
- Listener가 DB 저장, Push 알림, WebSocket 송신까지 모두 처리
- 처리 시간이 길어지며 수신 지연 발생
- 트래픽 완충(Buffering) 구조 부재
- Pub/Sub 특성상 지연 시 유실 리스크 증가
- 관찰 가능성(Observability) 부족
- 장애 발생 시 병목 지점을 즉시 파악하기 어려움
- 대응이 늦어지고 CS 처리 근거 확보도 어려움
Before
개선 방향
핵심 전략은 다음 3가지였습니다.
- Listener를 초경량화해 수신 경로 병목 제거
- Queue/Worker 분리로 버스트 트래픽 흡수
- 재처리 경로 + 운영 지표로 장애 복원력 확보
After
1) Listener 초경량화
기존 Listener는 수신 이후 비즈니스 로직까지 처리하고 있었습니다. 이를 메시지 파싱 + Queue 적재 역할로 축소했습니다.
- Listener: 수신 및 Queue 적재만 수행
- Worker: DB 저장, Push, WebSocket 송신 등 비즈니스 처리 담당
효과:
- Listener가 즉시 반환되어 수신 지연 완화
- Pub/Sub 소비 경로의 병목 감소
2) Listener 스레드 확장 + BlockingQueue 도입
단일 Listener로는 피크 트래픽 대응이 어려워 구조를 분리했습니다.
- Listener 스레드풀:
1 -> 4 - Listener-Worker 사이
BlockingQueue(3,000)도입 - Worker 스레드풀은 비즈니스 처리만 담당
스레드 4개 선정 근거
- 경량 Listener 기준 스레드당 처리량: 약 30 msg/s
- 피크 유입량: 약 100 msg/s
- 필요 스레드 수:
100 / 30 = 3.3 - 스파이크 여유를 포함해 4개로 결정
Queue 3,000 선정 근거
- 피크 유입(100 msg/s) 기준
- 유입량 > 처리량 상태를 약 30초 흡수 목표
100 x 30 = 3,000
효과:
- 역할 분리로 병목 제거
- 버스트 구간 안정성 향상
3) 재처리 큐: Redis Stream 도입
BlockingQueue 포화 또는 Worker 실패 시, 메시지를 즉시 폐기하지 않고 Redis Stream 재처리 큐로 이관하도록 했습니다.
보완된 점:
- Pub/Sub(at-most-once) 단점 완화
- Consumer Group 기반 재처리(at-least-once) 적용
실시간성 우선 정책
채팅은 늦게 도착한 메시지가 오히려 UX를 해칠 수 있어, 내구성과 실시간성 사이에서 아래 정책을 선택했습니다.
- 1차 실패: Stream으로 이관 후 재처리
- 재처리 실패: 장기 DLQ 보관 대신 유실 처리
즉, “모든 메시지 보존”보다 “대화 흐름 보존”을 우선했습니다.
4) 운영 관측성 강화
Micrometer + Grafana로 다음 지표를 구성했습니다.
- 성공 메시지 수
- 실패 메시지 수
- 메시지 처리 시간
- BlockingQueue 적재량 게이지
운영 중 Queue 적재량 급증 시 이상 징후를 빠르게 감지하고, 장애 원인을 단계별로 추적할 수 있게 됐습니다.
5) API 정리 및 화면 연동 개선
화면 설계 변경에 맞춰 채팅 관련 API를 재정리했습니다.
- 하나로 뭉쳐 있던 응답/액션 경로를 목적에 맞게 분리
- 화면 흐름과 API 계약을 맞추면서 프론트와 협업해 명세 정리
- 소켓 연결 수 집계에서 graceful 처리 여부 차이를 맞춰 정합성 개선
효과:
- 화면-API 불일치로 인한 오류 감소
- 운영 지표(세션/연결 수) 신뢰도 향상
검증
부하 테스트(k6)
- 동시 사용자: 100
- 초당 메시지: 10
- 테스트 시간: 5분
- 메시지 전송 성공률: 99.9%
테스트 기준에서 송수신 안정성이 개선됨을 확인했습니다.
운영 관점 결과
- 메시지 처리 경로에 대한 관측 가능성 확보
- 장애 징후(Queue 적재량 급증 등) 조기 탐지 가능
- 재처리 경로 도입으로 일시 실패에 대한 복원력 향상
비즈니스 기능/사용성 성과
- 채팅 기능 접근 빈도 증가
- 활성 사용자당 조회수:
14.68 -> 20.08(+36.8%) - 채팅 화면 접근 빈도와 반복 확인 행동이 증가
- 활성 사용자당 조회수:
- 채팅 내 상호작용 증가
- 활성 사용자당 이벤트:
23.67 -> 30.68(+29.6%) - 업무 관련 기능 사용과 상호작용이 전반적으로 증가
- 활성 사용자당 이벤트:
- 체류 및 업무 관리 활동 증가
- 활성 사용자당 평균 참여 시간:
5분 03초 -> 6분 22초(+26.1%) - 채팅 내 업무 관리/소통 활동 시간이 증가
- 활성 사용자당 평균 참여 시간:
- 소통 관련 문의 개선
- 소통 요청 문의 비율을 약 7% 수준으로 감소
VOC 기반 검증 (업무 > 작업 태그)
채팅은 고객의 업무 소통과 관리가 진행되는 주요 공간으로, 채팅 화면 내 기능과 연관된 업무 > 작업 태그 VOC를 중심으로 변화를 분석했습니다.
- 업무 > 작업 문의: -21.4% 감소
채팅 기능과 직접적으로 연관된 업무 문의가 감소했습니다.
- 업무 시간 연장 문의: -45.8% 감소
이는 채팅 화면 개선 이후 업무 관리 기능의 접근성과 가시성이 개선된 결과로 해석할 수 있습니다.
회고
이번 개선은 팀원들과 함께 데이터를 수집하고, 이를 기반으로 문제를 정의해 결과까지 연결한 경험이었습니다. 개발적으로는 대규모 성능 튜닝보다 안정성 강화에 초점을 맞췄지만, 실제 업무 관련 문의 감소로 비즈니스 성과를 확인할 수 있었습니다. 특히 개발자가 구현에만 머무르지 않고, 지표 설계와 결과 해석까지 주도적으로 이끄는 과정의 중요성을 배웠습니다.
- 실시간 시스템에서는 Listener의 작은 병목이 전체 장애로 확산될 수 있다.
- Buffering/역할 분리는 트래픽 급증 구간의 안정성에 직접적인 영향을 준다.
- “재처리 + 관측성”이 있어야 운영 가능한 실시간 시스템이 된다.
이번 작업은 단순 성능 튜닝이 아니라, 오류가 잦던 채팅 시스템을 운영 가능한 구조로 재정비한 프로젝트였습니다.


