반응형

1. Socket


소켓네트워크에서 데이터를 주고받을 수 있도록 네트워크 환경에 연결할 수 있게 만들어진 연결부이다.

 

마치 전화기와 비슷하다. 두 사람이 전화로 대화하려면 각자 전화기가 필요하듯이, 두 컴퓨터 프로그램이 대화하려면 각각 소켓이 필요하다. 따라서 클라이언트 소켓, 서버 소켓으로 구분된다.

 

현재 대부분의 네트워크 통신은 인터넷 프로토콜을 사용하며, 대표적으로 TCP/IP, UDP/IP가 존재한다. TCP와 UDP는 소켓을 통해 구현되는 가장 흔한 프로토콜이다.

 

 

 

2. WebSocket


웹 소켓하나의 TCP 접속에 전이중 통신 채널을 제공하는 컴퓨터 통신 프로토콜로, 소켓 통신에 기반하여 웹 애플리케이션에 맞게 발전한 형태이다. HTTP나 HTTPS 위에서 동작하도록 설계되었으며, 서버와 브라우저 간 연결을 유지한 상태로 추가적인 HTTP 요청 없이 데이터를 교환할 수 있다.

 

 

2.1 HTTP와 WebSocket의 차이

Http와 WebSocket Protocol의 가장 큰 차이는 수립된 커넥션을 어떻게 하느냐이다.

 

Http는 비 연결성 프로토콜로 클라이언트가 요청을 보낼 때마다 연결을 맺고 응답을 받은 후 연결을 끊어버린다.

웹 소켓 이전의 HTTP 기반으로 한 실시간 통신 방식으로 폴링(Polling)롱 폴링(Long Polling)이 있지만, 클라이언트가 요청을 지속적으로 보내야 하는 상황을 벗어나지 못했다.

 

반면에 웹 소켓의 경우 한번 연결을 맺고 나면 어느 한쪽에서 연결을 끊으라는 요청을 보내기 전까지 연결을 유지하기 때문에 매번 연결할 때마다 발생하는 비용을 줄일 수 있다.

 

다시한번 정리하면 HTTP는 요청과 응답이 한 쌍을 이루는 구조로 통신을 한다. 즉, 내가 원하는 어떤 것을 얻기 위해서는 항상 그것을 달라고 요청을 해야했다. 반면, 웹 소켓은 연결이 계속 유지되고 있는 상태이기 때문에, 연결된 채널, 소켓을 통해 상대가 보내오는 메세지를 듣기만 하면 된다.

 

그렇다면 WebSocket은 어떻게 연결되는 것일까?

 

 

2.2 핸드셰이크(handShake)

웹 소켓 커넥션을 만들려면 new WebSocket() 을 호출하면 되며 이때 handShake라는 과정을 HTTP 프로토콜을 사용해 시작한다.

 

핸드 셰이크가 성공적으로 완료되면, 연결은 HTTP에서 WebSocket Protocol로 전환되며 HTTP → ws://, HTTPS → wss:// 로 변경된다.

 

 

 

3. SpringBoot + WebSocket King Client


Chrome 확장 프로그램인 WebSocket King Client에서 WebSocket을 구현해보자.

 

 

3.1 환경설정

  • Chrome에서 WebSocket King Client 설치 후 접속
  • Spring Boot 2.7.18
  • Java 11

 

websocket과 JSON을 사용하기 위해 Gradle에 의존성 추가

gradle

    implementation 'org.springframework.boot:spring-boot-starter-websocket'
    implementation 'org.json:json:20211205'

 

 

3.2 Config

Spring에서 웹 소켓을 사용하려면 클라이언트가 보내오는 통신을 처리할 Handler가 필요하다.

java

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new SocketTextHandler(), "/ws")
                .setAllowedOriginPatterns("*");
    }
}

이후 구현할 Handler를 웹 소켓이 연결될 때 handShake할 주소와 함께 인자로 넣어주면 된다.

CORS 제어를 위해 setAllowedOriginPatterns를 설정하여 특정 도메인에서만 WebSocket 연결을 허용할 수 있다.

.withSockJs() 를 통해 SockJs를 활성화 할 수 있지만 WebSocket Client King에서 오류가 날 수 있으니 제거하고 실습하자.

 

 

3.3 Handler

java

public class SocketTextHandler extends TextWebSocketHandler{

    private final Set<WebSocketSession> sessions = ConcurrentHashMap.newKeySet();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        sessions.add(session);
        for (WebSocketSession s : sessions) {
            s.sendMessage(new TextMessage("user: " + session.getId() + " connected"));
        }
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String payload = message.getPayload();
        JSONObject jsonObject = new JSONObject(payload);
        for (WebSocketSession s : sessions) {
            s.sendMessage(new TextMessage(jsonObject.get("user") + ": " +  jsonObject.get("message")));
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        sessions.remove(session);
        for (WebSocketSession s : sessions) {
            s.sendMessage(new TextMessage("user: " + session.getId() + " disconnected"));
        }
    }
}

 

