iOS·Android 기기별 프레임 드랍 구간을 AI로 자동 탐지하고 리포팅하는 방법
모바일 게임의 기기별 스터터링을 프레임 타임 텔레메트리와 이상치 탐지로 자동 분류하는 실전 방법을 설명합니다. P95·P99 지표, 구간 병합, 원인 후보 리포트와 QA 운영 절차를 제공합니다.

핵심 요약
모바일 게임의 스터터링은 평균 FPS가 아니라 프레임 타임(frame time)의 순간적인 급등으로 탐지해야 한다. iOS·Android 기기에서 수집한 프레임 타임을 빌드, 기기 모델, 그래픽 설정, 씬 단위로 분리한 뒤 MAD 기반 이상치 탐지와 연속 구간 병합을 적용하면 자동화된 재현 후보와 리포트를 만들 수 있다.
핵심은 16.67ms(60FPS) 또는 33.33ms(30FPS) 같은 목표 프레임 예산을 넘긴 프레임을 단순 집계하지 않고 언제·어떤 기기·어떤 게임 이벤트에서 연속적으로 발생했는지를 기록하는 것이다.
모바일 게임 프레임 드랍과 스터터링은 왜 기기별로 다르게 발생하는가?
프레임 드랍은 일정 시간 동안 목표 FPS를 유지하지 못하는 상태이며 스터터링은 플레이어가 끊김으로 인지할 정도로 프레임 간 시간이 불규칙해지는 현상이다. 평균 FPS가 59~60이어도 한 프레임이 80ms로 튀면 플레이 경험은 나빠질 수 있다.
프레임 시간과 FPS의 관계는 다음과 같다.
60FPS 목표라면 한 프레임 예산은 다음이다.
다만 모바일에서는 디스플레이 주사율, VSync, 열 제어, 백그라운드 작업 때문에 16.67ms를 조금 넘는 프레임이 항상 결함은 아니다. 자동 탐지는 단일 프레임보다 초과 폭, 연속 횟수, 반복성을 함께 판단해야 한다.
| 원인 범주 | iOS에서 자주 확인할 항목 | Android에서 자주 확인할 항목 | 텔레메트리 단서 |
|---|---|---|---|
| 렌더링 부하 | Metal 드로우콜, 텍스처 메모리, 셰이더 컴파일 | GPU 드라이버 차이, Vulkan/OpenGL ES 경로, 해상도 | GPU 시간 또는 렌더 스레드 시간 급등 |
| CPU 부하 | 메인 스레드 동기 작업, 가비지 컬렉션 | 빅·리틀 코어 스케줄링, 제조사 서비스 | CPU 프레임 시간 급등 |
| 에셋 로딩 | AssetBundle 역직렬화, 텍스처 업로드 | APK/AAB 압축 해제, 저장소 성능 차이 | 씬 전환·스폰 직후 긴 프레임 |
| 발열·전력 | 저전력 모드, 열 상태 변화 | Thermal throttling, 배터리 절약 모드 | 플레이 시간이 길수록 기준선 상승 |

