Slack
Slack은 메신저 및 프로젝트 관리용 협업툴입니다. 원래 Slack은 처음부터 상업용으로 개발한 것이 아니고 타이니 스펙이라는 회사에서 Glitch라는 온라인 MMORPG 게임을 만들던 중 팀 커뮤니케이션을 위해 개발한 사내 도구였습니다. 하지만 Glitch가 실패하고 대신 협업 툴로 개발한 Slack을 발표했습니다. 그리고 막대한 성공을 거뒀습니다. 지금은 하루에만 억단위의 메시지를 전송하는 많은 사용자를 지니고 있습니다.
이제 Slack이 이런 메시지들을 안정적으로 처리하기 위해 시도한 내용들을 정리하려고 합니다.
초기 아키텍처
Slack은 초기부터 서버를 2개로 나누었습니다. 하나는 인증, 권한, API 엔드포인트, 세션 관리 등 대부분의 애플리케이션 로직을 담당하는 Web App, 다른 하나는 WebSocket, 메시지 전파, 타이핑 표시, 접속 상태 변화 알림 등 실시간 메시지를 처리하는 채널 서버입니다.
이러한 역할 분리는 소규모 팀을 대상으로 빠르게 개발을 진행하던 초기에는 잘 작동했지만 점점 서비스가 커지면서 여러 문제점이 발생했습니다.
- 모놀리식 아키텍쳐로 구성되어 있기 때문에 테스트가 어려워지고, 배포 리스크가 커졌습니다.
- 채널 서버가 State를 가지고 있어 장애 복구 및 확장이 복잡해졌습니다.
- 두 서버간의 의존성 때문에 장애가 더 복잡해졌습니다.
- 웹 앱에서 장애가 발생하면 채널 서버는 메시지를 영구 저장하지 못하는데 사용자에게는 여전히 메시지를 보낸 것처럼 나타날 수 있습니다.
특히 3번은 큰 문제점이였습니다. Slack과 같은 메신저 서비스는 누군가가 메시지를 보내고 화면에 나타나면 그 메시지가 사라지지 않고, 다른 사람들에게도 동일하게 보여질 거라는 신뢰성을 가지고 있어야 합니다. 이러한 신뢰성이 떨어진다면 해당 메신저 서비스는 아무도 사용하지 않을 겁니다. 즉 메신저 서비스는 다음과 같은 조건이 보장되어야 합니다.
- 메시지가 사라지지 않는다.
- 메시지의 순서가 일관성 있게 유지된다.
- 한 사용자가 보는 것과 다른 사용자들이 보는 것이 항상 일치한다.
이를 보장하기 위해 Slack이 세운 규칙은 다음과 같습니다.
- 채널에 메시지가 나타나면 모든 사용자에게 동일하게 보여야 한다.
- UI에 메시자가 표시될 때는 이미 DB에 저장된 상태여야 한다.
- 사용자가 과거 대화를 스크롤 해서 볼 때, 모든 사용자가 동일한 순서로 동일한 메시지를 봐야 한다.
초기의 Slack은 Low Latency를 지원하기 위해 사용자가 메시지를 보내면 채널 서버로 전송되고 채널 서버에서 다른 사용자들에게 메시지를 브로드캐스트하고 보낸 사용자에게는 ACK(전송 확인)을 반환했습니다. 그 뒤에 웹 앱에 메시지를 전달하여 색인화, 영속화, 기타 지연 작업들을 처리했습니다.

