백엔드 개발 포트폴리오 | 게임 매칭 서비스

내일배움캠프 4기 웹 개발 과정 Spring 트랙 수료생 최종 프로젝트 ‘즐겜빡겜’을 소개합니다.
Aug 17, 2023
백엔드 개발 포트폴리오 | 게임 매칭 서비스

친구 찾고 즐겁게 게임하자!

간단한 조건 입력으로 나와 함께 게임할 친구를 매칭하고 디스코드까지 한 번에!

 

아키텍처

notion image
 

주요 기술

  • Spring boot
  • PostgreSQL
  • Redis
  • Websocket
  • Stomp

기술적 의사결정

CI/CD 선택
🙅Jenkins 와 GitHub Actions 중 CI/CD를 어떤 툴을 선택할 지 의견이 나뉘었다.
🙆처음에는 Jenkins로 CI/CD를 구축을 시도하였지만 서버를 따로 두어야 하고 설정을 따로 해줘야 할게 많고, 이번 팀프로젝트 규모에는 사용하기엔 적절하지 않다고 판단하여 GitHub Actions을 사용하기로 정함.
GitHub Actions은 클라우드에서 실행되어 따로 서버를 둘 필요가없고, 지금 팀프로젝트처럼 규모가 작은 프로젝트에 적합하고 실행 및 디버그가 쉽다.
프론트 배포 선택
🙅 프론트 배포를 GitHub Pages로 선택하였는데 GitHub Pages는 디폴트주소가 https여서 ajax통신을 할 때 http와 보안성 문제가 발생하였다.
🙆 GitHub Pages CNAME을 가비아를 통해 http 주소로 변경하는 방법도 있었지만 S3를 CI/CD에도 사용하고 있어서 S3로 프론트를 배포하는 방식을 선택하였다.
Redis
🙅 I/O 접근이 많고 최종으로 매칭이 된 결과만 영구적으로 저장 ( 매칭 실패시 저장 x )
사람들을 매칭시켜줄 때마다 매칭할 사람들의 db가 저장 되어있어야 한다. ( 속도와 데이터의 무결성 중요)
지속적인 요청에 트래픽에 따라 데이터의 무결성과 속도가 보장 되어야 한다.
그에 따라 다른 기능들에 문제가 발생되지 않아야하고, 접근이 많아 기능에 문제가 발생 됐을경우
다른 기능에 문제를 일으키면 안된다
🙆 싱글스레드로 작동하는 Redis 를 활용하여 데이터의 무결성을 보장하고 외부에 서버를 따로 두어,
다른 기능들에 대한 안정성을 보장했습니다. 또한 많은 트래픽에 대비해 RDB를 사용한 네트워크 자원비용에
대한 문제를 해결하고 초당 5~60,000 속도로 처리속도의 안정성도 해결 하였고,
로그아웃과 다른 기능들에 확장성이 열려 있어서 선택하게 되었습니다.
Websocket & Stomp
🙅 채팅, 매칭은 사용자의 요청이 없더라도 실시간으로 응답을 받을 수 있어야했음
🙆 Websocket을 통해 실시간성을 보장하고 Stomp의 사용으로 pub/sub 구조의 체계적인 응답 형식 사용

⛹🏻‍♀️ 트러블 슈팅

Cannot deserialize json 에러
[문제 상황]
JSON parse error: Cannot deserialize value of type `java.util.ArrayList<>` from Object value (token `JsonToken.START_OBJECT`)
프론트에서 멤버 평가 결과를 List로 받아와 서버에서 처리하는 과정에서 에러가 발생.
 
[문제 원인]
RequestBody의 Dto에 List<MannerPointsRequest> 형식의 필드가 있었는데, 프론트에서 전달받은 JSON을 deserialize하지 못하는 상황.
같이 있던 Long 타입의 필드는 정상 parsing되었기 때문에 List<Object> 형식이라서 parsing이 안된다고 생각함.
 
