Introduction & Requirements
게임 업데이트가 공개되는 저녁을 생각해 보겠습니다. 운영자가 공지 채널에 글을 올리자 사람들이 동시에 채널을 엽니다. 다른 채널에서는 이용자들이 새 기능을 이야기하고, 음성 채널에서는 친구들이 게임을 하면서 대화합니다.
한 사용자가 휴대전화에서 메시지를 보냈는데 화면에는 전송 중이라는 표시가 남았습니다. 사용자는 전송 버튼을 다시 누릅니다. 그사이에 운영자는 다른 사용자를 비공개 채널에서 제외합니다. 제외된 사용자는 아직 WebSocket 연결을 유지하고 있습니다.
서버는 이 상황에서 서로 다른 약속을 지켜야 합니다.
전송 성공이라고 알려준 메시지는 다시 조회할 수 있어야 합니다. 같은 전송을 재시도해도 메시지가 두 개 생기면 안 됩니다. 같은 채널을 보는 사람들은 서버가 확정한 순서로 메시지를 보아야 합니다. 권한이 철회된 연결에는 새로운 본문을 계속 전달하면 안 됩니다. 음성 통화에서는 오래된 소리를 나중에 정확하게 보내는 것보다 지금의 소리를 제때 보내는 것이 중요합니다.
여기서 Discord의 서버는 물리적인 컴퓨터가 아니라 커뮤니티 공간입니다. 혼동을 줄이기 위해 커뮤니티를 Guild, 그 안의 대화 공간을 Channel이라고 부르겠습니다.
가장 단순한 구현은 애플리케이션 서버 한 대와 관계형 DB 하나로 시작할 수 있습니다. 메시지를 DB에 저장한 뒤, 해당 채널에 접속한 연결 목록을 순회하며 전달합니다. 과거 메시지는 채널 ID와 생성 순서로 조회합니다.
규모가 작다면 충분히 합리적입니다. 하지만 연결 수가 늘면 서버 메모리와 네트워크 전송량이 증가합니다. 인기 채널에는 조회가 집중되고, 느린 사용자에게 보내지 못한 데이터가 메모리에 쌓입니다. 서버를 재시작하면 연결뿐 아니라 구독 정보와 아직 전달하지 못한 이벤트도 사라질 수 있습니다.
이 글은 메시지의 영속 저장, 실시간 전달, 재접속 복구, 음성 전송을 분리해서 설계합니다. 어느 하나의 성공을 다른 경로의 성공으로 해석하지 않는 것이 출발점입니다.
공개된 구현과 이 글의 설계 가정
Discord는 2023년 메시지 저장소의 Cassandra에서 ScyllaDB로의 이전을 설명했습니다. 이 자료에서는 채널과 시간 구간을 결합한 메시지 파티션, 인기 파티션 문제, 같은 조회를 합쳐 DB 요청 수를 줄이는 Data Service를 다룹니다. 2026년 5월 자료에서도 메시지를 비롯한 주요 데이터를 ScyllaDB에 저장한다고 설명합니다. 메시지 저장 구조, 2026년 저장소 운영.
이후에 나오는 PostgreSQL 샤드, 테이블, 채널 순번, API, 시간 목표는 교육을 위한 제안입니다. Discord의 비공개 내부 구현을 재현한 것이 아닙니다. 특히 트랜잭션과 장애 복구의 경계를 분명하게 설명하기 위해 메시지 원본을 관계형 DB의 여러 샤드에 두는 설계를 선택합니다. 실제 Discord가 메시지 원본을 이 방식으로 저장한다고 해석하면 안 됩니다.
Functional Requirements
사용자는 Guild에 가입하고 권한이 있는 텍스트 채널의 메시지를 조회하고 작성할 수 있습니다.
사용자는 자신의 메시지를 수정하거나 삭제할 수 있고, 관리자는 부여받은 권한에 따라 메시지와 멤버 접근을 관리할 수 있습니다.
사용자는 채널의 새로운 메시지와 변경 사항을 실시간으로 받으며, 재접속하면 놓친 변경을 복구할 수 있습니다.
사용자는 여러 기기에서 접속하고, 서버에 반영된 읽음 위치를 기기 사이에 동기화할 수 있습니다.
사용자는 파일을 첨부하고, 접근 가능한 채널 안에서 메시지를 검색할 수 있습니다.
사용자는 음성 채널에 참여하고 나가며, 다른 참여자와 음성을 주고받을 수 있습니다.
시스템은 멘션과 알림 설정에 따라 오프라인 사용자에게 푸시 알림을 시도합니다.
Non-Functional Requirements
아래 수치는 모두 설계를 위한 가정입니다. 실제 Discord의 SLA나 처리량이 아닙니다.
메시지 작성은 같은 대륙의 정상 네트워크에서 클라이언트 요청부터 영속 저장 확인까지 p95 250ms, p99 700ms를 목표로 합니다.
온라인 수신자의 새 메시지 표시는 원본 커밋 이후 p95 500ms를 목표로 합니다. 서버의 소켓 쓰기 완료와 클라이언트 화면 표시는 별도로 측정합니다.
최근 메시지 50개 조회는 API 기준 p95 200ms를 목표로 합니다. 오래된 이력 조회와 검색에는 별도의 지연 예산을 둡니다.
메시지 작성과 조회 API는 월간 가용성 99.95%를 목표로 합니다. 저장소가 성공 여부를 확정할 수 없으면 전송 성공을 반환하지 않습니다.
성공한 메시지는 가용 영역 하나의 장애로 잃지 않도록 동기 복제를 사용합니다. 전체 리전 손실에 대한 보장 범위는 별도로 정의합니다.
같은 채널의 영속 변경은 서버가 부여한 순서를 따릅니다. 서로 다른 채널 사이의 전역 순서는 요구하지 않습니다.
권한 철회가 완료되면 서버는 해당 권한에 의존하는 새로운 전송을 허용하지 않습니다. 이미 사용자 기기에 전달된 데이터의 회수까지 보장하지는 않습니다.
음성은 같은 대륙의 양호한 네트워크에서 발화부터 상대 재생까지 p95 200ms, 통화 참여 p95 2초를 목표로 합니다.
인기 Guild의 높은 부하가 다른 Guild의 메시지 저장과 음성 연결 예산을 소모하지 않도록 처리량과 대기열을 격리합니다.
Out of Scope
DM과 그룹 DM의 별도 권한 모델
영상과 화면 공유의 세부 코덱 및 화질 선택
Stage 채널과 대규모 방송 시청자 분산
결제, Nitro, 광고, 게임 스토어
외부 개발자용 봇 플랫폼 전체와 추천 모델
녹음, 자동 전사, 서버에서 수행하는 음성 콘텐츠 분석

