Crashlytics와 Sentry로 안드로이드·iOS 게임 크래시 로그 분석하기
Firebase Crashlytics와 Sentry를 함께 연동해 Android/iOS 게임 크래시를 수집·심볼리케이션·분류하는 방법을 정리합니다. dSYM·매핑 파일, 사용자 식별, 릴리스 추적과 운영 대응 절차를 단계별로 설명합니다.

핵심 요약
Crashlytics는 Firebase 중심의 안정성 지표와 치명적 오류 집계에 Sentry는 릴리스·사용자 컨텍스트·브레드크럼을 포함한 원인 추적에 강점이 있다. 두 도구를 함께 쓰려면 동일한 release, userId, 게임 버전 규칙을 정의하고 Android의 R8/ProGuard 매핑 파일과 iOS의 dSYM을 반드시 업로드해야 사람이 읽을 수 있는 스택 트레이스를 얻을 수 있다.
Crashlytics와 Sentry를 함께 쓰는 이유는 무엇인가?
게임 크래시는 단순히 예외 메시지를 보는 것만으로 해결되지 않는다. 어떤 빌드에서 어떤 기기·OS·그래픽 설정에서 어떤 플레이 흐름 뒤에 발생했는지를 함께 봐야 재현과 우선순위 판단이 가능하다.
| 항목 | Firebase Crashlytics | Sentry |
|---|---|---|
| 주 용도 | Firebase 기반 크래시 집계와 안정성 모니터링 | 오류 원인 추적과 릴리스 단위 이슈 분석 |
| 강점 | 크래시 없는 사용자 비율, Firebase 생태계 연동 | 브레드크럼, 태그, 컨텍스트, 이슈 그룹화 |
| 비정상 종료 | 처리 가능 | 처리 가능 |
| 처리된 예외 | recordException으로 기록 | captureException으로 기록 |
| 운영 방식 | 전체 안정성 지표 확인 | 특정 이슈의 재현 조건과 영향 범위 조사 |
한 도구만 사용해도 되지만 라이브 게임에서는 Crashlytics로 장애 규모를 빠르게 확인하고 Sentry로 원인을 좁히는 조합이 실용적이다. 다만 동일한 예외가 두 곳에 중복 보고되므로 알림 채널은 도구별 역할을 나눠 과도한 경보를 막는다.
크래시 로그 분석은 어떤 흐름으로 진행되는가?
flowchart LR
A[클라이언트 예외 또는 네이티브 크래시] --> B[Crashlytics·Sentry SDK 수집]
B --> C[매핑 파일·dSYM 심볼리케이션]
C --> D[이슈 그룹화와 릴리스 연결]
D --> E[기기·OS·사용자 흐름으로 재현 조건 확인]
E --> F[핫픽스 배포 후 새 릴리스의 발생률 검증]
분석의 출발점은 스택 트레이스가 아니라 영향도다. 먼저 최근 릴리스의 크래시 없는 사용자 비율, 영향을 받은 사용자 수, 발생 시작 시점을 확인한다. 다음으로 동일 이슈가 특정 Android 기기, iOS 버전, 씬 전환, 광고 SDK 호출처럼 공통 조건을 갖는지 비교한다.

