길고 긴 작업 끝에 드디어 끝났다.
완벽하게 끝낸 건 아니지만 너무 오랜 시간 작업을 했고, 다른 하고 싶은 것들을 계속 못하고 있어서 이 정도로 마무리하기로 했다.
해결할 수 없어 포기한 부분도 있고, 해결하는 데 애먹은 부분들도 있고, 처음 사용해 보는 타입스크립트와 정보가 부족해 공식문서에 최대한 의지한 파이어베이스, PUBG API 그리고 vue3 플러그 인들을 쓰면서 많은 경험을 한 것 같아 만들면서 많은 도움이 된 것 같다.
이 프로젝트를 만든 이유와, 개발환경, 기술 스택, 구현된 화면 캡쳐 및 설명, 배포된 사이트 주소 등의 내용은 아래 깃허브 README에 모두 작성해 두었다.
https://github.com/smw0807/pubg_your.stat
이 글에서는 readme에 담지 못한 내용들을 작성하고자 한다.
1. 프로젝트 구조
프로젝트 파일 구조는 이렇게 되어 있다.
그리고 전체적인 흐름을 표현한다면 이렇다.
vite플러그인인 vite-plugin-vue-layouts를 이용해 nuxt의 layouts 디렉터리 기능을 구현했고, vite-plugin-pages를 이용해 pages디렉터리 안에 디렉터리, 파일을 자동으로 라우트경로를 만들어주는 기능을 사용했다.
특히 다이나믹 라우트를 이용해 소스가 중복되는 파일을 최소화했다.
이 프로젝트에 적용한 예를 들면, 기존에 map 디렉터리 안에 배그의 맵 종류별로 3개의 파일이 있었는데 다이나믹 라우트를 이용해 1개로 처리할 수 있게 했고, teams 디렉터리 안에도 스팀, 카카오 두 종류였는데 1개로 처리할 수 있게 했다.
** 이 기능에 대한건 https://minu0807.tistory.com/152 여기에 작성했다.
화면단(pages)에 각 화면에 필요한 컴포넌트들을 가져와 사용했고, 외부 데이터(파이어베이스, 배그API)가 필요하면 store에 요청을 한다.
store에서는 필요한 외부 데이터를 받아오는 apis에 요청을 하고, 받은 데이터들로 웹에 필요한 데이터를 만들어 준다.
만들어진 데이터는 store에서 화면단으로 반환하는 기능도 있고, state에 저장해서 사용하는 기능들도 있다.
apis안에 구조는 이렇게 되어 있고, 요청하는곳/기능으로 디렉터리를 나눠놨고, 그 안에 화면, 기능별로 파일을 구성했다.
각 파일에는 파이어베이스와 배그API로부터 필요한 데이터들은 받아오는 함수들이 기능별로 들어 있다.
store에서 요청받으면 그에 맞는 데이터를 반환한다.
store를 제외하곤 apis 쪽으로 직접 접근하지 않는다.
apis는 store에서 요청오면 그에 맞는 데이터를 반환한다.
간단하게 표현하면 이런 식이다.
pages -> store -> apis -> [firebase or pubg api] -> apis -> store -> [pages or void]
이렇게 쓰고자 나름의 규칙을 정해서 만들고 있었는데, 채팅과 팀 접속자 표시 기능 구현 중 발생한 문제는 해결 방법을 찾지 못해 api 파일에서 store에 접근해 처리하게 했다.
2. Firestore onSnapshot
파이어베이스 데이터베이스인 파이어스토어에서 제공하는 기능 중 onSnapshot을 사용하면 특정 도큐먼트를 감시할 수 있는데, 감시 중인 데이터가 변경이 되면 콜백을 받을 수 있다.
이 기능을 이용해 채팅 기능과 팀에 참여하거나 나갈 때 변동되는 접속자 표시를 구현했다.
구현하기 전 생각으론 스냅샷이 실행되면 api 파일에서 store에 어떻게 값을 보낼 수 있지 않을까 생각을 하긴 했지만, 막상 만드려고 보니 떠오르는 방법이 없었다.
그래도 뭔가 방법이 있지 않을까 하고 생각나는 것들을 만들어보기도 하고 찾아보기도 했는데 알아내지 못했다.
한번 어떻게 store로 값을 보냈는데 이게 화면단에서 store의 getters를 computed로 연결시켜 놨는 데, 데이터가 변경된 걸 인식하지 못했던 걸로 기억한다.
그래서 이 onSnapshot을 사용하는 딱 2개의 함수에서만 store에 직접 데이터를 조작하도록 했다.
** 위 소스 경로 : https://github.com/smw0807/pubg_your.stat/blob/main/src/apis/firebase/fireStore/TeamRoom.ts
3. PUBG API에서 스탯 API만 사용한 이유
생각보다 API의 종류가 많지 않았다.
핵심으로 보이는 API는 매치 정보 조회와 플레이어 스탯 조회인 것 같았고 그 외에 몇몇은 뭘 의미하는지 잘 모르겠었다.
매치 정보의 경우 문서를 봐보니 처음엔 매치 ID들만 받은 후에 각각 또 따로 요청해서 세부 정보를 받아오는 등 많은 과정을 거쳐야 했다.
만든다면 많은 시간을 투자해야 할 것 같기도 했고 이미 유명한 두 개의 사이트가 있어서 손대지 않았다.
스탯의 경우는 생각보다 제공해 주는 데이터가 많았다.
두 사이트에서는 정말 딱 핵심 스탯만 보여주고 있다.
랭크 스탯의 경우는 API에서 제공해 주는 데이터의 대부분을 보여주는데, 일반 스탯의 경우 제공해 주는 데이터의 종류가 정말 많았다.
그래서 난 이걸 보여주기로 했다.
다만, 이거 하나만 하자니 너무 허전해서 고민을 하다가....
배틀그라운드 공식 디스코드 채널에서 같이 게임할 팀원을 구하는데 불편함을 느낀 게 있어서 팀 구하기라는 기능을 생각해서 추가로 만들기로 했고, 이걸 구현하는 건 써보고 싶던 파이어베이스를 쓰기로 했다.
4. PUBG API KEY의 분당 요청 수 제한
펍지API를 사용하려면 먼저 키를 발급받아야 한다.
처음 받으면 무료 키를 제공해 주는데, 이건 분당 요청 수 제한이 있다.(분당 10회로 기억)
유료 신청을 하면 되긴 하는데 무슨 약관을 읽어보고 뭐하고 하라는데 다 영어에 읽어보라는 걸 내가 찾질 못해서 포기했다...
서비스까진 생각하진 않았어서 그냥 무료로 만족하기로 했다.
매치 정보 조회는 요청 수 제한이 없다는 문구를 본 것 같긴 하다.
무료 키를 사용할 때 1분 안에 요청이 10회가 초과하면 429 에러 코드를 받고 1분을 기다리고 다시 요청을 해야 정상적인 결과를 받을 수 있다.
근데 이게 막상 하다 보니 문제가 된 게 플레이어 1명의 스탯 정보를 가져오는데 여러 번의 요청을 보내야 한다.
난 카카오에서 플레이하는 유저의 스탯을 가져오고 싶다면 그냥 플랫폼으로 '카카오' 그리고 '닉네임'이 정도만 있으면 되지 않을까? 했는데 아니다.
한 명의 스탯 정보를 가져오는 과정을 설명하면 이렇다.
1. 플랫폼 값을 이용해 시즌 별 ID들이 담긴 값을 받는다. (여기서 조회할 시즌의 ID를 찾아서 써야함)
2. 플랫폼 값과 닉네임으로 해당 유저의 고유 ID 값을 구한다.
3. 위 1번과 2번에서 구한 시즌ID 값과 유저ID 값을 이용해 랭크 스탯을 받는다.
4. 위 1번과 2번에서 구한 시즌ID 값과 유저ID 값을 이용해 일반 스탯을 받는다.
이렇게 한 명의 스탯 정보를 가져오는데 4개의 요청을 써버린다.
1분 동안 딱 2명만 검색할 수 있고 세 번째부터는 에러를 받는다.
저걸 보고 다시 문서들은 전체적으로 한번 훑어봤는데 잘게 잘게 많이 쪼개져있는 느낌을 받았다.
** 위 소스들 경로 : https://github.com/smw0807/pubg_your.stat/tree/main/src/apis/pubg
심지어 결과를 받는 시간도 생각보다 약간의 딜레이가 있다.
이렇다 보니 검색하고 결과받는 시간도 있고 요청 수 제한 때문에 짧은 시간에 여러 명 검색도 힘들다 보니 고민하고 만든 게 파이어베이스에 검색 결과의 일부를 저장해서 사용하자 였다.
플랫폼과 닉네임을 넣고 검색을 하면 먼저 파이어베이스에 있는지 확인을 하고, 없으면 PUBG API에 요청을 해서 데이터를 받아온 후 파이어베이스에 넣는다.
그리고 저장된 파이어베이스 데이터를 가져와서 화면에 보여주도록 했다.
검색해서 파이어베이스에 있으면 그 데이터를 가져와 화면에 보여준다.
갱신 요청을 하면 다시없을 경우의 로직을 타게 된다.
5. [해결 못한 문제] 일반 스탯만 제공하지 않는 데스 수?
해결할 수 없는 문제가 더 맞는 것 같다.
공식문서에서 일반 스탯 모델 정보 중 일부이다.
이것만 보면 당연히 데이터를 주는 걸로 판단되는데 결과를 받아보면 데스 값은 무조건 0만 온다.
처음 배그API를 사용해 볼 때 시즌ID를 이상한 걸 넣고서 계속 안 온다고 시간 날렸던 게 생각나서 또 내가 잘못한 게 아닌가 하고 계속 봤다.
내가 오타를 낸 건가... 하고 인터페이스도 보고 타입 빼고 결과만 받아봤는데 다 0으로 넘어왔다.
도저히 모르겠어서 실제 서비스 중인 사이트에 가서 개발자도구를 열어서 찾아봤는데 넘어오는 필드 값이 달랐다.
아마 백엔드에서 별도로 가공해서 웹으로 줘서 그런 게 아닌가 싶었다.
너무 궁금해서 고민하다 한 개의 사이트에 문의 메일을 작성해 보냈다.
내가 처한 상황과 사용하는 공식API 문서 주소를 넣고 이걸 쓰는 게 맞는지 알려달라고 했는데
사용하는 건 맞는데 회사 세부 정책상 자세한 답변을 주기 어렵다는 답장을 받았다.
너무 깊게 물어보는 것도 예의는 아닌 것 같아서 같은 API가 맞는지만 확인 후 감사인사를 했다.
유료 키를 받아서 하면 뭔가 더 있는 건지 알 수가 없었고... 그럼 내가 그냥 별도로 계산하면 되지 않을까? 생각했지만 안된다는 결론이 나와서 그냥 0으로 화면에 표시하기로 했다.
솔로의 경우는 총 매치 판수에서 치킨 먹은 판을 빼면 데스가 나온다고 생각하면 되긴 한다.
하지만 문제는 듀오와 스쿼드다.
솔로처럼 총 매치 판수에서 치킨 먹은 판을 뺄 수가 없는 게, 난 죽고 팀원들이 치킨을 먹어주는 판만 생각해도 정상적인 값이 나오지 않는다. 특히 복귀전이 있는 태이고의 경우 한 게임에서 한 번 죽고 복귀전에서 살아나서 또 죽는... 한 게임에서 2데스가 가능하다 보니 아무리 머리를 굴려봐도 답이 안 나와 포기했다.
사실상 이 데스를 이용해 kda, kd를 구해서 보여줘야 하고 이 정보가 핵심 정보 중 하나인데 이걸 못 보여주는 게 너무 큰 아쉬움이었다.
랭크는 kda 데이터 자체를 주고, 데스도 줘서 내가 따로 계산을 해서 kd도 보여줬다.
6. 파이어베이스 구글 로그인
파이어베이스를 이용하면 구글, 페이스북, 트위터 등 다양한 플랫폼의 계정을 이용한 로그인 기능을 구현할 수 있다.
공식문서에 따르면 OAuth 2.0 및 OpenID Connect 등의 산업 표준을 활용하므로 커스텀 백엔드와 쉽게 통합할 수 있다고 작성되어 있다.
구현하려면 각 플랫폼 별로 로직을 만들어 줘야 해서 가장 사용을 많이 하는 구글만 이용했다.
구현하는 방법 또한 그렇게 어렵진 않았다.
만든 후 기능을 활성화하면 팝업이나 창을 띄워서 로그인할 구글 계정을 입력하거나 선택하면 로그인이 끝난다.
성공적으로 로그인이 되면 아래와 같은 결과 값들을 받게 되는데
난 여기서 user를 store에 저장해 두고 구글 이메일 주소, 계정 이미지 경로를 이용한 이미지 아바타 표시 정도만 사용했다.
토큰 정보들도 주긴 하는데 난 이걸 어떻게 써야 하는지 모르겠다...
처음엔 이 토큰 정보들을 쿠키나 로컬스토리지에 넣고 로그인을 유지하는 데 써야 하는 건가? 생각했다.
이 값들을 별도로 저장하지 않은 상태에서 로그인 중인 유저 정보를 가져오는 함수를 실행시켜 봤는데 사용자 정보를 가져온다.
로그인이 성공되면 로그인 한 브라우저 어딘가에 값이 저장된다는 걸 어디서 봤는데 어느 블로그였는지, 공식문서였는지 기억이 안 난다.
기본적으로 local이 default 값으로 되어 있고 난 그대로 사용했다.
다만 약간의 문제가 좀 있다.
로그인을 한 상태에서 브라우저를 새로고침을 했을 때 다시 유저 정보를 가져오는 함수이다.
문제는 이게 가져오는데 약간의 텀이 있다.
그래서 찾아보니 원래 이렇다는 것 같고.... 보통은 로딩을 보여주고 받아와지면 로딩을 해제하는 방식으로 처리를 하는 것 같았다.
난 화면 디자인 상 로딩을 넣을 수가 없어서 화면에서 hide 했다가 show 하는 방식으로 처리했다.
** 위 소스 경로 : https://github.com/smw0807/pubg_your.stat/blob/main/src/apis/firebase/auth/GoogleAuth.ts
** 위 소스를 쓰는 store 경로 : https://github.com/smw0807/pubg_your.stat/blob/main/src/store/User.ts
7. 클라우드타입을 이용한 배포
만든 웹 배포는 클라우드타입을 이용해 배포를 했다.
처음엔 이 프로젝트를 시작했을 땐 네트리파이를 생각하고 있었다.
그러던 중에 여기저기 웹서핑 다니다 알게 된 사이트인데 한글이기도 하고.... 생각보다 지원하는 게 많은 것 같아 완성되면 한 번 사용해 보기로 하고 링크를 따로 저장해 놨다가 사용했다.
큰 문제없이 정상적으로 될 거라 생각했었는데 예상하지 못한 문제를 좀 겪었다.
로컬에서 빌드 후 실행 했을 땐 문제가 없었는데 저런 에러가 계속 떠서 뭘까하고 찾아봤는데 정확한 정보를 찾을 수 없었다.
계속 고민하면서 파일명들을 보다가 생각난 게 처음 파일명 규칙을 카멜케이스로 만들고 커밋했다가 나중에 파스칼케이스 형식으로 바꿔서 커밋을 했던 게 생각이 났었다.
그래서 소스들을 따로 백업해 두고 파일을 삭제해서 커밋하고 다시 파스칼케이스 형식으로 만들고 커밋을 후 다시 배포해보니 ~~ only in casing. 에러는 해결이 됐다.
따로 저장을 안 해두긴 했는데 위 사진에 있는 RankStat.vue 말고도 여러 파일들이 떠서 다 삭제 후 커밋 생성 후 커밋으로 처리했다.
그리고 import 하는 부분에서도 파스칼로 바꾸지 않은 부분들도 있어서 수정했다.
declarations 에러는 처음부터 파스칼케이스로 만든 거라 파악하는 데 또 시간이 걸렸었다.
이건 또 도대체 뭘까 하고 고민을 하다 파일 이름이 자바스크립트 예약어, 내장객체와 중복돼서 그런가? 하고 바꿨더니 됐다...
Pinia의 경우는 설치된 노드패키지의 이름이 pinia와 중복이 돼서 뜬 것 같다.
이 외에도 로컬 빌드 후 실행 땐 에러가 없다가 에러 뜨는 게 좀 많았어서 당황스러웠었다.
8. 타입스크립트
예전부터 설치한 노드 패키지들을 사용하기 위해 소스를 둘러볼 때가 많았다.
패키지의 깃허브나 공식문서에서 사용할 수 있는 함수 같은 것들이 다 작성 안되어 있는 경우가 많아서 더 알아보기 위해 둘러볼 때가 좀 있다.
요즘 대부분 타입스크립트로 작성되어 있는 것들이 많아서 타입스크립트를 몰랐을 때는 파악하는 게 너무 어려웠었다.
타입스크립트를 프로젝트에 제대로 사용해보는 건 이번이 처음이었다.
퇴사 후 책을 통해 기초적인 부분은 어느정도 공부 했었고, 부족하거나 더 궁금했던 내용들은 여러 블로그의 글을 통해 공부를 했었다.
하지만, 막상 또 내가 생각한 것들을 만드려고 하니 어려움이 많았다.
기능 하나 만드는 데도 시간이 많이 걸렸고, 이유를 알 수 없는 무수한 에러와... 조금만 어긋나면 밑줄이 그어지고...돼야하는게 맞는 것 같은데 안되기도 하고...
특히, 대부분의 주요 기능들이 파이어베이스를 사용했고 구현을 위해 참고한 공식 문서에는 타입스크립트의 예시가 없었다.
그래서 기능을 구현할 때마다 사용하는 함수의 패키지가 설치된 위치로 들어가서 어떤 타입의 파라미터를 받아야하는지, 어떤 타입을 리턴하는지를 알아보고 이해하는 데 많은 시간을 사용했다.
덕분에 타입스크립트를 어떤 식으로 사용하는지 감을 잡는 데 많은 도움이 됐다.
만들 때 더 생각을 할 수 있었던 것 같았고, 일단 쓰다 보니 쓰는 게 좋은 것 같다는 생각이 들긴 했다.
그래도 아직도 사용한 패키지들 타입스크립트 소스를 보면서 이해안되는 게 많은 걸 보면 아직 내가 타입스크립트에 대해 부족한게 많다고 느껴진다.
9. 마치며
9월부터 백수가 된 상태에서 첫 레파지토리 생성 후 기초 틀만 잡아 놓고선 명확한 방향이 안 나와서 방치해 놨었고, 11월쯤 어느 정도 방향이 나와 시작을 한 것 같다.
그러다 12월에 취업을 해서 제대로 집중을 못해서 규모는 크지 않은 데 생각보다 작업이 오래 걸렸다.
처음 생각은 퇴근 후와 주말에 좀 하면 1~2월에는 끝낼 수 있지 않을까 했지만 3월 중순이 넘어서 끝이 났다.
만들면서 자꾸 생각이 바뀌어서 로직을 크게 바꾼 상황도 많았고, 만들다 보니 "아 이것도 추가하면 좋을 것 같은데"하고 추가도 하고 하니 더 작업이 길어지긴 했다.
작업을 하다보니 욕심이 생겨 뭔가 더 추가하고 싶다는 생각도 들고, 수정하고 싶은 것들도 자꾸 하나씩 늘어났다.
특히 팀 채팅 쪽 구현 중에 문득 든 생각이 어차피 팀은 여기서 구하되 실제 게임을 할 때는 디스코드를 하는데 디스코드API까지 조합해보고 싶은 생각도 들긴 했는데 이건 생각에서 멈췄다.
나중에 시간적 여유가 있고 디스코드 API를 써보고 싶은 마음이 들때 이 프로젝트에 한번 써봐야겠다.
어느정도 생각했던 메인 기능들은 다 구현을 했고, 계속 하다간 시간을 너무 많이 쓸 것 같아 이만 멈추기로 했다.
공부하려고 사놓은 책들과 이 프로젝트를 끝내는 데 집중 한다고 읽다 멈춘 책들에 먼지가 쌓이기 시작해서....
아무튼 이 프로젝트를 하면서 여러모로 큰 도움이 된 것 같다 생각한다.
아쉬운 게 있다면.... 디자인이 좀 만족스럽지 못한 것과 배그 API에서 일반 스탯은 데스를 안 줘서 중요한 스탯을 보여주지 못한 거...?
특히 디자인이 아쉬운데 늘 느끼는 거지만 디자인이 너무 어렵다.
팀 구하기에서 팀 채팅 방 구조는 만들고 나서 보니 작년에 nuxt로 만들었던 채팅방과 아주 유사했다.
그리고 다음부터는 블로그에 쓸 생각하고 작업하는 프로젝트는 쓸 내용들을 미리 좀 작성하면서 만들어야 할 것 같다.
작업하면서 블로그에 써야 겠다 생각한 것들이 많았는데 생각이 나는게 너무 없어서 불필요한 내용들을 너무 많이 쓴 것 같은 것도 아쉽다.
끝.
'ETC' 카테고리의 다른 글
디스코드 봇을 만들기 위한 애플리케이션 생성 (2) | 2024.03.23 |
---|---|
2022년을 마치며 (4) | 2022.12.31 |
NHN FORWARD 22 후기 (2) | 2022.11.24 |
PUBG API 사용기 (0) | 2022.11.07 |
주절주절... (2) | 2022.10.18 |