Skip to content

실시간 통신을 이용한 배틀형 리듬게임 웹 어플리케이션입니다.

Notifications You must be signed in to change notification settings

Team-Orm/the-beat-server

Repository files navigation

🤝 MeetUP

MeetUP 프로젝트는 전 세계의 다양한 지역에서 미팅을 개최하고 참가할 수 있는 Mobile application 입니다.


MeetUP 시연영상

Deployment🏠


📖 Table of Contents

💪 Motivation

이번 프로젝트의 목표는 부트캠프 교육 기간동안 학습하였던 지식들을 react native를 사용하여 프로젝트를 진행해보자! 라는 목표를 가지고 주제를 탐색해 보았습니다.

이러한 목표에 적합한 아이디어를 탐색하다가 미팅을 요청하거나 수락할때 불필요한 커뮤니케이션이 발생하는 것을 줄일 수 있는 프로젝트를 진행해보자 라는 아이디어가 떠올랐고 이 주제에 관한 자료 조사와 계획을 수립하였습니다.

계획한 프로젝트에서는 react native를 처음 사용해 보는것이므로 react native에대한 사전 조사가 필요하였고, react native로 미팅을 요청하고 수락하는 사용자들의 지역이 다르더라도 서로의 시간정보가 유저의 지역시간에 맞도록 미팅을 수락, 요청 할 수 있도록 구현한다면 프론트엔드와 백엔드 에서의 챌린지 요소를 해결해가면서 프로젝트가 완성했을때 프로젝트의 목표가 달성될 수 있을것이라고 판단하여 이프로젝트를 진행하였습니다.

📌 Feature

원하는 미팅 시간 설정

Jun-20-2023 13-56-17

미팅 신청하기 및 푸시알림

Jun-20-2023 14-05-59

요청 대기 중인 미팅 - 수락

Jun-20-2023 14-11-29

요청 대기 중인 미팅 - 거절

Jun-20-2023 14-13-50

거절 대기 중인 미팅 - 미팅취소

Jun-20-2023 14-16-56

거절 대기 중인 미팅 - 재신청

Jun-20-2023 14-22-18

미팅 일정 확인 및 주소 복사

Jun-20-2023 14-25-50


🔥 challenges

프로젝트를 진행하며 여러 Challenges가 있었지만 주요 Challenges는 다음과 같은 요소가 있었습니다.


1. 미팅요청 하는 유저와 수락하는 유저의 지역 정보가 다르다면 시간을 어떻게 표시 해야할까?

미팅요청 하는 유저와 수락하는 유저의 지역 정보가 다르다면 시간을 동적으로 표시하는데 아래의 어려움이 있었습니다.


1) 어떻게 위치기반의 캘린더를 만들수 있을까?


문제: 위치 기반의 동적 캘린더 생성을 어떻게 하지?

이 프로젝트의 목표 중 하나는 위치 기반의 동적인 캘린더를 만드는 것이었습니다. 이를 위해 선택할 수 있는 두 가지 기술적 방법이 있었는데, 바로 JavaScript의 내장 Date 객체와 Google Calendar API였습니다.


대안: 내장 Date 객체 vs Google Calendar API

내장 Date 객체는 간단한 날짜 및 시간 처리 기능을 제공하며, 인터넷 연결이 필요하지 않고 외부 의존성이 없다는 장점이 있습니다. Google Calendar API는 다양한 기능을 제공하며, 이를 통해 캘린더 동기화, 사용자 인증 및 권한 관리가 가능합니다. 또한 지속적인 업데이트를 통한 신기능 도입과 플랫폼 간 호환성이 가능하다는 장점이 있습니다. 시도: 외부 의존성 최소화를 위한 선택

프로젝트에서는 외부 의존성을 최소화하고자 하는 방향을 선택했습니다. 이는 Google Calendar API를 사용할 경우 구글 서비스의 변경사항이나 장애에 영향 받을 수 있기 때문입니다.


