✨ 실시간 채팅 기능 도전
중고등학생 기반 커뮤니티를 개발하던 중, 실시간 채팅 기능이 있으면 좋을 것 같다는 생각이 들었다.
그동안은 기본적인 CRUD 구현, Spring Security와 JWT, OAuth를 활용한 소셜로그인 기능, Firebase를 이용한 알림 설정 기능, CI/CD 환경 구축 등 여러 기술을 활용해 프로젝트를 구현해보았지만, 실시간 채팅 기능 구현은 아직 경험해보지 못했기 때문에 이번 기회에 도전해보고 싶었다.
CUDA를 활용해 소켓을 통신을 통해 실시간으로 데이터를 주고받는 기능이나, SMTP를 이용해 메일 전송 기능을 구현해본 적은 있지만, WebSocket, STOMP, Kafka, MongoDB 등의 기술들은 아직 한 번도 사용해보지 않았다. 낯설기도 하지만, 새로운 도전을 할 생각에 기대된다 🔥🔥🔥 WebSocket과 STOMP를 활용해 메시지를 송수신하고, Kafka를 통해 메시지 큐를 처리하며, MongoDB를 이용해 채팅 데이터를 저장할 계획이다.
⚒️ 기술 스택 선정 과정
📌 채팅 서버 메시지 처리 방식(Poling vs Long Poling vs WebSocket)
1️⃣ Poling – 가장 기본적인 방식
클라이언트(ex: 웹 브라우저)가 일정한 시간 간격으로 서버에 요청을 보내고, 새로운 메시지가 있는지 확인하는 방식이다.
✅ 특징
- WebSocket과 달리 지속적인 연결이 필요하지 않아 서버 리소스를 절약할 수 있다
- API 서버 개발만으로 간단하게 구현이 가능하다
⚠️ 단점
- 클라이언트가 정해진 주기로 요청하기 때문에 실시간성이 부족하다.
- 사용자가 새로운 메시지를 확인하기까지 지연 시간이 발생할 수 있다.
- 클라이언트마다 메시지를 확인하는 시점이 달라, 대화 내용이 일치하지 않을 수 있다.
📌 사용 예시
- 실시간성이 크게 중요하지 않은 단순 데이터 조회
2️⃣ Long Poling – Poling의 단점 개선
Long Poling은 Poling 방식의 단점을 보완한 방법으로, 서버가 새로운 메시지가 도착할 때까지 응답을 지연시키는 방식이다.
✅ 특징
- 새로운 메시지가 도착하면 즉시 클라이언트에 응답하여 실시간성을 높인다.
- Poling보다 네트워크 요청이 줄어들어 효율성이 증가한다.
⚠️ 단점
- 여전히 요청과 응답이 반복되므로 서버 리소스를 많이 사용한다.
- 서버가 클라이언트의 요청을 오래 유지하면, 특정 상황에서 오히려 부하가 증가할 수 있다.
📌 사용 예시
- 비교적 적은 트래픽의 실시간 알림 시스템
3️⃣ WebSocket – 실시간 메시지 처리에 최적화
WebSocket은 요청-응답 기반이 아닌, 클라이언트와 서버가 연결을 유지하며 양방향으로 데이터를 주고받는 방식이다.
✅ 특징
- 전이중(Full-Duplex) 통신을 지원해 양방향 데이터 전송 가능
- Poling 및 Long Poling보다 실시간성이 뛰어나고 서버 리소스 사용이 적음
- 연결이 유지되는 동안 메시지를 즉시 전송 가능
⚠️ 단점
- 클라이언트와 서버 간 지속적인 연결 유지가 필요하여, 서버당 처리할 수 있는 클라이언트 수에 제한이 있다.
- 네트워크 환경이 불안정할 경우, 연결 유지가 어려울 수 있음
📌 사용 예시
- 채팅 애플리케이션
- 실시간 주식 거래 시스템
- 온라인 게임
🏆 어떤 방식을 선택해야 할까?
방식 | 실시간성 | 서버 부하 | 구현 난이도 | 사용 사례 |
Poling | 낮음 | 높음 | 쉬움 | 실시간성이 중요하지 않은 데이터 조회 |
Long Poling | 중간 | 중간~높음 | 중간 | 실시간 알림, 간단한 채팅 |
WebSocket | 높음 | 낮음 | 어려움 | 실시간 채팅, 온라인 게임 |
➡️ 실시간 채팅 기능을 구현할 것이기 때문에, 높은 실시간성과 효율성을 고려해 WebSocket을 사용하는 것이 가장 적합하다고 생각했다.
📌 STOMP(Simple Text Oriented Messaging Protocol) 란?
WebSocket을 사용할 때, 왜 STOMP를 선택해야 할까요?
🥹 WebSocket의 한계
WebSocket은 Text와 Binary 메시지 전송을 정의하지만, 메시지 내용까지는 정의하지 않습니다. 또한, 낮은 수준의 프로토콜로, 실제 애플리케이션에서 효율적인 통신을 관리하려면 더 고수준의 프로토콜이 필요합니다.
✅ 특징
- WebSocket 위에서 작동하는 고수준의 메시징 프로토콜
- 클라이언트와 서버 간의 메시지 기반 통신을 간소화하고 표준화
- pub/sub(발행/구독) 구조를 따르고 있기 때문에 메시지를 전송하고 처리하는 과정이 명확함
- 채팅방 입장: 현재 입장한 채팅방의 번호를 구독합니다.
- 메시지 송수신: 구독한 채팅방에서 메시지를 주고받을 수 있습니다.
- Spring Security를 이용한 권한 제어 가능
- 다양한 메시지 브로커(RabbitMQ, Kafka, ActiveMQ 등)와 연동 가능
- 헤더 기반 메시징을 지원하여 메시지 라우팅, 우선순위 관리 가능
- 텍스트 기반 프로토콜로 개발 및 디버깅 용이
- 다양한 프로그래밍 언어 및 플랫폼에서 사용 가능
⚠️ 단점
- WebSocket보다 상대적으로 복잡한 구조
- 기본적으로 In-Memory Message Broker를 사용하지만, 이는 확장성과 안정성 측면에서 한계가 있을 수 있습니다.메시지 브로커를 사용하지 않을 경우, 성능상의 한계 발생 가능
➡️ 다양한 메시지 브로커(RabbitMQ, Kafka, ActiveMQ 등)와 호환되어 이러한 문제를 해결 가능 - 단순한 실시간 데이터 전송만 필요한 경우 불필요하게 복잡할 수 있음
📌 사용 예시
- 채팅방 권한 제어: Spring Security와 함께 사용하여 권한 기반으로 채팅방 입장을 제어할 수 있습니다. 예를 들어, 운영자만 입장 가능한 채팅방을 만들 수 있습니다.
- 메시지 브로커: Publisher에서 Subscriber로 메시지를 전달하는 중간 메시지 브로커를 사용할 수 있습니다. 이를 통해 메시지 전달을 효율적으로 관리할 수 있습니다.
❓ STOMP vs WebSocket
- WebSocket: 채팅방에 누가 접속 중인지, 각 채팅방의 세션을 직접 관리해야 하는 번거로움이 있습니다.
- STOMP: 사용자가 채팅방에 접속하면 해당 채팅방을 구독하고, 메시지를 보내면 자동으로 구독자들에게 메시지가 전달됩니다. 별도로 세션을 관리할 필요 없이 효율적으로 메시지를 분배할 수 있습니다.
📌 Apache Kafka vs RabbitMQ
실시간 메시지를 전송하고 받을 수 있는 메시징 시스템은 채팅 기능을 구현하는 데 필수적입니다. 메시지 브로커를 사용하면 대규모 분산 환경에서 메시지의 신뢰성 있는 전송, 확장성, 그리고 유연한 아키텍처 구성이 가능합니다. 메시지 큐를 떠올리면 대표적으로 Kafka와 RabbitMQ를 생각할 수 있습니다.
🐰 RabbitMQ 란?
RabbitMQ는 대표적인 메시지 브로커로, AMQP(Advanced Message Queuing Protocol)를 구현한 오픈 소스 메시지 큐입니다. 복잡한 라우팅, 메시지 큐잉, 메시지 확인 등 신뢰성 있는 메시지 전달을 보장하는 기능을 제공합니다.
✅ 특징
- 고급 메시징 패턴을 지원합니다.
- 다양한 프로그래밍 언어와 통합이 용이합니다.
- 메시지 큐에 저장된 데이터를 소비자가 가져가 처리할 수 있게 중간 역할을 합니다.
⚠️ 단점
- 수평 확장이 어려워 트래픽이 증가하면 성능이 저하될 수 있습니다.
- 메시지 재생이 어렵습니다. 이벤트 메시지가 성공적으로 전달되면 큐에서 삭제되기 때문에, 문제가 발생하면 메시지를 다시 재생할 수 없습니다.
📌 사용 예시
- 복잡한 라우팅을 요구하는 채팅 시스템
- 다양한 클라이언트와 서버 간의 실시간 메시지 전달
🦧 Apache Kafka 란?
Apache Kafka는 대규모 메시지 처리를 위해 설계된 분산 스트리밍 플랫폼입니다. 고처리량, 내구성, 확장성을 제공하며 실시간 데이터 피드를 처리하는 데 적합합니다.
✅ 특징
- 고처리량, 내구성, 확장성을 제공합니다.
- 이벤트 스트리밍 방식으로, 메시지가 로그에 기록되어 문제가 발생하면 재생이 가능합니다.
- 유연하고 느슨한 결합을 통해 시스템 확장이 용이합니다.
- Zookeeper와 결합하여 분산 코디네이션을 처리합니다.
⚠️ 단점
- Zookeeper 의존성이 있어 관리가 복잡할 수 있습니다.
- 메시지가 로그로 유지되므로, 저장공간을 적절히 관리해야 합니다.
📌 사용 예시
- 대규모 데이터 처리와 실시간 분석을 요구하는 시스템
- MSA(Microservices Architecture) 환경에서 이벤트 기반 아키텍처 구축
🆚 메시지 브로커 vs. 스트림 처리
메시지 브로커 | 이벤트 스트리밍 플랫폼 | |
처리 방식 | 메시지를 한 번에 하나씩 처리 | 데이터 스트림을 연속적으로 처리 |
메시지 관리 | 메시지가 소비되면 큐에서 삭제 | 메시지가 계속 유지되어 재생 가능 |
주요 특징 | 신뢰성 있는 전달, 순서 보장 | 실시간 데이터 처리, 대규모 데이터 집합에 적합 |
확장성 | 수평 확장에 어려움 | 유연한 확장성 제공 |
적합한 환경 | 낮은 트래픽 또는 중간 규모 시스템 | 대규모 실시간 데이터 처리 및 분석 |
❓Apache Kafka vs RabbitMQ
➡️ Kafka 선택
채팅 시스템을 구축할 때 RabbitMQ도 충분히 사용할 수 있지만, Kafka는 높은 처리량과 대규모 데이터 처리에 적합하고, 트래픽이 몰릴 때나 장애 발생 시 대응이 명확하다는 장점이 있습니다. 또한, 추후 MSA 아키텍처로 변경을 고려할 때 Kafka가 유리할 수 있습니다.
RabbitMQ는 교환(exchange)을 통한 pub/sub 구조를 구현할 수 있지만, 장기적인 확장성과 대규모 데이터 처리에 있어 Kafka가 더 적합하다고 판단하였습니다.
결론: 대규모 시스템에서 높은 처리량과 안정적인 실시간 데이터 처리가 필요한 경우, Apache Kafka가 적합하며, 다양한 메시징 패턴을 지원하는 경우 RabbitMQ가 유리할 수 있습니다.
📌 메시지 저장소 : MongoDB vs MySQL
채팅 메시지를 저장하기 위해서는 여러 가지 요소를 고려해야 합니다.
- 데이터 구조
- 메시지의 구조가 다양해질 수 있으며, 메시지 내용뿐만 아니라 이미지, 비디오 등 멀티미디어 파일도 포함될 수 있습니다.
- 데이터베이스 확장성
- 사용자 수와 메시지 양이 급격히 증가할 수 있기 때문에, 데이터베이스는 수평적 확장(스케일아웃)이 가능해야 합니다.
- 데이터베이스 클러스터를 확장하여 더 많은 서버로 트래픽과 데이터를 분산할 수 있어야 합니다.
- 실시간성
- 채팅 메시지는 실시간으로 전송되고, 사용자는 즉시 메시지를 받아야 합니다.
➡️ 빠른 쓰기 및 읽기 성능이 매우 중요합니다. - 실시간 처리가 주가 되므로, 수정이나 삭제보다는 저장과 조회가 주로 발생합니다
- 채팅 메시지는 실시간으로 전송되고, 사용자는 즉시 메시지를 받아야 합니다.
✅ 특징
MySQL (RDBMS)
- 고정된 스키마:
RDBMS는 스키마가 고정되어 있어, 데이터 구조가 변경될 때마다 스키마 수정이 필요합니다. 이는 사용자가 다양한 메시지 형식을 보내는 채팅 시스템에 적합하지 않습니다. - 수직적 확장:
RDBMS는 일반적으로 수직적 확장(스케일업)에 의존합니다. 즉, 서버의 성능을 향상시켜 처리 능력을 증가시킵니다. 이는 비용이 많이 들며, 대규모 트래픽 처리에는 한계가 있을 수 있습니다. - 성능 저하:
복잡한 JOIN 연산을 사용하는 RDBMS는 대용량 데이터에서 성능 저하를 경험할 수 있습니다.
MongoDB (NoSQL)
- 데이터 구조 변화의 유연성:
MongoDB는 Schema-Less 시스템으로, 데이터 구조의 변화에 매우 유연하게 대응할 수 있습니다. 채팅 메시지처럼 다양한 형태의 데이터를 저장할 때 유리합니다. - 수평적 확장:
MongoDB는 수평적 확장(스케일아웃)이 가능하여, 트래픽이나 데이터 양이 증가할 때 더 많은 서버를 추가하여 처리 능력을 증가시킬 수 있습니다. - 빠른 읽기/쓰기 성능:
문서 기반의 쿼리 시스템을 통해 대용량 데이터에서도 빠른 조회 성능을 제공합니다. 채팅 메시지처럼 특정 사용자나 시간대별로 데이터를 조회하는 경우에 특히 유리합니다.
✨ 결론
채팅 기록 저장:
- MongoDB를 사용하기로 결정하였습니다
- 빠른 읽기/쓰기 성능과 유연한 데이터 구조 덕분에 채팅 메시지를 저장하는 데 이상적입니다.
- 사용자는 실시간으로 데이터를 주고받으며, 메시지의 형식이나 내용이 자주 변경될 수 있기 때문에, MongoDB가 제공하는 수평적 확장성과 유연한 스키마가 큰 장점입니다.
채팅방 정보 관리:
- 그대로 MySQL을 사용하기로 결정하였습니다.
- 사용자, 설정, 채팅방 생성, 수정, 삭제 등의 관계형 데이터를 포함하는 채팅방 정보는 트랜잭션 처리와 JOIN 연산을 필요로 합니다.
➡️ 따라서 MySQL의 관계형 데이터베이스 특성이 더 적합하다고 생각했습니다.
결론:
- 채팅 내용은 MongoDB로 처리하고, 채팅방 정보는 MySQL로 처리하는 하이브리드 접근을 통해 각 데이터베이스의 강점을 최대화하였습니다. MongoDB는 빠른 메시지 처리에 유리하고, MySQL은 관계형 데이터 처리가 적합하다고 생각하였습니다.
💡 전체적인 흐름
- 클라이언트 구독
- 사용자 A가 웹 애플리케이션에서 채팅방에 참여하고, 해당 채팅방 경로("/sub/chat/room/1")를 STOMP를 사용하여 구독합니다. 이때 WebSocket 연결을 통해 실시간 메시지를 받을 준비를 합니다.
- 메시지 발행
- 사용자 B가 "안녕하세요, A님!" 메시지를 입력하고, 이 메시지는 Kafka에 발행됩니다. Kafka는 메시지를 특정 Topic에 저장하며, 예를 들어 "chat.room.1"이라는 Topic에 메시지를 보냅니다.
- Kafka Consumer
- Kafka의 Consumer는 "chat.room.1" Topic을 구독하고 있으며, 해당 Topic에서 메시지를 실시간으로 받아옵니다. 이 메시지는 STOMP를 통해 처리됩니다.
- 메시지 전송
- Consumer는 받은 메시지를 STOMP 프로토콜을 통해 "/sub/chat/room/1" 경로로 전송합니다. 이 경로를 구독한 모든 클라이언트가 실시간으로 메시지를 받게 됩니다.
- 클라이언트 수신
- 사용자 A와 같은 다른 클라이언트들이 "/sub/chat/room/1"을 구독하고 있으므로, 해당 채팅방의 메시지를 실시간으로 수신하게 됩니다.

