네트워크 가위바위보 난투

사요·2022년 12월 12일
0
post-thumbnail

사건의 발단

때는 바야흐로 종강 전 !!!! 이번에 KING 에서 좋은기회로 서울대학교 UPnL, SNUGDC 등 다른 게임 동아리와 연합해서 게임잼을 주최하게 되었다! 메이킹잼이 끝난지 한달도 채 되지 않아 새로운 게임잼 준비를 하게 될 줄은 상상도 못했다ㅎㅎ..

그치만 연합 게임잼은 처음이라 두근두근하다!!🤗

그런데 왜 TF인 내가 게임제작 회고록을 쓰고 있는지 궁금할텐데 ..ㅎㅎ

오리엔테이션에서 참가자들간 아이스브레이킹을 기획하던 중,
개인게임 + 팀게임 점수를 합산하여 기획 선택 우선권을 주자는 의견이 나왔다.
그러나 문제는 개인게임이었다. 참가자들이 최소 30 ~ 50명 되는 상황에서
동시에 참여하면서 1등 ~ 50등까지 점수가 나오는 게임을 해야하는데
그런 게임을 찾기가 쉽지 않았다.

네트워크 가위바위보

그래서 Upnl 회장님께서 가져오신 의견이 네트워크 가위바위보 라는 게임이었다!
게임룰은 엄청 간단하다. 실시간으로 참가자들은 가위, 바위, 보 중 하나를 선택해 채팅에 올릴 수 있고 바로 이전사람이 낸 가위바위보를 이기면 점수를 얻고 지면 점수를 잃는 것이다.

게임룰도 심플하고, 참여자들도 재밌게 할 수 있을 것 같아서 좋았는데...
사실 ... 치명적인 문제가 있었다. 처음엔 채팅으로 하는 것을 고려중이셨던 것 같은데, 폭풍우 처럼 쏟아지는 50명의 가위바위보 속에서 TF가 일일히 점수를 매기고 계산해주어야한다는 점이었다. 짧은 시간내에 점수를 도출해야하는 특성상 사실 거의 불가능에 가까운 일이었다.

그래서 해당 게임을 구현하기 위한 대체제로 구글폼 제출, 스프레드 시트 함수 이용, 디스코드 봇 등의 여러가지 대안을 생각해봤는데 그 어떤 방법도 재미 + 효율 을 모두 챙기기 힘들었다.

그래서 결국 이 게임을 웹으로 구현할 수 있지 않을까 하는 생각하는 단계까지 와버렸다 ...!

사실 처음에는 나포함 다른 TF 분들께서도..
우리가 급하게 준비하게 된 해커톤인 만큼 시간이 얼마남지 않은 상황에서 밑바닥부터 게임을 구현하는 것은 과투자가 아닌가라는 의견들이 많았다😢. 그래서 결국 다른 방법을 찾아보거나, 정 방법이 없으면 다른게임을 찾아보는것으로 마무리가 되었는데..

난쟁이가 쏘아올린 작은 공

그 다음날 바로 Upnl 회장님께서 아래와 같은 게임 기획문서를 들고 오셨다...!

https://docs.google.com/document/d/162EOEmH0KUXYosS3AtHvgdReKMvhaJRlQ8YT8RlmWQ8/edit

정말 깜짝 놀랐다!!. 하루만에 프로토타입이랑 대략적인 API 명세까지!! 열정과 실행력이 대단하신 분이라고 생각했다. 아무래도 서버를 사용하게 되는 게임이어서 백 + 프론트 둘다 개발해야하는데 혼자서 하기는 외롭기도 하고.. 부담스러우니(?) 같이 개발하실 팀원 한분을 모집하셨다!

그래서 보자마자 한치의 고민 없이 바로 프론트엔드에 지원했다ㅎㅎ

  1. 개발하는 것이 재밌을 것 같았고 (게임 개발은 언제나 즐겁다 😊)
  2. 개발하면서 많이 배우고 성장할 수 있을 것 같았다.

사실 기한도 제한되어 있고, 생각보다 요구사항이 만만치 않아서 "제대로 할 수 있을까
괜히 민폐만 끼쳐드리는 건 아닐까...😢" 생각을 했었는데 일단 저지르고 수습하는게
내 특기였기 때문에.. ㅎㅎ

안그래도 실시간 통신을 위한 웹소켓쪽에 관심있던 참이어서
이참에 웹소켓부터 제대로 공부해보기로 했다.

