게임의 승률과 덱 조합을 분석하려면 대량의 매치 데이터를 지속적으로 수집하고, 이를 효율적으로 저장한 뒤 의미 있는 통계로 가공해야 합니다. 이 글에서는 라이엇 게임즈의 카드 게임 레전드 오브 룬테라를 대상으로, AWS의 서버리스 서비스를 활용해 메타 분석 사이트를 구축한 과정과 개발 중 마주한 기술적 문제를 살펴봅니다.

레전드 오브 룬테라의 게임 구조와 메타 분석의 필요성
레전드 오브 룬테라는 라이엇 게임즈에서 개발한 디지털 카드 게임(CCG)으로, 두 플레이어가 각각 구성한 덱을 활용해 대결하는 방식으로 진행됩니다. 게임의 전략을 이해하려면 단순한 승패 기록뿐만 아니라 덱의 구성과 카드 간의 조합도 함께 분석해야 합니다.
40장의 카드로 구성되는 덱의 특징

레전드 오브 룬테라의 덱은 총 40장의 카드로 구성되며, 동일한 카드는 일반적으로 최대 3장까지 포함할 수 있습니다. 이 중 챔피언 카드는 덱의 전략을 결정하는 핵심 요소입니다. 챔피언마다 고유한 능력과 특성이 있으므로, 어떤 챔피언을 조합하느냐에 따라 덱의 운영 방식과 상대 덱에 대한 대응 전략이 달라집니다.
따라서 게임 메타를 분석할 때는 개별 카드의 사용 빈도뿐 아니라 챔피언 조합, 승률, 상대 덱과의 상성 등을 종합적으로 고려하는 것이 중요합니다.
챔피언 조합을 기준으로 덱을 분류하는 이유
동일한 전략을 사용하는 플레이어라도 상황에 따라 일부 카드를 교체할 수 있습니다. 예를 들어, 핵심 챔피언은 그대로 유지하면서 특정 주문이나 추종자 카드 한두 장만 변경하는 경우가 있습니다. 이런 덱을 모두 서로 다른 유형으로 취급하면 실제 전략의 유사성을 제대로 반영하기 어렵습니다.
이 문제를 해결하기 위해 분석의 기본 단위를 챔피언 조합으로 설정했습니다. 핵심 챔피언 구성이 같은 덱을 하나의 그룹으로 묶으면 세부 카드 구성의 차이에 지나치게 영향을 받지 않고 전략별 성과를 비교할 수 있습니다.
이러한 분류 방식은 다음과 같은 분석으로 확장할 수 있습니다.
- 챔피언 조합별 승률과 사용 빈도 비교
- 현재 메타에서 주목받는 덱 유형 식별
- 특정 덱을 상대로 유리하거나 불리한 조합 분석
- 카드 교체에 따른 덱 성능의 변화 탐색
Riot Games API를 활용한 매치 데이터 수집
메타 분석의 정확도를 높이려면 신뢰할 수 있는 원본 데이터가 필요합니다. 라이엇 게임즈에서 제공하는 공식 API를 이용하면 플레이어의 식별 정보부터 매치 기록과 경기 결과까지 단계적으로 수집할 수 있습니다.
PUUID를 이용해 플레이어를 식별하는 방법
Riot API에서 플레이어의 매치 기록을 조회하려면 고유 식별자인 PUUID가 필요합니다. PUUID는 플레이어를 구분하는 데 사용되는 식별자로, 게임 닉네임이 변경되더라도 플레이어를 식별하는 데 활용할 수 있습니다.
먼저 Riot ID에 해당하는 게임 이름과 태그를 입력해 PUUID를 조회합니다. 이때 사용하는 API는 다음과 같습니다.
PUUID를 확보한 뒤에는 해당 플레이어의 최근 매치 ID 목록을 요청할 수 있습니다.
이 API를 통해 최근 매치 ID를 최대 20개까지 조회할 수 있습니다. 수집된 매치 ID는 다음 단계에서 실제 경기 데이터를 가져오는 데 사용됩니다.
매치 ID를 이용해 상세 경기 정보 조회하기
매치 ID를 확보했다면 해당 경기에 대한 상세 데이터를 요청할 수 있습니다.
응답 데이터에는 경기에 참여한 플레이어의 정보, 사용한 덱의 카드 구성, 경기 결과 등 분석에 필요한 정보가 포함됩니다. 이러한 데이터를 누적하면 플레이어별 경기 기록을 넘어 덱 유형별 성능을 계산할 수 있습니다.
다만 API에서 제공하는 데이터의 범위와 형식은 게임 및 API 정책에 따라 달라질 수 있습니다. 실제 구현에서는 응답 구조를 확인하고, 필수 필드가 누락되거나 예상하지 못한 값이 반환되는 상황도 고려해야 합니다.
마스터 티어 랭크 매치로 분석 대상 선정하기
모든 플레이어의 매치 데이터를 무차별적으로 수집하는 방식은 데이터의 양만 늘릴 뿐, 분석 결과의 일관성을 보장하지는 않습니다. 따라서 이 프로젝트에서는 상위권 플레이어의 랭크 매치를 중심으로 분석 모집단을 구성했습니다.
최고 티어 플레이어를 기준으로 삼는 이유
플레이어의 실력과 게임 이해도에 따라 덱 선택과 운영 방식이 달라질 수 있습니다. 초보자부터 숙련자까지 모든 플레이어의 데이터를 동일한 비중으로 집계하면 특정 덱의 실제 경쟁력을 해석하기 어려울 수 있습니다.
이에 따라 분석 대상을 마스터 티어 플레이어 간의 랭크 매치로 제한했습니다. 이 방식은 상위권 경쟁 환경에서 사용되는 덱과 전략을 파악하는 데 유용합니다.
현재 시즌의 랭크 순위 정보를 가져오는 데 사용할 수 있는 API는 다음과 같습니다.
다만 랭킹 API에서 제공하는 정보만으로는 모든 플레이어의 PUUID를 곧바로 확보하기 어렵습니다. 특히 닉네임만으로는 Riot ID에 필요한 태그 정보를 알 수 없는 경우가 있으므로, 추가적인 수집 절차가 필요합니다.
실제 랭크 매치를 활용해 플레이어 목록 확장하기
마스터 티어에 속한 플레이어의 PUUID를 확보하기 위해 실제 경기 기록을 활용했습니다. 우선 이미 확인된 플레이어의 매치 데이터를 수집하고, 해당 경기에서 만난 상대 플레이어의 정보를 확인합니다.
상대 플레이어의 닉네임이 리더보드에 등록된 플레이어와 일치하면 해당 플레이어가 분석 대상에 포함될 가능성이 있다고 판단할 수 있습니다. 이후 Riot ID의 태그 정보를 확보할 수 있는 경우 PUUID를 조회하고, 확인된 식별 정보를 데이터베이스에 저장합니다.
이 과정을 반복하면 기존 플레이어 목록을 기반으로 새로운 상위권 플레이어를 점진적으로 발견할 수 있습니다.
다만 닉네임만으로 플레이어를 확정해서는 안 됩니다. 동일하거나 유사한 닉네임이 존재할 수 있으므로, 가능한 경우 태그와 PUUID 등 고유 식별 정보를 이용해 일치 여부를 검증해야 합니다. 또한 매치의 실제 랭크 여부와 참여자의 티어를 확인할 수 있는 데이터가 있다면 이를 함께 활용하는 것이 분석 품질을 높이는 데 도움이 됩니다.
AWS S3와 Athena를 이용한 매치 데이터 저장 전략
매치 데이터는 수집한 뒤 여러 번 분석할 수 있어야 하지만, 원본 데이터를 관계형 데이터베이스에 전부 저장하면 저장 공간과 관리 비용이 커질 수 있습니다. 따라서 원본 데이터와 분석 결과를 분리해 저장하는 구조를 선택했습니다.
원본 매치 데이터를 압축 파일로 보관하기
이 프로젝트에서는 원본 매치 데이터를 CSV 형식으로 변환한 뒤 Gzip으로 압축하여 Amazon S3에 저장합니다. S3는 대량의 파일을 객체 단위로 보관할 수 있는 스토리지 서비스로, 수집 데이터의 장기 보관에 적합합니다.
원본 데이터를 별도의 파일로 관리하면 관계형 데이터베이스에 모든 경기 정보를 적재할 필요가 없습니다. 데이터베이스에는 웹사이트에서 빠르게 조회해야 하는 집계 결과나 주요 분석 정보를 저장하고, 원본 데이터는 필요할 때 다시 처리할 수 있도록 유지합니다.
이 구조는 다음과 같은 장점을 제공합니다.
- 원본 데이터 보관 비용을 줄일 수 있습니다.
- 데이터베이스의 저장 공간과 조회 부하를 절약할 수 있습니다.
- 분석 기준이 변경되더라도 원본 데이터를 이용해 다시 계산할 수 있습니다.
- 특정 기간이나 조건에 해당하는 매치만 선택적으로 분석할 수 있습니다.
Amazon Athena로 원본 데이터 분석하기
S3에 저장된 CSV 파일을 다시 분석해야 한다면 Amazon Athena를 활용할 수 있습니다. Athena는 S3에 저장된 데이터를 SQL로 조회할 수 있는 분석 서비스입니다.
예를 들어, 새로운 덱 분류 기준을 적용하거나 기존 집계 방식에 문제가 발견되었을 때 원본 데이터를 활용해 필요한 통계를 다시 계산할 수 있습니다. 이 과정에서 매번 별도의 분석용 데이터베이스를 구축할 필요가 없다는 장점이 있습니다.
다만 Athena의 실제 쿼리 비용은 스캔하는 데이터의 양에 영향을 받습니다. 따라서 파일을 적절한 단위로 나누고, 필요한 열만 조회하며, 데이터가 증가하면 Parquet과 같은 열 지향 형식으로 변환하는 방안도 고려할 수 있습니다.
EventBridge와 Lambda로 자동화하는 데이터 파이프라인
매치 데이터를 지속적으로 수집하려면 정기적인 실행과 작업 간의 연결이 필요합니다. 이를 위해 EventBridge, Lambda, SQS, S3, RDS를 조합한 자동화 파이프라인을 구성했습니다.

