본문 바로가기
programming/FE

React에서 SockJs + StompJs 로 소켓 통신하기

by s2econd.blue 2022. 8. 6.

개요

소켓 통신 관련된 글을 작성하려고 하니 왜 다른 블로그들이 모두 SockJs와 WebSocket의 차이가 무엇인지, 

StompJs가 무엇인지에 대한 선행 학습을 요구한 이유를 알 것 같았습니다.

단순하게 말하자면 WebSocket과 SockJs는 동일한 기능을 하는 프로토콜이지만 연결 방식이 다르고 STOMP는 그 둘의 하위 프로토콜로써 작동하는 기술입니다.

조금 더 자세한 이해를 위해 아래 블로그를 방문하는 것도 좋을 것 같습니다.

https://velog.io/@kwj2435/Web-WebSocket-STOMP-SocketJS-%EC%B0%A8%EC%9D%B4

 

[Web] WebSocket - STOMP - SockJS 차이

유저간 채팅 기능 구현중 학습한 내용 정리WebSocket, STOMP, SocketJs의 차이점WebSocket 동작 순서Http의 특징 중 하나는 단방향 통신이다.클라이언트와 서버간의 통신은 단건으로 이루어지며 하나의 요

velog.io

 

 

기존에 WebSocket만 사용해보신 분들이라면 "굳이 SockJs와 Stomp라는 것을 사용해야 할까?" 라는 의문을 가질 수도 있을 것 같습니다.

하지만 사용해보니 SockJs만의 장점이 존재하고 Stomp가 BE에서의 통신 구현을 매우 간단하고 효율적으로 만들어주는 것입니다.

이제부터 코드를 통해서 이것들이 어떻게 작동되는지 알아보겠습니다.

프레임워크는 리액트이지만 리액트 부분만 제거하고 보셔도 큰 문제는 없습니다. (뭐가 리액트 코드인지도 모르겠다면 이해가 되지 않는다면 리액트 코드입니다.)

작성일인 2022.08.06 기준으로 공식 도큐먼트를 참고했습니다.

정확한 라이브러리 명칭은

SockJs : sockjs-client
https://www.npmjs.com/package/sockjs-client

 

sockjs-client

SockJS-client is a browser JavaScript library that provides a WebSocket-like object.. Latest version: 1.6.1, last published: 2 months ago. Start using sockjs-client in your project by running `npm i sockjs-client`. There are 894 other projects in the npm r

www.npmjs.com


StompJs : @stomp/stompjs
https://www.npmjs.com/package/@stomp/stompjs

 

@stomp/stompjs

STOMP client for Javascript and Typescript. Latest version: 6.1.2, last published: a year ago. Start using @stomp/stompjs in your project by running `npm i @stomp/stompjs`. There are 183 other projects in the npm registry using @stomp/stompjs.

www.npmjs.com

 


코드

1. import, require로 추가

//socktjs
import SockJS from "sockjs-client";

//NodeJS에서
var StompJs = require("@stomp/stompjs");

//typeScript or ES6에서
import {Client, Message} from "@stomp/stompjs";

require는 NodeJS에서, import는 TypeScript or ES6 에서 사용하라고 되어있는데 리액트 기준 무엇을 사용하든 괜찮습니다.
import 방식은 앞에 StompJs라는 접두사가 없어지는 것과 같으니 편한 방식으로 사용하시면 됩니다. 전

전 처음에 StompJs라는 클래스를 import해야 하는 줄 알고 시행착오를 겪었는데 공식 문서에 보면

There is no StompJs class/object to be imported.

저같은 사람들을 위해 StompJs는 임포트할 수 있는 클래스나 객체가 없다고 큼직하게 작성되어 있더군요.

 

2. Client 객체 생성

const client2 = StompJs.Client({
    //websocket 주소만 입력 가능 * ws://, wss:// 로 시작
    // brokerURL: "https://i7d106.p.ssafy.io:8080/ws",
    connectHeaders: {},
    debug: function (str) {
      console.log(str);
    },
    reconnectDelay: 5000,
    heartbeatIncoming: 4000,
    heartbeatOutgoing: 4000,
  });

다음으로 필요한 것은 여러가지 설정이 담긴 클라이언트 객체를 만들어주는 것입니다.

다양한 설정이 존재하고 저 또한 기본적으로 권장하는 기능 이외에는 무엇이 있는지 상세하게 분석하지는 못했지만 위 코드에 적힌 것들은 포함하는 것을 추천드립니다.

이제 위의 세부 설정에 대해 각각 알려드리겠습니다.

  1. brokerURL : StompJs는 기본적으로 WebSocket(이하 ws)을 사용하길 권장합니다.
    소켓 통신의 베이스가 ws이고 SockJs(이하 SJ) 또한 ws을 보완하기 위해 등장한 것이거든요.
    brokerURL에는 ws, wss로 시작하는 주소만 입력 가능하기 때문에 저흰 필요하지 않습니다. SJ 주소 추가는 밑에서 마저 설명해드리겠습니다.
  2. debug : 콜백함수로 str에 대한 log를 찍는 것을 볼 수 있습니다. 눈치가 빠르신 분들은 이 설정의 역할이 해당 클라이언트로 발생하는 모든 로그가 콘솔창에 찍힌다는 것을 알아채셨을 것입니다.

  3. reconnectDelay : 의미 그대로 연결이 끊겼을 때 재접속을 몇 ms마다 실행해줄 것인지에 대한 설정입니다. 기본 5000ms입니다.

  4. heartbeatIncoming : 직역하면 심장박동 수신입니다. 이것도 눈치가 빠르신 분들이라면 어떤 기능인지 알아채셨을 것입니다.
    바로 연결이 유지되고 있는지 끊겼는지 파악하기 위한 신호를 몇ms마다 송신할 것인지에 대한 설정입니다.
    이 기능은 Stomp 1.1에 추가된 것으로 아는데 클라이언트가 신호를 보내지 않아도 연결의 상태를 파악할 수 있기 때문에 매우 유용한 기능이라고 합니다. 
    outcoming은 내보내는 heartbeating의 주기를 의미합니다. 0이면 heartbeating 신호를 내보내지 않는 것입니다.
    incoming은 얼마나 자주 heartbeating을 요구하는지를 의미합니다. 0이면 요구하지 않는 것입니다.

 

