
HTTP Polling VS HTTP Long Polling
| 항목 | HTTP Polling | HTTP Long Polling |
|---|---|---|
| 🧭 동작 방식 | 클라이언트가 일정 주기로 계속 요청 | 클라이언트가 요청을 보내고, 데이터가 생길 때까지 대기 |
| 🔁 요청 빈도 | 짧은 주기로 자주 보냄 | 필요할 때만 재요청 |
| 🧠 서버 부하 | 많은 요청 처리 필요 → 부하 큼 | 요청 수가 적어 상대적으로 부하가 적음 |
| 📥 응답 타이밍 | 즉시 응답 (데이터가 없어도) | 데이터 생기면 응답 |
| 📦 실시간성 | 낮음 (최신 데이터를 놓칠 수 있음) | 높음 (거의 실시간에 가깝게 데이터 수신) |
| 🔌 연결 지속 시간 | 짧음 (매번 연결을 다시 맺음) | 길음 (요청이 대기 상태로 유지됨) |
| 🚀 사용 예시 | 뉴스 피드 갱신, 주기적인 상태 체크 | 채팅, 알림 시스템 등 실시간성이 중요한 경우 |
📌요약
HTTP Polling은 반복적으로 "새로 생긴 게 있니?"라고 묻는 방식이고,
HTTP Long Polling은 "생기면 알려줘"라고 하고 기다리는 방식입니다.
실시간성을 높이고 네트워크 자원을 줄이려면 Long Polling이 더 유리하지만,
궁극적으로는 WebSocket이나 SSE(Server-Sent Events) 같은 기술로 가는 게 효율적입니다.
WebSocket 이란?


Spring 에서 WebSocket 을 사용하는 대표적인 두가지
public class ChatWebSocketHandler extends TextWebSocketHandler {
//세션을 관리하는 객체
// Collections.synchronizedSet <- 여러 스레드가 동시에 이 객체에 접근할 때 동시설 문제를 안전하게 만들어주는 역할
// 동시성 문제를 해결 - 서버에 여러 클라이언트 접속 시에 발생할 수 있는 데이터 손실 고려
private final Set<WebSocketSession> sessions = Collections.synchronizedSet(new HashSet<>());
// json 문자열 -> 자바 객체로 변환
private final ObjectMapper objectMapper = new ObjectMapper();
//방과 방 안에 있는 세션을 관리하는 객체
private final Map<String, Set<WebSocketSession>> rooms = new ConcurrentHashMap<>(); //<-동시성 문제 해결을 위해 사용
// 클라이언트가 보낸 메세지를 서버가 받았을 때 호출
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message)
throws Exception {
super.handleTextMessage(session, message);
// json 문자열 -> 자바 객체
ChatMessage chatMessage = objectMapper.readValue(message.getPayload(), ChatMessage.class);
String roomID = chatMessage.getRoomId(); //클라이언트에게 받은 메세지에서 roomID를 추출
if(!rooms.containsKey(roomID)){ //방을 관리하는 객체에 현재 세션이 들어가는 방이 있는지 확인
rooms.put(roomID, ConcurrentHashMap.newKeySet()); //없으면 새로은 방을 생성
}
rooms.get(roomID).add(session); //해당 방에 세션 추가
for(WebSocketSession s : rooms.get(roomID)) {
//for (WebSocketSession s : sessions) {
if (s.isOpen()) {
s.sendMessage(new TextMessage(objectMapper.writeValueAsString(chatMessage)));
System.out.println("전송된 메세지 = " +chatMessage.getMessage());
}
}
}
// 클라이언트가 웹 소켓 서버에 접속했을 때 호출
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
sessions.add(session); // 연결된 클라이언트 저장
System.out.println("접속된 클라이언트 세션 ID = " + session.getId());
}
// 클라이언트가 연결이 끊어졌을 때 호출
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status)
throws Exception {
super.afterConnectionClosed(session, status);
sessions.remove(session);
//연결이 해제되면 소속되어 있는 방에서 제거
for(Set<WebSocketSession> room : rooms.values()) {
room.remove(session);
}
}
}
순수 WebSocket 메세지
그냥 문자열(텍스트/바이너리)만 주고 받음
예시:

서버/클라이언트가 "어떤 의미인지, 누구한테 보내는지" 따로 해석해야 한다.(정해진 규약이 없음)
@Controller
public class ChatController {
@MessageMapping("/chat.sendMessage")
@SendTo("/topic/public")
public ChatMessage sendMessage(ChatMessage message) {
return message;
}
}
STOMP 메세지의 기본 형식

예시 : 클라이언트가 메세지를 서버에 전송(SEND)

예시 : 클라이언트가 구독 요청(SUBSCRIBE)

예시 : 서버가 메세지 전달(MESSAGE)