위의 방식은 사용자에게 빠른 응답을 제공해줬지만 앞서 Slack이 세운 규칙을 지키지 못 할 수도 있었습니다.
만약 채널 서버가 메시지 전송을 확인한 후 Web App에 영속화를 하기 전에 장애가 발생하면 사용자는 메시지를 보낸 것처럼 보이지만 Web App을 복구한 이후에는 다른 사용자 화면에서 해당 메시지가 존재하지 않을 수 있습니다. 이런 문제들로 인해 Persistent Buffer나 Retry Loop 같은 보완책을 도입했지만 그럼에도 완전히 해결하진 못했습니다.
The Web App Takes Lead
Slack은 위 문제를 해결하기 위해 사용자의 메시지를 채널 서버가 아닌 Web App에 전송되도록 바꿨습니다. 사용자의 메시지를 Web App에 전송하면 Web App은 해당 메시지를 Job Queue에 기록합니다.(이 과정에서 영속화, 색인화, 파싱 등이 수행됩니다.) 그 후 Web App이 채널 서버를 호출하여 메시지를 실시간으로 브로드캐스트 합니다.

이러한 변경으로 기존 버전보다 초기에 더 많은 작업을 수행하지만, 영속성에 대해 더 강력한 보장을 제공할 수 있게 되었습니다. 덕분에 메시지 전송 도중 어떤 시스템이 중단되더라도, 메시지가 저장되거나 사용자가 명확한 실패 응답을 받는 것을 보장할 수 있게 되었습니다. 또한 채널 서버에서 Buffer나 Retry Loop가 필요 없어져 Stateless할 수 있게 되었습니다. 추가로 메시지 전송을 HTTP 기반으로 수행하기 때문에 더 이상 메시지 전송에 WebSocket 연결이 필요하지 않게 되었습니다.
Flannel
이전의 Slack은 사용자가 세션을 시작하면 Web App이 하나의 JSON payload를 생성했습니다. 이 JSON payload에는 사용자 프로필, 채널 목록, 채널 멤버쉽 정보, 읽지 않은 메시지 수 등이 포함되어 있습니다. 즉, 팀의 상태를 완전한 스냅샷 형태로 전달하여 사용자가 처음부터 전체 상태를 로드하고, 이후에는 실시간 변경 사항만 받아 동기화를 유지할 수 있도록 했습니다.
이 방식은 작은 팀에서는 JSON payload가 작기 때문에 괜찮았지만 대규모 조직에서는 payload의 크기가 매우 커서 사용자가 응답을 파싱하고 로컬 캐시를 구축하는데 수십 초가 걸렸습니다. 특히 네트워크 장애가 발생하면 이후 수천 명의 사용자가 동시에 재접속을 하면서 서버에 큰 부담을 줬습니다.
이를 해결하기 위해 Slack에서는 Flannel이라는 전용 마이크로 서비스를 도입했습니다. Flannel은 세션 초기화를 위한 지리적으로 분산된 캐시 역할을 합니다.
Flannel에서는 팀 메타 데이터를 인메모리 캐시하고 있습니다. 그리고 실제 클라이언트처럼 WebSocket 이벤트를 직접 수신합니다. 이를 통해 캐시를 항상 최신 상태로 유지합니다. 이를 전 세계 여러 지역에 분산 배치하여 사용자가 가까운 Flannel에 연결하여 글로벌 사용자들에게 낮은 latency로 세션 초기화가 가능하도록 했습니다. 또한 Flannel은 WebSocket연결을 직접 종료하고 세션 유효성 검증도 함께 수행합니다.
그 결과 사용자의 요청이 오게 되면 클라이언트는 Flannel에 연결됩니다. 이때 사용자는 인증 토큰을 전달하고 Flannel은 해당 토큰을 검증하고 필요 시에는 Web App에 위임합니다. 만약 해당 팀의 최신 정보를 이미 캐시로 보관 중이라면 Web App까지 요청을 전달할 필요없이 Flannel에서 응답을 반환합니다.

