1️⃣ 서비스 소개
2️⃣ 서비스 화면
3️⃣ 팀원 소개 및 역할
4️⃣ 개발 환경
5️⃣ 활용 기술
6️⃣ 프로젝트 산출물
7️⃣ 개발 회고
온라인 화상 블러핑 카드 게임, Secret Zoo.
화제의 보드게임, "바퀴벌레 포커"를 온라인으로 즐길 수 있다는 것을 알고 계신가요? 지금 당장 Secret Zoo에서 친구들과 플레이 해보세요!!
- 시간 / 공간적 제약으로 인해 오프라인으로 지인들과 함께 보드게임을 즐길 수 없는 사람들
- 아이스브레이킹과 함께 사람들끼리 친해지기 위한 방법이 필요한 사람들 (ex. 대학교 OT, 싸피데이, 심심풀이 게임)
- 웃고 떠들며 재밌게 게임하고 싶은 사람들
- 라이트하게 게임을 즐기고 싶은 모든 분들
- 화상 카드 게임 서비스
- WebRTC와 카드 게임을 결합하여 실제 만난 것과 같이 게임을 플레이 할 수 있습니다.
- 게임 랭킹 서비스
- 플레이어들을 완벽하게 속여 공격, 수비, 패스별 랭커에 도전할 수 있습니다.
- 업적 서비스
- 게임 플레이를 통해 동물 별로 있는 숨겨진 다양한 업적을 수집해볼 수 있습니다.
- 회원가입 / 회원탈퇴 ✅
- 로그인 / 로그아웃 ✅
- 게임 점수 실시간 랭킹 ✅
- 게임 플레이 ✅
- 게임 업적 서비스 ✅
- 내 정보 조회 ✅
- WebRTC 활용한 실시간 화상 화면 ✅
- 소셜 로그인( 구글 / 네이버 / 카카오) ✅
- 유저 검색 및 조회 ✅
- 비회원 유저 게임 플레이✅
- 실시간 채팅 ✅
- 배경음악, 효과음 ✅
- 중복 로그인 방지 ✅
- 게임 후 벌칙❌
- 화면 낙서 및 음성 변조 ❌
- 친구 및 메신저 기능❌
- 구글, 네이버, 카카오 계정으로 회원가입, 로그인을 할 수 있습니다.
- 방 대기 화면
![](README_IMG/image.png)
- 게임 시작
-
공격
- 공격 성공
- 공격 실패
-
수비
- 수비 성공
- 수비 실패
-
패스
-
게임 진행 중 다른 유저들에게 보이는 화면
-
게임 결과 화면
- 회원 정보, 랭킹, 업적을 확인할 수 있습니다.
- 공격, 수비, 패스별로 실시간 랭킹을 확인할 수 있습니다.
- 유저 닉네임, 아이디로 전적 검색을 할 수 있습니다.
- 서버 점검시 미니 게임을 할 수 있습니다.
전형욱👑 |
김관우👨🏻 |
김중광👨🏻 |
조현수👨🏻 |
김재윤👨🏻 |
0️⃣ Front-end(팀장) 1️⃣ Web-RTC(Openvidu) 전담 2️⃣ 게임 플레이 화면 구현 |
0️⃣ Front-end 리더 1️⃣ 게임 플레이 구현 및 UI 2️⃣ 소켓 서버 개발 3️⃣ 서기 담당 |
0️⃣ Front-end 1️⃣ UI/UX 담당 2️⃣ 메인 페이지 구현 |
0️⃣ Back-end 1️⃣ 인프라 담당 2️⃣ 게임 Ranking API 개발, QA 3️⃣ 플레이어 Rewards API 개발 |
0️⃣ Back-end 1️⃣ Spring Security 담당 2️⃣ 유저 API 개발, QA 3️⃣ 소셜 로그인(구글, 네이버, 카카오) |
react 18.2.0,
redux 5.0.1
redux-persist 6.0.0
flowbite-react 0.7.2
tailwindcss 3.4.1
socket.io 4.7.4
openvidu-browser 2.29.1
spring boot 3.2.1
spring-boot-starter-data-jpa
spring security 6.2.1
lombok 1.18.30
azul-17
mysql 8.2
redis 7.0.4
swagger3
gradle
docker 25.0.1
docker-compose 2.21.0
jenkins LTS 2.401.1
WebRTC 에서 제공해주는 것이 가장 많은 openvidu 를 선택하여 n:m 통신을 진행했습니다. openvidu에서 client server(React)를 저희 코드에 맞게 바꾸어 사용하였고 signaling server(백엔드 session 생성 및 token 생성 요청)를 사용해 Media-server로 요청을 보냈습니다. 저희 서비스에서 방 생성을 할때 입력한 방 제목을 sessionid 로 요청을 보냈는데 한글로 sessionid를 보내면 오류가 나기에 방 제목을 인코딩 시켜 session 생성 요청을 보낼 수 있도록 하였습니다. 입장한 자신은 publisher 가 되고 나를 제외한 사람들은 subscriber가 되어 화상 통신을 하게됩니다.
서비스 회원가입, 로그인을 Spring Security와 JWT를 활용하여 구현하였습니다. Spring Security Oauth2.0을 통해 소셜 로그인(구글/네이버/카카오)을 백엔드에서 구현하였고, JWT를 사용하기 위해 Security Stateless 세션 정책을 사용하였습니다. 그러다보니 중복 로그인을 직접 막아야하는 문제가 발생하였습니다. 해당 서비스가 게임이라는 특성상 중복 로그인 방지는 필수 요소이므로 저희는 JWT 토큰을 통해 중복 로그인을 방지하였습니다. 프론트엔드에서는 세션 스토리지를 사용하였고, 백엔드에서는 Redis를 이용해 엑세스 토큰 블랙리스트를 생성하여 비정상적인 접근의 엑세스 토큰을 저장하여 관리하였습니다.
Socket.io 를 이용하여 웹 소켓 서버를 생성하여 클라이언트의 플레이 정보와 진행 상황 등을 관리하고 게임 플레이의 로직들을 전반적으로 담당하였습니다. Node.js, Express 서버를 통하여 구현하였으며, 클라이언트 방 접속 시 유저로부터 정보를 받아 미리 만들어둔 자료구조에 저장하며 클라이언트에서는 정보 변경 없이 웹 소켓 서버로 부터 정보를 받아 보여주는 형식을 취해 게임의 정보에 대한 변조를 방지했습니다. 각각의 소켓 서버 메시지를 구성하여 게임 플레이 진행에 알맞는 메시지를 클라이언트와 주고 받아 게임의 진행 도중 새로고침을 하여도 바로 플레이에 다시 참가할 수 있습니다.
redux toolkit 을 사용하여 유저의 정보를 관리하고 토큰을 포함하는 api 요청을 관리 하였습니다. 유저 정보를 여러 컴포넌트에서 사용하고 있었기 때문에 상태 업데이트의 동기화를 위해 redux 를 통한 관리가 필요했습니다. 그래서 리덕스를 통해 유저정보를 관리하여 컴포넌트별 유저 정보의 일관성을 유지하였습니다.헤더에 jwt 토큰을 담아야하는 요청들이 많았고 그러한 요청들은 토큰의 만료시간에 따라 재발급 받거나, 중복로그인을 사전에 확인 하는 작업이 필요했습니다.jwt토큰을 요하는 요청들을 redux에서 관리하고 axios 인터셉터를 통해 간소화 하였습니다.
플레이어의 점수를 기반으로 실시간 랭킹을 보여주기 위해서 Redis를 사용했습니다. 처음에는 MySQL로만 관리를 했었는데, Select 쿼리의 Order By를 통해 순위를 가져올 수 있지만, 플레이어의 수가 많아질 수록해당 쿼리의 성능 저하가 우려됐습니다. 예를 들어 유저가 100만명일 경우 실시간으로 순위를 가져오려고 한다면 전체 튜플을 계속 Order By로 정렬해서 select 쿼리를 날려야 하기 때문에 좋은 방법이 아니라고 생각했고, 이를 해결 하기 위해서, Key-value 형태로 저장할 수 있는 NoSQL을 생각했고, Redis의 경우 Log(N)의 속도로 데이터를 삽입하면서 바로 정렬할 수 있는 실시간 정렬에 적합한 SortedSet 자료구조가 있었습니다. 따라서 해당 자료구조를 활용해서 Redis로 실시간 랭킹 점수를 관리하는 방법으로 랭킹 서비스의 성능을 최적화했습니다.
![](README_IMG/architecture.png)
![](README_IMG/erd.png)
![](README_IMG/doc2.png)
![](README_IMG/doc3.png)
[커밋 타입]: [작업내용]
❕feat : 새로운 기능 추가
❕fix : 버그 수정
❕docs : 문서 수정
❕style : 코드 포맷팅, 세미콜론 누락, 코드 변경이 없는 경우
❕refactor : 코드 리펙토링
❕test : 테스트 코드, 리펙토링 테스트 코드 추가
❕chore : 빌드 업무 수정, 패키지 매니저 수정
❕comment : 필요한 주석 추가 했을 경우
❕design : css나 디자인 변경, 이미지 추가 등
# 제목
[S10P12A406-1] 기능 이름
## 개요
<!---- 변경 사항 및 관련 이슈에 대해 간단하게 작성해주세요. 어떻게보다 무엇을 왜 수정했는지 설명해주세요. -->
<!---- Resolves: #(Isuue Number) -->
## PR 유형
어떤 변경 사항이 있나요?
- [x] 새로운 기능 추가
- [ ] 버그 수정
- [ ] CSS 등 사용자 UI 디자인 변경
- [ ] 코드에 영향을 주지 않는 변경사항(오타 수정, 탭 사이즈 변경, 변수명 변경)
- [ ] 코드 리팩토링
- [ ] 주석 추가 및 수정
- [ ] 문서 수정
- [ ] 테스트 추가, 테스트 리팩토링
- [ ] 빌드 부분 혹은 패키지 매니저 수정
- [ ] 파일 혹은 폴더명 수정
- [ ] 파일 혹은 폴더 삭제
## PR Checklist
PR이 다음 요구 사항을 충족하는지 확인하세요.
- [ ] 커밋 메시지 컨벤션에 맞게 작성했습니다. Commit message convention 참고 (Ctrl + 클릭하세요.)
- [ ] 변경 사항에 대한 테스트를 했습니다.(버그 수정/기능에 대한 테스트).
- 이번 저희 팀의 주제는 webrtc 였는데 이 부분이 생전 처음 들어보는 기술이었기에 걱정이 많았습니다. 그런 걱정을 뒤로하고 제가 이 기술을 담당하기로 하였고 해당 기술을 이해하는데 상당한 시간이 소요되었습니다. WebRTC 기능을 제공해주는 openvidu 를 알게되었고 어떻게 화상 통신을 하는지 이해하게 되었습니다. 저희 팀 6명의 영상이 server에 올라가서 모두 연결되는데 까지 시간도 매우 오래 걸렸지만 성공했을때는 정말 짜릿했습니다. 저는 WebRTC가 back-end에서 할일이 많다고 생각했는데 알고보니 대부분의 필요한 작업은 front-end 였다고 생각합니다. 그래서 저는 의도와 다르게 front-end 에서 react를 다뤘는데 생각보다 재밌고 더 해보고 싶다는 생각이 들었습니다. 이번에 팀장을 맡았지만 팀장으로써 역할이 많이 부족했다고 생각하고 앞으로 팀장이 어떤 역할을 하면 좋을지 더 느끼게 되었습니다.
- 인프라 담당 예정이었던 팀원의 갑작스러운 부재로, 인프라를 담당하게 되었는데 인프라 경험이 없었기 때문에 처음에 어려움을 많이 겪었지만, 처음 부터 지식을 차곡차곡 쌓아가는 것이 재밌었습니다. 아직 완벽한 인프라 구성을 했다고 말할 순 없지만, 컨설턴트님께서 조언해주신 "성에는 대문을 하나만 열어두어라"는 말씀을 마음에 새기며 우리의 EC2 라는 큰 성에 외부 접근을 한 곳으로만 열어두는 방향으로 인프라를 구축하는 데 힘썼습니다. SSL 인증서 적용, Nginx 리버스 프록시, Jenkins와 Gitlab을 이용한 CI/CD 구축 그리고 도커를 이용한 컨테이너 기술의 활용 등 새롭게 알게된 지식들이 많은 것 같아 얻어가는 것이 많은 프로젝트였다고 생각합니다. 또한, 평소에 경험해보지 못했던, 랭킹과 플레이어 업적 서비스 등을 개발하면서 고민할 부분도 많았고, 해당 고민거리를 해결해 가는 과정도 매우 재밌었습니다. 실시간 랭킹을 위한 레디스 DB 활용, 동점자 처리를 위한 우선순위큐 자료구조의 활용 등 새로운 고민과 배움을 즐길 수 있었습니다. 팀원들이 각자 맡은 부분을 최선을 다해서 열심히 해주어서 고마웠으며, 또 도움이 필요할 때면 업무를 분담해서 같이 해결해나가는 과정 덕분에 더욱 완성도 있는 프로젝트를 만들 수 있었던 것 같습니다. 주제가 게임인 만큼, 프로젝트를 만들어 가는 과정이 재밌었고, 더 몰입해서 즐겁게 할 수 있었다고 생각합니다. 이번에 구현하지 못한 기능들이 아쉽지만, 이런 아쉬움들을 잘 새겨서 다음에 더 완성도 있는 프로젝트를 만드는 밑거름으로 활용하고 싶습니다.
- Spring Security, Redis등 전에 써보지 못한 기술을 써보며 한 걸음 성장할 수 있는 기회였습니다. 인증, 인가 과정에 대해 많이 배우게 되었고, 웹 서비스 배포 과정과 아키텍쳐에 대해 알게 되어 개발 경험치를 많이 쌓을 수 있었습니다. 또한, git과 jira를 통해 효율적인 일정 관리와 원활한 협업을 할 수 있어서 좋았습니다. 프로젝트 기간동안 팀원들과 힘든 것보다 매일 매일 웃으며 즐겁게 코딩했던 것 같습니다. 팀원들 모두 너무 고생했고 감사하다는 말을 전하고 싶습니다.
- socket.io 를 이용한 서버와 클라이언트를 동시에 개발하면서 많은 지식과 성장을 얻을 수 있었습니다. 복잡한 로직을 개발하며 계획적이고 체계적인 구조가 먼저 잡혀있어야 반복하여 코딩하는 일 없이 순탄하게 개발을 할 수 있다라는 점을 절실히 알게 되는 프로젝트였습니다. 상태유지를 위해 redux를 사용하며 상태를 저장하고 가져오는 과정에서의 동기화 문제가 많이 발생하여 이에 많은 어려움을 겪기도 하였습니다. 이를 해결하기 위해 상태유지변수를 사용하는 페이지에서는 한번 더 변수를 저장하여 많은 오류를 없앨 수 있었습니다. 이번 프로젝트는 JIRA와 Gitlab을 이용하여 더 체계적인 협업을 통해 가치 있는 경험을 얻을 수 있었습니다. 또 좋은 팀원들을 만나 제대로된 협업의 장점을 몸소 느낄 수 있었습니다. 다른 팀원 분들에게도 여러모로 배울 점이 많았기에 제 자신의 실력 향상 뿐만 아니라 가치관에도 크나큰 발전을 가져다 준 프로젝트였습니다.
- 이번 프로젝트는 여러 사람과 함께하는 첫 경험이었기에 처음에는 다소 낯설었습니다. 하지만 훌륭한 팀원들과의 협력을 통해 많은 것을 배우고 성장할 수 있었던 소중한 기회였습니다. 프로젝트를 진행하며 처음으로 React를 사용해 보았습니다. 특히 랭킹, 업적, 로그인 기능의 화면을 구현하는 과정에서 다양한 라이브러리를 활용하였습니다. 이러한 경험은 새로운 기술 스택을 빠르게 익히고 적용하는 능력을 키우는 데 큰 도움이 되었습니다. 그럼에도 불구하고, React에 대한 이해가 완벽하지 않았다는 점은 아쉬움으로 남습니다. 때로는 기능 구현에 있어 예상치 못한 어려움에 부딪히기도 했습니다. 또한 초기 기획한 기능을 모두 구현하지 못햇다는 아쉬움이 남았습니다. 다음 프로 젝트에서는 이러한 아쉬움을 개선하여 앞으로의 프로젝트에서 더 나은 성과를 낼 수 있도록 노력할 것입니다.