1. 생산성과 유지 보수성
2. 코드가 간결하고 실수 가능성이 적음
3. 확장성/변경 용이
1. 초고성능/초경량 통신이 필요할 때
2. 메세지 구조를 직접 커스텀 해야 할 때
3. 아주 단순한 서비스
🗃️순수 WebSocket VS STOMP over WebSocket 비교하기 1
| 항목 | 순수 WebSocket | STOMP over WebSocket |
|---|---|---|
| 🧭 프로토콜 구조 | WebSocket만 사용 | WebSocket 위에 STOMP 프로토콜 추가 |
| 🧩 메시지 포맷 | 바이너리 또는 텍스트 (자유 형식, JSON 등 직접 정의) | STOMP 형식 (프레임 기반: SEND, SUBSCRIBE, 등 명확함) |
| ⚙️ 구현 난이도 | 낮음 (단순한 구조), 그러나 직접 구현 많음 | 높음 (초기 셋업 필요), 하지만 기능은 풍부 |
| 📦 내장 기능 | 없음 (구독, 대상, 헤더 등 직접 구현해야 함) | 지원 (구독/발행, 헤더, ACK 등 표준화된 메시지 구조 지원) |
| 📬 메시지 라우팅 | 서버에서 직접 구현 필요 | STOMP의 destination 기반으로 라우팅 가능 |
| 💥 확장성/유지보수 | 구조가 단순하지만 기능 추가 시 복잡도 증가 | 미리 정의된 메시지 구조로 유지보수 쉬움 |
| 🧪 디버깅/가시성 | 메시지 구조 파악 어려움 | 텍스트 기반 프레임 → 디버깅 쉬움 |
| 📘 표준 명세 | WebSocket (RFC 6455) | WebSocket + STOMP (Simple Text Oriented Messaging Protocol) |
| 🧰 클라이언트 라이브러리 | 브라우저 WebSocket API | @stomp/stompjs, SockJS, spring-websocket 등 사용 |
| 🔒 보안/인증 | 직접 처리해야 함 | 메시지 헤더 등으로 인증 정보 쉽게 전달 가능 |
| 🌐 주요 사용 예시 | 게임, 채팅, 실시간 알림 (단순한 실시간 통신) | 채팅, 알림, 주식 시세, 협업 앱 등 복잡한 실시간 기능 |
🗃️순수 WebSocket VS STOMP over WebSocket 비교하기 2
| 항목 | 순수 WebSocket | STOMP over WebSocket |
|---|---|---|
| 헤더 | 거의 없음 | 많음 (command, destination, content-type 등) |
| 형식 | 이진/텍스트 가능 | 텍스트 전용 |
| 오버헤드 | 최소 (2~14 bytes/frame) | 큼 (헤더 수에 따라 50~200 bytes 이상) |
| 총 크기 예 | 약 115 bytes (100자 메시지 기준) | 300 bytes 이상 |
| 실시간성 | 매우 빠름 | 약간의 딜레이 존재 |
| 메시지 크기 | 작음 → 효율적 | 큼 → 오버헤드 존재 |
| 개발 난이도 | 낮음 (직접 구현 필요) | 높지만 Spring에서는 자동 처리 |
| 기능 지원 | 직접 구현 (pub/sub, ACK 등) | 내장 기능 많음 (구독, 구분, 에러 처리 등) |
🗃️순수 WebSocket VS STOMP over WebSocket 비교하기 3
| 구분 | 순수 WebSocket | STOMP over WebSocket |
|---|---|---|
| ✅ 성능 | ✔ 매우 가볍고 빠름 ✔ 메시지 크기 작음 ✔ 오버헤드 거의 없음 | ❌ 상대적으로 무거움 ❌ 메시지 크기 큼 ❌ 헤더 많음 → 오버헤드 큼 |
| ✅ 기능 | ❌ 기능 거의 없음 → 직접 pub/sub, ack, reconnect 등을 구현해야 함 | ✔ 구독, 라우팅, 에러 처리, 메시지 구분 등 내장 기능 풍부 |
| ✅ 개발 편의성 | ❌ 어렵고 수동 구현 많음 | ✔ Spring 등에서는 자동 지원으로 개발 쉬움 |
순수 WebSocket : 경량이지만 기능을 직접 만들어야 한다.
STOMP over WebSocket : 오버헤드가 있지만 기능이 풍부하고 Spring과 잘 통합됩니다.
단순한 통신만 필요하고, 모든 처리를 직접 컨트롤하고 싶다. => 순수 WebSocket
성능이 중요한 경우(예: 실시간 게임, 고빈도 메시징) => 순수 WebSocket
: 빠르고 오버헤드 적음. 하지만 개발은 조금 힘들고 복잡함.
메시징 시스템처럼 구독/발행 구조, 메시지 형식화가 필요하다 => STOMP over WebSocket
기능과 개발 생산성이 중요한 경우(예: 실시간 채팅, 알림 시스템) => STOMP over WebSocket
: 기능이 이미 다 구현돼 있고, Spring 등에서 쉽게 사용 가능
상황과 필요에 따라 성능 ↔ 기능 사이에서 선택하면 됩니다!