본문 바로가기
웹 프로젝트/👨‍👨‍👧‍👧소셜 가계부

[React / Socket.io] 채팅 구현하기

by 청량리 물냉면 2023. 9. 13.
반응형

프로젝트 개발 당시 프론트는 리액트, 백엔드는 스프링으로 진행하기로 했는데 백엔드 인원이 갑작스레 빠지게 되면서 혼자 백엔드까지 구현 해야 하는 상황이 되었다. 

스프링은 한번도 해본 적이 없어서 급하게 노드로 백엔드 서버를 만들고 리액트로 프론트를 구현하기로 했다.

구글링을 통해 노드 채팅 코드를 검색했고 레퍼런스를 통해 코드의 구조를 대략적으로 이해하는 시간을 가졌다 .이후에 서버의 데이터를 프론트에서 받아 사용하는 코드를 작성했다.

 

🌅 서버 구현

우선 아래 패키지를 모두 설치해준다.

npm i nodemon express socket.io cors

 

📂 Server > 📄 index.js 

const httpServer = require("http").createServer(); //서버 객체 생성
// http server를 socket.io server로 upgrade
const io = require("socket.io")(httpServer, {
  cors: {
    origin: "http://localhost:3000",
    methods: ["GET", "POST"],
  },
}); //cors 정책 설정

//socket 연결
io.on("connection", (socket) => {
  console.log("connection");
  socket.on("init", (payload) => {
    console.log(payload);
  });

  //연결이 되면 클라이언트에 메시지를 전송하고 클라이언트로부터 메시지를 수신한다
  socket.on("send message", (item) => {
    console.log(item.author + " : " + item.message);
    io.emit("receive message", { author: item.author, message: item.message, time: item.time });
    //클라이언트에 이벤트를 보냄
  });
});

httpServer.listen(80); //80번 포트 서버 실행

 

 

🎨 프론트엔드 구현

프론트엔드에 서버 코드를 적용하는 데는 시간이 조금 더 걸렸다.

우선 프론트에서도 socket.io를 사용할 수 있도록 패키지를 설치한다.

npm i socket.io-client

 

📂 Front > 📄 ChattingView.js 

 

//80번 포트와 연결
const socket = io.connect("http://localhost:80");

서버와 연결한다.

 

  const [userInputMsg, setUserInputMsg] = useState("");
  const [chatArr, setChatArr] = useState([]);

state는 2개를 지정했다.

  • 사용자에게 메시지를 입력받는 state ---> 사용자가 입력하는 내용을 보여주고, 메시지 전송 시 창을 비워준다. 서버에 사용자의 입력을 포함한 객체를 전송한다.
  • 서버에서 받은 {author, message, time} 객체를 저장하는 배열 state

실제 채팅 구현 부분은 아래와 같다.

//서버에게서 받은 receive message이벤트에 대한 콜백
  useEffect(() => {
    socket.on("receive message", (message) => {
      setChatArr((chatArr) => chatArr.concat(message));
    });
  }, [socket]);

  //버튼 클릭 시 send message 이벤트 발생(메시지 전송)
  const sendMessageHandler = useCallback(() => {
    socket.emit("send message", {
      author: userInfo.name,
      message: userInputMsg,
      time: new Date(Date.now()).getHours() + ":" + new Date(Date.now()).getMinutes(),
    });
    setUserInputMsg("");
  }, [userInputMsg, userInfo.name]);

  //사용자에게 입력받은 채팅 내용을 기반으로 state 변경
  const changeMessage = useCallback((e) => {
    setUserInputMsg(e.target.value);
  }, []);
  • 'receive message'로 받은 메시지는 chat 배열에 추가해준다. 이때 push가 아니라 concat을 이용해 원본 배열의 변경이 없도록 한다. (concat은 값을 추가한 새로운 배열을 반환한다)
  • 사용자가 입력창에 메시지를 작성하고 전송 버튼을 누르면 sendMessageHandler 함수가 실행된다. 이때 서버에 유저이름, 작성 메시지, 작성 시간을 객체로 넘겨준다.
  • 사용자가 입력한 메시지를 userInpuMsg 변수에 저장한다. useCallback 사용하지 않을 시 엔터를 쳐서 함수 실행하면 여러번 메시지가 전송되는 버그가 발생하므로 useCallback을 반드시 설정해 준다.

 

