전체 글 목록으로

이벤트 샘플링 설계: 잦은 위치 기록과 드문 주요 이벤트의 보존율 나누기

초당 다섯 번 보내는 위치 이벤트와 한 번의 플레이에서 한 번만 보내는 클리어 이벤트에는 서로 다른 샘플링 정책이 필요합니다. 둘 다 10%만 남기면 긴 세션에서는 위치 기록이 많이 남지만, 그 한 번의 클리어 이벤트가 샘플링 검사를 통과할 확률도 10%가 됩니다.

이벤트 샘플링은 대상 이벤트를 정해진 확률로 남기는 방식입니다. 먼저 데이터로 어떤 질문에 답할지 정한 다음, 이벤트 이름별로 보존율을 선택합니다. 반복해서 관측하는 상태는 일부만 남겨도 충분할 수 있습니다. 반면 실패나 체크포인트 도달은 각각의 발생 자체가 조사에 필요한 정보일 수 있습니다.

아래 설정은 Framedash Unity SDK 0.1.8 구현과 대조한 Track 이벤트 경로에 관한 내용입니다. 선택적으로 사용하는 프레임 단위 performance run 측정은 별도 경로를 사용합니다. 이벤트 수는 가정에 따른 계산이며, Framedash의 실측 결과나 모든 게임에 권장하는 설정값이 아닙니다.

반복 관측과 상태 변화를 구분하기

이벤트마다 언제 호출하고 무엇을 세려는지 적습니다.

  • player_position: 대략적인 이동 패턴을 살펴보기 위해 일정 간격으로 기록하는 위치
  • player_death: 플레이어가 사망 상태에 들어갈 때 한 번 기록하는 이벤트
  • level_completed: 레벨이 완료 상태로 바뀔 때 한 번 기록하는 이벤트

뒤의 두 이벤트는 상태가 바뀌는 코드에서 호출해야 합니다. 체력이 0인 동안 매 프레임 player_death를 호출하면 샘플링 전에 이미 중복이 생깁니다. 보존율을 낮추면 중복 일부가 가려질 뿐, 이벤트 정의가 고쳐지지는 않습니다.

위치 기록의 호출 간격은 보존율과 별도로 정합니다. 초당 다섯 번 실행하는 타이머는 관측을 시도할 시점을 정하고, 10% 보존율은 그 관측 중 일부를 무작위로 선택합니다. 많은 호출을 평균하면 초당 0.5개의 위치 기록이 남습니다. 하지만 2초마다 하나씩 남는 것은 아니며, 기록 사이의 최대 간격도 보장하지 않습니다.

짧은 전투에서 정확한 이동 경로를 알아야 한다면 무작위 샘플링으로 생기는 빈 구간이 너무 클 수 있습니다. 해당 질문에 맞는 한정된 시간의 상세 기록을 준비하거나 전투의 시작과 종료를 명시적인 이벤트로 남깁니다. 다른 이벤트의 보존율을 높여도 이런 기록이 자동으로 추가되지는 않습니다.

커스텀 이벤트 수 계산하기

20분 세션에서 위치 이벤트를 정확히 6,000번, 사망 이벤트를 세 번, 클리어 이벤트를 한 번 호출한다고 가정합니다. 아래 표는 샘플링 검사를 통과하는 이벤트 수의 기댓값입니다. 버퍼링, 전송, 수집 처리의 성공 여부는 포함하지 않습니다.

커스텀 이벤트가정한 호출 수모두 보존모두 10% 보존위치 10%, 주요 이벤트 100%
player_position6,0006,000600600
player_death330.33
level_completed110.11
합계6,0046,004600.4604

기댓값은 소수일 수 있지만 한 세션에서 실제로 남는 수는 정수입니다. 네 개의 주요 이벤트를 각각 독립적으로 확률 0.1로 남긴다고 가정하면, 하나도 남지 않을 확률은 0.9^4, 약 66%입니다. 이 예에서는 네 개를 모두 샘플링 검사에서 통과시켜도 전체 수가 거의 달라지지 않습니다.