결과: 내장 Date 객체의 활용

따라서 위치 기반의 캘린더를 만들기 위해 내장 Date 객체를 활용하였습니다. 이를 통해 외부 서비스의 변경사항이나 장애에 영향 받지 않고 프로젝트를 안정적으로 유지할 수 있었습니다. 또한, 필요한 캘린더 기능은 직접 구현하였고, 캘린더 부분에서는 내장 Date 객체를 사용하여 로컬 시간을 처리하였습니다.


2) 사용자의 지역에 따른 로컬시간이 다르다면 서로의 시간은 어떻게 표시해야 하지?

서로 다른 지역의 사용자들이 일정 시간을 표시 및 확인 할 때, 각 사용자의 지역의 TimeZone에 맞추어 일정 시간을 표시해 하기위한 어려움이 있었습니다.


<<<<<<< HEAD

2-1) 사용자의 지역에 따른 로컬시간이 다른경우 어떻게 시간을 통일화 해서 DB에 저장해야 할까?

=======

Web Audio API 기본 원리


Web Audio API를 통해 오디오를 다루는 원리는 다음과 같습니다.

  1. 먼저 Audio Input을 입력받습니다.
  2. AudioContext 인터페이스를 통해 오디오 관련 작업을 진행합니다.
  3. Destination으로 출력합니다.
image

우선 S3에서 받은 오디오 데이터를 Buffer 데이터로 변환 시키는 것부터 해주어야 했습니다.

image

여기서 Buffer가 무엇인지 알 필요가 있습니다.

Buffer란? RAM에 작은 영역인 Buffer란 이름의 버스 정류장을 만들어 일련의 데이터 스트림이 모이면 (출발 시간이 되면) 처리되기 위해 내보내어 집니다.

Buffer에 Audio Data를 8비트의 정수 배열로 변환 시켜 담아 이걸 AudioContext의 시작 지점인 SoucrNode와 연결 시킵니다.

다음은 AudioBuffer 예시입니다.

image

image

이렇게 연결된 SourceNode와 일련의 작업 노드들의 가공을 통해 Destination(output)으로 출력이 됩니다.


image >>>>>>> 7ed2c43c6cb5b3255d4c2cf37a0f7bc89e3fa9d1

문제: 서로 다른 시간대의 사용자 간 일정, 시간 동기화 문제

다양한 지역에서 사용하는 사용자들이 앱을 사용할 때, 각자의 지역 시간에 따른 일정 표시의 차이로 인해 혼란이 발생할 수 있습니다. 이 때문에 시간을 표준화하여 모든 사용자가 동일한 시점의 일정을 확인할 수 있도록 하는 문제에 직면하게 되었습니다. 이를 해결하기 위해서는 모든 사용자에게 일정 시간을 동일하게 표시하되, 각 사용자의 로컬 시간대를 고려하는 방법이 필요하였습니다.


대안,시도: 시간 정보의 표준화

이 문제를 해결하기 위한 대안으로, 사용자가 미팅을 생성하거나 신청할 때 해당 일정의 시간 정보를 UTC (협정 세계시)로 변환하여 저장하는 방법을 생각해 보았습니다. 이를 통해 다양한 지역에서 사용하는 사용자들의 일정 시간을 표준화하고, 사용자 간의 시간 차이를 고려할 수 있습니다. 이렇게 통일화된 UTC를 DB에 저장해 주었습니다.

const convertToUTCDate = (localDate, hour) => {
  const date = new Date(localDate);
  date.setHours(hour, 0, 0, 0);
  return date.toISOString();
};

위의 함수를 사용하여, 주어진 로컬 날짜와 시간을 UTC 형태의 시간으로 변환하는 함수를 만들어 주었습니다.

그 다음 Task는 나 자신의 점수와 스코어, 콤보를 관리하고 실시간으로 상대방에게 넘겨주는 것을 해결해야 했습니다.