return (
    <Section>
      <div className="contentContainer">
        {chatArr.map((messageContent) => {
          return (
            <div>
              {userInfo.name === messageContent.author ? (
                <SeneChatCard massage={messageContent.message} time={messageContent.time} />
              ) : (
                <RecieveChatCard
                  massage={messageContent.message}
                  time={messageContent.time}
                  author={messageContent.author}
                />
              )}
            </div>
          );
        })}
      </div>
      <div className="sendContainer">
        <input
          type="text"
          placeholder="메시지를 입력하세요"
          value={userInputMsg}
          onChange={changeMessage}
          onKeyDown={(event) => {
            event.key === "Enter" && sendMessageHandler();
          }}
        />
        <button onClick={sendMessageHandler}>전송</button>
      </div>
    </Section>
  );

작성자의 이름과 서버로 전송된 메시지의 작성자가 동일하면 내가 보내는 것으로 간주하고 스타일링했다.

🙄 변경점
이 부분은 이름이 동일한 사람도 있을 수 있으니 id값을 확인하는 것으로 변경해야겠다.

 

✨ 최종 화면

채팅 구현 화면

채팅목록이나 채팅 상단의 제목은 하드코딩된 상태이지만 채팅 기능은 정상적으로 동작한다.

실제 채팅 화면은 아래와 같다.

 

- 1:1 채팅

 

 

- 2명 이상 채팅

크롬, 크롬시크릿, 마이크로 엣지 3개의 브라우저로 세 명의 유저가 채팅을 진행하도록 했다. 더 많은 인원이 채팅하는 것도 가능하다.

 

 

🎈 개선점

- 채팅 유저 확인 이름->아이디로 변경

- 입력값 검증 (아무것도 입력하지 않았을 시 서버에 객체 전송 x)

- 채팅방 개설 기능

- 채팅 목록 선택 가능하도록

- 채팅방 개념 도입 (각 방에서 주고받은 메시지는 그 방에 참여 중인 사용자에게만 보이게 한다.)

 


 

참고

 

https://tinyurl.com/yt4clkjz

 

나만의 채팅 서비스 만들기 (react + express + Socketio)

갑자기 왜? 마이스터고등학교에 재학하며 다양한 프로젝트 경험을 쌓을 수 있었습니다. 다양한 프로젝트를 진행하며 여러 기술을 사용해보았지만, 정작 꼭 사용해보고 싶었던 기술인 Websocket기

velog.io

 

https://poiemaweb.com/nodejs-socketio

 

Node.js(Express)와 Socket.io | PoiemaWeb

WebSocket, Socket.io를 사용한 실시간 채팅 애플리케이션

poiemaweb.com

 

https://tinyurl.com/yw6bf94e

 

react 로 socket io 연결

webSocket에 이전부터 관심이 있어서 찾아보았는데 webSocket은 크로스 브라우징이 안되는 곳이 있다고 해서 socket.io라이브러리로 만들어 보았다.처음에 백엔드에서 설정을 해줘야 해서 폴더를 server

velog.io

 

https://roothyo.tistory.com/42

 

socket.io 실시간 chat 구현 (react - Node.js) - (2)

다음은 앞서 설명했던 단계들이 socket.io에서 어떻게 구현되어 있는지를 알아보겠다. 클라이언트단은 React.js 서버단은 Node.js를 사용하였다. 1. 소켓 생성 : 클라이언트는 socket.io-client에서 server의 I

roothyo.tistory.com

 

https://github.com/machadop1407/socket-io-react-example

 

GitHub - machadop1407/socket-io-react-example

Contribute to machadop1407/socket-io-react-example development by creating an account on GitHub.

github.com

 

반응형