안녕하세요~ 1팀 Ugizz 발표를 시작하겠습니다!

-------

진행 순서는 다음과 같습니다. 소개, 요구사항, 웹소켓, api서버, 배포와 회고, 마지막으로 Q&A입니다.  

-------

팀원 소개 및 역할을 안내하겠습니다.  
클라이언트 및 PM을 담당하며 협업 환경 관리를 진행한 대대장 전상언입니다.
다음은 인프라, 웹소켓, 클라이언트, 아키텍처, ERD 설계를 진행하고 개발 블로그 제작 및 
관리를 맡은 기술대장 이찬우님입니다.
다음은 API server, 스웨거로 API 문서관리를 하고 CI/CD를 진행한 API 대장 양수원님입니다.
다음은 DB, WebLogin Server를 맡은 DB 대장 최현용님입니다.

--------

저희 팀 개발환경입니다.

서버에서는 타입스크립트, 클라이언트에서는 유니티를 활용하였고, 
DB는 MySQL과 Redis를 사용하였습니다. 
프레임워크로는 콜리세우스, Nestjs와 Nextjs입니다.

저희가 여러가지 스킬들을 활용하였는데 JWT, TypeORM, Webview, RESTful API를 
사용하여 완성도를 높였습니다.

--------

협업환경은 다음과 같습니다.

트렐로, 노션, 깃허브, 유니티 플라스틱SCM, 스웨거를 활용하였고 

--------
스웨거의 경우에는 클라이언트와 원활환 소통을 목적으로 사용 하였습니다.

응답데이터의 경우 일관성 유지를 위해 ResponseEntity 클래스를 활용하였습니다.

ResponseEntity는 code, message, data 로 구성되어 동일한 응답 체계를 구축하였습니다.

--------

이제 프로젝트에 대해 설명 드리겠습니다.
크로스 플레이로 캐주얼 멀티 플레이어 술래잡기를 구현하였습니다. 

--------

인프라 소개입니다. 클라이언트에서 오른쪽의 서버 기능들을 각각 이용하게 됩니다.
 
로그인 성공 후 엑세스 토큰을 가지고, 친구나 게임결과 조회에 이용할 수 있고, 
소켓서버에서 게임 룸으로 입장하여 다른 유저들과 게임을 즐길 수 있습니다.
API 서버의 경우, 마이크로서비스 아키텍쳐 디자인패턴을 사용하여, 
부하분산과 확장성이 용이하도록 설계하였습니다.

--------
ci/cd입니다.
저희 프로젝트는 소켓서버 1개, 로그인 서버 1개, api 서버 4개로 총 6개의 서버를 사용하고 있습니다. 
이를 깃허브 메인 브랜치에 push 할때 깃허브 워크 플로우를 이용해 
도커 파일빌드하고 자동으로 배포될수 있도록 하였습니다.

-------
다음은 ERD를 보겠습니다.
유저, 친구요청, 친구, 게임 결과, 역할 테이블로 구성되어있습니다. 
유저 테이블은 유저의 정보와 로그인에 필요한 정보를 저장 합니다.

유저 가입시 전달 받은 패스워드와 SALT를 Hash 하여 password에 저장합니다.
로그인시 전달 받은 패스워드와 SALT를 Hash하여 password를 검증 합니다.

guestid는 게스트 로그인에서 사용되며, 기기의 고유값을 사용하여 저장 합니다.

------

클라이언트는 웹소켓을 통해 서버와 커넥션 합니다.
커넥션을 성공하면, 클라이언트에서 사용될 상태값을 전달하고, 
클라이언트는 이를 통해 기본 셋팅을 하게 됩니다.
그 이후에는 일정 간격으로 상태 값을 패치받습니다. 
클라이언트에서는 서버로부터 받은 상태값을 이용해, 
유저들의 위치 정보 역할에 대해 판별하게 됩니다.

------

