Skip to main content

Command Palette

Search for a command to run...

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

Updated
•3 min read•View as Markdown
이제 실전이다! 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

More from this blog

Bundling Fundamentals

들어가며 프론트엔드에서 번들링을 이해하는 과정은 웹 성능 최적화를 위해서 꼭 짚고 넘어가야할 부분이다.토스에서 작성해둔 bundling Fundamentals 과정을 통해서 번들링이 '어디서 어떻게' 일어나는지 함께 이해해보자! 번들링이란 번들링은 여러개의 웹 개발에 필요한 리소스 파일들을 하나 또는 몇 개의 파일로 묶는 작업을 얘기하고, 이렇게 묶인 파일

Mar 30, 20261 min read

진짜 git 아직도 모르냐!!!

들어가며 진짜 회사 입사한지 일년이 됐지만 아직까지 git을 초보자처럼 쓰고있는 내 모습에 너무 황당해서 쓰는 블로그.. 회사에서는 그냥 git clone해서 애초에 git fetch, git pull, git push, git commit, git add 등등 명령어만 남발하면서 충돌 없이 최대한 조심스럽게 익숙한 방식대로만 써왔음 에러나면 무조건 ai한테 왜이래 남발하면서 충돌해결하고 rebase, merge 차이도 잘 모르겠고,, 심지어 ...

Feb 4, 20261 min read

OpenLayers , Feature 찾는법

안녕하세요! 오늘은 개발 과정에서 겪었던 까다로운 버그와 그 해결 과정을 공유해볼까 합니다. OpenLayers를 사용한 지도 애플리케이션에서 발생한 문제로, 분명히 존재해야 할 피처(feature)를 getFeatureById로 찾지 못하는 상황이었습니다. 문제 발생: "ID도 있고, 레이어도 있는데 왜 못 찾니?" 문제는 otdr.js 파일의 highlightTracks라는 함수에서 시작되었습니다. 이 함수는 특정 트랙(track)들을 ...

Jan 28, 20263 min read
마

마리월드

13 posts