이제 실전이다! React + Next.js 환경에서 WebSocket으로 실시간 채팅 구현해보기-(2)

이제 실전이다! React + Next.js 환경에서 WebSocket으로 실시간 채팅 구현해보기-(2)

WebSocket을 구현하기 위해 도움을 주는 친구들, SocketJS 와 STOMP !

기본적으로 WebSocket API 는 아주 간단한 기능들만을 제공한다.
예를들어, 해당 메시지가 어떤 요청인지, 어떤 포맷으로 오는지, 메시지 통신 과정을 어떻게 처리해야 하는지 정해져 있지 않다는 것이다.
이것을 일일이 구현하게 되면 굉장히 까다로워지고 코드의 복잡성이 증가할 수 있다.
따라서 SockJS 라이브러리를 추가적으로 사용하고,
STOMP 라는 프로토콜을 서브 프로토콜로 사용해 구현할 예정이다.

1 ) SockJS
브라우저에서 사용하기 위한 클라이언트 javascript 라이브러리이다.
모든 브라우저에서 웹소켓을 지원한다는 보장이 없기 때문에, sockjs 는 websocket 을 시도하고 실패할 경우 http streaming, Long-polling 같은 http 기반의 다른 기술로 전환해 다시 연결을 시도한다.
보통 Spiring 환경에서 SockJS 라이브러리를 사용하고,
Node.js 환경에서는 Socket.IO 라이브러리를 함께 사용한다.

2)STOMP
Simple Text Oriented Messaging Protocol 의 약자로, 메시지 형식이나 유형, 내용을 정의해주는 프로토콜이다. 웹소켓 위에서 동작한다.

구현순서를 큰 틀로 나누어보자.

  1. SockJS 객체를 생성한다

  2. STOMP 객체를 생성한다.

  3. STOMP 객체의 connent() 메서드를 사용해 웹소켓 연결을 설정한다.

  4. STOMP 객체의 subscribe() 메서드를 사용해 채팅방을 구독한다.

  5. STOMP 객체의 send() 메서드를 사용해 채팅 메시지를 보낸다.

첫째, 클라이언트 생성

이 단계에서 SockJS 객체와 STOMP 객체를 생성한다.
SockJS는 핸드쉐이크를 통해 클라이언트와 서버 간 양방향 통신을 설정한다.
클라이언트가 서버에 HTTP 요청을 보내고, 서버는 해당 요청에 대한 응답으로 WebSocket 연결을 수락한다.

// 1. SockJS 객체를 생성한다
 const socket = new SockJS("연결할 서버 URL");
// 2. STOMP 객체를 생성한다.
 client = Stomp.over(socket);

둘째, 서버연결

이제 서버에 STOMP 객체의 connet 프레임을 전송한다.
프레임이란? 명령어, 선택사항인 헤더 집합, 선택사항인 body로 구성되어 있으며 이 프레임이라는 것을 클라이언트와 서버가 주고받음으로써 통신을 실시간으로 할 수 있다.

client.connect(
  { Authorization: token },
  () => {
 // 연결 성공 시 실행되는 콜백 함수
  },
 // 연결 실패 시 실행되는 콜백 함수
  (error:any) => {
  console.error("Connection error:", error);
  }
);

셋째, 채팅방 구독

위의 프레임에서 연결 성공 시 실행되는 콜백 함수 안에 subscribe 프레임을 이용해 특정 URI(URN) 에 대해 구독한다.
subscribe()의 첫번째 인자는 구독할 URI를 넣어주고,
두번째 인자로는 구독한 후 실행 될 콜백함수이다. 구독 이후 상대방으로부터 메시지를 수신 받을때마다 해당 콜백함수가 실행된다.
이때 매개변수로 받은 message인 JSON 형식의 텍스트를 자바스크립트 객체로 변환한다.
세번째 인자는 SUBSCRIBE 프레임을 전송할 때 같이 보내는 헤더를 설정하는 곳이다.

client.connect(
  { Authorization: token },
  () => {
 // 연결 성공 시 실행되는 콜백 함수
 client.subscribe(
   `/sub/chat/room/${roomIdQuery}`,
   (message:any) => {
      if (message && message.body) {
      let newMessage = JSON.parse(message.body);
      setRealTimeMessage(newMessage);
     }
   },
  { Authorization: token }
 );
},
 // 연결 실패 시 실행되는 콜백 함수
  (error:any) => {
  console.error("Connection error:", error);
  }
);

넷째, 메시지 보내기

메시지를 전송할 때 SEND 프레임을 전송한다.
첫번째 인자는 해당 프레임을 전송할 때 필요한 URI를 입력한다.
두번째 인자는 해당 프레임을 전송할 때 헤더를 설정한다.
세번째 인자는 해당 프레임을 전송할 때 보낼 데이터를 설정하는 body이며 이 부분에 상대방에게 보내고 싶은 메시지가 들어간다.
세번째는 서버 개발자가 지정한 양식에 따라 입력해야 할 데이터를 넣어주면 된다.

 const sendMessage =  () => {
    if (clientRef.current && isConnected && inputMessage.trim() !== "") {
       try {
        clientRef.current.send(
        "/pub/chat/message",
        {Authorization: token},
        JSON.stringify({ type: "TALK", roomId: roomIdQuery, message: inputMessage})
        );
       } catch (error) {
        console.error("Error sending message:", error);
       }
    }
 };

🖥️ 이렇게해서 완성된 UI

👉🏻 화면 구성 및 시연 영상

⌨️ 전체코드

👉🏻 깃허브 이동

📚 참고

실시간 채팅 구현 👉🏻 https://fgh0296.tistory.com/24
sockJS와 STOMP 👉🏻 https://hani-develop-history.tistory.com/70