[TIL] SockJS Stompjs

이현동·2023년 3월 14일
1

TIL

목록 보기
38/59

채팅 기능

실전 프로젝트에서 채팅 기능을 넣기로 했다. 채팅 기능과 관련해서 알아보았을 때, soket.io를 많이 사용한다고 해서 soket.io로 하려고 했으나, 추가적으로 알아보니 백엔드가 Spring일 때에는 SockJSStompjs를 많이 사용한다고 한다. 이전의 soket.ioNode.js와 많이 사용한다고 한다.
우리 프로젝트의 백엔드 팀은 Spring을 사용 중이라서 SockJSStompjs를 사용해서 기능을 구현하려고 한다.

WebSocket

웹 소켓은 http에서 실시간 통신을 할 수 없는 문제를 해결하기 위해 나온 기술이다.

http로는 실시간 통신을 할 수 없는데, 다음과 같은 특징을 가지기 때문이다.

  • 비연결성 (단방향)
  • 매번 연결을 맺고 끊는 과정의 비용
  • request-response의 구조
  • header의 비중이 매우 크다. → 실시간성으로 많은 데이터를 주고 받고자 하는 경우에는 매우 부담이 됨

요정을 보매면 단방향적 구조로 통신을 하기 때문에 TCP/IP 프로토콜을 사용하는 소켓처럼 계속 connection이 유지되는 실시간 통신을 할 수 없기 때문에 웹소켓 프로토콜이 나오게 되었다.

그래서 자주 사용하는 상황은 다음과 같다.

  1. 게임을 할 때
  2. 채팅
  3. 실시간 주식거래 사이트
    등등..

Socket.io, SockJS

Socker.io, SockerJS가 html5이전의 기술로 구현된 서비스에서 웹 소켓처럼 사용할 수 있도록 도와주는 기술이다. 이걸로 실시간 통신을 도와준다.

STOMP(Simple/Streem Text Oriented Message Protocol)?

  • websocket 위에서 동작하는 문자 기반 메세징 프로토콜로써 클라이언트와 서버가 전송할 메세지의 유형, 형식, 내용들을 정의하는 매커니즘이다.
  • TCP와 웹소켓과 같은 신뢰할 수 있는 양방향 스트리밍 네트워크 프로토콜에서 사용할 수 있다.
  • 기본적으로 pub / sub 구조로 되어있어, 메세지를 전송하고 받아 처리하는 부분이 확실히 정해져있다.
  • http와 마찬가지로 frame을 사용해 전송하는 프로토콜이다.

구현

우선 와이어 프레임이 나오지 않아서 HTML, CSS 작업은 하지 못했다. 완전히 처음 배우는 내용이라 프로젝트에서 꽤 오래 걸릴 것 같은(?) 기능이라서 우선 기능부터 완성해보기로 하였다. 서버 쪽 코드는 없고, 프론트 쪽에서 콘솔로만 찍어서 확인을 해보았다.

설치

  1. yarn add stompjs sockjs 명령어를 사용해서 설치
  2. pakage.json의 "dependencies""net": "^1.0.2"를 추가해주고 yarn install을 해준다.

2번은 Uncaught Error: Cannot find module 'net'. stomp-node.js:14 오류로 Node.js의 내장 모듈 중 하나인 'net' 모듈을 찾을 수 없다고 해서 추가로 설치해주었다.

  • chatSlice
// Get Chat Room
...
export const __getChatRoom = createAsyncThunk(
  "getChatRoom",
  async (payload, thunkAPI) => {
    try {
      const response = await instance.post(`/chat/room/${payload}`);
      console.log("chat response", response.data);
      return thunkAPI.fulfillWithValue(response.data);
    } catch (error) {
      console.log(error.response.data.message);
      throw new Error(error.response.data.message);
    }
  }
);
...

채팅방이 필요해서 우선 Redux thunk를 사용하여 RoomId를 서버 쪽에서 받아오고 state에 저장해 사용한다.

...
const ChatRoom = () => {
  const roomId = useSelector((state) => state.chat.roomId);

  ...

  useEffect(() => {
    dispatch(__getChatRoom(postId));
    wsConnectSubscribe();
    return () => {
      onbeforeunloda();
    };
  }, [roomId]);

  const socket = new SockJS(`${process.env.REACT_APP_CHAT_TEST}/ws/chat`);
  // SockJS를 내부에 들고 있는 stomp를 내어줌
  const ws = Stomp.over(socket);

  // 서버에 보내줄 값
  const content = {
    type: "TALK",
    sender: "test1",
    message: message,
  };

  // 연결 시 실행
  const waitForConnection = (ws, callback) => {
    setTimeout(
      () => {
        // 연결되었을 때 콜백함수 실행
        if (ws.ws.readyState === 1) {
          callback();
          // 연결이 안 되었으면 재호출
        } else {
          waitForConnection(ws, callback);
        }
      },
      1 // 밀리초 간격으로 실행
    );
  }; //stomp 메시지 에러 waitForConnection함수로 해결

  const wsConnectSubscribe = () => {
    try {
      // headers에는 jwt token을 넣었음
      ws.connect(headers, (frame) => {
        // connect 성공시 연결시 실행
        ws.subscribe(`/sub/${roomId}`, (response) => {
          // 상대 채팅을 받아옴 
          let data = JSON.parse(response.body);
        });
      });
    } catch (error) {
      console.log("error message ->", error);
    }
  };

  const onbeforeunloda = () => {
    try {
      //연결 끊기
      ws.disconnect(
        () => {
          ws.unsubscribe("sub-0");
          clearTimeout(waitForConnection);
        },
        { Access_Token: localStorage.getItem("Access_Token") }
      );
    } catch (e) {}
  };
  
  const handleSubmit = () => {
    waitForConnection(ws, () => {
      // message 보내기
      ws.send(`/pub/${roomId}`, {}, JSON.stringify(content));
    });
  };

  ...

처음 만져보는 기능이라 공부하는데 시간도 걸렸고, 서버 쪽에서 CORS에러도 발생하고 연결이 잘 되지 않았던 것 같다.
위의 코드들은 우선 간단하게 콘솔로만 확인을 해보았다. 메세지가 잘 오고 감을 확인 할 수 있었고, contenttype을 달리 해보면서 백엔드 팀원과 확인을 해보았다.
와이어 프레임이 나오면 채팅 기능을 완전히 완성할 것인데, 그때는 오류가 나지 않길,, 😂


참고자료

소켓과 웹소켓 한번에 정리

Stomp란?

profile
https://hdlee.dev

0개의 댓글