이노베이션 캠프 실전 (챕터2. 채팅기능 구현과정)

yesolog·2022년 10월 27일
1

TIL,WIL

목록 보기
5/6
post-thumbnail
post-custom-banner

저번에는 채팅 라이브러리에 어떤것이 있고 어떤것을 선택할지 결정했다.
우리조는 서버가 스프링이기 때문에 SockJs를 stomp와 함께 사용하기로 결정했다.

그렇다면 이제 이 websocket을 어떻게 사용해야할지... 고민이 많아진다.

나는 최신버전인 v5를 사용했고 stomp 페이지에서 코드를 참고했다.

이제 구현순서를 설명해보려고 한다.

1. 기본환경 설정

먼저 stomp와 sockjs를 사용하기 위해 해당 라이브러리를 설치하고 임포트해준다.

import * as StompJs from "@stomp/stompjs";
import * as SockJS from "sockjs-client"

2. 클라이언트 생성 및 소켓 연결

클라이언트 생성은 다음과 같다.

  const connect = () => {
    client.current = new StompJs.Client({
      //websocket 주소만 입력 가능 * ws://, wss:// 로 시작
      brokerURL: process.env.REACT_APP_CHAT_WEBSOCKET,
      connectHeaders: {
        Authorization: Authorization,
      },
      debug: function (str) {
      },
      reconnectDelay: 5000,
      heartbeatIncoming: 4000,
      heartbeatOutgoing: 4000,
      onConnect: () => {
        //구독요청
        subscribe();
      },
    });
    client.current.activate();
  };

클라이언트를 생성해서 채팅이 brokerURL을 연결하고 연결을 위한 헤더에는 서버와 협의하여 Authorization 토큰을 보내준다. 디버그는 앞으로 통신이 어떤식으로 되는지 보여주는 가장 핵심적인 부분이다.

onConnect가되면 subscribe로 구독을 시작한다. subscribe는 다음과 같이 작성했다.

  const subscribe = () => {
    client.current.subscribe(`/sub/chat/room/${해당되는 방}`, function (chat) {
      const content = JSON.parse(chat.body);}

백과 소통을 통해 구독시 엔드포인트를 우리는 방의 아이디로 설정했고 해당 방으로 구독을 신청한다. 그다음 받아오는 chat으로 받아오는 정보를 contents라는 변수에 저장한다. 이때 chat.body에 해당 내용이 담겨와서 json 파싱해주었다.

아 websocket은 IE나 특정 브라우저에서 지원을 안하기 때문에 SockJs로 해당부분을 커버해줘야한다.
stomp에서는 websocketFactory의 형태로 사용해서 여기를 통해 websocket 미지원 브라우저를 처리해주었다.

 client.webSocketFactory = () => {
    return new SockJS(process.env.REACT_APP_CHAT_SOCK);
  };

3. 채팅 구현

채팅에서는 타입을 통해서 입퇴장시 메세지처리, 채팅, ban처리 등을 구현했다.

이 부분은 구독과 동시에 이루어져야하는데 왜냐하면 웹소켓을 통해 받은 chat의 바디에 담긴내용을 사용할것이기때문이다.

채팅은 1)보내는 경우, 2)받는 경우로 나누어 설명하겠다.

1) 보내는 경우

일단 채팅을 구독한 방으로 보내면 해당 메세지가 서버에서 오는걸 확인할 수 있다. 그래서 보내는 걸 먼저 진행했다.

먼저 submit이라는 함수를 만들었다. submit은 채팅을 보내는 버튼을 onClick하면 작동되도록 구현했다. 해당 기능은 input창의 현재 입력된 값을 body에 message라는 키값으로 엔드포인트가 "/pub/chat/message"인 곳으로 보낸다. 이때 이것이 채팅용이라는 것을 알려주기 위해 사전에 서버와 협의된 채팅의 type인 2를 보내도, 구독된 방의 아이디와 함께 헤더에는 Authorization 토큰을 보냈다.

전송할 데이터(채팅메세지)는 Json형태로 바꾸어서 보내주었다.

     const submit = () => {
    
      client.current.publish({
        destination: "/pub/chat/message",
        headers: { Authorization: Authorization },
        //전송할 데이터를 입력
        body: JSON.stringify({
          type: 2,
          message: inputRef.current.value,
          roomId: roomId.id,
        }),
      });
  };
  };

2) 받는 경우

채팅을 받는 경우에는 먼저 useState로 채팅 메세지만 담을 state를 만들어준다.

  const [messages, setMessages] = useState([
    {chatMessage: "",user: "",type: "",  image: "",time:"" },
  ]);

각각 메세지, 보낸사람, 컨텐츠의 타입, 이미지, 채팅시간 등이 담겨있다.

여기에서 변수에 저장한 컨텐츠의 타입이 2인 경우만 사람들이 서로 주고받는 채팅이라고 서버와 협의했다. 그래서 컨텐츠 타입이 2인 경우만 messages라는 state에 저장되도록 다음과 같이 코드를 작성했다.

if(content.type === 2) {
        setMessages((_messages) => [
          ..._messages,
          {
            chatMessage: content.msg,
            user: content.sender,
            type: content.type,
            image: content.image,
            time: content.chatTime,
          },
        ]);           
      }

이후에는 state로 관리중인 메세지를 채팅창 ui에 map함수를 이용해서 순차적으로 띄워주면 된다.

map을 돌린 코드를 보여주면 좋지만 카카오톡 ux를 따라하느라 너무 복잡해져서 처음부터 보면 "이...이게뭐야" 이럴수있다...ㅎㅎ 그래서 해당 코드는 안보여주는걸로...

아무튼 이와 같은 과정을 거쳐서 내가 완성한 채팅창은 다음같은 모습이다.

움짤은 내가 만든건 없어서 리드미에서 가져왔다 오른쪽 하단과 같이 상대방의 프로필 사진도 확인 가능하다. 시간이나 프로필 사진이 뜨는 ux는 카카오톡이랑 똑같이 하느라 정말 힘들었다...(알고리즘 그잡채...) 그래도 여기까지 하니까 정말 괜찮은 채팅창의 모습이라 상당히 만족스러웠다.

다음에는 화상연결, 채팅에서 필수적인 강퇴 기능과 방장위임 기능 및 현재 신고같은 채팅 부가기능의 구현과정에 대해 설명하도록 하겠다.

post-custom-banner

0개의 댓글