웹 소켓 서버의 역할로 저희는 크게 3가지로 구성하였습니다.
방 상태 관리 (로비) - 방 생성,입장,퇴장
시간관리 - 게임이 시작되고, 역할 분배와 시민이 도망갈 시간을 주게됩니다. 
종료조건이 된다면 게임을 종료합니다.
유저 상태 - 좌표, 역할, 세션아이디
실시간으로 클라이언트에게 상태값패치를 해주는 역할을 하게 됩니다.

------

다음은 인게임화면의 일부입니다. 
로비룸에서는 유저 위치, 유저의 컬러, 시작, 호스트 정보 등을 패치하고, 
인게임에서는 이동, 시간, 위치정보, 게임 종료조건 등의 상태값을 패치합니다.

------

API 서버의 구조와 구동 방식에 대해서 살펴보겠습니다.

Auth Server를 예시로 보여드리겠습니다.
게이트웨이에서 요청을 받게 되면 제일 먼저 Controller에서 
알맞은 Service의 함수를 호출하게 됩니다. 
Service에는 JWT 토큰 발급 등과 같은 비지니스 로직을 여기서 처리하게 됩니다. 
마지막으로 repository는 데이터 베이스에 직접적으로 접근하여 데이터를 
삽입, 삭제, 수정, 조회 등 CRUD를 TypeORM을 통해 처리하게 됩니다.

------

클라이언트에서 로그인에 성공하게 되면 Auth 서버에서 access 토큰을 발급하게 되는데 
이때 payload에 user의 pk가 담겨 전송됩니다. 
그 토큰으로 친구 신청이나, 게임결과를 전송하는데 필수적으로 사용됩니다.
클라이언트가 요청하게 된다면 미들웨어인 Passport가 HTTP 헤더에 
Authorization Bearer 토큰을 가져와서 토큰정보가 유효한지 확인하게 됩니다. 
유효하지 않다면 401 오류를 클라이언트에게 전송하게 되고 
만약 정보가 일치한다면 페이로드의 user pk 정보를 Gateway Server을 통해 
Auth Server의 User 정보를 가져오게됩니다. 
최종적으로 친구 신청이나 
게임결과에 해당유저의 정보를 가지고 전송할수 있게 됩니다.

------

클라이언트에서 API를 활용하는 화면 예시입니다.
왼쪽에 보이는 화면은 웹뷰 로그인에서 id, 닉네임은 get으로 요청하고, 
회원가입 및 게스트 로그인은 post로 요청을 보냅니다.
클라이언트 화면은 친구창과 게임 결과창입니다.

------

다음은 저희가 제작한 게임을 즐기실 수 있게 QR을 준비했습니다. 
설치안내부터 설명드리면, 해당 QR로 접속하셔서 안드로이드는 갤럭시24는 불가합니다. 
해당 링크로 접속하셔서 안드로이드 -> apk를 눌러서 설치하시면되고, 
이후 회원가입 또는 게스트로 접속이 가능합니다. 

설치하시는 동안 저희가 겪었던 오류 상황에 대해 간략하게 이야기를 드리겠습니다.

ci/cd : 총 6개의 서버를 사용하고 있습니다. 
이를 깃허브 메인 브랜치에 push 할때 깃허브 워크 플로우를 이용해 
도커 파일빌드하고 자동으로 배포될수 있도록 하였습니다. 
이를 통해 발생하는 여러 오류 상황이 있었습니다. 

그 중 대표적인 오류 해결 상황을 설명드리겠습니다. 
ec2 하드디스크 30GB와 메모리 1GB인 프리티어를 사용하고 있었습니다. 
6개의 서버가 동시에 동작하고 거기에 DB까지 백그라운드에서 
동작하다보니 메모리 용량이 부족해 서버가 꺼져버렸습니다. 
이때 스왑메모리를 이용해 하드디스크의 공간을 메모리 공간처럼 사용할수 있도록 하여 해결하였습니다. 
스왑메모리를 떠올리기 전에는 인스턴스를 추가로 올려서 해결을 할지 고민하였는데, 
스왑메모리를 떠올리고 나니 우리에게 주어진 한정된 자원을 효율적으로 사용할 수 있었습니다.