이렇게 나는 해커톤을 위한 메타 해커톤을 시작하게 되었다😄

✅ 목표 : 1.6 일까지 멀티플레이 게임 개발

초기 프로토타입

: 대략적인 UI 프로토타입 스케치를 해주셨다.



디자인은 구체적으로 개발하면서 입혀볼 예정이다.

프로토타입 1.0

: 그리고 프론트엔드를 맡았던 나는..~ 갑자기 이상한 욕심이 생겨서 ㅋㅋ..
피그마까지 켜서 디자인을 완성했다! (디자인이라곤 하지만 사실 색깔만 입힌게 전부이다 ㅎㅎ)

🎨 피그마 링크
https://www.figma.com/file/rTjA54yhBRaXBRrXpbQ4Fu/2023-%EC%97%B0%ED%95%A9%EA%B2%8C%EC%9E%84%EC%9E%BC?node-id=0%3A1&t=ftJDVHr3QqbB8DXz-0

계정접속, 대기, 관리, 게임화면 총 4개의 화면이 필요했다.

  • 계정접속
  • 대기

  • 게임화면(인게임)

  • 결과

화면 퍼블리싱

: 1.16 - 1.17 동안은 이제 본격적으로 화면 퍼블리싱에 돌입했다!
css 프레임워크로sass 를 쓸까 styled-component 쓸까 고민했는데,
사실 본 프로젝트는 크게 복잡한 UI 구조를 가지고 있지 않고 단순한 편이어서 styled-component를 쓰면 직관적이고 빠르게 개발할 수 있을 것 같다고 생각했다.

사실 퍼블리싱은 뭐 ..ㅎㅎ
여러번 해봤던거라 크게 어렵지 않았는데, 이상한데에서 발목을 잡혀서 조금 헤맸다 ㅋㅋㅋ...히><

🧩 퍼블리싱 로그
https://github.com/flowersayo/network_rps_front/commits/main

사람들이 게임에 접속하면 입장할 방번호를 부여받는 구조로 되어있으니
해당 방번호를 URL의 parameter로 사용하기로 했다.

우선, 중첩라우팅을 활용해서 방에 접속한 상태인지 아닌지를 분기했다.

  • 계정 접속창(랜딩페이지) :/
  • 게임방 : /room/${room_id}/*
 <Background>
        <Routes>
          <Route path="/" element={<LandingPage />} />
          <Route path="/room/:room_id/*" element={<GameRoomPage />} />
        </Routes>
      </Background>

그리고 게임 방 내부에서는 총 3가지의 상태(대기, 게임중, 게임종료/결과)가 존재하므로 아래와 같이 상태 depth를 더해서 각각의 고유한 페이지를 라우팅해주었다.

  • /room/{id}/wating : 대기룸
  • /room/{id}/game : 게임중
  • /room/{id}/result : 결과창
export default function GameRoomPage() {
  return (
    <>
      <Routes>
        <Route path="/waiting" element={<WatingGamePage />} />
        <Route path="/game" element={<InGamePage />} />
        <Route path="/result" element={<GameResultPage />} />
      </Routes>
    </>
  );
}

따라서 최종 라우팅 규칙은 다음과 같다.

  • 계정 접속창(랜딩페이지) :/
  • 게임방 : /room/${room_id}/*
    - 대기방 : /room/${room_id}/wating
    • 게임중 : /room/${room_id}/game
    • 게임종료/결과 :/room/{id}/result

기능 개발

: 기능개발에는 생각보다 시간이 많이 걸렸다. 개발이 어려웠다기보다.. 처음에 길을 잘못 들었다 하하..
멀티 플레이 게임을 만들어본 경험이 없어서ㅋㅋㅋ😢 처음에는 무작정 HTTP 프로토콜로 통신을 시도하려고 했었다. 처음엔 되는건가..? 싶었는데 역시 ㅎㅎ

다음과 같은 한계점들이 보였다.

  1. HTTP 요청은 클라이언트가 요청(Request)을 보내야만 서버로부터 응답(Response)을 받을 수 있는 구조로 되어있다. 따라서 클라이언트에서 서버상태가 변경되었는지 알기 위해서는 일정 주기 (t) 간격으로 요청을 보내는 수 밖에 없다. 이걸 알고 있었지만 처음엔 해당 주기를 매우 짧게 가져가면, 실시간 통신을 하고 있는 느낌을 줄 수 있을거라고 생각했었다.

그러나 빠른 업데이트를 위해서 요청보내는 주기(t)를 줄이자니, http는 단발성 통신이기에 header가 매우 무거운 프로토콜중 하나이다. 더군다나 여러개의 방이 동시에 플레이될 수 있고 한방에 최대 50명까지 함께할 수 있다는 것을 고려하면 그 주기 t당 {최대 동시 플레이 방 갯수} {한 방당 최대 인원수} 3(컴포넌트 업데이트용) 개의 요청이 서버에 가서 서버에 매우 무거운 부하를 주게 될 텐데 제대로 동작할지에 대한 확신이 없었다.

예시로, 우리 게임에서는 유저가 정보를 입력하면 대기방으로 안내하는데,
해당 방의 유저 목록을 업데이트 하기 위해서 백엔드로부터 실시간으로 정보를 받아와야한다.

그래서 다음과 같이 useInterval Hook을 사용해서
일정 주기 간격으로 서버에서 response를 받아오는 코드를 작성했다.

const _fetchRoomInfo = () => {
    /*방 인원 정보 받아오기*/

    HTTP.get(`/room/${room_id}/game`)
      .then((res) => {
        if (res.status == 200) {
          setNumberOfUser(res.data.length);
          setUsers(res.data);
          console.log(res.data);
        } else {
          console.log(res.data.detail);
        }
      })
      .catch((err) => {
        console.log(err);
      });
  };

  useInterval(_fetchRoomInfo, 1000); // 1초간격

