예전부터 가끔 하던 게임이 있었다. League of Legends(LoL). 프로 경기를 보다 보면 하나가 눈에 들어왔다. 팀을 짜고, 밴/픽을 하고, 상대 팀과 스크림을 잡는 과정. 일반 유저에게는 이걸 할 수 있는 플랫폼이 없었다.

2021년 3월, 동료 한 명과 만들기 시작했다. 5vs5 League의 줄임말, 55L. 나중에 GGScrim이라는 이름이 붙었다.

서비스

55L은 두 축으로 구성했다.

ggscrim.com은 팀 매칭 플랫폼이다. 팀을 등록하고, 상대 팀을 찾아 스크림을 잡는다. banpick.kr은 가상 밴/픽 시뮬레이터다. 프로 씬과 같은 밴/픽 순서를 시뮬레이션할 수 있다.

밴/픽 서비스에 10,000명 이상의 유저가 모였다.

기술 선택

API 서버는 PHP로 만들었다. 이전에 PHP로 서버를 구현해본 경험이 있었고, 빠르게 시작하는 것이 우선이었다. MVC 패턴에 DI, Factory, Singleton을 적용했고, Nginx를 리버스 프록시로 앞에 두었다.

채팅과 알림은 실시간 양방향 통신이 필요했다. Node.js + Socket.io로 별도 서버를 구성했다 — HTTP 요청/응답만으로는 이 요구사항을 해결할 수 없었다.

데이터 저장은 MariaDB, 인증 토큰 관리는 Redis를 사용했다.

클라이언트는 TypeScript + Lit으로 SPA를 만들었다. 당시 Web Components 기반 프레임워크 중에서 Lit이 가장 간결했고, 표준 API 위에서 동작해 장기적인 지속성이 있다고 판단했다. Webpack으로 빌드했고, Firebase를 호스팅에 활용했다.

데스크톱 앱은 Electron으로 만들었다. LoL 클라이언트와 소켓 통신을 해야 했는데, 웹 브라우저에서는 로컬 프로세스에 직접 접근할 수 없다. Electron이 이 제약을 해결했다. 웹 코드를 재사용하면서 Windows와 Mac을 하나의 코드베이스로 대응할 수 있었다.

모바일은 PWA로 처리했다. 별도 네이티브 앱 없이 설치형으로 제공할 수 있었다. 개발 비용을 줄이면서 모바일 접근성을 확보하는 방법이라 판단했다.

아키텍처

전체 시스템은 크게 세 영역으로 나뉜다.

클라이언트는 데스크톱 앱(Electron), 웹(SPA), 모바일(PWA) 세 가지 형태로 제공했다. 데스크톱 앱만 LoL 클라이언트와 소켓으로 직접 통신하고, 나머지는 브라우저 기반이다. 세 클라이언트 모두 같은 API 서버와 Socket.io 서버에 연결된다.

서버는 인증 서버와 API 서버를 분리했다. 인증은 JWT 기반으로 설계했다. 인증 로직과 비즈니스 로직을 분리하면 각각 독립적으로 수정하고 배포할 수 있다고 봤다. JWT 인증 구조와 세션 방식의 차이는 별도 글에서 정리했다.

API 서버는 Riot API와 통신해서 챔피언, 소환사 등 LoL 데이터를 조회하고 MariaDB에 저장했다.

처음에는 둘이었다. 서비스 아키텍처 설계, JWT 인증 서버, 데스크톱 앱은 전부 직접 맡았다. 웹 클라이언트도 대부분 담당했다.

서비스가 커지면서 인원이 늘었다. 5개월 동안 4명까지 합류했다. 역할이 나뉘기 시작했고, 혼자 모든 기술 결정을 내리던 구조에서 분담하는 구조로 바뀌어갔다.

돌아보면

모든 기술 결정을 직접 내려야 하는 환경이었다. 선택의 근거를 스스로 만들어야 했고, 트레이드오프를 직접 마주했다.

Lit 선택은 다시 한다면 바꿨을 것이다. Web Components 표준의 지속성을 보고 골랐지만, 생태계 크기와 개발 속도를 생각하면 React가 더 나은 선택이었다고 본다. 당시에는 기술의 방향성을 우선했지만, 창업 초기에는 속도가 더 중요했다.

가끔 하던 게임에서 시작된 프로젝트였다. 없던 플랫폼을 직접 만들었고, 그 과정에서 생긴 기준점은 이후 실무에서도 쓰고 있다.

참고