마지막으로 아쉬운점
게임에서 승리한 유저에게 CW20 토큰 (UTT) 를 지급하는 기능을 구현 하였으나,
단일 토큰 전송에는 성공하였지만, 다중 토큰 전송시 문제가 있어 마무리 하지 못해 아쉬움이 남았습니다.

------
안녕하세요~ 발표를 맡은 OOO입니다. 1팀 Ugizz 발표를 시작하겠습니다!

진행 순서는 다음과 같습니다. 소개, 요구사항, 웹소켓, api서버, 배포와 회고, 마지막으로 Q&A입니다.

팀원 소개 및 역할을 안내하겠습니다.

개발은 서버는 타입스크립트, 클라이언트는 유니티로 제작되었습니다.

저희팀은 협업환경은 트렐로, 노션, 깃허브, 유니티 플라스틱scm, api문서는 swagger를 사용하였습니다.
트렐로의 경우엔 장단점

swagger
클라이언트와 원활한 소통을 위해 스웨거를 사용하였습니다. 
post 요청시 Request Body에 필수적으로 입력되어야할 데이터들의 
타입과 정보를 명시해 두어 클라이언트 개발자가 한눈에 알아볼수 있도록 작성하였습니다. 
Response 데이터 들도 마찬가지로 명시해주었는데요, ResponseEntity 클래스를 
만들어 응답을 주는 데이터들을 균일한 형태로 전송하였습니다. 
statuseCode를 통해 상태코드를 명시하고, 상태코드에 맞는 message를 전송합니다.
그리고 각 응답 객체를 ResponseEntity의 data에 담아 전송하였습니다.

크로스 플랫폼으로 캐주얼 멀티 플레이어 술래잡기를 구현하였습니다.

인프라 소개입니다.
클라이언트에서 오른쪽의 서버 기능들을 각각 이용하게 됩니다.
로그인 성공 후 엑세스 토큰을 가지고, 친구나 게임결과를 이용할 수 있고, 소켓서버에서 게임 룸으로 입장하여
유저들과 게임을 즐길 수 있습니다.

ERD
유저, 친구요청, 친구, 게임 결과, 역할 테이블로 구성되어있습니다.
유저 테이블은 가장 기본이 되는 유저의 정보가 저장됩니다. 
SALT는 패스워드를 HASH화 할 떄 붙여서 사용하는 값이고, GuestID는 게스트 로그인에서 사용될 
디바이스 아이디를 나타냅니다.
친구 테이블은 친구요청테이블에서 친구 수락을 했을 경우, IS_Friend가 True가 되는 친구 리스트를
나타내기 위한 테이블입니다.
친구요청id는 친구신청,요청 테이블에서 참조합니다.

웹소켓
클라이언트에서 웹소켓서버에 연결을 요청하면, 서버로부터 클라에서 사용 될 모든 상태 값을 받아오게 되고, 
그 이후에는 일정 간격으로 상태 값을 패치받습니다. 클라이언트에서는 서버로부터 받은 상태값을 이용해, 
유저들의 위치 정보나 술래인지 여부에 대해 판별하여 로직을 실행할 수 있습니다.

웹서버의 역할입니다.
방 상태 관리, 시간관리, 유저 상태관리를 실시간으로 클라이언트에게 상태값패치를 해주는 부분입니다.

다음은 인게임화면의 일부입니다.
로비룸에서는 유저 위치, 유저의 컬러, 시작, 호스트 정보 등을 패치하고,
인게임에서는 이동, 시간, 위치정보, 게임 종료조건 등의 상태값을 패치합니다.

------
API
마이크로 서비스 아키텍처를 이용해 API별로 별도의 서버를 두어 분산처리를 하여 서버의 부담을 줄였습니다.
또한 게이트 웨이를 거쳐 DB에 접근하는 구조 이다 보니 외부에서 DB에 직접적으로 접근하기 
더 어려워 보안이 더 좋아졌습니다. 이러한 구조는 이전의 모놀로식 아키텍처에 비해 확장성이 
좋기 때문에 서비스의 기능이 추가 될 수록 더욱 빛날수 있을것으로 생각이 듭니다. 