1단계: EventBridge에서 신규 매치 확인 작업 실행
Amazon EventBridge를 이용해 한 시간마다 check match Lambda 함수를 실행하도록 구성합니다. 이 함수는 RDS에 저장된 마스터 티어 플레이어 목록을 조회한 다음, 각 플레이어의 최근 매치 ID를 Riot API에서 가져옵니다.
이후 이미 처리한 매치 ID와 비교해 새롭게 발견된 경기만 선별합니다. 신규 매치 ID는 곧바로 상세 데이터 수집 작업으로 전달하지 않고, Amazon SQS의 대기열에 등록합니다.
이처럼 수집 대상과 실제 처리 작업을 분리하면 일시적으로 처리량이 증가하더라도 작업을 대기열에 보관할 수 있습니다.
2단계: SQS에 쌓인 매치 ID를 상세 데이터로 변환
SQS에 일정량 이상의 매치 ID가 쌓이면 save match data Lambda 함수가 실행되도록 구성합니다. 원래 설계에서는 대기열에 100개 이상의 데이터가 쌓이는 것을 실행 조건으로 사용했습니다.
이 함수는 전달받은 매치 ID를 이용해 Riot API에서 상세 경기 정보를 가져옵니다. 이후 응답 데이터를 CSV로 변환하고 Gzip으로 압축한 다음 S3에 파일로 저장합니다.
이 단계에서는 API 호출 실패, 일시적인 네트워크 오류, 잘못된 응답 데이터 등을 고려해야 합니다. 특히 메시지가 여러 번 전달될 가능성이 있으므로 동일한 매치가 중복 처리되더라도 결과가 일관되도록 설계하는 것이 중요합니다.
3단계: S3에 저장된 파일을 분석해 RDS에 반영
새로운 매치 데이터 파일이 S3에 저장되면 analyze match data Lambda 함수가 해당 파일을 분석합니다. 분석 과정에서는 챔피언 조합과 경기 결과 등 필요한 정보를 추출하고, 이를 덱별 승률이나 사용 빈도 등의 집계 데이터로 가공합니다.
최종적으로 가공된 결과를 RDS에 저장하면 웹사이트에서는 복잡한 원본 데이터를 매번 분석하지 않고도 필요한 통계 정보를 조회할 수 있습니다.
이러한 파이프라인은 데이터 수집, 원본 보관, 통계 계산, 웹서비스 조회라는 역할을 분리합니다. 각 단계를 독립적으로 관리할 수 있으므로 향후 분석 항목이 추가되더라도 기존 수집 구조를 크게 변경하지 않고 확장할 수 있습니다.
Riot API 요청 제한과 처리량 계산
외부 API를 활용하는 서비스에서는 요청 횟수 제한을 고려한 설계가 필수적입니다. 호출량을 미리 추산하고 작업을 적절히 분산해야 수집 과정이 중단되는 상황을 줄일 수 있습니다.
최대 요청 횟수 추산하기
당시 설계에서는 시즌 말 마스터 티어 플레이어 수가 최대 4,000명에 도달할 수 있다고 가정했습니다. 모든 플레이어의 최근 매치 ID를 확인하려면 플레이어당 한 번씩 요청해야 하므로, 기본 조회에 필요한 요청은 최대 4,000회입니다.
여기에 각 플레이어의 최근 20개 매치가 모두 새로운 데이터라고 가정하면 상세 경기 정보를 가져오는 요청은 다음과 같이 계산됩니다.
- 플레이어 수: 4,000명
- 플레이어당 확인할 매치 수: 최대 20개
- 상세 매치 조회 요청: 4,000 × 20 = 80,000회
- 매치 ID 조회까지 포함한 총 요청 수: 최대 약 84,000회
이는 모든 플레이어의 매치가 새롭게 발견되는 극단적인 상황을 가정한 값입니다. 실제 요청량은 이미 수집한 매치의 비중과 플레이어별 신규 경기 수에 따라 달라집니다.
SQS를 활용해 요청량을 분산하기
초기 설계에서는 시간당 30,000회, 초당 100회의 요청 제한을 기준으로 호출량을 검토했습니다. 실제 적용 시에는 API 키의 유형과 Riot Developer Portal에 표시되는 현재 제한을 반드시 확인해야 합니다. 요청 제한은 API 종류와 키의 용도 등에 따라 달라질 수 있기 때문입니다.
테스트 과정에서는 2주 동안 API 요청을 수행했으며, 일반적으로 시간당 약 4,000~24,000회의 요청이 발생하는 것으로 관찰했습니다. 이러한 측정값은 당시 데이터와 구현을 기준으로 한 결과이므로, 서비스 규모가 커지면 다시 측정해야 합니다.
SQS를 이용하면 즉시 처리하지 못한 작업을 대기열에 남겨두고 이후에 처리할 수 있습니다. 그러나 대기열이 있다고 해서 API 제한을 초과한 요청이 자동으로 해결되는 것은 아닙니다. 따라서 재시도 간격을 늘리는 지수형 백오프, 호출 속도 제한, 실패 메시지의 별도 관리 등을 함께 적용해야 안정적인 수집이 가능합니다.
비용 절감을 우선한 AWS 서버리스 아키텍처
프로젝트의 주요 목표 중 하나는 초기 운영 비용을 낮추는 것이었습니다. 상시 실행되는 서버를 대여하는 대신, 실제 작업이 발생할 때 컴퓨팅 자원을 사용하는 서버리스 서비스를 중심으로 구조를 설계했습니다.
API Gateway와 Lambda를 이용한 백엔드 구성
백엔드는 API Gateway를 통해 외부 요청을 받고, 필요한 작업을 여러 Lambda 함수에서 처리하도록 계획했습니다. Lambda는 서버 운영체제나 실행 환경을 직접 관리하지 않아도 코드를 실행할 수 있어, 트래픽이 일정하지 않은 소규모 서비스에 유용합니다.
또한 사용량에 따라 비용이 발생하는 구조이므로 초기 단계에서 상시 실행 서버를 운영하는 부담을 줄일 수 있습니다. 다만 Lambda의 실행 시간, 메모리 설정, 호출 횟수와 다른 AWS 서비스의 요금까지 함께 고려해야 실제 비용을 판단할 수 있습니다.
RDS를 선택한 이유와 데이터 저장 구조
데이터베이스는 서버리스 방식으로 구성하는 대신 Amazon RDS를 사용했습니다. 당시 검토한 서버리스 NoSQL 데이터베이스는 관계형 데이터와 조회 패턴을 처리하려는 요구사항에 적합하지 않다고 판단했기 때문입니다.
관계형 데이터베이스는 플레이어, 매치, 덱, 집계 결과 사이의 관계를 관리하는 데 유용합니다. 특히 여러 조건으로 데이터를 조회하거나 집계 결과를 일관성 있게 유지해야 할 때 장점이 있습니다.
프론트엔드는 AWS에 직접 배포하지 않고 Vercel을 이용하도록 구성했습니다. 이로써 백엔드 처리와 데이터 저장은 AWS에서 담당하고, 웹사이트 배포는 별도의 플랫폼에서 관리하는 형태를 선택했습니다.
다만 서버리스 아키텍처라고 해서 유지보수나 운영 관리가 완전히 사라지는 것은 아닙니다. 권한 설정, 로그 확인, 오류 추적, 비용 모니터링, API 제한 관리 등은 계속 필요합니다.
Lambda와 RDS 사이에서 발생한 데이터베이스 연결 문제
개발 과정에서 해결해야 했던 주요 문제 중 하나는 Lambda와 RDS를 함께 사용할 때 발생할 수 있는 데이터베이스 연결 수 제한이었습니다. Lambda의 동시 실행이 증가하면 RDS에 연결 요청이 집중될 수 있으므로 연결 관리 전략이 필요합니다.
RDS Proxy를 이용하는 연결 관리 방식
첫 번째 해결책은 Amazon RDS Proxy를 도입하는 것입니다. RDS Proxy는 애플리케이션과 데이터베이스 사이에서 연결을 관리하고 재사용할 수 있도록 지원합니다.
Lambda 함수가 동시에 많이 실행되더라도 데이터베이스 연결을 효율적으로 관리하는 데 도움이 됩니다. 특히 트래픽이 불규칙하거나 동시 실행이 빈번한 서비스에서 고려할 만한 방법입니다.
하지만 프록시 사용에 따른 비용과 네트워크 구성이 추가될 수 있습니다. 비용을 최소화해야 하는 프로젝트라면 예상되는 연결량과 운영 요구사항을 바탕으로 도입 여부를 결정해야 합니다.
EC2를 활용해 연결 풀을 관리하는 방식
두 번째 방법은 EC2 인스턴스를 별도로 운영하면서 해당 서버에서 데이터베이스 연결 풀을 관리하는 것입니다. 연결 풀은 기존 데이터베이스 연결을 재사용해 불필요한 연결 생성과 종료를 줄이는 방식입니다.
이 구조는 연결 관리 정책을 직접 조정할 수 있다는 장점이 있지만, EC2 운영 비용과 서버 관리 작업이 추가됩니다. 따라서 단순히 연결 문제를 해결하기 위해 도입하기보다는 다른 기능이나 서비스에서도 해당 서버를 활용할 필요가 있는지 함께 검토해야 합니다.
연결 수를 제한하고 작업이 끝나면 연결 종료하기
세 번째 방법은 Lambda 함수에서 데이터베이스 연결 풀의 크기를 작게 유지하고, 작업이 끝나면 연결을 적절히 반환하거나 종료하는 것입니다. 당시 프로젝트에서는 비용을 우선적으로 고려해 이 방식을 선택했습니다.
연결 풀 크기를 1로 설정하면 함수 인스턴스 하나가 유지하는 동시 연결 수를 제한할 수 있습니다. 또한 데이터베이스 작업이 끝난 뒤 연결을 정리하도록 구성하면 불필요한 연결이 장시간 유지되는 것을 줄일 수 있습니다.
다만 이 방식이 데이터베이스 전체의 연결 수를 1개로 제한하는 것은 아닙니다. Lambda 인스턴스가 여러 개 동시에 실행되면 각 인스턴스가 별도의 연결을 생성할 수 있습니다. 따라서 동시 실행 수를 제한하고 연결 재사용 방식과 종료 시점을 적절히 관리해야 합니다.
RDS Proxy는 연결 관리 측면에서 더 편리한 선택지가 될 수 있지만, 모든 상황에서 반드시 필요한 것은 아닙니다. 소규모 프로젝트에서는 단순한 연결 관리 방식을 먼저 적용하고, 실제 연결 수와 오류 빈도를 모니터링하면서 구조를 개선하는 접근도 가능합니다.
Runeterras.com 개발 결과와 개선 과제
이번 프로젝트에서는 게임 데이터를 수집하고 분석하는 과정을 자동화하면서, 서버리스 아키텍처를 실제 웹서비스에 적용하는 경험을 얻을 수 있었습니다. 동시에 데이터 품질과 사용자 인터페이스, 운영 구조 측면에서 추가로 개선할 부분도 확인했습니다.
GitHub 소스 코드와 서비스 확인하기
프로젝트의 소스 코드는 GitHub 저장소에서 확인할 수 있습니다. 실제 사이트를 통해 구현된 기능과 화면 구성을 살펴볼 수도 있습니다.
- GitHub 저장소: https://github.com/junah201/Runeterras.com
- 서비스 웹사이트: https://Runeterras.com
- 서비스 운영 영상: https://youtu.be/rLm9bAiNkPw
당시에는 모바일 화면에 대한 최적화가 충분하지 않았으므로, 사이트를 확인할 때는 데스크톱 환경을 이용하는 편이 더 적합했습니다. 현재 사이트의 운영 상태와 화면 구성은 이후 변경되었을 수 있습니다.
TypeScript와 React 개발 경험 개선
개발 당시에는 React 환경에서 TypeScript를 본격적으로 활용한 경험이 많지 않았습니다. 따라서 타입 정의와 코드 구조, 예외 처리 등 여러 부분에서 개선할 여지가 있었습니다.
타입을 명확하게 정의하면 API 응답의 형태를 검증하고 데이터 처리 과정에서 발생할 수 있는 오류를 줄이는 데 도움이 됩니다. 특히 외부 API의 응답을 데이터베이스에 저장하거나 분석용 데이터로 변환하는 코드에서는 타입 안정성이 중요합니다.
사용자 인터페이스와 사용성 개선
데이터를 정확하게 분석하는 것만큼 사용자가 결과를 쉽게 이해하도록 만드는 것도 중요합니다. 덱별 승률과 사용 빈도뿐 아니라 표본 수, 분석 기간, 상대 덱과의 상성 등을 함께 표시하면 통계의 의미를 더 명확하게 전달할 수 있습니다.
또한 모바일 화면에 맞는 레이아웃을 제공하고, 검색 및 정렬 기능을 개선하면 다양한 환경에서 서비스를 편리하게 이용할 수 있습니다.
데이터베이스까지 포함한 아키텍처 최적화
마지막으로 데이터베이스까지 완전히 서버리스 구조로 구성하지 못했다는 점도 아쉬움으로 남았습니다. 다만 서버리스 여부 자체가 아키텍처의 품질을 결정하는 것은 아닙니다.
중요한 것은 데이터의 특성, 예상 트래픽, 운영 복잡도, 비용을 종합적으로 고려해 적절한 서비스를 선택하는 것입니다. 초기에는 단순한 구조로 시작하고, 실제 사용량과 병목 지점을 확인한 뒤 필요한 부분부터 개선하는 것이 효율적입니다.
이번 개발 과정은 대량의 매치 데이터를 수집하고, 저장 비용을 관리하며, 분석 결과를 웹서비스에 제공하기까지의 흐름을 직접 설계했다는 점에서 의미가 있습니다. 안정적인 데이터 수집, 효율적인 저장 구조, 적절한 비용 관리를 함께 고려하는 것이 소규모 데이터 분석 서비스의 핵심이라는 점을 확인할 수 있었습니다.