매치메이킹을 설명할 때 흔히 비슷한 실력의 이용자끼리 상대를 찾아주는 것이라고 이야기한다. 그러려면 우선 무엇을 실력으로 볼지 정해야 한다. 승패로 추정한 점수를 사용할 수도 있고, 캐릭터의 성장 정도를 함께 봐야 할 수도 있다. 팀으로 경기한다면 개인의 점수를 모아 팀의 실력을 어떻게 나타낼지도 정해야 한다.
여러 게임 팀과 매칭 조건을 정하고, 데이터 분석가와 함께 평가 모델을 서비스에 적용했다. 이 과정에서는 개인이 얼마나 잘하는지와 어떤 이용자들을 함께 묶을지를 따로 봐야 했다. 실력이 비슷하더라도 파티 구성에 따라 다른 조합을 선택할 수 있고, 오래 기다린 이용자에게는 상대를 찾는 범위를 넓혀야 할 수도 있기 때문이다.
게임별로 평가 방식을 추가할 때는 기존 엔진의 탐색 과정을 재사용할 수 있도록 계산기를 연결했다. 새 계산에 필요한 데이터도 준비하고, 기존 방식과 비교할 수 있도록 적용 대상과 설정을 나눠야 했다. 실력을 어떻게 나타낼 것인지에서 시작한 일이 데이터 연동과 엔진 구현, 설정과 실험으로 어떻게 이어졌는지 정리해 보려 한다.
01 / 실력과 조합 평가
개인의 실력에서 팀의 구성으로
우선 승패를 바탕으로 실력을 추정한다고 하자. 점수가 같더라도 경기를 충분히 치른 이용자와 이제 몇 경기만 치른 이용자의 추정치를 똑같이 신뢰하기는 어렵다. 예를 들어 트루스킬(TrueSkill)은 실력 추정치와 그 불확실성을 함께 표현하고, 경기 결과가 들어올 때 두 값을 갱신한다.
캐릭터가 성장하는 게임이라면 이용자의 숙련도가 비슷해도 캐릭터의 능력에 따라 결과가 달라질 수 있다. 승패로 추정한 실력과 캐릭터의 능력값은 서로 다른 정보를 담는다. 어느 쪽을 사용할지, 함께 쓴다면 각각 얼마나 반영할지에 따라 실력을 나타내는 방식도 달라진다.
다만 매칭에 사용하는 정보를 모두 실력이라고 부르기는 어렵다. 이용자가 얼마나 잘하는지 추정하는 것과 함께 플레이할 상대를 정하는 것은 다르다. 실력이 비슷해도 파티 구성이나 행동 특성에 따라 조합을 다르게 평가할 수 있고, 플랫폼 호환처럼 반드시 충족해야 하는 조건도 있다.
- 실력 추정: 이용자의 경기 수행 능력을 어떤 값으로 나타낼 것인가.
- 조합 평가: 어떤 이용자들을 모아 어느 팀에 배치할 것인가.
- 매칭 조건: 어떤 상대와 매칭할 수 있으며, 기다리는 동안 어느 조건을 완화할 것인가.
개인의 실력 점수가 정해져도 팀을 어떻게 평가할지는 따로 정해야 한다. 두 팀의 평균을 맞추는 것이 한 가지 방법이지만, 평균이 같다고 팀 안의 구성까지 비슷한 것은 아니다.
표를 좌우로 밀어 비교할 수 있습니다.
| 구성 | 팀 A | 팀 B |
|---|---|---|
| 팀 안의 차이가 큰 경우 | 1300 + 1700 → 평균 1500 | 1300 + 1700 → 평균 1500 |
| 팀 안의 차이가 없는 경우 | 1500 + 1500 → 평균 1500 | 1500 + 1500 → 평균 1500 |
서로 다른 두 후보 집합으로 만든 2인 팀 예시다. 같은 이용자를 재배치한 결과는 아니다.
두 경우 모두 팀의 평균은 1500점이고 상대 팀과의 차이도 없다. 하지만 첫 번째 구성에서는 같은 팀 안에서도 400점의 차이가 난다. 팀 평균의 차이만 계산해서는 이 둘을 구분할 수 없다. 가장 잘하는 이용자 한 명이 경기의 흐름을 크게 바꾸는 게임이라면, 그 이용자의 영향을 평균보다 더 크게 반영할 수도 있다.
그래서 개인의 점수는 그대로 두면서 팀 조합을 평가하는 계산을 바꿀 수 있도록 했다. 팀 평균의 차이, 팀 안의 실력 차이, 특정 이용자의 영향을 각각 얼마나 반영할지에 따라 다른 계산기를 구현하고 엔진에 연결했다.
행동 관련 속성을 실력 차이와 함께 평가하거나, 경기 후 이탈 가능성을 조합 평가에 반영하는 시도도 했다. 이탈 예측의 경우에는 준비된 모델을 서비스 코드로 옮기고 후보 이용자들의 속성을 모델 입력으로 변환했다. 예측 결과는 엔진이 후보 조합을 비교하는 데 사용할 품질 값으로 반환했다.
이런 계산에서 반환하는 값은 개인의 실력보다 조합을 얼마나 좋게 평가하는지를 나타낸다. 개인 점수를 바꾸려는 것인지, 같은 점수로 다른 조합을 선택하려는 것인지에 따라 필요한 데이터와 수정할 계산도 달라졌다.
02 / 입력 데이터
평가에 필요한 데이터를 연결하기
계산에 사용하는 값은 경기 기록에 한정되지 않았다. 이용자 특성이나 서비스 이용 이력을 가공한 값도 사용했고, 게임 서버가 매칭 요청에 직접 담아 보내는 정보도 받았다. 요청에 없는 정보는 저장된 이용자 데이터와 데이터웨어하우스에서 조회해 계산에 사용할 수 있도록 연결했다.
분석 과정에서 값을 만들었다고 곧바로 계산기에 넣을 수 있는 것은 아니었다. 어느 이용자의 값을 조회할지, 어떤 이름과 자료형으로 전달할지부터 맞춰야 했다. 그 값을 개인의 실력 추정에 쓸지, 조합 평가에 쓸지, 매칭 대상을 제한하는 조건으로 쓸지도 정해야 했다.
예를 들어 최근 경기 기록을 가공한 속성을 추가한다고 하자. 경기를 치른 이용자에게는 값이 있지만, 기록이 없는 이용자에게는 없을 수 있다. 조회에 실패한 경우와 조회는 됐지만 데이터가 없는 경우도 구분해야 한다. 데이터가 없을 때 어떤 기본값을 쓰느냐에 따라서도 평가 결과가 달라진다.
값이 언제 만들어졌는지도 확인해야 한다. 미리 계산해 두면 요청마다 같은 계산을 반복하지 않아도 되지만, 다음 갱신까지는 이전 값을 사용하게 된다. 매칭 요청 시점의 값이 필요하다면 게임 서버가 그 값을 요청에 담아 보내줘야 한다. 같은 항목이라도 어느 시점의 값인지에 따라 결과를 다르게 해석해야 할 수 있다.
조회한 값과 요청에 담긴 속성을 모은 뒤, 설정된 식으로 필요한 값을 계산하도록 구성했다. 새 속성을 추가할 때마다 엔진의 탐색 코드에 조회 로직을 넣지 않아도 되도록 한 것이다. 다만 속성의 의미와 기본값, 갱신 시점은 게임 팀과 데이터 분석가, 서비스 개발자가 함께 맞춰야 했다.
03 / 평가 모델과 엔진
서로 다른 평가 방식을 같은 엔진에 연결하기
게임마다 평가 방식은 달라도 후보를 모으고 팀을 구성하는 과정까지 매번 새로 만들 필요는 없었다. 기존 엔진에서 새 평가 방식을 사용할 수 있도록 계산기를 공통 인터페이스에 맞춰 구현했다. 이때 계산식뿐 아니라 엔진이 전달하는 후보 정보를 모델 입력으로 바꾸는 코드도 필요했다.
계산기를 호출하는 순서는 초기화, 이용자 추가, 품질 계산이다. 새 조합을 평가하기 전에 이전 후보의 데이터를 비우고, 이번 후보들을 차례로 넣은 뒤 품질을 구한다. 같은 이용자라도 어느 팀에 배치하느냐에 따라 결과가 달라지므로, 이용자 속성과 함께 팀·파티 정보도 전달했다.
- 후보 구성: 엔진이 후보 이용자를 모으고 팀 배치를 정한다.
- 입력 변환: 계산기가 전달받은 팀·파티 정보와 이용자 속성을 읽어 평가에 필요한 입력을 만든다.
- 품질 계산: 조합의 품질을 하나의 값으로 반환한다. 여러 계산을 결합할 때도 반환값은 하나다.
- 비교와 수락: 엔진이 조합의 품질을 수락 기준과 비교하고, 탐색 중 평가한 조합들의 품질도 비교한다.
여러 평가 기준을 함께 쓸 때는 계산기들을 묶어 호출하는 결합 계산기를 만들었다. 초기화와 이용자 추가를 각 계산기에 똑같이 전달하고, 반환된 품질 값에 각각의 가중치를 곱해 더했다. 이때 각 가중치를 가중치의 총합으로 나눠 비율로 사용했다. 결합 계산기도 같은 인터페이스를 따르므로, 엔진의 탐색 코드는 그대로 사용할 수 있었다.
다만 가중치를 1:1로 둬도 두 기준이 결과에 미치는 영향까지 같아지는 것은 아니다. 한쪽 계산의 값이 다른 쪽보다 큰 폭으로 변한다면, 두 값을 더한 결과도 그쪽에 더 크게 좌우될 수 있다. 비율을 정하기 전에 각 계산의 값이 어느 범위에서 얼마나 달라지는지 살펴봐야 한다.
기존 평가 기준의 비중만 바꿀 때는 가중치를 조정하면 된다. 새로운 평가식이 필요하면 공통 인터페이스에 맞는 계산기를 추가한다. 준비된 예측 모델을 옮길 때도 이 방식으로 입력 변환과 품질 계산을 연결했다. 여기서 다룬 작업은 모델을 새로 설계하거나 학습하는 것이 아니라, 준비된 모델을 매칭 과정에서 사용할 수 있도록 구현하는 일이었다.
계산기 밖에서 바꿔야 할 부분도 있다. 필요한 속성이 늘면 데이터 정의와 조회를 수정해야 하고, 상대를 찾는 범위나 플랫폼 호환 조건을 바꾸려면 탐색·필터 로직을 수정해야 한다. 계산기가 반환한 품질을 보고 조합을 받아들일지도 엔진에서 판단한다. 계산과 탐색을 분리해 두더라도, 새 평가 방식을 적용할 때는 이 조건들을 함께 확인해야 했다.
04 / 탐색과 빈자리 보충
후보를 찾고 조합을 받아들이는 조건
계산기는 엔진이 구성한 후보 조합을 평가한다. 그러려면 먼저 어떤 이용자를 후보로 찾을지 정해야 한다. 후보를 정렬하는 점수나 탐색 범위가 바뀌면, 계산기로 평가할 조합도 달라진다.
가령 실력 점수의 계산식을 바꾼 뒤 특정 점수 구간에 이용자가 더 많이 모였다고 하자. 이전과 같은 폭으로 상대를 찾아도 그 구간에서는 후보가 많아지고, 사람이 적은 양끝에서는 상대를 찾기 어려울 수 있다. 탐색 설정은 그대로여도 점수 분포가 달라지면 대기 시간에 영향을 준다.
그래서 대기 시간을 추정할 때는 풀 전체가 아닌, 상대를 찾는 점수 구간의 유입량을 사용했다. 해당 구간에서 초당 몇 명이 매칭을 요청하는지 집계하고, 현재 파티 외에 더 필요한 인원을 이 속도로 나눠 대기 시간을 추정했다.
같은 설정을 적용할 매칭 요청을 모으는 단위가 풀이다. 풀마다 탐색 범위와 품질 수락 기준을 정하고, 대기 시간에 따라 기준을 얼마나 완화할지 조정할 수 있도록 했다. 이때 플랫폼 호환처럼 매치가 성립하려면 반드시 지켜야 하는 조건은 완화할 수 있는 기준과 구분했다.
후보를 찾고 평가하는 데 쓸 수 있는 계산량도 한정돼 있다. 가능한 팀 조합을 전부 평가할 수 없으므로 후보 수나 탐색 횟수에 한도를 두고, 실제로 살펴본 조합 중에서 고른다. 평가 방식의 차이를 비교하려면 계산기뿐 아니라 탐색 범위와 한도, 수락 조건도 같았는지 확인해야 했다.
새로 팀을 구성하는 경우와 이미 만들어진 매치의 빈자리를 채우는 경우도 다르게 처리해야 했다. 빈자리 보충(backfill)은 기존 이용자와 팀 배치를 유지한 채 추가할 후보를 찾는다. 후보를 넣은 뒤의 품질뿐 아니라, 넣기 전보다 얼마나 달라지는지도 함께 계산했다.
최소 품질 기준만 적용하면 현재 구성보다 나아지는 후보도 기준에 못 미쳐 거절할 수 있다. 그래서 보충 전보다 품질이 개선되거나 최소 기준을 충족하면 품질 검사를 통과하도록 구현했다. 반대로 현재보다 품질이 낮아지더라도 최소 기준을 충족하면 통과한다.
표를 좌우로 밀어 비교할 수 있습니다.
| 보충 전 → 후 | 품질 기준 | 품질 검사 결과 |
|---|---|---|
| 40 → 50 | 60 | 기준 미달이어도 이전보다 좋아져 통과 |
| 80 → 70 | 60 | 이전보다 낮아져도 기준을 충족해 통과 |
| 40 → 35 | 60 | 개선도 기준 충족도 없어 거절 |
숫자는 실제 설정값과 무관하며 높을수록 좋은 품질을 뜻한다. 각 행은 별개의 후보 상황이다. 품질 검사 외에 팀 인원과 호환 조건 등도 충족해야 한다.
05 / 설정과 비교
기존 방식과 새 방식을 비교하기
실험군별로 사용할 데이터와 계산 플러그인, 탐색·수락 조건을 함께 확인해야 했다. 같은 평가식을 사용해도 입력 데이터나 탐색 범위가 다르면 결과가 달라질 수 있기 때문이다.
표를 좌우로 밀어 비교할 수 있습니다.
| 항목 | 비교안 예시 |
|---|---|
| 입력 데이터 | 기존 속성만 사용할지, 새로 가공한 속성을 더할지 |
| 계산 방식 | 같은 실력 추정치에 다른 조합 평가를 적용할지 |
| 매칭 설정 | 탐색 범위와 품질 수락 기준을 어떻게 둘지 |
| 적용 대상 | 어느 풀에서 어떤 비율과 기간으로 나눌지 |
설명을 위해 구성한 비교 항목이다.
팀 간 평균 차이를 더 중시하는 방식과 팀 내부의 실력 차이를 더 중시하는 방식을 비교한다고 하자. 이때 입력 데이터나 탐색 한도까지 다르면, 결과의 차이가 평가 방식 때문인지 구분하기 어렵다. 여러 항목을 바꿀 수 있더라도 비교하려는 항목을 정하고 나머지 조건은 같게 유지해야 한다.
새 평가 방식의 결과를 기존 방식과 비교하려면 적용 대상을 나눌 수 있어야 했다. 풀 설정과 A/B 테스트를 관리하는 대시보드에 새 매칭 규칙을 추가하고, 입력한 설정을 검증하는 처리를 보완했다. 일반 매칭과 backfill에 각각 어떤 규칙을 적용할 수 있는지도 구분했다.
설정 형식이 바뀌면 이미 저장된 풀과 실험 설정도 새 형식에 맞춰야 했다. 화면에서 새 규칙을 입력할 수 있게 만드는 것과 함께, 이전 설정을 변환하는 코드와 테스트를 추가했다. 계산기를 바꾸는 작업에 설정을 저장하고 읽는 부분의 수정도 필요했던 이유다.
설정이 요청 처리에 적용되는 과정도 확인할 수 있어야 했다. 엔진에서는 요청을 어느 하위 풀로 보낼지 정하는 라우팅 설정과 이력을 읽어 해당 풀을 찾도록 구현했다. 필요한 라우팅 정보가 캐시에 없으면 설정을 다시 읽었다. 요청을 처리한 풀과 당시 적용한 설정을 확인하는 데 필요한 처리였다.
결과를 볼 때도 매칭 성공 건수만으로는 부족했다. 상대를 찾았지만 게임 시작에 실패하거나, 게임은 시작했는데 경기 결과가 들어오지 않는 경우를 구분해야 한다. 요청부터 경기 결과까지 확인하는 데 필요한 로그의 변환·전달과 공통 오류 수집을 보완했다.
예를 들어 한 실험군에서 완료된 경기 수가 적게 나왔다고 하자. 조건이 맞지 않아 오래 기다리다가 취소한 것인지, 상대는 찾았지만 게임 시작에 실패한 것인지에 따라 살펴볼 곳이 달라진다. 완료된 경기만 비교하면 매칭을 취소했거나 게임을 시작하지 못한 이용자는 비교 대상에서 제외된다. 매칭 요청 이후 어느 단계까지 진행됐는지를 함께 봐야 한다.
운영상의 문제를 확인한 뒤에도 어떤 매칭이 더 나은지는 따로 판단해야 한다. 대기 시간, 경기 중 이탈, 재방문은 서로 다른 결과를 보여준다. 특히 재방문은 매칭 외의 영향도 받으므로 수치가 달라졌다는 것만으로 계산식의 효과라고 단정하기 어렵다. 실험에서 무엇을 바꿨고 어떤 조건을 같게 두었는지 함께 봐야 한다.
실력을 어떤 점수로 나타낼지 정하는 것은 매칭 기준의 일부였다. 같은 점수를 사용해도 후보를 찾는 범위와 팀을 구성하는 방식에 따라 다른 경기가 만들어졌다. 그래서 평가 방식을 바꿀 때는 계산기를 구현하는 일과, 그 계산이 적용된 매칭 결과를 비교할 수 있게 하는 일이 함께 필요했다.