나 자신의 정보를 관리하기 위해서는 하나의 Resource에서 전부 관리해주는 것이 옳다고 판단하였고, 전역 상태 관리 툴인 Redux를 선택하게 되었습니다.

  1. date 객체의 시간을 주어진 hour로 설정하고, 분, 초, 밀리초를 0으로 초기화합니다. 이렇게 하면, 원하는 시간을 가진 새로운 날짜 및 시간 객체가 생성됩니다.

image

  1. 상대방한테 내 정보를 표시하는 것은 Redux를 통해 관리한 내 정보를 Socket을 통해 전송하면 Socket에서는 BattleUser의 정보로 전달 받음.

image

image

  1. GameController에서는 BattleUser의 정보가 props로 있을 경우 BattleUser의 정보를 표시, 아닐 경우 현재 currentCombo 표시!

image

image

image

useLayoutEffect의 적용


이제는 노래가 끝날 때 결과창을 표시해주면 됐습니다. 그러나 결과 값이 원하는 대로 표시되지 않는 이슈가 있었습니다.

처음 해결 방법은 입력된 정보들을 결과창으로 그대로 보내주면 되지 않을까? 라고 단순히 생각하여,

단순히 노래의 길이와 현재 시작한 시간이 같아질 때 저장한 정보를 dispatch하면 되겠다 라는 생각을 했으나,

  1. console.log(콤보)를 확인해 보니 두번 렌더링이 되며 값이 제대로 들어오지 않는 것을 알 수 있었습니다.

image

그 이유에 대해 조사를 해보며 이유를 알 수 있었습니다.

  1. useEffectrequestAnimationFrame을 같이 이용.
  2. requestAnimationFrameRepaint 이전 주어진 콜백을 실행한 후 LayoutPaint를 진행 합니다.
  3. useEffectcleanup이 브라우저의 Paint 이후에 실행되며 requestAnimationFrame을 한 번 더 호출해 값이 초기화되는 것이었습니다.

image

위 이미지처럼 한 프레임 안에서 콜백을 먼저 호출한 뒤 Painting을 진행합니다.

반면, useEffect는 실제로 DOM이 업데이트된 후 동기적으로 실행되는 것이 아니라, 나중에 실행된다는 것이 문제였습니다.


2-2) UTC(협정 세계시)로 저장되어 있는 데이터를 로컬시간으로 변환


그래서 useEffect가 실행되기 전에, requestAnimationFrame이 스케쥴을 선점해 Repaint 할 수 있다는 것이 문제였습니다.

image

  • 이 말은 useEffectcleanup이 나중에 실행되기 때문에, rAF가 한번 더 호출되고 cleanup이 실행되어 값이 원하는 값으로 들어오지 않았습니다.
  • 이를 해결하기 위해 useEffect 외에도 DOM이 업데이트 된 후 동기적으로 실행된다는 점을 제외하면 동일한 방식인 useLayoutEffect를 도입해 해결해보고자 하였습니다.

image

HookReactDOMRefs를 최신화 시킨 뒤 실행이 되지만 차이가 있었습니다.

  • useLayoutEffectReactDOM을 최신화 시킨 뒤 곧 바로 Paint 이전에 실행하고 정리합니다.
  • useEffectReactPaint를 한 직 후 Effect를 실행하고 정리합니다.

이를 적용해 requestAnimationFrame의 호출이 이루어지기 전에 dispatch를 통해 값을 받음으로써 원하는 결과값을 얻어 이슈를 해결하였습니다.



Socket.IO를 더 효율적으로 사용해보기

노트에 대한 많은 정보가 전달될 때 상대의 키값 또한 전달되므로 순간적으로 Latency의 증가가 발견되어 Socket.IO를 통한 실시간 배틀에서의 최적화를 고민하게 되었습니다.