이를 통해 초기화 시점의 높은 연상 비용을 캐시 기반의 재사용 구조로 전환했습니다.
추가적인 문제 제어
메시지 중복
Slack과 같은 대규모 메신저 서비스에서는 단순히 초당 더 많은 메시지를 처리하는 것을 넘어서 장애가 발생해도 사용자에게 부정적인 경험이 남지 않도록 하는 것이 중요합니다. 대표적인 예시가 메시지 중복입니다. 메시지 중복은 사용자가 메시지를 두 번 보냈다고 느끼는 현상입니다. 이는 대부분 사용자의 재시도에서 비롯된 부작용입니다.
- 모바일 클라이언트가 메시지를 전송합니다.
- 네트워크 불안정으로 서버의 응답(ACK)이 지연됩니다.
- 사용자는 타임아웃이 발생했다고 판단하고 재전송합니다.
- 결과적으로 두 개의 메시지가 서버에 도착하거나, 같은 메시지가 두 번 저장되는 상황이 발생합니다.
이 문제를 Slack은 멱등성을 통해 극복합니다. 각 메시지에는 클라이언트가 생성한 고유 ID와 같은 값이 포함되어 있습니다. 서버에서는 동일한 메시지 ID임을 확인하면 새로운 저장해야 될 메시지가 아님을 인식할 수 있습니다. 이 방식이 중복을 완전히 제거해주진 못하지만 문제를 최소화하는 것에는 도움을 줍니다.
백엔드의 재시도나 실패 처리
메시지가 전송되면 여러 문제 상황이 발생할 수 있습니다.
- 메시지가 Web App에서 채널 서버에는 전송됐지만 영속화에는 실패
- Job Queue에는 저장했지만 클라이언트에게 전송 실패
- 일부 클라이언트에는 전송됐지만 다른 클라이언트에는 전송 실패
시스템은 이러한 모든 상황을 정확히 감지하고 복구해야 합니다. 그 과정에서 메시지를 유실하거나 순서가 잘못되는 등 사용자에게 부정적인 상황을 연출해서는 안 됩니다.
Slack은 Kafka와 Redis를 통해 내구성과 속도에서 균형을 맞추고 있습니다. Kafka를 통해 durable message queuing을 보장하고 Redis는 처리 중인 작업을 위한 일시적이지만 빠른 메모리 역할을 해줍니다. Kafka에서 꺼낸 작업은 실패할 경우 재시도가 가능하며 시스템은 같은 메시지를 중복 처리하지 않도록 방지하고 멈춘 작업을 무한히 기다리는 일도 피할 수 있습니다.
결론
Slack의 아키텍쳐는 계속 진화해왔습니다. 서비스가 성장하면서 확장성의 한계, 사용자의 피드백 등 여러 문제들을 해결하기 위해 시스템을 계속 바꿔왔습니다. 우리는 Slack의 아키텍쳐를 따라할려는 것이 아닌 Slack의 아키텍쳐가 발전해온 그 과정과 이유를 이해하고 우리 시스템을 어떻게 개선해나갈지 고민하는 방법을 배워야 합니다. 결국 시스템에 따라 필요한 요구사항이 다르고 그에 따른 적합한 설계가 다르기 때문입니다.
참고자료
How Slack Supports Billions of Daily Messages
How Slack Supports Billions of Daily Messages
At peak weekday hours, Slack maintains over five million simultaneous WebSocket sessions. That’s not just a metric, but a serious architectural challenge.
blog.bytebytego.com
'System Architecture' 카테고리의 다른 글
| 메시징 패턴: Pub-Sub, Queues, and Event Streams (1) | 2025.06.05 |
|---|---|
| 엔지니어링의 트레이드오프: 실제 환경에서의 최종적 일관성 (Eventual Consistency) (0) | 2025.05.29 |
| 마이크로서비스 설계 패턴 핵심 요약 (0) | 2025.05.10 |
| 당신이 알아야 할 시스템 설계의 핵심 개념 20가지 - 2. 캐싱 (0) | 2025.05.01 |
| 당신이 알아야 할 시스템 설계의 핵심 개념 20가지 - 1. 로드 밸런싱 (0) | 2025.05.01 |