Reference
[Spring Boot, WebSocket, Kafka, MongoDB] 채팅 기능 구현#1. 기술 선정
성장🪜CI/CD 환경 구축, 모니터링 시스템 구축, 기본적인 CRUD 작업, AOP 구현 등 여러 기술을 활용한 프로젝트를 경험해봤다.이러한 경험을 바탕으로 새로운 기능을 구현해봄으로써, 더 넓은 경험
yenjjun187.tistory.com
Stomp + Kafka를 이용한 채팅 기능 개발하기 - (with Spring Boot) #1 (Kafka와 Stomp는 무엇일까?)
이번에 진행했던 분양 플랫폼 프로젝트에서 팀원들과 온라인 킥오프를 하며 기능을 도출하고, 각 기능별 담당자를 배정하는 도중 우리 프로젝트의 핵심이자 큰 챌린지가 될 수 있는 채팅 기능
velog.io
'프로젝트 > Wedle' 카테고리의 다른 글
[Spring Boot/Kafka/Stomp] 실시간 채팅 구현 – 5. Redis와 Kafka를 활용한 실시간 채팅 시스템 구현: WebSocket 기반의 고성능 메시징 (1) | 2025.03.19 |
---|---|
[Spring Boot/Kafka/Stomp] 실시간 채팅 구현 – 4. MongoDB 설정 및 연동 (0) | 2025.03.19 |
[Spring Boot/Kafka/Stomp] 실시간 채팅 구현 – 3. WebSocket 연결 및 핸들러 구현 (0) | 2025.03.19 |
[Spring Boot] 실시간 채팅 구현 – 2. Kafka 설치 (0) | 2025.03.10 |
[AWS] AWS S3 이용한 파일 업로드 - 프로필사진 등록 📸 (1) | 2025.03.07 |