본문 바로가기
Projects/Socket Omok

[Socket Omok] 3. 대기실 기능 개발

by DevJaewoo 2022. 6. 5.
반응형

Intro

개발 환경을 구축하고 사용자가 처음 접속하면 보여질 방과 관련된 기능들을 개발했다.

사용자가 방을 생성하고, 다른 사용자가 생성된 방에 참가하면 오목 경기를 시작시킬 것이다.

그 후에 들어오는 사용자들은 관전자로 놔둘 생각이다.

React를 처음 써봐서 기능을 개발하며 많이 헤맸지만, 생각보다 간단하고 강력한 기능을 제공해줘서 금방 이정도까지 만들 수 있었던것 같다.


방 생성

방 생성 UI

input에  방 이름을 입력하고 Enter를 치거나 '방 만들기' 버튼을 클릭하면 해당 이름으로 방이 생성된다.

 

아래의 경우 방이 생성되지 않도록 설정했다.

 

  • 같은 이름의 방이 이미 존재하는 경우
  • 사용자가 이미 다른 방에 참가중인 경우
  • 방 이름에 아무것도 입력하지 않았을 경우

코드는 아래와 같다.

이벤트 발생 시 roomname이 공백인지 확인하고 서버에 'room_new' Event로 방 이름을 전달하도록 했다.

 

const NewRoom = () => {
  const handleNewRoom = (event) => {
    event.preventDefault();
    const name = event.target.roomname.value;
    event.target.roomname.value = "";
    if (name.length == 0) return;
    socket.emit("room_new", name);
  };

  return (
    <div className="newroom">
      <form className="newroom__form" onSubmit={handleNewRoom}>
        <input
          className="newroom__input"
          type="text"
          name="roomname"
          placeholder="방 이름"
        ></input>
        <button className="newroom__submit">방 만들기</button>
      </form>
    </div>
  );
};

방 목록

방 목록 UI

사이트에 접속하면 소켓이 연결되며 방 목록을 조회하도록 설정했다.

서버에서 온 Object Array를 React Component Array로 바꾸는데 고생 좀 했다.

 

socket.on을 함수 밖으로 빼내고 싶었는데 roomList는 함수 밖으로 꺼내면 에러가 발생해서 어쩔 수 없이 socket.on을 함수 내부에 뒀다.

같은 socket event listener를 여러개 둘 수 있기 때문에 별 문제는 안되겠지만 나중에 해결방법이 떠오르면 고쳐야겠다.

const RoomItem = (room) => {
  const handleEnterRoom = () => {
    socket.emit("room_enter", room.name);
  };

  return (
    <li key={room.name} className="room-list__item">
      <p className="room-list__name">{room.name}</p>
      <button className="room-list__enter" onClick={handleEnterRoom}>
        입장하기
      </button>
    </li>
  );
};

const RoomList = () => {
  const [roomList, setRoomList] = React.useState([]);

  socket.on("room_change", (list) => {
    setRoomList(list);
  });

  return (
    <div className="room-list">
      <h3>방 목록</h3>
      <ul className="room-list__container">{roomList.map(RoomItem)}</ul>
    </div>
  );
};

 

서버 코드는 아래와 같다.

 

wsServer.on("connection", (socket) => {
  socket.onAny((event) => {
    console.log(`Socket event: ${event}`);
  });

  //방 만들기
  socket.on("room_new", (name) => {
    name = name.trim();
    console.log(`Socket ${socket.id} is creating room ${name}.`);

    //Socket은 ID와 같은 Room을 Default로 갖고 있음
    if (socket.rooms.size > 1) {
      console.log(`socket ${socket.id} is already in room.`);
      console.log(socket.rooms);
      socket.emit("error", "이미 다른 방에 참가중입니다.");
      return;
    }

    //동일한 방이 존재할 경우
    if (!checkDuplicateRoomName(name)) {
      console.log(`Room name ${name} already exists.`);
      socket.emit("error", "동일한 방이 이미 존재합니다.");
      return;
    }

    const roomInfo = {
      name: "room",
      blackPlayer: "",
      whitePlayer: "",
      takes: [],
    };

    roomInfo.name = name;
    roomInfo.blackPlayer = socket.id;

    publicRoom.push(roomInfo);
    wsServer.sockets.emit("room_change", publicRoom);

    socket.join(name);
    socket.emit("room_enter", name);

    console.log(publicRoom);
  });

방 참가 / 퇴장

방 나가기 UI

방 생성 / 참가 이후에 서버에서 'room_enter' 이벤트를 주면 오목 게임이 진행되는 방으로 화면이 전환된다.

게임 화면은 아직 구현되지 않았다.

방 나가기 버튼을 누르면 서버에 'room_leave' 이벤트를 주고, 서버에서 이벤트를 다시 주면 대기실 화면으로 전환된다.

 

'room_enter', 'room_leave' 이벤트가 들어오면 gaming state를 변경하고, state에 따라 화면을 다시 렌더링한다.

const WaitingRoom = () => {
  return (
    <>
      <NewRoom />
      <RoomList />
    </>
  );
};

const GamingRoom = () => {
  return (
    <>
      <button
        onClick={() => {
          socket.emit("room_leave");
        }}
      >
        방 나가기
      </button>
    </>
  );
};

const App = () => {
  const [gaming, setGaming] = React.useState(false);
  socket.on("room_enter", () => {
    setGaming(true);
  });

  socket.on("room_leave", () => {
    setGaming(false);
  });

  return (
    <>
      <Header />
      {gaming ? <GamingRoom /> : <WaitingRoom />}
    </>
  );
};

 

방 퇴장 시 자꾸 이런 에러가 뜨는데 나중에 수정해야겠다.

에러


대기실 화면

대기실 화면 UI

다음엔 실제 오목 게임을 구현해야겠다.

반응형