프레임 드랍 AI 자동 탐지에 필요한 데이터는 무엇인가?
탐지 모델보다 먼저 이벤트 스키마를 고정한다. 기기별 비교가 목적이라면 프레임 하나마다 모든 정보를 담을 필요는 없지만 스터터 구간을 원인 후보와 연결할 최소한의 문맥은 필요하다.
권장 프레임 텔레메트리 스키마
| 필드 | 예시 | 용도 |
|---|---|---|
timestamp_ms | 184233 | 구간 재구성 |
frame_time_ms | 42.8 | 스터터 판정의 원본 |
cpu_frame_ms | 28.1 | CPU 병목 후보 분류 |
gpu_frame_ms | 11.4 | GPU 병목 후보 분류 |
device_model | iPhone15,3, SM-S918N | 기기군 비교 |
os_version | iOS 18.1, Android 15 | OS별 회귀 확인 |
build_id | 1.8.0(421) | 빌드 회귀 추적 |
scene | Battle_03 | 재현 위치 식별 |
event_name | boss_spawn | 게임 이벤트 상관관계 |
quality_level | High | 설정별 분리 |
Unity 2022 LTS 이상이라면 Time.unscaledDeltaTime으로 프레임 시간을 수집할 수 있다. 아래 코드는 샘플링 간격을 둬 전송량을 줄이는 최소 예시다. 실제 서비스에서는 메모리에 배치로 모은 뒤 HTTPS로 전송하고 개인정보나 계정 식별자는 수집 대상에서 제외한다.
using System.Collections.Generic;
using UnityEngine;
public sealed class FrameTimeSampler : MonoBehaviour
{
private const int SampleEveryNFrames = 6;
private readonly List<float> frameTimesMs = new();
void Update()
{
if (Time.frameCount % SampleEveryNFrames != 0) return;
float frameTimeMs = Time.unscaledDeltaTime * 1000f;
frameTimesMs.Add(frameTimeMs);
}
public float[] DrainSamples()
{
float[] result = frameTimesMs.ToArray();
frameTimesMs.Clear();
return result;
}
}
샘플링만으로는 1~2프레임짜리 급등을 놓칠 수 있다. 그래서 출시 전 자동 성능 테스트나 의심 기기군에서는 매 프레임 수집을 사용하고 라이브 빌드에서는 5~10프레임 간격 수집과 임계치 초과 시 즉시 원본 프레임 기록을 조합하는 편이 현실적이다.
iOS·Android 스터터링 구간은 어떻게 자동 탐지할까?
가장 작은 운영 가능한 해법은 고정 프레임 예산 + 강건한 이상치 기준이다. 기기 성능이 다른 환경에서 모든 기기에 같은 절대 임계치만 적용하면 저사양 기기는 과다 보고되고 고사양 기기의 미세 회귀는 놓친다.
Step 1. 목표 프레임 예산으로 후보 프레임을 거른다
게임의 목표 FPS에 따라 기본 예산을 정한다.
| 목표 FPS | 프레임 예산 | 초기 경고 기준 예시 |
|---|---|---|
| 60 | 16.67ms | 33.33ms 초과 |
| 45 | 22.22ms | 44.44ms 초과 |
| 30 | 33.33ms | 66.67ms 초과 |
초기 경고 기준을 예산의 2배로 잡으면 VSync 경계의 작은 흔들림보다 눈에 띄는 히치를 우선 수집할 수 있다. 이 값은 제품 요구사항에 따라 조정한다.
Step 2. MAD로 기기별 기준선에서 벗어난 프레임을 찾는다
평균과 표준편차는 긴 히치 몇 개에 영향을 크게 받는다. 모바일 프레임 타임처럼 한쪽으로 긴 꼬리를 가진 데이터에는 중앙값과 MAD(Median Absolute Deviation)가 더 안정적이다.
같은 build_id, device_model, scene, quality_level 그룹에서 robust z >= 3.5인 프레임을 이상치 후보로 둔다. MAD가 0인 매우 안정적인 구간은 목표 프레임 예산 초과 여부만 사용하면 된다.
Step 3. 연속 이상치를 하나의 스터터 구간으로 병합한다
단일 이상치 프레임을 각각 버그로 만들면 리포트가 폭주한다. 후보 프레임 사이의 간격이 500ms 이하면 하나의 구간으로 합치고 구간마다 최대 프레임 시간과 초과 프레임 수를 계산한다.
from dataclasses import dataclass
@dataclass
class StutterRange:
start_ms: int
end_ms: int
peak_frame_ms: float
over_budget_frames: int
def merge_stutters(samples, budget_ms, max_gap_ms=500):
ranges = []
current = None
for timestamp_ms, frame_ms in samples:
if frame_ms <= budget_ms * 2:
continue
if current and timestamp_ms - current.end_ms <= max_gap_ms:
current.end_ms = timestamp_ms
current.peak_frame_ms = max(current.peak_frame_ms, frame_ms)
current.over_budget_frames += 1
else:
current = StutterRange(timestamp_ms, timestamp_ms, frame_ms, 1)
ranges.append(current)
return ranges
assert len(merge_stutters([(0, 17), (100, 40), (300, 55), (900, 50)], 16.67)) == 2