그러나 역시 기대한만큼의 실시간성이 보장되지 않았다. 부자연스러운 느낌이었다.

결론적으로, 지금 방식으로는 서버가 업데이트 되었는지 확인하려면 클라에서 n초간격으로 request를 계속 보내야 하는데 서버에 매우 짧은 간격으로 요청을 계속 보내는 것은 좋지 못한 방법이라는 생각이 들었다.

다른 멀티플레이 게임들을 찾아보니, 보통 실시간성이 들어간 게임에 HTTP 통신은 적합하지 않고, 양방향 통신이 가능한 웹소켓이나 다른 프로토콜을 이용해서 구현하는 것이 정석인것 같은데 ..

그래서 BE 개발자님께 의견을 말씀드렸다!

그리고 다행히 백엔드 구현할 때 썼던 FastAPI 프레임워크에서 웹소켓 통신도 지원해줘서 쉽게 구현할 수 있을 것 같다는 답변을 받았고, HTTP 프로토콜로 구현되어있던 기존 서버코드를 갈아엎고 며칠만에 새롭게 Websocket 통신을 위한 서버를 구축해주셨다. 👍

어떻게 보면 시간낭비했다고 생각할 수 있겠지만 ..

오히려 실제 http 랑 websocket의 차이점을 몸소 느낄 수 있어서 좋았다!
만약 이러한 시행착오 없이 처음부터 당연하게 websocket으로 구현했으면, 왜 http 통신으로는 구현이 어려운지 제대로 설명할 수 없었을 것 같다.

각 프로젝트의 QOS(Quaility Of Service)를 잘 고려해서 적당한 통신방법(protocol)을 적용하는 것이 중요하다는 걸 배웠다! ㅎㅎ

완성 결과물

해커톤 며칠전에.. 후다닥 배포를 마치고

  • 1.5 : 서버 배포
  • 1.6 : 클라 배포

드디어, 1.7 NDM 연합 해커톤 당일 오리엔테이션 시간에 모든 참여자 분들께서
내 게임을 시연할 수 있는 날이 왔다!


수십명의 사람이 내가 만든 게임을 즐겨준다는 것은 굉장히 뿌듯하고 기쁜일이었다.

그러나 예상외의 난관이 있었는데..
60명이 한 방에 접속해서 플레이하려고 하니 제대로 동작하지 않았던 것이다.

그래서 결국 20명, 20명, 20명씩 방을 나누어 총 3번의 게임을 진행했다.

느낀점

나는 모두가 열심히하고, 칭찬하고 격려하는 분위기가 좋다.
내가 그런사람이고 싶기도 하고 ..ㅎㅎ 이번에 그런 분위기에서 작업 할 수 있어서 정말 좋았다!!!!!!!!

배운점

  • 리액트는 UI 라이브러리라서 실시간으로 동작하는 게임을 만들기에는 적절하지 않은 느낌?
  • 게임만드는데 최적화되어있는 다른 라이브러리를 알아봤을 것 같다.
profile
하루하루 나아가는 새싹 개발자 🌱

0개의 댓글