[해결 방법]
DTO로는 우선 String으로 받아오게 한 뒤에, 서비스단에서 objectMapper의 readValue 메소드로 deserialize하고 싶은 형식을 직접 지정하여 파싱해 해결.
ObjectMapper objectMapper = new ObjectMapper(); List<MannerPointsRequest> mannerPointsRequests = objectMapper.readValue( request.getRequests(), new TypeReference<List<MannerPointsRequest>>() {});
 
Race condition 해결
[문제 상황]
notion image
기존코드에 작성되었던 로직에선 InMemory 방식을 사용하고 있어, 사용자가 매칭중엔 다른 행동을 하지 못하는 일이 발생 하였고, Timeout이 일어날 경우 계속 매칭요청을 계속 보냄.
[해결 방법]
websocket을 활용하여 대기중인 유저들의 세션관리를 하여 유저들의 세션을 확인 하고, 유저들의 timeout
의 문제를 해결 하였고, 이전 코드와 같이 데이터의 무결성을 위해 lock을 사용한것을
redis의 싱글스레드인점을 활용하여 데이터의 무결성을 해결 함.
 
서로 다른 기능의 공통된 기술 사용으로 발생한 문제
[문제 상황]
채팅 정상동작까지 마친 후, 배포 시에 다음과 같은 에러 발생
StompSubProtocolHandler : Failed to send message to MessageChannel in session o4dbob51:Failed to send message to ExecutorSubscribableChannel[clientInboundChannel]
 
stomp 연결 및 각 channel을 구독, 프론트의 채팅 데이터를 해당 destination에 send 하는 것까지는 정상동작하는 것을 확인
stomp 연결 stomp.min.js:8 >>> SUBSCRIBE id:sub-0 destination:/sub/chat/room/2 >>> SEND destination:/pub/chat/message content-length:47 {"roomId":2,"message":"dfdf","receiver":"tttt"}
 
발행된 메세지를 구독자들에게 보내는 부분에서 문제가 생겼음을 확인
<<< ERROR message:Failed to send message to ExecutorSubscribableChannel[clientInboundChannel] content-length:0
 
[문제 원인]
채팅 기능 구현 이후, 매칭 기능에 stomp가 사용되었고 ChannelIntercepter의 presend 메소드를 통해 SEND 요청들을 모두 잡아 매칭 기능에 해당하는 로직을 실행시키도록 함
 
채팅에서 사용하지 않는 redis에 대한 검증 로직이 있어 채팅 응답이 모두 예외처리되었음
 
[해결 방법]
presend의 로직이 적용되는 대상이 매칭 응답에 한하도록 바꾸는 방법이 가상 이상적이라고 생각
 
하지만, 거의 최종 배포 후 문제가 발견되어 시간적 여유가 없었고 채팅에서 문제가 되는 SEND의 경우만 If문을 통해 처리
 
문제가 되는 요청의 destination인 “/pub/chat/message”일 경우, presend 메소드의 다른 로직을 타지 않도록 함
if (accessor.getDestination().equals("/pub/chat/message")) { break; }
CI/CD build.gradle 에러
[문제 상황]
github action으로 CI/CD를 시도 하던 중 build.gradle에서 에러가 발생함
[문제 원인]
Execution failed for task ':compileQuerydsl'. > Annotation processor '' not found Querydsl 컴파일 에러가 발생
[해결 방법]
build.gradle에서 아래와 같이 변경해줌
implementation "com.querydsl:querydsl-core" implementation "com.querydsl:querydsl-collections" implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" annotationProcessor "jakarta.persistence:jakarta.persistence-api" // java.lang.NoClassDefFoundError (javax.annotation.Entity) 대응 코드 annotationProcessor "jakarta.annotation:jakarta.annotation-api" // java.lang.NoClassDefFoundError (javax.annotation.Generated) 대응 코드
 

notion image
 
 
 
 
 
 
 
Share article
Subscribe Newsletter
Stay connected for the latest news and insights.
RSSPowered by inblog