개발 공부 기록

나는 무엇을 하는가?

프로젝트/chat-app

react+socket.io로 채팅 앱 구현하기

진!!!!! 2024. 12. 4. 19:00

프로젝트를 하며 WebSocket을 이용해 채팅 기능을 구현할 일이 생겼는데, 채팅의 기술 원리를 잘 모르고 무작정 개발하니 백엔드와 소통도 잘 안되는 것 같아... node.js로 직접 구현해보며 공부해보기로 했다.

 

기술 스택

  • client : react, typescript
  • server : node.js, express, socket.io
  • DB: 추후에 mongoDB 연결 예정

 

완성 화면 미리보기

1. react 세팅

https://ko.vite.dev/guide/

 

Vite

Vite, 프런트엔드 개발의 새로운 기준

ko.vite.dev

vite로 설치해줬다.

 

$ npm create vite@latest
Need to install the following packages:
create-vite@5.5.4
Ok to proceed? (y) y

> npx
> create-vite

√ Project name: ... chat-client
√ Select a framework: » React
√ Select a variant: » TypeScript

 

2. node.js 세팅

서버 디렉토리 생성

npm init -y
npm install express --save

 

npm init -y로 package.json 파일을 만든 후 express를 설치한다.

 

//app.js

import express from 'express';
import { createServer } from 'node:http';

const app = express();
const server = createServer(app);

app.get('/', (req, res) => {
  res.send('<h1>Hello world</h1>');
});

server.listen(3000, () => {
  console.log('server running at <http://localhost:3000>');
});

 

터미널에 node app.js를 입력하면 서버가 켜지고 러닝중이라는 콘솔 로그가 찍힌다.

 

socket.io 설치

https://socket.io/docs/v4/tutorial/step-1

 

Tutorial step #1 - Project initialization | Socket.IO

The first goal is to set up a simple HTML webpage that serves out a form and a list of messages. We’re going to use the Node.JS web framework express to this end. Make sure Node.JS is installed.

socket.io

 

 

 

import express from "express";
import { createServer } from "node:http";
import { Server } from "socket.io";

const app = express();

const server = createServer(app);

server.listen(3000, () => {
  console.log("server running at <http://localhost:3000>");
});

const io = new Server(server, {
  cors: {
    origin: ["<http://localhost:5173>"],
  },
});

app.get("/", (req, res) => {
  res.send(`
    <!DOCTYPE html>
    <html>
      <head>
        <title>Socket.IO Test</title>
        <script src="/socket.io/socket.io.js"></script>
        <script>
          const socket = io();
          
          socket.on('connect', () => {
            console.log('Connected to server');
          });
          
          socket.on('disconnect', () => {
            console.log('Disconnected from server');
          });
        </script>
      </head>
      <body>
        <h1>Socket.IO Test</h1>
      </body>
    </html>
  `);
});

io.on("connection", (socket) => {
  console.log("a user connected");
  socket.on("disconnect", () => {
    console.log("user disconnected");
  });
});

클라이언트 코드를 만들기 전 서버 내에서 socket이 잘 적용되는지 확인하고 싶어서 res.send 내부에 socket을 넣어서 로그를 찍었다.

localhost:3000에 접속했을 때 로그가 잘 찍히네요!

cors origin에는 클라이언트 주소를 입력해줍니다. react -vite 기본 포트인 5173으로 설정했어요.

3. client

socket.io client를 설치해준다.

npm i socket.io-client
import { useEffect, useState, FormEvent } from "react";
import { io } from "socket.io-client";
import "./App.css";

interface MessageProps {
  text: string;
  username: string;
  timestamp: string;
}

const App = () => {
  const socket = io("<http://localhost:3000>");
  const [messages, setMessages] = useState<MessageProps[]>([]);
  const [messageInput, setMessageInput] = useState("");
  const username = "테스트";

  useEffect(() => {
    socket.on("connect", () => {
      console.log("connected");
    });

    socket.on("message", (message) => {
      setMessages((prev) => [...prev, message]);
      console.log(message);
    });

    return () => {
      socket.off("connect");
      socket.off("message");
    };
  }, []);

  const sendMessage = (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    if (messageInput.trim()) {
      const messageData = {
        text: messageInput,
        username: username,
        timestamp: new Date().toISOString(),
      };
      socket.emit("send_message", messageData);
      setMessageInput("");
    }
  };

  return (
    <>
      {messages.map((msg, idx) => (
        <div key={idx}>
          <div>
            <span>{msg.username}</span>
            <span>{msg.text}</span>
            <span>{new Date(msg.timestamp).toLocaleTimeString()}</span>
          </div>
        </div>
      ))}

      <form onSubmit={sendMessage}>
        <div>
          <input
            type="text"
            value={messageInput}
            onChange={(e) => setMessageInput(e.target.value)}
            placeholder="메시지를 입력하세요..."
          />
          <button type="submit">전송</button>
        </div>
      </form>
    </>
  );
};

export default App;

 

간단하게 메세지를 보내는 폼과 도착한 메세지를 매핑하는 화면을 만들었다.

 

4. server 수정

import express from "express";
import { createServer } from "node:http";
import { Server } from "socket.io";

const app = express();

const server = createServer(app);

server.listen(3000, () => {
  console.log("server running at http://localhost:3000");
});

const io = new Server(server, {
  cors: {
    origin: ["http://localhost:5173"],
  },
});

io.on("connection", (socket) => {
  console.log(`a user connected: ${socket.id}`);

  socket.on("send_message", (messageData) => {
    console.log(messageData);
    io.emit("message", messageData);
  });

  socket.on("disconnect", () => {
    console.log(`User disconnected: ${socket.id}`);
  });
});

get으로 html을 그리기 위한 코드를 지워줬다.

 

const socket = io("http://localhost:3000");

const socket = io("ws://localhost:3000");

WebSocket은 http라는 프로토콜을 이용하니까 ws:// 에서만 소켓이 연결될 줄 알았는데, http에서도 된다. socket.io 내부에서 내부적으로 ws로 변경시켜주는게 아닐까 싶다.

 

 

WebSocket을 쓰면 데이터를 보낼 때 문자열로 변형시켜서 보내야하는데, socket.io를 사용하면 json을 그대로 써도 된다.

 

css는 shadcn/ui 도입하면서 조금 달라졌습니다!

 

Repository 바로가기 (계속 업데이트 됨)

https://github.com/eunjin11/chat-app

 

GitHub - eunjin11/chat-app: react, node.js, socket.io를 이용한 채팅 어플리케이션 실습

react, node.js, socket.io를 이용한 채팅 어플리케이션 실습. Contribute to eunjin11/chat-app development by creating an account on GitHub.

github.com