3. Stomp Client에 SockJS 물리기

client2.webSocketFactory = () => {
    return new SockJS("https://i7d106.p.ssafy.io:8080/ws");
  };

여기서 소켓 통신을 원하는 주소를 저장한 SockJs 객체를 stomp client에게 줄 수 있습니다.

webScoketFactory 또한 정해진 양식이기 때문에 따르는 것을 추천드립니다.

위에 BrokerURL과 차이는 SockJs는 주소의 시작이 http 또는 https로 시작되어야 합니다.
ws:// 혹은 wss://를 입력하면 콘솔창에 그러한 주소로 시작될 수 없다고 친절하게 알려줍니다.

 

4. 구독 설정하기

이제 다 왔습니다.

let subGatherRoom;

client2.onConnect = (frame) => {
	//이곳에서 모든 구독(subScribe)가 되어야 합니다.
    subGatherRoom = client2.subscribe("/sub/room/0", onMessageReceived);
	
	client2.publish({
        destination: "/sub/room/0",
        headers: { "content-type": "application/json" },
        //전송할 데이터를 입력
        body: JSON.stringify({
        }),
        skipContentLengthHeader: true,
      });
  };

연결되었을 때 설정을 해주는데 공식문서에는 이 곳에서 모든 구독(subscribe)을 마쳐야 한다고 합니다.

Do something, all subscribes must be done is this callback
This is needed because this will be executed after a (re)connect

 

번역하자면

모든 구독이 이 콜백에서 완료되어야 하는 이유는 (재)연결을 수행했을 때 필요한 부분이기 때문입니다.

위 코드에서 Stomp의 강점을 알 수 있습니다.

Stomp는 subscribe(소켓 통신을 받을 채널)과 publish(소켓 통신을 보내는 행위)를 수행하는데 이것은 주소로 구분합니다.

굳이 특정 통신을 특정 누구에게 전송하겠단 로직을 BE에서 짤 필요 없이 주소로 구분만 해주면  되는 것입니다.

subscribe 
수신할 채널의 주소와 수신했을 때 수행할 콜백함수를 요구합니다.
그 외에 추가적인 옵션들이 있으니 궁금하거나 필요하신 분들은 공식문서를 참고해주세요.

publish
destination에는 데이터를 전송할 주소를, headers에는 전송할 데이터의 타입, body에는 전송할 데이터를 입력하면 됩니다. 형태는 axios와 유사합니다.
보낼 수 있는 데이터는 기본적으로 문자열과 binary(이진) 타입입니다. 보통은 JSON으로 전송하기 때문에 위 코드처럼 JSON.stringify로 json화해서 전송하면 됩니다.

skipContentLengthHeader는 전송 시 데이터의 길이를 출력하지 않게 해주는데 보통은 필요하지 않기 때문에 true로 해주면 콘솔창의 로그가 조금 더 짧아지는 장점이 있습니다.

 

아래는 에러가 났을 때 실행하는 함수입니다.
기본적으로 에러 메세지와 추가적인 내용을 출력하는데 필요하다면 추가 로직을 삽입할 수 있습니다.

client2.onStompError = function (frame) {
    // Will be invoked in case of error encountered at Broker
    // Bad login/passcode typically will cause an error
    // Complaint brokers will set `message` header with a brief message. Body may contain details.
    // Compliant brokers will terminate the connection after any error
    console.log("Broker reported error: " + frame.headers["message"]);
    console.log("Additional details: " + frame.body);
  };

 

만약 해당 구독으로부터 더 이상 데이터를 받지 않기 위해선 구독 시 반환받은 객체에 포함된 구독해제 함수를 실행하면 됩니다.

subGatherRoom.unsubscribe();

 

5. 클라이언트 활성화

//활성화
client2.activate();

//비활성화
client2.unacitivate();

이제 클라이언트를 활성화해줍니다.

문제가 없다면 onConnect에서 작성한 내용들이 실행될 것이고 접속하는 서버에 문제가 있다면 cors에러 혹은 reconnect 시도가 반복될 것입니다.

만약 소켓 통신을 종료한다면 unactivate함수를 실행하면 됩니다.

 


후기

이상으로 Stomp와 SockJs를 활용해 소켓 통신하는 법에 대해 알아보았습니다. 리액트 프레임워크를 기반으로 했는데 앞서 말씀드렸지만 리액트를 모르시는 분들을 위해 코드는 최대한 배제했습니다.

위 코드는 함수형 리액트 내부에서도 잘 작동하며 바닐라Js에서도 문제없이 동작할 것입니다.

통신이 성공했을 때 출력 로그, 에러 이미지 첨부가 부족했는데 추후 발생할 수 있는 에러의 종류 등도 포스트해보겠습니다.

 


참고자료

1. https://github.com/stomp-js/stompjs, StompJs github

2. https://stomp-js.github.io/guide/stompjs/using-stompjs-v5.html#heart-beating, StompJs 공식 Document  

3. https://stomp-js.github.io/api-docs/latest/index.html, StompJs 공식 API docs

4. https://stomp.github.io/stomp-specification-1.2.html#Heart-beating, Stomp protocol 1.2 명세서

 

 
 
 
 

댓글