StompJS를 사용한 웹소켓 연결

dudgus5766·2024년 1월 8일
2

React-Native

목록 보기
9/9

진행하는 프로젝트에서 채팅방을 StompJSSockJS를 사용하여 만들게 되었습니다.(백엔드에서 스프링을 사용하고 stomp를 가지고 예전에 작업을 해본 경험이 있다고 하셨기 때문)
채팅 주고받기 성공을 기념하며..웹소켓을 연결 방법과 메세지 구독 발행 방법을 적어봅니다! 📨

StompJS

StompJS 공식문서

서버와 클라이언트간에 비동기적으로 메시지를 전송하기 위한 프로토콜입니다. 웹소켓 위에서 동작하며 클라이언트와 서버가 전송할 메시지의 유형, 형식 내용들을 정의합니다.

Stomp 흐름

서버와 연결할 클라이언트를 연결(connect)
⬇️
메세지 구독(subscribe)
⬇️
메세지 발행(publish)
⬇️
연결 종료(disconnect)


웹소켓 연결 구현

// ...
import SockJS from 'sockjs-client';
import StompJs, {Message as MessageType, Client} from '@stomp/stompjs';
// ...

StompJS와 SockJS의 객체를 사용할 파일을 생성하고 사용할 객체를 다음과 같이 라이브러리에서 import합니다.

const client = useRef<Client | null>(null);

STOMP 프로토콜을 사용하기 위해 클라이언트 라이브러리 @stomp/stompjs를 사용하는데, Client는 호환성을 가진 STOMP 클라이언트 객체를 나타냅니다.

  • client 변수로 useRef를 사용하여 초기화하면 Client 객체가 생성되어 해당 객체를 참조할 수 있게 됩니다.
  • useRef를 사용하였기 때문에 client.current를 통해 Client 객체에 접근할 수 있고, 해당 객체를 사용하여 소켓 연결(connect)이나 메시지 구독(subscribe) 등을 처리할 수 있습니다. 또한 값이 바뀌어 렌더링이 되는 것을 막아주기도 합니다.

1. 소켓 연결 및 구독

// sockJS 클라이언트 객체를 생성, 웹소켓 연결
const socket = new SockJS(`${API_BASE_URL}/stomp/chat`);

client.current = new StompJs.Client({
  webSocketFactory: () => socket, // 프록시를 통한 접속
  debug: str => {
    console.log(str);
      },
  reconnectDelay: 5000, // 자동 재 연결
  heartbeatIncoming: 4000,
  heartbeatOutgoing: 4000,

  onConnect: () => {
	  client.current?.subscribe(
	     `/exchange/chat.exchange/room.${roomHash}`,
    	    onMessageReceived,
            {
              //유효성 검증을 위한 header 넣어 줘야함
            },
          );
      },
  onStompError: frame => {
        console.error(frame);
      },
    });

  client.current.activate();

방에 입장했을 시에, useEffect 훅 내에 함수를 넣어 소켓을 연결합니다.

  • webSocketFactory는 URL 프로토콜 컨벤션에 맞추어 설정하는데 SockJS를 사용하기때문에 http:https:를 활용합니다.
  • debug는 디버깅을 위한 문자열을 보내줍니다.
  • onConnect는 소켓 연결이 되었을 때 호출하는 콜백함수입니다. client.current.subscribe 메서드를 통해 소켓 메세지 구독을 합니다.
  • onConnect내부에 pulish를 등록해 해당 사용자가 입장했다는 메세지를 보낼 수 있습니다.(추가 예정)

2. 메세지 보내기

const sendHandler = (message: string) => {       
  client.current?.publish({
      destination: `/pub/chat.message.${roomHash}`,
      body: JSON.stringify({
        message: message,
        roomHash: roomHash,
        memberId: memberId,
        nickname: nickname,
      }),
    });
};

client.current.publish 메서드를 통해 메시지를 전송하면 client 객체가 구독(subscribe)했던 방으로 메시지가 전달됩니다.

3. 연결 종료

  const disconnect = () => {
    client.current?.deactivate();
  };

  useEffect(() => {
    connectHandler(); // 소켓 연결 구독 함수
    return () => {
      disconnect(); // 소켓 연결 종료 함수
    };
  }, []);