표에는 SDK 내부에서 생성하는 이벤트나 다른 계측 코드를 포함하지 않았습니다. 청구액이나 전달 완료된 이벤트 수를 추산하는 표도 아닙니다. 실제 데이터양에는 페이로드 크기, 세션 길이, 다른 이벤트 종류와 전달 결과도 영향을 줍니다.

초기화 후 이벤트별 보존율 설정하기

SDK가 성공적으로 초기화된 후, 게임플레이가 시작되기 전에 정책을 적용합니다.

using Framedash;

// Run after the project's TelemetrySDK.Initialize(...) call.
var sdk = TelemetrySDK.Instance;
sdk.SetEventSamplingRate("player_position", 0.10f);
sdk.SetEventSamplingRate("player_death", 1.0f);
sdk.SetEventSamplingRate("level_completed", 1.0f);
sdk.SetEventSamplingRate("map_load", 1.0f);

이벤트 이름은 게임에서 실제로 사용하는 이름과 일치해야 합니다. 예제의 player_position, player_death, level_completed는 애플리케이션에서 직접 정의한 이름입니다. 보존율을 설정한다고 SDK가 이 이벤트를 생성하는 것은 아닙니다. map_load는 SDK의 맵 로드 보고 메서드에서 생성합니다.

이벤트별 재정의는 Unity SDK 가이드에 설명되어 있습니다. 0.1.8의 샘플링 구현에서 이벤트별 값은 전역 값을 대체합니다. 전역 값이 0.1이고 이벤트별 값이 1.0이면 해당 이벤트에는 1.0을 사용하며, 두 값을 곱하지 않습니다. 이름은 대소문자를 구분하며 숫자로 지정한 비율은 0에서 1 사이로 제한됩니다. 이 범위 안의 유한한 값을 전달하세요.

전역 기본값은 1.0이며, 컴포넌트에 직렬화된 샘플링 설정을 초기화할 때 읽습니다. 이벤트 종류가 적다면 이 기본값을 유지하고, 명시한 고빈도 이벤트만 낮추면 정책을 검토하기 쉽습니다. 목록에 없는 새 이벤트에는 기본값이 적용되므로 실수로 고빈도 호출을 추가하지 않았는지도 검토해야 합니다. 설정과 초기화 코드는 TelemetrySDK.cs에서 확인할 수 있습니다.

임시 재정의를 제거하려면 다음을 호출합니다.

sdk.RemoveEventSamplingRate("player_position");

전역 보존율로 돌아가므로 프로젝트 설정에 따라 결과가 1.0이 아닐 수 있습니다. 이벤트별 설정은 런타임 상태입니다. SDK를 종료하고 다시 초기화했다면 설정도 다시 적용해야 합니다. 변경은 이후의 샘플링 판단에만 영향을 주며, 이미 버퍼에 들어갔거나 제외된 이벤트에는 소급 적용되지 않습니다.

샘플링을 거치지 않는 SDK 이벤트 확인하기

위치와 주요 커스텀 이벤트는 이벤트별 샘플링을 거치고, SDK 내부의 세션 시작과 heartbeat는 우회한다. 모든 경로는 이후 버퍼링과 전달 처리를 거친다.

이벤트 기록 구현은 다음 경로를 구분합니다.

  • SDK 내부의 session_start와 perf_heartbeat는 이벤트 샘플링을 우회함
  • 커스텀 Track(...) 호출은 이벤트별 보존율을 사용하고, 재정의가 없으면 전역 값을 사용함
  • EndMapLoad() 또는 ReportMapLoad(...)가 생성하는 map_load는 Track(...)을 거치므로 샘플링 정책이 적용됨

여기서 “자동”은 SDK 내부의 기록 경로를 뜻합니다. BeginAutomatedSession(...)은 세션 메타데이터를 추가하며, 커스텀 이벤트를 샘플링 대상에서 제외하지 않습니다. 커스텀 Track 호출에 내부 이벤트와 같은 이름을 전달해도 내부 경로로 바뀌지 않습니다.