이제 각각의 API 서버 내부에는 어떤 구조를 가지는지 살펴볼텐데요, 
게이트웨이에서 요청을 받게 되면 젤 먼제 Controller에서 알맞은 Service의 함수를 호출하게 됩니다. 
Service에는 JWT 토큰 발급 또는 게임 승리시 제공되는 자체적으로 만든 CW-20 토큰인 
UTT토큰을 전송 하는 등과 같은 비지니스 로직을 여기서 처리하게 됩니다. 
마지막으로 repository는 데이터 베이스에 직접적으로 접근하여 데이터를 
삽입, 삭제, 수정, 조회 등 CRUD를 TypeORM을 통해 처리하게 됩니다.

------
클라이언트에서 로그인에 성공하게 되면 Auth 서버에서 access 토큰을 
발급하게 되는데 이때 payload에 user의 pk가 담겨 전송됩니다. 
그 토큰으로 친구 신청이나, 게임결과를 전송하는데 필수적으로 필요합니다. 
클라이언트가 요청하게 된다면 미들웨어인 Passport가 
HTTP 헤더에 Authorization Bearer 토큰을 가져와서 
토큰정보가 유효한지 확인하게 됩니다. 
유효하지 않다면 Unauthorized 401 오류를 다시 클라이언트에게 
전송하게 되고 만약 정보가 일치한다면 패이로드의 user pk 
정보를 Gateway Server을 통해 Auth Server의 User 정보를 가져오게됩니다. 
최종적으로 친구 신청이나 게임과에 해당유저의 정보를 가지고 전송할수 있게 됩니다.  

------
CI/CD

다음은 저희가 제작한 게임을 즐기실 수 있게 QR을 준비했습니다. 설치안내부터 설명드리면, 해당 QR로
접속하셔서 안드로이드는 갤럭시24는 불가합니다.
해당 링크로 접속하셔서 안드로이드 -> apk를 눌러서 설치하시면되고, 
이후 회원가입 또는 게스트로 접속이 가능합니다.

ci/cd : 저희 프젝트는 소캣서버 1개, 로그인 서버 1개, api 서버 4개 총 6개의 서버를 사용하고 있습니다. 
이를 깃허브 메인 브랜치에 push 할때 깃허브 워크 플로우를 이용해 도커 파일빌드하고 자동으로 배포될수 있도록 
하였습니다. 이를 통해 발생하는 여러 오류 상황이 있었습니다. 

그 중 대표적인 오류 해결 상황을 설명드리겠습니다. 
ec2 하드디스크 30GB와 메모리 1GB인 프리티어를 사용하고 있었습니다. 6개의 서버가 동시에 동작하고 거기에 
DB까지 백그라운드에서 동작하다보니 메모리 용량이 부족해 서버가 꺼져버렸습니다. 
이때 스왑메모리를 이용해 하드디스크의 공간의 메모리 공간처럼 사용할수 있도록 하여 해결하였습니다. 
스왑메모리를 떠올리기 전에는 요금제를 올려서 해결을 할지 고민하였는데, 
스왑메모리를 떠올리고 나니 우리에게 주어진 한정된 자원을 효율적으로 사용할 수 있었습니다.

Swagger

클라이언트와 원활한 소통을 위해 스웨거를 사용하였습니다. post 요청시 Request Body에 필수적으로 입력되어야할 데이터들의 타입과 정보를 명시해 두어 클라이언트 개발자가 한눈에 알아볼수 있도록 작성하였습니다. Response 데이터 들도 마찬가지로 명시해주었는데요, ResponseEntity 클래스를 만들어 응답을 주는 데이터들을 균일한 형태로 전송하였습니다. statuseCode를 통해 상태코드를 명시하고, 상태코드에 맞는 message를 전송합니다. 그리고 각 응답 객체를 ResponseEntity의 data에 담아 전송하였습니다.

API