🚫 문제 1 : 바뀐 문법

StompJS 공식 관련 문서에서 Stomp.over() 메서드와 CompatClient.send() 메서드 사용법을 검색하는 도중, 다음 stomp 버전에서는 더이상 메서드들을 사용하지 않는다는 문구를 발견했습니다.

StompJS 업그레이딩 문서를 통해 자세히 알 수 있었는데, 버전 4까지는 클라이언트가 웹소켓을 직접 관리를 했다고 합니다. 클라이언트가 웹소켓을 열고, 웹소켓 연결이 끊어지면 클라이언트가 자동으로 웹소켓을 재연결하는 방식이었는데, 버전 5에서는 클라이언트가 웹소켓 연결을 직접 관리하지 않고, 대신 웹소켓 인스턴스를 인자로 받아서 사용하도록 했기 때문에 문법이 바뀐 것 입니다.

 // stomp 프로토콜 위에 sockJS가 돌아가도록 Stomp.over()의 인자로 변수를 넣어줌
    // *** stomp 다음 버전에서 사용 중단될 예정이기 때문에 문법을 교체
    // client.current = Stomp.over(() => {
    //   return new SockJS(`${API_BASE_URL}/stomp/chat`);
    // });

원래 코드를 리팩토링해 최신 문법으로 적용하여 진행했습니다.

++ 꿀팁
debug에 자꾸 <<<MESSAGE같은 디버깅 메세지가 작성되는데, 이는 클라이언트 인스턴스 중 debug: () => {}로 적어주면 콘솔에서 디버깅 메세지가 사라집니다!

🚫 문제 2 : RN에서 text-encoding 에러

TextEncoder는 문자열을 인코딩(특정 형식으로 변환)하는 데 사용되는 웹 API입니다. 이는 특히 문자열을 Uint8Array로 인코딩하는 데 사용됩니다. 이 API는 대부분의 웹 브라우저에서 기본적으로 제공되지만, React-Native의 JS 엔진인 Hermes에서는 지원되지 않습니다.
따라서, TextEncoder가 지원되지 않는 환경에서 이를 사용하려면, TextEncoder의 기능을 모방하는 코드(polyfill)를 사용해야 합니다.

const TextEncodingPolyfill = require('text-encoding');

Object.assign('global', {
  TextEncoder: TextEncodingPolyfill.TextEncoder,
  TextDecoder: TextEncodingPolyfill.TextDecoder,
});

해당 코드를 추가하여 에러를 없앨 수 있었습니다.

다른 방법은 react-native-polyfill-globals 라이브러리에서 TextEncoder와 TextDecoder만을 가져와 사용할 수 있습니다.

import 'react-native-polyfill-globals/global/TextEncoder';
import 'react-native-polyfill-globals/global/TextDecoder';

구현 모습


추가 구현

  1. context api를 사용하여 웹소켓 연결을 전역으로 빼내어 단체, 개인 채팅방에서 사용할 수 있게 할 예정
  2. 채팅 메세지 UI/UX 개선
    • 채팅방 입장과 퇴장 UI추가
    • 이전 메세지 구독
    • 읽은 메세지 표시
    • 단체 채팅에서 읽은 사용자 수 표시
  3. 헤더에서 햄버거 버튼을 클릭 시 drawer가 열리고 그 안에 채팅방 info 추가


📚 참고 링크

StompJS 공식문서
https://dmdwn3979.tistory.com/7
https://velog.io/@emprore/Come-onReact-Native-%EC%9B%B9%EC%86%8C%EC%BC%93-%EC%97%B0%EA%B2%B0-%ED%95%98%EA%B8%B0

profile
RN App Developer

1개의 댓글

comment-user-thumbnail
2024년 2월 7일

const client = useRef<Client | null>(null); 혹시 이거 사용하실때 Cannot use namespace 'Client' as a type.ts(2709)오류는 나지 않으셨나요? 저는 처음에 @stomp/stompj를 설치하고 import할때 ts파일이 정의되어있지 않다고 해서 declare module '@stomp/stompjs';로 해당 오류를 해결하고 진행중인데 혹시 같은 오류가 나신적이 있는지 여쭤보고 싶습니다...

답글 달기