보존율 1.0은 유효한 호출을 샘플링 판단으로 제외하지 않는 설정입니다. 전달을 보장하지는 않습니다. 초기화, 입력 검증, 버퍼 용량, 종료 처리, 네트워크나 수집 처리의 실패는 별도로 확인해야 합니다. 샘플링을 우회하는 SDK 이벤트에도 이 제한이 적용됩니다.

샘플링으로 줄일 수 있는 작업은 판단 이후의 작업입니다. 이 SDK의 Track은 직렬화할 속성을 준비하고 이벤트의 성능 스냅샷을 수집하기 전에 비율을 확인합니다. 게임이 Track을 호출하기 전에 수행한 딕셔너리 할당이나 계산은 이미 발생한 비용입니다. 해당 작업이 무겁다면 보존율뿐 아니라 호출 주기와 페이로드 준비 방식도 살펴봐야 합니다.

이벤트 수와 비율의 의미 유지하기

샘플링 후 이벤트 수는 남은 관측 수입니다. 알려진 고정 보존율 0.1로 600개가 남았다면, 무작위 샘플링 모델이 성립하고 다른 손실이 없다는 가정 아래 600 / 0.1로 원래 호출 수를 6,000개로 추정할 수 있습니다. 누락된 타임스탬프, 위치, 세션 기록까지 복원하는 것은 아닙니다.

샘플링 정책은 빌드나 테스트 설정과 함께 기록합니다. 실행 도중 비율을 바꾸면 각 구간을 나누어 볼 수 있도록 자체 계측 기록에 정보를 남겨야 합니다. 비율이 어떻게 섞였는지 모르는 데이터에는 단일 보정 계수를 적용할 수 없습니다. 기존 대시보드가 보존 확률의 역수를 이용한 가중치를 자동으로 적용한다고 가정하지 마세요.

비율을 계산하려면 이벤트 정의와 모집단이 맞아야 합니다. 시도 횟수 대비 클리어율을 구한다면 시도 시작과 클리어 이벤트를 함께 정의하고, 데이터양이 허용하는 경우 둘 다 샘플링하지 않습니다. 샘플링하지 않은 클리어 수를 샘플링한 위치 기록 수로 나눠도 클리어율이 되지 않습니다. 두 비율이 모두 1.0이어도 결과를 해석하기 전에 중복 호출과 미전달을 확인해야 합니다.

이벤트 단위 샘플링은 이벤트 순서 일부도 누락시킵니다. 클리어만 남고 시작이 남지 않았다고 해서 플레이어가 시작 단계를 건너뛰었다는 근거가 되지는 않습니다. 드문드문 남은 기록으로 전체 퍼널이나 한 플레이어의 이동 경로를 재구성하는 용도는 피하세요.

호출 수를 아는 시나리오로 검증하기

정책을 널리 적용하기 전에 개발용 실행에서 다음을 확인합니다.

  1. 시나리오가 실제로 호출한 커스텀 이벤트 수를 백엔드에 도착한 수와 별도로 계산합니다
  2. 재시도와 재시작 경로를 포함해, 드문 이벤트가 의도한 상태 변화에서 한 번만 발생하는지 확인합니다
  3. 고빈도 테스트 이벤트의 보존율을 잠시 0으로, 다음에는 1로 설정해 예상한 변화가 있는지 확인합니다. SDK 내부 이벤트는 이 테스트 수에서 분리합니다
  4. 계획한 보존율로 되돌린 뒤 충분한 횟수를 반복합니다. 세션마다 정확한 비율을 요구하지 않고, 기댓값 주변에서 수가 달라지는지 관찰합니다
  5. SDK의 플러시 및 진단 안내에 따라 버퍼 처리와 종료 시 전달을 별도로 검증합니다

호출 간격, 이벤트별 보존율, 각 이벤트로 답하려는 질문을 같은 계측 검토에 남깁니다. 다음 개발자가 주변의 Track 호출을 복사하기 전에, 그 데이터의 누락을 허용할 수 있는지 판단할 수 있어야 합니다.