본문 바로가기

카테고리 없음

리액트) 디스코드 클론 - 3

저번 포스팅에선 클라이언트와 서버의 셋팅을 진행했었다.

이번에는 웹 소켓의 기본 사용법을 알아보자.

그 전에 우선 클라이언트의 전체적인 부분을 변경할 것이다.

//App.js

import React from "react";
import Home from "./Home";
import { Routes, BrowserRouter, Route } from "react-router-dom";
import Room from "./Room";

const App = () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/room/:roomid" element={<Room />} />
      </Routes>
    </BrowserRouter>
  );
};

export default App;

 

//Home.js

import React from "react";
import { useNavigate } from "react-router-dom";
import { v4 as uuid } from "uuid";
const Home = () => {
  const navigate = useNavigate();
  return (
    <div>
      <button
        onClick={() => {
          navigate(`/room/:${uuid()}`);
        }}
      >
        Create Room
      </button>
    </div>
  );
};

export default Home;

 

//Room.js

import React, { useEffect, useRef } from "react";
import { io } from "socket.io-client";

const Room = () => {
  const socketRef = useRef(null);
  useEffect(() => {
    socketRef.current = io.connect("http://localhost:9000");
  }, []);

  return <div></div>;
};

export default Room;

App 컴포넌트를 라우터로 사용하고 홈을 추가, Room.js를 생성해 기존 소켓을 옮겨왔다.

socket을 socketRef라는 Ref로 따로 옮긴 이유는 추후 설명할 예정이지만 useEffect구문안에 지역변수로 설정하는 것보단, ref.current객체에 담아 useEffect함수 외부에서도 사용하기 위함이다.

이제 본격적인 socket의 사용법을 알아보자.

우선 클라이언트에 다음과 같이 코드 한 줄을 추가해보자.

//Room.js

import React, { useEffect, useRef } from "react";
import { io } from "socket.io-client";

const Room = () => {
  const socketRef = useRef(null);

  useEffect(() => {
    socketRef.current = io.connect("http://localhost:9000");

    socketRef.current.emit("join-room"); //add!!
  }, []);

  return <div></div>;
};

export default Room;

그 다음 server.js로 넘어가 다음과 같이 수정하자.

const express = require("express");
const app = express();

const cors = require("cors");
app.use(cors());

const server = require("http").createServer();
const io = require("socket.io")(server, {
  cors: {
    origin: "*",
    methods: ["GET", "POST"],
    credentials: true,
  },
});

const PORT = 9000;

io.on("connection", (socket) => {
  console.log("connection!!");

  socket.on("join-room", () => { //add!!!
    console.log("join-rooom!!");
  });
});

server.listen(PORT, () => console.log("server is running on ", PORT));

웹페이지를 http://localhost:3000/room/roomid로 설정하고 서버로그를 확인해보자.

다음과 같이 로그가 나오는 것을 확인할 수 있다.

로그를 보면 connection과 join-room이 두 번씩 찍히는 것을 알 수가 있는데 이는 리액트 프로젝트를 생성하면 기본적으로 strict모드가 on으로 설정되어 있기 때문이다. 이것이 거슬리면 이를 해제하면 된다.

// client/src/index.js

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

만일 strict 모드에 대해 궁금하다면, 또는 모른다면 다음 리액트 공식문서를 확인해보는 것을 추천한다.

이제 스트릭트 모드를 제거했으면 connection!!과 join-room!!이라는 콘솔이 한 번씩 찍히는 것을 확인할 수 있다.

소켓의 사용법에 대한 설명은 이게 끝이다.

socket.emit(클라이언트의 경우 ref.current객체로 소켓을 담았으니 socketRef.current.emit())으로 소켓 이벤트를 발생시키고 socekt.on()으로 이벤트를 받는다. 이 때문에 useEffect()함수 내부에 이벤트를 등록한 것이다.  javascript의 addEventListener와 상당히 유사하다.

이벤트 발생(emit)의 경우 onClick함수 등에 담아 

    const onClick = () => {
        socketRef.current.emit('event-start')
    }

위와 같이 소켓 이벤트를 발생시킬 수 있지만, 이벤트를 받는 on()의 경우는 반드시 effect()내부에 (의존성 배열이 빈 배열인 useEffect()) 등록해야 한다. 안그런다면 버튼이 누를 시점에만 on()이벤트가 발생해 그 때만 이벤트를 받기 때문이다.

자 다음 포스팅은 peerjs에 대해 알아볼 것이다.