순수웹소켓

이상민·2025년 5월 22일
0
  • 클라이언트가 connect 경로로 채팅을 요청하면 채팅은 http 프로토콜이아닌 웹소켓 프로토콜이기때문에
  • controller가 요청을 처리하지 않고 위와 같이 핸들러를 추가하여 핸들러가 처리한다.

Handler의 역할

@Component
public class SimpleWebSocketHandler extends TextWebSocketHandler {

}

-클라이언트 A,B, C 가 각각connect 요청을 하면 서버의 websocket handler가 서버의 메모리에 클라이언트의 정보를 각각 저장

  • 만약 클라이언트 A가 메세지를 서버에 전송하면 handler는 서버 메모리에 저장해둔 A,B,C 모두에게 전파.
  • 이때 A 는 예외처리를 하는 코드를 추가할 수 있음
주의: http 프로토콜이 아니기 때문에 헤더에 토큰을 넣어서 보낼 수도 없고 ws에 토큰을 넣어 보낸다고 하더라도 authentication을 수행 할 수 없다. 따라서 filter에서 제외처리해야한다.
그러면 서버에서 사용자 인증을 어떻게 할까? 
package com.example.chatserver.chat.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

//connect로 요청이 들어 올때 처리할 핸들러
@Component
@Slf4j
public class SimpleWebSocketHandler extends TextWebSocketHandler {
    //여러 사용자가 한꺼번에 올수 있으니
    private final Set<WebSocketSession> sessions = ConcurrentHashMap.newKeySet();

    public SimpleWebSocketHandler() {
        super();
    }


    /**
     * 연결 되면 set에 사용자의 정보를 등록
     * @param session
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //세션에는 사용자의 ip, MAC 과 같은 정보가 들어있음
        //세션을 서버에 저장해둠
        sessions.add(session);
        log.info("Connect: {}", session.getId());

    }

    /**
     * 사용자의 메세지를 전송하는 메소드
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
       //payload: 사용자의 메세지가 담겨 있음
        String payload = message.getPayload();
        log.info("received message : {}", payload);
        //반복문 돌며 전송
        for(WebSocketSession s :  sessions){
            if(s.isOpen()){
                s.sendMessage(new TextMessage(payload));
            }
        }

    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
       sessions.remove(session);
       log.info("disconnected!!");
    }

    /**
     * 연결이 끊기면 세션을 날려줌
     * @return
     */
    @Override
    public boolean supportsPartialMessages() {
        return super.supportsPartialMessages();
    }
}

Spring WebSocket 구성 요약

@Configuration
@EnableWebSocket
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketConfigurer {
    private final SimpleWebSocketHandler simpleWebSocketHandler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // "/connect url로 연결 요청이 들어오면 , 핸들러 클래스가 처리"
        registry.addHandler(simpleWebSocketHandler, "/connect")
         //securityConfig에서의 cors예외는 http예외임
         //따라서 websocket 프로토콜에 대한 요청에 대해서는 별도의 cors 설정이 필요
        .setAllowedOrigins("http://localhost:3000");
    }
}

1. 구성

  • @EnableWebSocketWebSocketConfigurer 구현으로 웹소켓 엔드포인트 등록
  • TextWebSocketHandler를 상속받아 메시지 처리

2. CORS 설정

  • 웹소켓도 최초 요청은 HTTP이므로 CORS 설정 필요
    setAllowedOrigins("http://localhost:3000")

3. 인증 처리 방식

  • 웹소켓 요청에는 브라우저에서 커스텀 헤더 설정 불가
    → 보통 쿼리 파라미터, 쿠키 사용
  • 서버 측에서는 WebSocketSession 내 정보 추출 후 직접 인증
    • 또는 WebSocketHandlerDecorator로 인증 로직 감쌈

4. 세션 관리

  • 연결된 클라이언트의 세션을 Map 등에 저장하여 메시지 전파
  • 예외 처리, 연결 종료 등도 고려해야 함

5. 주의

  • 필터에서의 인증 제외는 HTTP 요청에만 적용되며, WebSocket 핸들러에 별도 인증 로직 필요
profile
잘하자

0개의 댓글