The Beat 리듬게임 프로젝트에서는 Socket.IO을 사용하여 서버와 여러 클라이언트 간의 실시간 통신을 합니다. 따라서 저희는 Socket.IO 통신의 효율성과 성능이 중요하다고 판단했습니다. 그리고 다음과 같은 다양한 방안을 고안하여 Socket.IO 최적화를 하고자 노력하였습니다.


Socket.IO 프레임워크를 사용한 이유


저장된 UTC 시간 정보를 사용자의 로컬 시간대로 변환하여 보여주는 방법을 고려했습니다. UTC 시간 정보를 로컬시간으로 변환해주는 방법은 간단하게 해결되었습니다.

저희는 WebSocket을 단독으로 사용할지 아니면 Socket.IO 프레임워크를 사용할지 고민해봤습니다.

Socket.IO를 선택한 이유
Socket.IO는 양방향 통신을 하기위해 WebSocket 기술을 활용하는 라이브러리입니다.
WebSocket만 사용해도 실시간 양방향 통신을 제공하지만, 아래와 같은 Socket.IO가 제공하는 몇몇 기능들은 기본적으로 제공하지 않습니다.

  • 실시간 이벤트 기반 통신: 음악 게임에서는 사용자의 입력에 따라 실시간으로 게임이 반응해야 합니다. Socket.IO는 이벤트 기반의 통신을 지원하므로, 사용자의 각각의 액션을 이벤트로 취급하고 서버에 실시간으로 전달하는 것이 가능합니다.

  • 자동 재연결 지원: 게임 중 네트워크 상태가 불안정할 경우, 플레이어의 경험을 저해할 수 있습니다. Socket.IO는 연결이 끊어졌을 때 자동으로 재연결을 시도하므로, 사용자의 게임 경험이 중단되는 것을 최소화할 수 있습니다.

  • 네임스페이스와 룸 기능: Socket.IO는 네임스페이스와 룸을 지원하여 다수의 사용자가 동시에 게임을 즐길 수 있도록 만듭니다. 또한, 룸을 사용하면 여러 플레이어가 동일한 게임 세션에 참여할 수 있으며, 서버는 특정 룸의 모든 클라이언트에게 쉽게 메시지를 전송할 수 있습니다

  • 환경 호환성 보장: 모든 웹 브라우저나 네트워크 환경이 WebSocket을 지원하지 않는 경우, Socket.IO의 HTTP Long-Polling fallback기능은 어떠한 환경에서도 실시간 통신을 가능하게 합니다. 따라서, 저희 프로젝트는 어떠한 웹 브라우저에서도 실시간 통신이 가능합니다.


위와 같은 특징이 저희 프로젝트에 필요하여 Socket.IO를 선택하였습니다.

시도: 로컬 시간 변환 함수의 구현


소켓 최적화는 컴퓨터 네트워크에서 소켓 통신의 성능과 효율성을 향상시키는 프로세스를 말하며, 소켓 최적화는 응용 프로그램과 장치 간의 데이터 전송 속도와 안정성을 크게 향상시킬 수 있기 때문에 중요합니다.

  1. 빈 객체 meetingsByDate를 생성합니다. 이 객체는 각 날짜별로 미팅 목록을 저장하기 위한 목적으로 사용됩니다.

  2. 미팅 시작 시간startTime을 기반으로 내로컬 시간에 맞도록 새로운 Date 객체를 생성합니다.

  3. 생성된 meetingDate 객체의 월과 연도가 주어진 monthyear와 일치하는지 확인합니다. 일치하는 경우에만 다음 단계를 진행합니다.

  4. meetingDate 객체의 일day를 가져와 dateKey로 저장합니다.

  5. meetingsByDate 객체에 dateKey에 해당하는 키가 없다면, 빈 배열을 할당하여 초기화합니다.

  6. meetingsByDate 객체의 dateKey에 해당하는 배열에 미팅 추가합니다.