Android와 iOS 크래시 로그를 읽을 수 있게 하려면?
난독화 또는 컴파일된 네이티브 코드가 포함된 빌드는 원본 함수명 대신 난독화된 클래스명이나 메모리 주소를 보고할 수 있다. 이를 사람이 읽는 호출 스택으로 되돌리는 과정을 심볼리케이션이라고 한다.
Android: R8·ProGuard 매핑 파일을 보관한다
Android 릴리스 빌드에서 R8 또는 ProGuard를 쓰면 mapping.txt가 생성된다. 이 파일은 빌드마다 달라지므로 배포한 AAB/APK와 정확히 같은 빌드의 파일을 Crashlytics와 Sentry에 업로드해야 한다.
- CI에서 릴리스 빌드 직후 매핑 파일 업로드를 자동화한다.
versionCode와 Git 커밋 SHA를 빌드 산출물 메타데이터로 남긴다.- 매핑 파일을 나중에 재생성할 수 있다고 가정하지 말고 배포 아티팩트와 함께 보관한다.
iOS: dSYM 누락 여부를 먼저 확인한다
iOS 크래시 주소를 함수명과 소스 행으로 해석하려면 해당 앱 바이너리의 UUID와 일치하는 dSYM이 필요하다. Xcode Archive 생성 뒤 dSYM이 업로드되지 않으면 0x000000... 형태의 주소 중심 스택만 남아 원인 파악 시간이 크게 늘어난다.
- Xcode의 Archive 산출물에 dSYM이 포함되는지 확인한다.
- Bitcode를 사용하는 배포 구성이라면 App Store 처리 후 생성되는 심볼 파일 흐름도 점검한다.
- Crashlytics와 Sentry 모두에 같은 릴리스의 dSYM을 업로드한다.
Crashlytics 및 Sentry는 어떻게 구현할까?
1. 릴리스 식별자를 통일한다
두 SDK의 릴리스 이름을 게임이름@버전(빌드번호)처럼 동일한 형식으로 설정한다. 예를 들어 [email protected](208)처럼 기록하면 대시보드와 배포 이력을 쉽게 연결할 수 있다.
// Android Kotlin 예시
FirebaseCrashlytics.getInstance().setCustomKey("release", "[email protected](208)")
FirebaseCrashlytics.getInstance().setCustomKey("content_version", "2026.08.09")
Sentry.configureScope { scope ->
scope.setTag("release", "[email protected](208)")
scope.setTag("content_version", "2026.08.09")
}
Sentry SDK의 실제 release 설정은 SDK 초기화 시점에 지정하는 편이 이슈와 릴리스 화면의 연결에 더 적합하다. 위 태그는 분석 편의를 위한 보조 식별자다.
2. 개인정보를 제외한 사용자·세션 맥락을 기록한다
이메일, 실명, 채팅 원문, 인증 토큰은 오류 로그에 넣지 않는다. 대신 내부 계정 ID를 일방향 해시하거나 서비스용 익명 ID를 사용한다. 서버와 클라이언트에서 같은 사용자를 추적해야 한다면 개인정보 처리 정책과 보존 기간도 함께 정한다.
val playerId = "anon_8f13c4" // 개인정보가 아닌 서비스 내부 식별자
FirebaseCrashlytics.getInstance().setUserId(playerId)
FirebaseCrashlytics.getInstance().setCustomKey("scene", "Battle_03")
FirebaseCrashlytics.getInstance().setCustomKey("renderer", "Vulkan")
Sentry.configureScope { scope ->
scope.user = io.sentry.protocol.User().apply { id = playerId }
scope.setTag("scene", "Battle_03")
scope.setTag("renderer", "Vulkan")
}
3. 처리된 예외와 브레드크럼을 남긴다
앱이 종료되지 않은 예외도 콘텐츠 다운로드 실패, 저장 데이터 역직렬화 실패, 결제 응답 파싱 실패처럼 플레이 중단으로 이어질 수 있다. 예외 직전의 씬 이름, 네트워크 요청 종류, 저장 슬롯 등 최소한의 문맥을 남긴다.
try {
loadPlayerSave(slotId)
} catch (error: Exception) {
FirebaseCrashlytics.getInstance().setCustomKey("save_slot", slotId)
FirebaseCrashlytics.getInstance().recordException(error)
Sentry.addBreadcrumb("플레이어 저장 데이터를 불러오는 중 오류 발생")
Sentry.captureException(error)
}
Sentry 브레드크럼에는 매 프레임 이벤트나 대용량 응답 본문을 넣지 않는다. 로그량이 늘고 개인정보 유출 위험이 커지며 정작 중요한 사용자 행동이 묻힐 수 있다.
게임 크래시 우선순위는 어떻게 정할까?
발생 건수만으로 순위를 정하면 반복 재시도하는 소수 사용자 문제를 과대평가할 수 있다. 사용자 영향을 기준으로 한 간단한 우선순위 점수는 다음처럼 잡을 수 있다.
AffectedUsers: 고유 사용자 수Severity: 시작 불가·결제·저장 손상은 높은 값, 복구 가능한 기능 오류는 낮은 값ReleaseRecency: 신규 배포 직후 문제를 빠르게 포착하기 위한 가중치
예를 들어 로그인 직후 종료되는 이슈는 발생 건수가 적어도 심각도가 높다. 반대로 특정 선택 기능에서만 복구 가능한 오류가 반복된다면 사용자 수와 전환 영향까지 확인한 뒤 우선순위를 정하는 편이 낫다.
운영에서 재현 가능한 이슈로 바꾸는 방법
- 영향 범위를 고정한다. 이슈의 첫 발생 릴리스, OS 버전, 기기 모델, 그래픽 API, 네트워크 상태를 확인한다.
- 공통 브레드크럼을 찾는다. 크래시 직전의 씬 진입, 에셋 번들 로드, 광고 호출, 저장 작업 등 반복되는 순서를 비교한다.
- 수정과 검증을 분리한다. 핫픽스에는 원인 가설과 방어 코드를 기록하고 배포 뒤 새 릴리스에서 고유 영향 사용자 수와 크래시 없는 사용자 비율이 회복되는지 확인한다.
수정 코드에는 예외를 무조건 삼키는 방식보다 실패 원인을 기록하고 안전한 대체 경로를 제공하는 방식을 우선한다. 예를 들어 저장 데이터가 손상됐을 때 기본값으로 복구할 수 있다면 복구 사실과 데이터 버전만 로그로 남기고 원본 저장 내용은 전송하지 않는다.
자주 묻는 질문 (FAQ)
Crashlytics와 Sentry를 둘 다 사용하면 크래시가 중복 집계되지 않는가?
각 서비스에는 같은 크래시가 각각 기록된다. 전체 안정성 KPI는 Crashlytics, 조사와 알림은 Sentry처럼 책임을 나누고 하나의 합산 수치로 해석하지 않으면 혼선을 줄일 수 있다.
iOS 스택 트레이스가 함수명 대신 주소로 보이는 이유는 무엇인가?
대부분 해당 릴리스와 일치하는 dSYM이 누락됐거나 UUID가 맞지 않기 때문이다. 배포한 Archive의 dSYM을 찾아 업로드하고 CI에서 자동 업로드하도록 구성한다.
처리된 예외도 모두 전송해야 하는가?
아니다. 사용자 경험과 복구 가능성에 영향을 주는 오류를 우선 수집한다. 반복성이 높은 예상 실패는 샘플링하거나 지표로 집계해 오류 추적 시스템의 노이즈를 줄인다.
정리
안정적인 크래시 분석의 핵심은 SDK를 추가하는 일이 아니라 릴리스 식별, 심볼 파일 업로드, 개인정보를 제외한 문맥 기록, 배포 후 지표 검증을 하나의 운영 절차로 만드는 데 있다. Crashlytics로 장애 규모를 보고 Sentry로 재현 조건을 확인하면 Android와 iOS의 크래시를 수정 가능한 작업 항목으로 전환할 수 있다.