마이크로 서비스 아키텍처를 이용해 API별로 별도의 서버를 두어 분산처리를 하여 서버의 부담을 줄였습니다. 또한 게이트 웨이를 거쳐 DB에 접근하는 구조 이다 보니 외부에서 DB에 직접적으로 접근하기 더 어려워 보안이 더 좋아졌습니다. 이러한 구조는 이전의 모놀로식 아키텍처에 비해 확장성이 좋기 때문에 서비스의 기능이 추가 될 수록 더욱 빛날수 있을것으로 생각이 듭니다.

이제 각각의 API 서버 내부에는 어떤 구조를 가지는지 살펴볼텐데요, 게이트웨이에서 요청을 받게 되면 젤 먼제 Controller에서 알맞은 Service의 함수를 호출하게 됩니다. Service에는 JWT 토큰 발급 또는 게임 승리시 제공되는 자체적으로 만든 CW-20 토큰인 UTT토큰을 전송 하는 등과 같은 비지니스 로직을 여기서 처리하게 됩니다. 마지막으로 repository는 데이터 베이스에 직접적으로 접근하여 데이터를 삽입, 삭제, 수정, 조회 등 CRUD를 TypeORM을 통해 처리하게 됩니다.

API - JWT 인증 방식

클라이언트에서 로그인에 성공하게 되면 Auth 서버에서 access 토큰을 발급하게 되는데 이때 payload에 user의 pk가 담겨 전송됩니다. 그 토큰으로 친구 신청이나, 게임결과를 전송하는데 필수적으로 필요합니다. 클라이언트가 요청하게 된다면 미들웨어인 Passport가 HTTP 헤더에 Authorization Bearer 토큰을 가져와서 토큰정보가 유효한지 확인하게 됩니다. 유효하지 않다면 Unauthorized 401 오류를 다시 클라이언트에게 전송하게 되고 만약 정보가 일치한다면 패이로드의 user pk 정보를 가지고 Gateway Server을 통해 Auth Server의 User 정보를 가져오게됩니다. 최종적으로 친구 신청이나 게임결과에 해당유저의 정보를 가지고 전송할수 있게 됩니다.

ERD

데이터베이스는 다음과 같이 구성된다. TB_USER 테이블(중앙), TB_FRIEND_REQUEST 테이블 (우측 위), TB_FRIEND 테이블(우측 아래), TB_GAME_RESULT 테이블(좌측 위), TB_ROLE 테이블(좌측 아래)이 있다.

유저테이블은 가장 기본이 되는 테이블로 유저의 정보가 저장된다. USER_ID는 기본키로 설정되어 있는 고유 번호이고, ADDRESS는 유저가 로그인 시 사용하는 아이디이다. SALT는 패스워드를 HASH화 할 때 붙여서 사용하는 값이고 GUEST_ID는 게스트 로그인 시 사용될 디바이스 아이디를 나타낸다.

친구요청 테이블은 친구 요청에 관련된 정보를 담고 있다. FROM_USER와 TO_USER는 각각 친구요청을 보낸 유저와 받는 유저를 나타내고, 이 정보는 유저 테이블에서 참조한다.

친구 테이블은 친구 요청 테이블에서 친구 수락 했을 경우, 즉 IS_FRIEND가 TRUE가 되었을 때 작성이 되는, 친구 리스트를 나타내기 위한 테이블이다. USER_ID와 FRIEND_ID는 각각 조회를 시도하는 유저와 그 유저의 친구 정보를 나타내고 이 유저들의 정보는 유저 테이블을 참조한다. 또한 FRIEND_REQUEST_ID는 친구 요청 테이블을 참조한다.

게임 결과 테이블은 게임이 종료되었을 때 결과에 대한 정보를 저장하는 테이블이다. 게임이 종료되면 유저의 승/패 여부, 게임 종료 시간, 유저의 역할, 방의 세션 정보, 유저의 세션 정보가 이 테이블에 저장된다.

마지막으로 역할 테이블은 게임 내 역할에 대한 정보를 나타낸다.

리프레시 토큰 미개발 질문이 온다면

웹소켓