일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 데이터베이스
- 프로그래밍
- 프로그래밍언어
- 소프트웨어
- 데이터분석
- 알고리즘
- 자료구조
- 웹개발
- 머신러닝
- 인공지능
- 데이터과학
- Yes
- I'm Sorry
- 데이터구조
- 사이버보안
- 버전관리
- springboot
- 네트워크
- 컴퓨터비전
- 컴퓨터과학
- 소프트웨어공학
- 디자인패턴
- 네트워크보안
- 파이썬
- 보안
- 빅데이터
- 컴퓨터공학
- 클라우드컴퓨팅
- 딥러닝
- 자바스크립트
- Today
- Total
스택큐힙리스트
Spring Boot 실시간 채팅 서버 만들기 — WebSocket + STOMP, 하트비트까지 완전 정복 본문
왜 WebSocket + STOMP인가?
채팅·알림처럼 양방향 저지연이 필수인 기능은 HTTP Polling이나 SSE만으로 한계가 있습니다. WebSocket으로 영구 연결을 열고, STOMP 프로토콜로 채널·헤더·구독 개념을 입혀주면 메시지 라우팅이 쉬워집니다. Spring Boot는 spring-websocket 모듈 하나로 이 조합을 깔끔히 지원해 왔고, 2025년 현재 Boot 3.3에서도 설정이 거의 변하지 않았습니다.
1️⃣ 의존성 한 줄
implementation("org.springframework.boot:spring-boot-starter-websocket")
외부 브로커(RabbitMQ, Redis Streams 등)를 쓰려면 대응 스타터를 추가하세요.
2️⃣ WebSocket 설정 클래스
@Configuration
@EnableWebSocketMessageBroker
class WebSocketConfig(
private val taskScheduler: TaskScheduler // 하트비트용
) : WebSocketMessageBrokerConfigurer {
override fun registerStompEndpoints(registry: StompEndpointRegistry) {
registry.addEndpoint("/ws") // 핸드셰이크 경로
.setAllowedOrigins("https://myapp.com")
.withSockJS() // 레거시 브라우저 대처
}
override fun configureMessageBroker(registry: MessageBrokerRegistry) {
registry.enableSimpleBroker("/topic", "/queue")
.setHeartbeatValue(longArrayOf(10_000, 20_000)) // S↔C 10s/20s
.setTaskScheduler(taskScheduler) // 필수!
registry.setApplicationDestinationPrefixes("/app") // 서버 수신 네임스페이스
}
}
TaskScheduler가 없으면 심플 브로커는 하트비트를 못 보냅니다. 값은 [서버보내기, 서버받기] ms 쌍으로, 0이면 해당 방향 하트비트 비활성화.
3️⃣ 클라이언트(Javascript) 연결 예시
import { Client } from '@stomp/stompjs';
const client = new Client({
brokerURL: 'wss://myapp.com/ws',
debug: console.log,
reconnectDelay: 5000, // 자동 재연결
heartbeatIncoming: 20000, // 클라이언트→서버
heartbeatOutgoing: 10000 // 서버→클라이언트
});
client.activate();
@stomp/stompjs가 탭 비활성 상태에서 JS 스레드가 멈춰 하트비트를 못 보내는 이슈가 있습니다. 실무에서는 heartbeatIncoming=0으로 끊김을 감수하고 서버만 보내도록 해도 안정적이라는 사례가 많습니다.
4️⃣ 하트비트 전략 베스트 프랙티스
- 10s/20s 규칙 — 서버는 10 초마다 ping, 20 초 이상 무응답이면 연결 종료. Spring 문서도 기본 예제로 권고합니다.
- 대규모 부하 테스트 — 수천 클라이언트를 한 머신에서 시뮬레이션할 땐 클라이언트 하트비트를 끄세요. 연결마다 스케줄 태스크가 생겨 성능 편차가 커집니다.
- 브라우저 백그라운드 — 모바일·데스크톱 브라우저가 탭을 잠그면 JS 타이머가 멈춥니다. 서버 단에서만 하트비트를 보내고, 클라이언트는 재연결 로직으로 복원하세요.
- 외부 브로커 — RabbitMQ / Artemis 같은 STOMP 브로커를 쓰면 클러스터 환경에서도 하트비트를 자동 중계해 줍니다. 반대로 심플 브로커는 인메모리라 인스턴스 간 공유가 안 되므로 스케일아웃 시 반드시 외부 브로커나 Redis Pub/Sub를 병행하세요.
- TLS 필수 — wss://로 업그레이드하지 않으면 중간 프록시가 하트비트를 잘라내거나 연결을 강제로 종료하는 사례가 있습니다.
5️⃣ 핸드셰이크·메시지 흐름, 한눈에
- Handshake — 브라우저가 /ws로 Upgrade: websocket 요청 → Spring의 DefaultHandshakeHandler가 101 응답, 세션 생성.
- Send — 클라가 /app/chat.send 등으로 메시지 전송 → @MessageMapping 메서드 → 브로커(/topic/room1)로 publish.
- Subscribe — 클라가 /topic/room1 구독 → 브로커가 세션 목록에 추가 → 새 메시지 오면 모든 세션에 push.
- Heartbeat — taskScheduler 트리거로 서버→클라 빈 프레임 전송, 클라 응답 대기 → 타임아웃 시 SessionDisconnectEvent.
6️⃣ 통합 테스트 팁
WebSocketStompClient + taskScheduler를 주입해 실제 포트를 열고, client.connect() 후 subscribe() 콜백에서 CountDownLatch를 풀어 성공 여부를 검증하세요. 하트비트 간격을 짧게(1s) 줄이면 테스트가 빨라집니다.
마치며
40줄 남짓의 설정으로도 Spring Boot 3.x에서 안 끊기는 채팅 서버를 만들 수 있습니다. 하트비트만 제대로 잡아두면 브라우저 백그라운드·네트워크 홀수 마이크로단절까지 대부분 커버됩니다. “속도는 WebSocket, 구조화는 STOMP” — 이 조합은 2025년에도 여전히 최강입니다.
'개발' 카테고리의 다른 글
Template Method 패턴: “뼈대는 내가 잡을게, 살은 네가 붙여” (0) | 2025.07.23 |
---|---|
State 패턴: if-else 없이 굴러가는 ‘상태 머신’의 힘 (1) | 2025.07.23 |
SSE vs WebSocket, 언제 써야 빛을 볼까? (6) | 2025.07.22 |
Redis Streams vs Pub/Sub: 저장형 알림 설계, 무엇을 써야 할까? (3) | 2025.07.22 |
Redis Pub/Sub로 5초 만에 터지는 알림, 멀티 인스턴스에서도 놓치지 않는 비결 (0) | 2025.07.22 |