OLD/chat-app

[ChatApp] Socket.io를 활용하여 간단한 채팅 어플 만들어보기 - 01. 연결 및 간단한 소통

joseph0926 2023. 10. 14. 12:52

1. 기본 정보

  • 환경
    • Front: React, Socket.io-client
    • Back: Node, Express, Socket.io
  • 목표
    • socket.io을 활용하여 간단한 채팅 앱 만들기
    • React + Node (express)
    • DB x

 

2. 세팅

React 생성 -> npx create-vite@latest client (VITE)

Node 생성 -> npm init -y

 

  • 라이브러리 설치
    • front: npm install socket.io-client (socket.io-client)
    • back: npm install socket.io (socket.io) / npm install -D nodemon ts-node-dev @types/express @types/node tsconfig-paths typescript

 

back init code

const PORT = process.env.PORT || 5000;
const app = express();
const expressServer = app.listen(PORT, () => {
  console.log(`서버가 포트번호 ${PORT}에서 정상 작동중입니다.`);
});

 

3. Socket.io 이용

 

전체적인 흐름정리

- 백 소켓 - 클라이언트 소켓 연결 -> 1. 백에서 연결됨을 알림 -> 2. 클라이언트 해당 내용을 렌더링 -> 3. 클라이언트에서 입력된 메시지 서버로 전송 -> 4. 서버에서 해당 내용 받아서 다시 클라이언트로 전성(보통 이떄 메시지를 db에 저장) -> 5. 클라이언트에서 서버로부터 받은 데이터를 렌더링

 

Back

- socket.io 설정

const io = new Server(expressServer, {
  cors: {
    origin: process.env.NODE_ENV === 'production' ? false : ['http://localhost:3000']
  }
});

- 연결

io.on('connection', (socket) => {
  // broadcast => 해당 메신자의 송신자는 제외함 (자세한 내용: https://socket.io/docs/v3/broadcasting-events/)
  socket.broadcast.emit('message', `User ${socket.id.substring(0, 5)}가 연결되었습니다.`);
  // 1. 백에서 연결됨을 알림

  socket.on('message', (data) => {
    io.emit('message', `${socket.id.substring(0, 5)}: ${data}`);
	// 4. 서버에서 클라이언트의 입려값(data) 받아서 다시 클라이언트로 전성(보통 이떄 메시지를 db에 저장)
	});
});

 

Front

- socket.io-client 설정

const [socket, setSocket] = useState<Socket | null>(null);

useEffect(() => {
    const newSocket = io("http://localhost:5000");
    setSocket(newSocket);

    return () => {
      newSocket.disconnect();
    };
  }, []);

(useEffect에서 해당 로직을 시키는 이유 => state 변경등으로 인한 컴포넌트 재렌더링마다 새로운 연결을 할것이 아니므로, 의존성을 []로 설정해 첫 로딩시에만 연결하도록 설정)

 

- 메시지 수신 / 송신

useEffect(() => {
    if (socket) {
      socket.on("message", (data) => {
        setMessages((prev) => [...prev, data]);
      });
      // 2. 클라이언트 해당 내용을 렌더링 / 5. 클라이언트에서 서버로부터 받은 데이터를 렌더링


    }

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

const submitHandler = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    if (message !== "" && socket) {
      socket.emit("message", message);
      setMessage("");
    }
  };
  // 3. 클라이언트에서 입력된 메시지 서버로 전송

++ TIP

 

- 메시지 입력중인거 캐치하기

// back

io.on('connection', (socket) => {
  socket.broadcast.emit('message', `User ${socket.id.substring(0, 5)}가 연결되었습니다.`);

  socket.on('message', (data) => {
    io.emit('message', `${socket.id.substring(0, 5)}: ${data}`);
  });

	// 추가된 부분
  socket.on('activity', (name) => {
    socket.broadcast.emit('activity', name);
  });
});

// client

const [activity, setActivity] = useState("");
const [isTyping, setIsTyping] = useState(false);
const typingTimeoutRef = useRef<number | null>(null);

useEffect(() => {
    if (socket) {
      // 추가된 부분
      socket.on("activity", (name) => {
        setActivity(`${name}님이 작성중입니다,,,`);
        if (typingTimeoutRef.current) {
          clearTimeout(typingTimeoutRef.current);
        }
        typingTimeoutRef.current = setTimeout(() => {
          setActivity("");
        }, 1000);
      });

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

// 추가된 부분
const keyDownHandler = () => {
    if (socket && !isTyping) {
      socket.emit("activity", socket.id.substring(0, 5));
      setIsTyping(true);
    }
  };
  const keyUpHandler = () => {
    setIsTyping(false);
  };


return (
    <div>
      <form onSubmit={submitHandler}>
        <input
          type="text"
          value={message}
          onChange={(e) => setMessage(e.target.value)}
          onKeyDown={keyDownHandler}
          onKeyUp={keyUpHandler}
        />
        <button type="submit">Send</button>
      </form>
      <ul>
        {messages.map((msg, idx) => (
          <li key={idx}>{msg}</li>
        ))}
      </ul>
      <p>{activity}</p>
    </div>
  );

 

 

4. 데모

깃허브: Github-chatapp