전통적인 HTTP 요청—응답 모델은 클라이언트 요청이 있어야만 서버가 응답할 수 있어 서버의 변경 사항을 즉시 전달하기 어렵다.
변경을 확인하려면 폴링을 반복해야 하므로 네트워크와 서버 자원이 지소적으로 소모된다. 이러한 구조적 특성 때문에 실시간성을 요구하는 채팅, 알림, 모니터링 서비스에는 한계가 있다.
웹 애플리케이션은 채팅, 알림, 스트리밍처럼 즉각적인 반응이 필요한 기능이 크게 늘어나며 실시간성 요구가 높아지고 있다.
사용자는 페이지 새로고침 없이 변화가 바로 반영되는 자연스러운 경험을 기대한다.
이에 따라 서버는 빠른 이벤트 전송과 지속 연결을 지원하는 통신 방식이 점점 더 중요해지고 있다.
| 서비스 유형 | 설명 | 예시 |
|---|---|---|
| 실시간 채팅 | 사용자가 메시지를 입력하면 상대방에게 지연 없이 전달되어야 하는 양방향 실시간 통신 서비스 | 카카오톡, 슬랙, 디스코드 |
| 실시간 알림(Notification) | 서버에서 발생한 이벤트를 즉시 사용자에게 전달하는 단방향 또는 양방향 스트리밍 서비스 | 주문 상태 변경 알림, 새 댓글/좋아요 알림 |
| 금융 서비스(Stock/Trading) | 주식·코인 등 시세는 밀리초 단위로 변하므로 지연 없이 클라이언트에 반영해야 하는 서비스 | 업비트 실시간 가격, 증권사 HTS/MTS |
| 실시간 위치 기반 서비스 | 다수의 사용자의 이동 위치를 실시간으로 서버가 받고 클라이언트에 반영해야 하는 서비스 | 배달의민족 배달 위치 추적, 카카오 차량 위치 |
| 온라인 게임(멀티플레이) | 다수 사용자 간 상태 동기화를 위한 초저지연 통신이 요구됨 | FPS 게임, MOBA, 실시간 전략 게임 |
| 라이브 스트리밍/라이브 커머스 | 영상·음성을 실시간으로 전송하며 시청자 상호작용이 동시에 발생하는 서비스 | 유튜브 라이브, 네이버 쇼핑 라이브 |
| IoT 센서/모니터링 시스템 | 기기·센서의 상태 변화를 서버가 실시간으로 수집하고 대시보드에 갱신해야 하는 서비스 | 공장 설비 모니터링, 스마트홈 센서 알림 |
| 협업 서비스(문서/보드) | 여러 사람이 동시에 문서를 편집할 때 실시간으로 모든 변경분을 공유하는 서비스 | 구글 Docs, 피그마 |
폴링: 클라이언트가 일정 간격으로 서버에 요청을 보내 업데이트가 있는지 확인하는 방식이다. 구현이 단순하지만 실시간성이 필요한 경우 오버헤드가 발생 할 수 있다.
롱 폴링: 서버가 새로운 데이터가 생길 때까지 요청을 오래 유지했다가 응답을 반환하는 방식이다. 불필요한 요청을 줄여 실시간성은 높아지지만 소켓 연결 유지 비용이 증가한다.
폴링은 클라이언트가 정해진 주기로 계속 요청을 보내기 때문에 서버 변화가 없어도 불필요한 트래픽이 발생한다. 요청 횟수가 많아질수록 서버·네트워크 자원이 낭비되고 지연도 커진다. 이러한 구조는 대규모 사용자 환경에서 확장성과 효율성이 크게 떨어지는 방식이다.
롱 폴링은 요청을 오래 유지해 실시간성을 높이지만, 연결이 많아지면 서버가 유지해야 하는 세션 수가 급격히 증가한다. 또한 응답 후 다시 요청을 재수립해야 해 완전한 지속 연결처럼 매끄럽지 않다. 결국 대규모 트래픽 환경에서는 서버 부하와 지연 문제가 여전히 남아 확장성에 한계가 생긴다.
단방향 통신: 서버 또는 클라이언트 한쪽에서만 데이터를 보낼 수 있는 구조로, 메시지 흐름이 단순하고 구현이 가볍다. 그러나 상호작용이 필요한 서비스에는 제약이 있어 활용 법위가 제한된다.
양방향 통신: 클라이언트와 서버가 서로 데이터를 주고 받을 수 있어 실시간 상호작용이 가능하다. 게임, 채팅처럼 즉각적인 반응이 필요한 서비스에 적합하지만 연결 상태를 지속 관리해야 한다.
WebSocket은 클라이언트와 서버가 하나의 연결을 지속적으로 유지하며 데이터를 주고받을 수 있는 양방향 통신 프로토콜이다. 초기에는 HTTP로 핸드셰이크를 수행하지만 이후에는 독립된 전용 소켓 연결로 전환된다. 이를 통해 실시간 상호작용이 필요한 서비스에서 낮은 지연과 효율적인 통신을 제공한다.
Server-Sent Events는 서버가 클라이언트에게 일방향으로 지속적인 이벤트 스트림을 전송하는 기술이다. HTTP 기반의 단일 연결을 유지하며 서버에서 새로운 데이터가 생길 때마다 즉시 전달한다. 구현이 가볍고 브라우저 지원이 좋아 실시간 알림이나 모니터링에 적합하다.
웹훅(Webhooks)은 서버에서 특정 이벤트가 발생했을 때 등록된 외부 서버로 즉시 HTTP 요청을 보내 알림을 전달하는 방식이다. 클라이언트가 주기적으로 요청을 보내지 않아도 되어 폴링 대비 효율적이다. 주로 결제 완료, 상태 변경, Git push 같은 이벤트 기반 자동화를 위해 사용된다. (카카오 로그인, 페이, 토스 페이 등)
| 기술 | 통신 방식 | 주요 특징 | 적합한 활용 사례 |
|---|---|---|---|
| Polling | 클라이언트 → 서버 (반복 요청) | 구현 간단하나 불필요 요청 많아 비효율적 | 간단한 주기적 갱신 |
| Long Polling | 요청 유지 후 서버 이벤트 시 응답 | 실시간성↑, REST와 쉽게 통합, 그러나 연결 비용 부담 | 알림, 메시지 갱신 |
| SSE (Server-Sent Events) | 서버 → 클라이언트 단방향 스트리밍 | HTTP 기반, 브라우저 지원 좋음, 구현 쉬움 | 실시간 알림, 모니터링 |
| WebSocket | 서버 ↔ 클라이언트 양방향 | 낮은 지연, 상호작용 서비스 최적 | 채팅, 게임, 공동 편집 |
| gRPC Streaming | 스트림 기반 양방향/단방향 | 고성능 바이너리, MSA 간 통신 적합 | 서버 간 실시간 데이터 처리 |
| WebHook | 서버 → 외부 서버(HTTP 요청) | 이벤트 발생 시 즉시 외부 시스템으로 푸시,폴링 불필요 | 결제 알림, GitHub 이벤트, 자동화 트리거 |
| 프로토콜 | 통신 방식 | 주요 특징 | 사용 사례 |
|---|---|---|---|
| MQTT | Pub/Sub | 매우 경량, 저전력·저대역폭 환경 최적화 | IoT 센서, 스마트홈 |
| AMQP | 메시지 큐 기반 | 라우팅·보장 전달·트랜잭션 등 고급 메시징 지원 | RabbitMQ, 금융·백엔드 메시징 |
| CoAP | REST 기반 (UDP) | IoT 장치용 경량 프로토콜, 빠르고 단순 | 임베디드 IoT, 소형 디바이스 |
| WebRTC | P2P 실시간 | 오디오·비디오·데이터 전송, 브라우저 내장 지원 | 화상회의, 화면 공유 |
| STOMP | 메시징 (WebSocket 기반) | WebSocket 위에서 동작하는 텍스트 기반 Pub/Sub | 웹 채팅, 알림 서비스 |
| RTMP | 스트리밍 (TCP) | 지연이 낮은 스트리밍 업로드에 최적화 | OBS → 서버 라이브 방송, 라이브 플랫폼 업로드 |
| RTP/RTCP | 스트리밍 (UDP) | 실시간 오디오·비디오 전송 표준, QoS 관리 | Zoom, WebRTC 내부 미디어 전송 구성요소 |
WebSocket은 클라이언트와 서버가 하나의 연결을 지속적으로 유지하며 데이터를 주고받을 수 있는 양방향 통신 프로토콜이다. 초기에는 HTTP로 핸드셰이크를 수행하지만 이후에는 독립된 전용 소켓 연결로 전환된다.
이를 통해 실시간 상호작용이 필요한 서비스에서 낮은 지연과 효율적인 통신을 제공한다.
WebSocket은 처음에 HTTP 요청을 보내며 Upgrade: websocket 으로 프로토콜 전환을 요청한다.
서버는 요청을 검증한 뒤 101 Switching Protocols 응답을 보내 업그레이드를 승인한다.
이후 기존 HTTP 연결은 WebSocket 전용의 지속 연결로 전환된다.
핸드셰이크가 끝나면 클라이언트와 서버는 WebSocket 프레임을 통해 양방향 실시간 통신을 수행한다.
WebSocket은 TCP 위에서 동작하며 단일 연결로 동시에 송수신이 가능한 전이중(Full-Duplex) 통신을 제공한다.
클라이언트와 서버는 서로의 응답을 기다리지 않고 독립적으로 메시지를 주고받을 수 있다.
이 덕분에 낮은 지연과 실시간 상호작용이 필요한 서비스에 최적화되어 있다.
WebSocket 프레임은 FIN, opcode, MASK, payload length 등의 헤더 정보로 구성되어 데이터의 종류·길이·전송
방식을 정의한다. 필요 시 확장 길이와 마스킹 키가 추가되며, 본문은 Payload Data 영역에 담겨 전송된다.
클라이언트는 항상 데이터에 마스킹을 적용해 전송하며, 서버는 이를 해제하고 필요 시 다중 프레임을 조합해 메시지를 완성한다.
Spring WebSocket은 클라이언트와 서버가 하나의 지속 연결을 통해 양방향으로 데이터를 주고받는 실시간 통신 기술이다.
HTTP 핸드셰이크 후 WebSocket 프로토콜로 전환되어 낮은 지연의 메시지 송수신이 가능하다.
Spring은 메시지 기반 아키텍처(Channel, Handler)를 사용해 확장성과 구조적 처리를 지원한다.
STOMP 프로토콜과 연계하면 Pub/Sub 방식으로 브로커 기반의 메시지 전달도 쉽게 구성할 수 있다.
STOMP는 메시지를 목적지 기반으로 라우팅하는 구조로, 클라이언트의 SEND/SUBSCRIBE 명령, 메시지를 분배하는 브로커, 메시지를 처리하는 애플리케이션 핸들러, 그리고 이를 전달하는 채널(Request/Response Channel) 로 구성된다.
이 구성요소들이 결합되어 WebSocket 기반의 안정적인 Pub/Sub 메시징 모델을 완성한다.
STOMP는 텍스트 기반 메시징 프로토콜로, 목적지(/topic, /queue)에 따라 메시지를 라우팅하는 Pub/Sub 모델을 WebSocket 위에서 단순하게 구현한다.
클라이언트는 SEND와 SUBSCRIBE 명령을 사용해 메시지 전달과
구독을 수행하고, 브로커는 이를 기반으로 메시지를 여러 구독자에게 효율적으로 분배한다.
| 명령어 | 용도 | 설명 |
|---|---|---|
| CONNECT | 세션 초기화 요청 | 클라이언트가 STOMP 세션을 시작하기 위해 서버에 연결을 요청함. |
| CONNECTED | 세션 수립 확인 | 서버가 CONNECT 요청을 승인하고 세션이 활성화되었음을 알림. |
| SEND | 메시지 전송 | 클라이언트가 서버의 Application endpoint로 메시지를 전송할 때 사용. |
| SUBSCRIBE | 메시지 수신 구독 | 특정 Topic/Queue를 구독하여 서버가 push하는 메시지를 받음. |
| UNSUBSCRIBE | 구독 해지 | 기존 SUBSCRIBE 요청을 취소해 더 이상 메시지를 받지 않도록 함. |
| ACK | 메시지 처리 확인 | 메시지를 정상적으로 수신·처리했음을 서버에 알림. |
| NACK | 메시지 처리 실패 알림 | 처리 중 오류 발생을 서버에 알리고 재전송 전략에 활용됨. |
| BEGIN | 트랜잭션 시작 | 여러 SEND 메시지를 하나의 트랜잭션 단위로 묶기 위함. |
| COMMIT | 트랜잭션 커밋 | BEGIN 이후의 작업을 확정하여 메시지 전송을 실제 반영. |
| ABORT | 트랜잭션 취소 | BEGIN 이후의 메시지 처리 작업을 취소함. |
| DISCONNECT | 세션 종료 요청 | 클라이언트가 STOMP 세션 종료 및 연결 정리를 요청. |
| MESSAGE | 서버 메시지 전달 | 서버가 구독자에게 메시지를 push할 때 사용하는 프레임. |
| RECEIPT | 명령 처리 확인 | 서버가 특정 명령(SEND, SUBSCRIBE 등)을 정상 처리했음을 알림. |
| ERROR | 오류 알림 | 서버가 오류를 감지하여 클라이언트에 상세 오류 정보를 전달함. |
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
// 1) 클라이언트가 접속할 STOMP WebSocket 엔드포인트
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
.withSockJS(); // 필요 없으면 제거 가능 (순수 WebSocket만 사용)
}
// 2) 메시지 브로커(SimpMessaging) 설정
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app"); // 서버 메시지 핸들러(@MessageMapping)
registry.setUserDestinationPrefix("/user"); // 1:1 메시징 prefix
registry.enableSimpleBroker("/topic", "/queue"); // 구독자에게 push하는 내장 메시지 브로커
}
// 3) 클라이언트 → 서버 요청(Inbound) 처리 쓰레드풀
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.taskExecutor()
.corePoolSize(4)
.maxPoolSize(16)
.queueCapacity(100);
}
// 4) 서버 → 클라이언트 응답(Outbound) push 쓰레드풀
@Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
registration.taskExecutor()
.corePoolSize(4)
.maxPoolSize(16)
.queueCapacity(200);
}
// 5) WebSocket 전송 옵션 설정
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
registry.setMessageSizeLimit(64 * 1024) // 최대 메시지 크기 (64 KB)
.setSendTimeLimit(20_000) // 메시지 전송 timeout (20초)
.setSendBufferSizeLimit(512 * 1024); // 서버 버퍼 크기 (512 KB)
}
}