AI 리포트는 어떤 기준으로 우선순위를 정해야 하는가?
여기서 AI는 원인을 확정하는 도구가 아니라 대량의 성능 로그를 분류하고 QA가 확인할 첫 지점을 제안하는 도구로 쓰는 것이 안전하다. 원인 판정에는 프로파일러 캡처와 재현 테스트가 필요하다.
리포트 우선순위는 아래처럼 단순한 점수로 시작할 수 있다.
peak_frame_ms: 구간의 최대 프레임 시간occurrence_rate: 동일 시나리오 실행 중 발생 비율affected_device_weight: 목표 기기군 또는 매출 비중이 높은 기기군에 부여한 가중치
LLM에 전달할 때는 원본 수천 프레임을 그대로 넣지 않는다. 집계된 구간, 이전 빌드 대비 변화량, 직전·직후 이벤트, CPU/GPU 시간만 구조화된 JSON으로 제공한다. 그러면 모델은 다음과 같이 요약할 수 있다.
{
"device": "SM-S918N",
"build": "1.8.0(421)",
"scene": "Battle_03",
"event": "boss_spawn",
"stutter_window": "184233-185010ms",
"peak_frame_ms": 92.4,
"over_budget_frames": 8,
"cpu_frame_ms_peak": 76.2,
"gpu_frame_ms_peak": 14.8,
"baseline_build_peak_ms": 31.6
}
이 입력에서 자동 생성할 리포트에는 반드시 관측 사실과 추정 원인을 분리한다.
| 리포트 항목 | 예시 | 성격 |
|---|---|---|
| 관측 | Android 15의 SM-S918N에서 보스 스폰 직후 92.4ms 피크 발생 | 측정값 |
| 비교 | 이전 빌드 피크 31.6ms 대비 60.8ms 증가 | 측정값 |
| 분류 | CPU-bound 가능성 높음 | 추정 |
| 확인 요청 | 스폰 시 동기 에셋 로드와 GC Alloc 프로파일 확인 | 다음 행동 |
자동 성능 리포팅 파이프라인은 어떻게 운영할까?
flowchart LR
A[자동 플레이 테스트] --> B[프레임 텔레메트리 수집]
B --> C[기기·빌드·씬별 집계]
C --> D[이상치 탐지와 구간 병합]
D --> E[이전 빌드 비교]
E --> F[AI 요약 리포트]
F --> G[QA 재현 및 프로파일러 확인]
G --> H[버그 등록 또는 기준선 갱신]
- 고정 시나리오를 만든다. 로그인, 로비 진입, 전투 시작, 보스 스폰, 스킬 연계, 결과 화면처럼 스파이크가 나기 쉬운 행동을 자동 플레이에 포함한다.
- 기기 매트릭스를 최소화한다. 출시 지원 기기 전체를 매 빌드에 돌리기보다 iPhone, Galaxy, Pixel 등에서 OS와 SoC 세대를 대표하는 핵심 기기군부터 선정한다.
- 이전 정상 빌드와 비교한다. 절대 기준 초과뿐 아니라 동일 기기·동일 시나리오에서 P95, P99, 최대 프레임 시간의 변화량을 회귀 조건으로 둔다.
- QA가 재현 가능한 정보만 티켓으로 보낸다. 빌드, 기기 모델, OS, 그래픽 설정, 시나리오 단계, 구간 시각, 이벤트, 로그 링크를 템플릿에 포함한다.
P95와 P99는 전체 프레임 중 각각 하위 성능 5%, 1% 경계의 프레임 시간을 뜻한다. 평균 FPS가 유지돼도 P99가 악화됐다면 플레이어가 느끼는 끊김이 늘었을 가능성이 높다.
자주 묻는 질문 (FAQ)
평균 FPS만으로 프레임 드랍을 탐지할 수 있나?
권장하지 않는다. 평균 FPS는 짧고 큰 스파이크를 숨긴다. 최대 프레임 시간, P95, P99, 목표 프레임 예산 초과 프레임 수를 함께 본다.
AI가 스터터링의 원인을 확정할 수 있나?
아니다. AI는 로그 기반으로 CPU 병목, GPU 병목, 에셋 로딩 같은 후보를 우선순위화할 수 있다. 원인 확정은 Unity Profiler, Xcode Instruments, Android GPU Inspector 등으로 재현해 확인한다.
저사양 Android 기기의 느린 프레임을 모두 버그로 등록해야 하나?
아니다. 기기 등급과 목표 FPS를 먼저 정의하고 해당 기준선에서 이전 빌드보다 악화됐거나 지원 정책을 벗어난 경우를 회귀 버그로 등록한다.
정리
기기별 스터터링 자동 탐지는 복잡한 모델부터 도입할 필요가 없다. 프레임 타임 수집, 목표 예산 초과 감지, MAD 기반 이상치 탐지, 연속 구간 병합, 이전 빌드 비교만 갖춰도 QA가 수동 로그를 훑는 시간을 크게 줄일 수 있다.
AI는 측정값을 대체하지 않는다. 측정된 구간을 읽기 쉬운 재현 후보와 원인 확인 목록으로 바꾸는 역할에 한정할 때, iOS·Android 성능 회귀를 더 빠르고 신뢰성 있게 운영할 수 있다.