<<<<<<< HEAD 결과: 시간대를 고려한 일정 시간 표시의 성공

우리 프로젝트에서 Socket.IO를 어떻게 최적화 할 수 있을까?


>>>>>>> 7ed2c43c6cb5b3255d4c2cf37a0f7bc89e3fa9d1

이 방법을 통해, 사용자의 지역정보에 따른 시간대가 달라도 로컬 시간에 맞게 일정 시간을 표시할 수 있었습니다. 모든 날짜를 UTC 형태로 저장하고 사용자의 로컬 시간에 맞도록 변환하여, 각 사용자에게 맞는 시간대로 일정을 표시할 수 있었습니다. 이를 통해 서로 다른 지역의 사용자 간에 시간 표시의 혼동을 최소화하고, 사용자에게 정확한 일정 정보를 제공할 수 있었습니다.

스크린샷 2023-06-07 오전 10 08 27

(좌측 한국 TimeZone 우측 브라질 TimeZone)
현재 프로젝트에서 지역에 따른 TimeZone이 다르더라도 자신의 지역정보의 TimeZone으로 변환되어 나타나는것을 확인할 수 있습니다.


3) 미팅이 특정 시간만큼 지나야지만 미팅완료를 할 수 있게 하고싶은데 어떻게 해야하지?


Socket.IO 최적화 적용

대안: 미팅 시작 시간 기준 특정 시간 경과 후에 미팅 완료 버튼 활성화

이 문제를 해결하기 위해, 미팅 시작 시간으로부터 특정 시간이 경과한 경우에만 미팅 완료 버튼을 활성화하는 방법을 생각하였습니다. 하지만, 여기서 또 다른 문제가 발생했습니다. 서로 다른 지역의 사용자들이 있기 때문에, 각 지역의 시간대를 고려하여 이 특정 시간을 계산해야 하는 문제였습니다.


시도: getTimezoneOffset()을 이용한 시간 차이 계산

  • 효율적인 메시지 전송: 최적화된 소켓 구조를 사용하면, 필요한 클라이언트에게만 메시지를 전송할 수 있어 트래픽이 줄어들고 통신이 효율적으로 이루어집니다.
  • 성능 향상: 불필요한 메시지 전송이 줄어들어 클라이언트의 메시지 처리 부하가 감소하고, 전체적인 성능이 향상됩니다.
  • 유지 보수성: 소켓 구조를 최적화하면 아래의 프로젝트에 사용한 코드처럼 구조가 명확해져서 유지 보수와 확장이 쉬워집니다.




🗓 Schedule

프로젝트 기간 : 2023.04.03 ~ 2023.04.23 / 기획 7일 개발 14일

  • 1 주차 : 기획 및 설계
    • 아이디어 수집
    • 기술 스택 선정
    • Figma를 사용한 Mockup 제작
    • MongoDb를 이용한 DB Schema 설계
    • Notion을 이용한 칸반 작성
  • 2주차, 3주차 : 기능 개발
    • 백엔드 서버 구현
    • 프로잭트 기능 구현
    • 테스트 코드
    • 리팩토링 및 버그 수정

🔗 Repository Link


🛠 Tech Stacks

Frontend

  • React
  • Redux
  • React Router
  • Styled Components
  • Web Audio API
  • Canvas API
  • Socket.io
  • ESLint
  • Firebase
  • Netlify

Backend

  • Node.js
  • Express
  • MongoDB
  • Mongoose
  • Socket.io
  • ESLint
  • AWS S3
  • AWS Elastic Beanstalk


✅ Test

  • Frontend: React Testing Library, Jest
  • Backend: Jest, Supertest
  • E2E: Puppeteer


🚀 Deployment



🏠 Members

정영빈 프로필 이상혁 프로필 허수빈 프로필

About

실시간 통신을 이용한 배틀형 리듬게임 웹 어플리케이션입니다.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •