개발 일지/개발 일지

[개발 일지] 채팅 기능을 구현해보자 (Websocket, Stomp, SockJS)

2023. 2. 10. 22:15
목차
  1. REST API vs Websocket API
  2. Websocket API
  3. Sock.js
  4. Stomp
  5. Stomp 연결 순서
  6. 1. 서버와 연결할 클라이언트 connect
  7. 2. 메시지 전송 전 subscriber와 publisher 지정
  8. 2.1 subscribe : 해당 url로 나에게 메시지를 보낼 수 있는 경로가 생김
  9. 2.2 publisher ; publish한 url로 메시지가 이동
  10. 3. disconnect ; 연결 끊기
  11. 채팅 시현 화면
  12. Console에 찍힌 Debug

REST API vs Websocket API

채팅은 REST API로도 구현할 수 있다. 그러나 Rest API를 이용하여 반복하여 요청해서 데이터를 요청하고, 데이터를 받아올 수 있게 되면 자원 낭비가 일어나게 된다. 그렇다면 한 번만 요청하고 그 뒤로 정보가 변할 때마다 그냥 불러오도록 할 수 없을까?

여기서 새로운 방식의 API가 필요하게 된다. 바로 Websocket API이다. 구독형 API라고도 할 수 있다.

 

Websocket API

 : Statefull protocol, 요청을 매번 보내는 게 아니라 connection을 유지해서 양방향 통신 또는 데이터 전송이 가능하도록 하는 기술

- Websocket API 방식에서는 데이터 요청자와 데이터 제공자 간에 흐르는 채널이 열리게 된다.

- REST API는 필요시마다 문을 두드려서 문을 여는 것이라면, Websocket API의 경우에는 계속해서 물이 흐르는 것과 같음

 

Sock.js

; Websocket과 비슷한 기능을 제공하는 브라우저 JS 라이브러리

- 브라우저와 웹 서버 사이에서 짧은 지연시간, 그리고 크로스 브라우징을 지원하는 API

- 크롬 뿐 아니라 사파리, 파이어 폭스 그리고 websocket프로토콜을 지원하지 않는 최신 브라우저에서도 정상 작동

- handShake과정을 통해 Client와 Server 접속을 유지 

 

Stomp

; Single Text Oriented Messaging Protocol의 약자

- 클라이언트와 서버가 서로 통신하는 데 있어 메시지의 형식, 유형, 내용 등을 정의해 주는 프로토콜

- 단순한 Binary, Text가 아닌 규격을 갖춘 메시지를 보낼 수 있음

- spring에 종속적이며 구독 방식을 사용

 


Stomp 연결 순서

1. 서버와 연결할 클라이언트 connect

  • StompJs를 이용하여 클라이언트를 생성해 줍니다.
    • 옵션은 링크에서 추가적으로 확인할 수 있으며, 페이지 이동 시 reconnect를 방지하기 위해 reconnectDelay:0을 넣어주었습니다.
  • 인터넷 익스플로러 구버전 등 Stomp를 지원하지 않는 사용자를 위해 SockJS로도 연결해 줍니다.
client.current = await createClient('/ws-connection');
...
const createClient = (endpoint) => {
  const client = new StompJs.Client({
    brokerURL: `wss://atties.shop${endpoint}`,
    connectHeaders: { Authorization: access },
    debug: (res) => {
      console.log(res);
    },
    reconnectDelay: 0,
  });
  client.webSocketFactory = () => {
    const socketIn = new SockJS(
      `${process.env.NEXT_PUBLIC_API_BASE_URL}${endpoint}`,
    );
    return socketIn;
  };
  return client;
};

2. 메시지 전송 전 subscriber와 publisher 지정

2.1 subscribe : 해당 url로 나에게 메시지를 보낼 수 있는 경로가 생김

  • subscribe, 즉 채팅 실시간 받기입니다.
  • 채팅방 목록과 채팅방 내부가 있습니다.
    • 채팅방 목록에서는 서버에 한 번 connect 후, 각 채팅방을 모두 subscribe를 하고
    • 채팅방 내부에서는 서버에 한 번 connect 후, 해당 채팅방에 subscribe를 하여 진행합니다.

채팅방 목록에서의 onConnected 함수입니다.

client.current.onConnect = await onConnected;
...
const onConnected = () => {
  if (chatRoomList?.length > 0) {
    chatRoomList.forEach((chatRoom) => {
      subscribe(client.current, chatRoom?.chatRoomId, subscribeCallback);
    });
  }
};

채팅방 내부에서의 onConnected 함수입니다.

client.current.onConnect = await onConnected;
...
const onConnected = () => {
  subscribe(client.current, id, subscribeCallback, true);
};

subsribe 함수입니다. 채팅방 내부에 접속하면 ‘읽음’처리가‘읽음’ 처리가 되어야 하고, 채팅방 목록에서는 ‘읽음’ 처리가 되지 않아야 합니다. 그래서 이 둘을 서버 측에서 구분할 수 있도록 채팅방 내부에서 subscribe 할 때는 action:’enter’을 넣어 전송하였습니다.

const subscribe = (client, roomId, subscribeCallback, isRoom = false) => {
  if (isRoom) {
    client.subscribe(`/queue/chat-rooms/${roomId}`, subscribeCallback, {
      Authorization: access,
      action: 'enter',
    });
  } else {
    client.subscribe(`/queue/chat-rooms/${roomId}`, subscribeCallback, {
      Authorization: access,
    });
  }
};

subscribeCallback 함수입니다. 이 함수는 client.subsribe의 세 번째 인수로 넣어주었으며, subscribe 즉 구독한 채팅방에서 채팅을 전송하거나 채팅이 왔을 때 실행되는 함수입니다. 채팅이 왔을 때 채팅방 query를 refetch하여 화면을 서버와 최신화를 해줍니다.

  • 이 부분은 추후에 서버로부터 refetch 받기 전에 클라이언트 내에서 state를 이용해 먼저 최신화할 수 있도록 추가 구현 예정입니다.
const subscribeCallback = () => {
  refetchChatRoomList();
};

2.2 publisher ; publish한 url로 메시지가 이동

  • publish, 즉 채팅 전송입니다.
  • destination과 body를 함께 전송합니다.
    • body에는 채팅방 id, 전송자 id, 채팅 내용을 담았습니다.
const publish = (client, roomId, senderId, chat) => {
  client.publish({
    destination: '/app/send',
    body: JSON.stringify({
      chatRoomId: roomId,
      senderId: senderId,
      content: chat,
    }),
  });
};

3. disconnect ; 연결 끊기

  • 채팅방 페이지를 벗어나면, disconnect를 해줍니다.
useEffect(() => {
  connect();
  return () => {
    disconnect();
  };
}, []);
...
const disconnect = () => {
  if (client != null && client.current.connected) {
    client.current.deactivate();
  }
};

채팅 시현 화면

Console에 찍힌 Debug

위에서 구현한 웹소켓의 debug를 console에 찍어본 화면입니다. 제가 채팅방에 접속하여, 채팅을 전송한 debug입니다. >>>은 제가 서버 측에 보낸 정보, <<<는 서버 측에서 저희에 온 정보입니다.

1. CONNECT : 서버와 Connect를 합니다.

  • 이때, Authorization, 즉 토큰은 담아서 전송합니다. 

2. CONNECTED : 연결이 완료되었습니다.

3. SUBSRIBE : subscribe 즉, 구독을 합니다.

4. SEND : 메시지를 전송하였습니다.

5. MESSAE : 메시지가 왔습니다.

  • 이때, 아래 코드에 보면 subscription:sub-0이라 적혀있습니다. 구독한 채널에서 날아온 메시지라는 뜻입니다.
  • destination:/queue/chat-rooms/7. 제가 subscribe 한 채널입니다.
  • 위 메시지의 내용은 subsribe함수의 세 번째 인수인 subscribeCallback에서 받아서 사용할 수 있습니다. 

Websocket API는 처음이라, 채팅 구현은 처음이라 시행착오를 많이 겪었습니다. 공식 문서도 참고하고, 다른 사람들 블로그를 여럿 읽어보며 참고하여 구현하였습니다. Stomp는 다른 자료들보다 문서가 적어 에를 많이 썼었는데, 해결하고 났을 때의 그 쾌감은 말로 달리 표현할 수 없더라구요. 제 블로그를 참고해서 채팅 API구현을 성공적으로 마쳤기를 바라며, 댓글로 질문 남겨주시면 아는 선에서 알려 드릴게요 😀

 

마지막으로 채팅 구현한 github 링크 공유하고 마칠게요 !

채팅방 목록 ; https://github.com/Att-ies/frontend/blob/dev/src/pages/chat/index.tsx

채팅방 내부 ; https://github.com/Att-ies/frontend/blob/dev/src/pages/chat/room.tsx

채팅 api ; https://github.com/Att-ies/frontend/blob/dev/src/apis/chat/socketConnect.js

 

 

저작자표시 (새창열림)

'개발 일지 > 개발 일지' 카테고리의 다른 글

[개발 일지] React성능 최적화 (2) : React Query의 캐싱 기능  (1) 2023.05.05
[개발 일지] React성능 최적화 (1) : React.memo로 사용자 경험 개선하기  (0) 2023.05.03
[개발 일지] Next.js에서 접근성 향상하기 (접근성 점수 84점 -> 97점 향상시킨 썰)  (0) 2023.04.27
[개발 일지] React Query - Custom Hook 만들기 (+Typescript)  (0) 2023.04.13
[개발 일지] JWT + 소셜 로그인을 정복해보자 (React + Spring Boot, Kakao Login, Naver Login)  (0) 2023.04.11
[개발 일지] Intersection Observer API & react-query를 이용한 무한스크롤 구현  (0) 2023.02.28
[개발 일지] Suspense를 이용하여 모든 api에 Loading 화면을 설정하자 (NextJS, Suspense)  (0) 2023.02.18
[개발 일지] Axios instance 설정 끝장내기 (Axios, instance, interceptors, JWT)  (0) 2023.02.11
  1. REST API vs Websocket API
  2. Websocket API
  3. Sock.js
  4. Stomp
  5. Stomp 연결 순서
  6. 1. 서버와 연결할 클라이언트 connect
  7. 2. 메시지 전송 전 subscriber와 publisher 지정
  8. 2.1 subscribe : 해당 url로 나에게 메시지를 보낼 수 있는 경로가 생김
  9. 2.2 publisher ; publish한 url로 메시지가 이동
  10. 3. disconnect ; 연결 끊기
  11. 채팅 시현 화면
  12. Console에 찍힌 Debug
'개발 일지/개발 일지' 카테고리의 다른 글
  • [개발 일지] JWT + 소셜 로그인을 정복해보자 (React + Spring Boot, Kakao Login, Naver Login)
  • [개발 일지] Intersection Observer API & react-query를 이용한 무한스크롤 구현
  • [개발 일지] Suspense를 이용하여 모든 api에 Loading 화면을 설정하자 (NextJS, Suspense)
  • [개발 일지] Axios instance 설정 끝장내기 (Axios, instance, interceptors, JWT)
피터s
피터s
1년차 프론트엔드 개발자입니다 😣 아직 열심히 배우는 중이에요! 리액트를 하고있어요 :) - gueit214@naver.com - https://github.com/gueit214
피터s
피터의 성장기록
피터s
전체
오늘
어제
  • 분류 전체보기 (200)
    • 코딩 테스트 (25)
      • 프로그래머스 (16)
      • LeetCode (8)
      • 백준 (1)
    • 개발 독서 일지 (1)
    • 기업 분석 (4)
    • 개발 일지 (19)
      • 최신기술 도전기 (1)
      • 에러 처리 (5)
      • 개발 일지 (12)
    • 개발 일상 (36)
      • 개발 회고 (22)
      • 개발 이야기 (12)
      • 개발 서적 (1)
    • 취업 관련 지식 (11)
    • 알고리즘 (17)
    • WebProgramming (84)
      • WebProgramming (8)
      • HTML (5)
      • CSS (8)
      • JS (21)
      • React (40)

블로그 메뉴

  • About
  • 2022년 개발 성장기
  • 앞으로의 계획
  • github
  • 일상 blog

공지사항

인기 글

태그

  • Kakao Tech Internship
  • 반복문
  • Union-find
  • 1년 회고
  • 개발 is life
  • 함수
  • 1일 1커밋 후기
  • dfs
  • 개발 회고
  • 누적합
  • LV2
  • 구름톤
  • 구름
  • 카카오
  • KAKAO BLIND
  • 카카오 채용연계형 인턴십
  • lv3
  • BFS
  • 스터디 후기
  • Retry
  • 해커톤
  • 개발 일상

최근 댓글

최근 글

hELLO · Designed By 정상우.
피터s
[개발 일지] 채팅 기능을 구현해보자 (Websocket, Stomp, SockJS)
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.