WebSocket

hznnoy·2025년 11월 2일

WebSocket이란?

WebSocket은 클라이언트와 서버 간의 양방향 통신(Full-Duplex Communication)을 지원하는 프로토콜이다.

기존 HTTP 통신은 요청(Request)-응답(Response) 방식으로 클라이언트가 먼저 요청해야만 서버가 응답할 수 있었지만, WebSocket은 연결이 한 번 맺어지면 서버가 클라이언트로 자유롭게 데이터를 보낼 수 있다.

즉, 실시간 양방향 데이터 송수신이 가능한 통신 방식이다.


2. 기존 HTTP와의 차이점

구분HTTPWebSocket
통신 방식요청-응답 (Request/Response)양방향 (Full-Duplex)
연결 방식요청 시마다 새 연결한 번 연결 후 지속 유지
프로토콜HTTP/1.1ws:// 또는 wss://
주요 목적문서 및 API 요청실시간 데이터 전송 (채팅, 알림 등)
헤더 오버헤드요청마다 있음초기 연결 이후 최소화

HTTP는 짧은 생명주기를 갖는 반면, WebSocket은 연결을 유지하며 서버 → 클라이언트 방향의 이벤트 전송이 가능하다.

따라서 채팅, 알림, 주식 시세, 게임 등 실시간성이 중요한 시스템에 주로 사용된다.


WebSocket 동작 과정

  1. 핸드셰이크(Handshake)

    클라이언트가 ws:// 또는 wss:// 프로토콜로 서버에 연결을 요청한다.

    서버는 HTTP 101 Switching Protocols 응답을 보내며 WebSocket 연결로 전환한다.

    GET /chat HTTP/1.1
    Host: example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    Sec-WebSocket-Version: 13
    
  2. 연결 유지 (Persistent Connection)

    한 번 연결되면 TCP 기반의 단일 소켓을 통해 양방향 통신이 이루어진다.

  3. 메시지 송수신

    클라이언트와 서버는 Text 또는 Binary 형태의 메시지를 주고받는다.

  4. 연결 종료 (Close Frame)

    어느 한쪽이 Close 프레임을 전송하면 세션이 종료된다.


Spring에서의 WebSocket 구조

Spring은 WebSocket을 쉽게 구현할 수 있도록 spring-websocket 모듈을 제공한다.

Spring WebSocket은 크게 두 가지 방식으로 사용할 수 있다.

저수준 API (WebSocketHandler)

WebSocketHandler를 직접 구현하는 방식으로, 프레임 단위의 세밀한 제어가 가능하다.

@Component
public class ChatHandler extends TextWebSocketHandler {

    private final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();

    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
        for (WebSocketSession s : sessions.values()) {
            s.sendMessage(message);
        }
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        sessions.put(session.getId(), session);
    }
}

그리고 WebSocket 설정 클래스를 등록한다.

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    private final ChatHandler chatHandler;

    public WebSocketConfig(ChatHandler chatHandler) {
        this.chatHandler = chatHandler;
    }

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(chatHandler, "/ws/chat")
                .setAllowedOrigins("*");
    }
}

STOMP + SockJS 기반 (고수준 추상화)

실무에서는 대부분 STOMP 프로토콜SockJS를 함께 사용한다.

이는 WebSocket 메시지 통신을 Pub/Sub 구조로 확장하여, 브로드캐스팅이 용이하다.

STOMP란?

STOMP(Simple Text Oriented Messaging Protocol)는 메시징 프로토콜로,

WebSocket 위에서 “채널(Topic)” 개념을 통해 메시지를 전송하고 구독할 수 있게 해준다.SockJS란?

SockJS는 WebSocket을 지원하지 않는 브라우저에서도 동작하도록 도와주는 폴백(Fallback) 라이브러리다.


Spring STOMP 예시 구조

설정

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic"); // 구독 경로
        registry.setApplicationDestinationPrefixes("/app"); // 발행 경로
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws") // 연결 엔드포인트
                .setAllowedOrigins("*")
                .withSockJS();
    }
}

Controller

@Controller
public class ChatController {

    @MessageMapping("/chat.sendMessage")
    @SendTo("/topic/public")
    public ChatMessage sendMessage(ChatMessage message) {
        return message;
    }
}

클라이언트 (JavaScript)

var socket = new SockJS('/ws');
var stompClient = Stomp.over(socket);

stompClient.connect({}, function() {
    stompClient.subscribe('/topic/public', function(message) {
        console.log(JSON.parse(message.body));
    });
});

function sendMessage() {
    stompClient.send("/app/chat.sendMessage", {}, JSON.stringify({ content: "hello" }));
}

이 구조에서:

  • /app/chat.sendMessage → 메시지 발행 경로
  • /topic/public → 메시지 구독 경로 로 동작한다.

적용 시 고려사항

구분설명
인증JWT 토큰 기반의 사용자 인증 필요 (HandshakeInterceptor 사용)
확장성단일 서버 한계를 넘어서는 경우 Redis Pub/Sub 또는 Kafka 사용
에러 처리연결 종료/예외 발생 시 세션 관리 필요
성능연결 유지 수가 많을 경우 커넥션 풀 관리 중요
보안CORS, 토큰 검증, 세션 스니핑 방지 필요
profile
노력에는 지름길이 없으니까요

0개의 댓글