웹 소켓은 기본적으로 Tex t또는 Binary 타입을 지원한다.

필요에 따라 TextWebSocketHandler 또는 BinaryWebSocketHandler라는 Spring이 기본적으로 제공하는 클래스를 상속하고 구현하면 된다.

 

이때, 메서드 인자로 받아오는 WebSocketSession이 있다. 흔히 말하는 HTTP 세션과는 다른 맥락으로 간단히 말하면 웹 소켓이 연결될 때 생기는 연결정보를 담고 있는 객체라고 보면 된다.

 

핸들러에서 웹 소켓 통신에 대한 처리를 하기 위해 이 세션들을 컬렉션으로 담아 관리하는 경우가 많으며, 위 코드를 보면 커넥션이 맺어질 때 컬렉션에서 웹 소켓 세션을 추가하고 끊어질 때 제거를 하고 있다.

 

추가로 세션이 연결됨과 끊어질 때, 입장과 퇴장 메세지를 보낼 수 있게 설정해놓았다.

 

handleTextMessage는 WebSocket을 통해 텍스트 메세지를 받았을 때 실행되는 핸들러로 받은 메세의 내용(payload, 클라이언트에서 JSON으로 보냈다고 가정)을 각 연결된 세션에 새 메세지를 보내게된다.

 

 

3.4 WebSocket King Client

상단에 CONNECTIONS을 하나 더 늘려 유저 A와 B가 각각 입장하여 채팅을 한다고 테스트해보자.

 

ws://localhost:8080/ws 주소로 연결과 함께 자동으로 부여된 session Id가 출력됨을 확인할 수 있다. /ws는 config에서 설정한 end-point이다.

 

이후 채팅을 JSON 형태로 보내게 되면, Handler에서 ‘user’‘message’ 의 key 값으로 메세지를 처리하는 걸 확인할 수 있다.

 

개발자 도구를 살펴보면 처음 handShake 과정에서 HTTP를 사용하기 때문에 유사한 양의 헤더 정보를 주고받게 되지만 한번 연결이 수립되고 나서는 간단한 Messages들만 오고 가는 것을 확인할 수 있다.

 

 

  • Origin : 클라이언트 origin을 나타낸다.
  • Connection : Upgrade, 클라이언트 측에서 프로토콜을 바꾸고 싶다는 신호를 보냈다는 것을 나타낸다. (http → ws)
  • Upgrade : websocket, 클라이언트 측에서 요청한 프로토콜은 'websocket’이라는 것을 의미한다.
  • Sec-WebSocket-Key : 보안을 위해 브라우저에서 생성한 키로, 서버가 웹 소켓 프로토콜을 지원하는지를 확인하는 데 사용된다.

 

마치며

WebSocket은 실시간 양방향 통신을 제공하는 강력한 기술이지만 몇 가지 한계가 있다.

 

1️⃣ 메세지 구조화

WebSocket은 단순한 텍스트 교환만을 제공하며 메세지 형식에 대한 규격이 없다. 우리는 handler에서 String 값인 payload를 JSON으로 파싱하여 사용하였다. 따라서 복잡한 메시지 패턴이나 로직들을 개발자가 직접 정의해야하고 구현해야 한다.

 

2️⃣ 클라이언트 관리

세션의 ID는 서버가 클라이언트와의 WebSocket 연결될 때 자동으로 생성되는 랜덤한 값이기 때문에 연결된 클라이언트들을 추적하고 관리하는 시스템을 직접 구축해야한다.

 

3️⃣ 메세지 라우팅

특정 주제(Topic)나 채널(Channel)에 따라 메시지를 구분하거나 라우팅하는 기능이 내장되어 있지 않기 때문에, 이를 구현하려면 추가적인 로직과 구조를 설계해야한다.

 

 

 

이러한 어려움을 해결하기 위해 STOMP와 같은 상위 프로토콜을 사용하면 메지 라우팅과 세션 관리를 더 쉽게 구현할 수 있다.

 

다음 글에서는 STOMP에 대해 알아보고 실습해 보는 시간을 가져보자.

 

 

[Spring] STOMP란? (Spring Boot + STOMP TESTER로 구현해보기)

지난 글(WebSocket)에 이어서 STOMP에 대해 알아보자.  1. STOMPSTOMP(Simple/Streaming Text Oriented Messaging Protocol)란 클라이언트와 서버 간의 메시지 전송을 위한 텍스트 기반 프로토콜이다. 이 프로토콜은 Pu

murphytklee.tistory.com

 

반응형
반응형

+ Recent posts