개발을 하다보면 예상치 못한 에러를 자주 마주하게 된다. 보통 Android 앱을 개발할 때, 가상 머신이나 실제 기기를 PC 와 연결하여 Android Studio 의 Logcat 이나 Run 창을 확인하면서 에러의 내용을 파악하고, 그에 맞게 코드를 수정한다. 그런데, Logcat 이 가끔씩 연결이 끊겨서 실시간으로 로그를 볼 수 없는 경우나 디버깅 모드가 지원되지 않는 기기에 APK 를 설치하여 테스트해야하는 경우 등, PC 와 연결하지 못한 상태에서 에러가 발생하는 경우도 흔하다. 그럴 때는 보통 전후 과정을 확인하고, 어디에서 오류가 발생했을 지 추측을 하여 에러를 잡는다. 하지만, 에러문을 직접 눈으로 확인하지 못하기 때문에 불확실한 추측으로 코드를 수정하게 될 수도 있다. 게다가, 디버그 상태가 아니라, 릴리즈 상태에서는 각기 다양한 유저들의 앱 사용으로 인해 정말 많은 경우의 수에 에러가 발생하게 될 수도 있다. 이런 경우에 에러 발생 당시의 로그가 한 곳에 모이면 좋겠다는 생각이 든다. 그래서 필요한 것이 Android Vitals 와 Firebase Crashlytics 이다. 본 글은 Android Vitals 와 Firebase Crashlytics 에 대한 전반적인 내용과 모니터링 하고자 하는 앱과 연동하는 방법에 대해 정리한다.
1. Android Vitals 와 Firebase Crashlytics
Android 앱은 에러, 예외(Exception) 등이 발생하면 ANR(Android Not Responding) 이 발생하여 앱이 비정상적으로 종료되고, 재실행된다. 비정상적인 종료 발생률, ANR 발생률, 불필요한 wakeup 등 다양한 기술 품질 측정항목 전체에서 앱의 성능을 검토하는 것을 Android Vitals 가 수행한다. 한편, Firebase Crashlytics 는 앱의 비정상 종료에 관한 자세한 오류 보고 데이터를 생성하고, 비정상 종료는 Stack Trace 가 유사한 클러스터로 그룹화하여 사용자에게 미치는 영향의 심각도에 따라 분류한다. 이 외에도 커스텀 이벤트를 기록하여 비정상 종료가 발생하는 단계를 파악할 수 있게 해준다. Android Vitals 와 Firebase Crashlytics 의 기능 차이를 표로 정리하면 다음과 같다.
기능 | Android Vitals | Firebase Crashlytics |
비정상 종료 발생률 | O | O |
비정상 종료 통계 | O | |
애플리케이션 응답 없음(ANR) 데이터 제공 | O | |
SDK 또는 앱 변경 불필요 | O | |
비정상 종료 데이터 맞춤설정을 위한 로그 및 키 | O | |
NDK 의 자세한 비정상 종료 추적 | O | O |
BigQuery 에 비정상 종료 데이터 내보내기 | O | |
사전 정의된 Google 애널리틱스 이벤트 캡처 | O | |
크로스 플랫폼 지원 | O | |
외부 도구(Jira, Slack ...) 와의 통합 | O | |
이상치 알림 | O | |
비정상 종료 상태 추적 및 회귀 알림 | O | |
심각하지 않은 문제 기록 | O |
표를 통해 확인할 수 있듯이, 전체적으로 Firebase Crashlytics 가 많은 데이터를 수집할 수 있으나, Android Vitals 만이 수집할 수 있는 데이터도 존재한다. 따라서, 두 보고 시스템을 같이 사용하는 것이 권장된다.
권장사항
1. Android Vitals 대시보드에서 앱 성능을 확인한다.
Android Vitals 를 통해 비정상 종료 발생률, ANR 발생률, 불필요한 wakeup, 부분적인 장기간 wake lock 등을 확인할 수 있다. 알림 환경설정에서 "Android vitals 알림" 을 설정하면 중요한 핵심 vitals 회귀 문제가 발견될 때 이메일을 받을 수 있다.
2. 커스텀 로그(log) 및 키(key)로 커스텀 오류 보고 데이터를 가져온다.
커스텀 로그와 키를 사용하여 추가 비정상 종료 데이터를 기록할 수 있다. 그러면 특정 시점의 정보 스냅샷이 제공되고, 비정상 종료로 이어진 사용자 이벤트가 기록된다.
3. 앱을 Google 애널리틱스와 통합하여 사전 정의된 이벤트를 캡처한다.
탐색경로를 사용하여 사전 정의된 Google 애널리틱스 이벤트를 자동으로 캡처한다. 커스텀 로그 및 키와 마찬가지로 탐색경로는 사용자가 비정상 종료 전에 실행한 작업과 이벤트 내 매개변수를 표시하여 오류 보고를 추가로 개선할 수 있다.
4. 비정상 종료 데이터를 더 세밀하게 제어한다.
비정상 종료 데이터를 Firebase Crashlytics 에서 BigQuery 로 내보내 비정상 종료 데이터에 대한 커스텀 분석을 실행한다.
2. Android Vitals
위 링크를 타고 Android Vitals 페이지로 접속하면, [Play Console로 이동] 버튼을 볼 수 있다. 만약 Play 개발자 계정 상태가 아니라면, 해당 버튼을 눌렀을 때 접속한 Google 계정을 Play 개발자 계정으로 전환해주는 과정을 거치게 된다.
* Play 개발자 계정을 만들기 위해서는 생성 당시에 25달러를 지불해야한다. 2022년 11월 1일 기준, 25달러 == 35,333원... 환율이 조금 떨어지면 시도해보자... *
3. Firebase Crashlytics
Firebase Crashlytics 를 사용하여, 앱 크래쉬, 비정상적인 종료, ANR 에러에 대한 리포트를 받을 수 있다. 리포트를 받기 위해서는 Firebase 콘솔과 Android Studio 양쪽에 Crashlytics 를 세팅해줘야한다.
3-1. Add Firebase Console & Connect App
3-2. Add the Crashlytics SDK to App
App 모듈의 Gradle 파일에서 Android Crashlytics 라이브러리에 대한 Dependency 를 추가한다. 추가하는 Dependency 는 다음과 같다.
- Firebase Android BoM : 라이브러리 버전 관리 제어
- Firebase Crashlytics : App Crash, ANR, 비정상적인 종료 등의 에러 리포트를 받을 수 있는 Crashlytics
- Firebase Analytics : 충돌 없는 사용자, 이동 경로 로그, 속도 경고 등의 통계치를 볼 수 있는 Google Analytics
// [Project_Name]/app/build.gradle
dependencies {
// Import the BoM for the Firebase platform
implementation platform('com.google.firebase:firebase-bom:31.0.2')
// Add the dependencies for the Crashlytics and Analytics libraries
// When using the BoM, you don't specify versions in Firebase library dependencies
implementation 'com.google.firebase:firebase-crashlytics-ktx'
implementation 'com.google.firebase:firebase-analytics-ktx'
}
보통 Crashlytics 와 Analytics 라이브러리의 버전 숫자까지 붙여주지만, Firebase BoM 을 같이 써주었기 때문에 BoM 이 해당 앱에 호환되는 버전으로 관리를 해준다. 위 Gradle 코드는 Kotlin 전용으로, Java 앱에서는 라이브러리 이름의 끝에 있는 "-ktx" 를 없애주면 된다.
3-3. Add the Crashlytics Gradle plugin to App
Root 레벨의 Gradle 파일에서 Crashlytics Gradle plugin 에 대한 빌드 스크립트를 추가한다. Root 레벨의 Gradle 파일에는 plugins 스크립트가 이미 있을 것이다. 추가할 Crashlytics Gradle plugin 스크립트는 plugins 스크립트의 앞에 추가해야한다.
// [Project_Name]/build.gradle
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.2'
classpath 'com.google.gms:google-services:4.3.14'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2'
}
}
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
...
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Root 레벨의 Gradle 파일에 Crashlytics 에 대한 Plugin 빌드 스크립트를 추가하고, Sync 했을 때 아무런 문제 없이 빌드가 성공했다면, 다음 단계로 넘어간다. 이번에는 App 레벨의 Gradle 파일에서 Plugin 스크립트를 추가한다. App 레벨의 Gradle 파일에서 맨 상단에 존재하는 plugins 스크립트 블럭의 안에 다음과 같이 추가한다.
// [Project_Name]/app/build.gradle
plugins {
...
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
}
android {
...
}
dependencies {
...
}
App 모듈의 Gradle 파일에서도 스크립트를 추가하고, Sync 했을 때 아무런 문제가 없다면 Crashlytics 연동은 모두 마무리 되었다. 이제 오류를 유도하여 리포트가 제대로 들어오는지 확인한다.
3-4. Force a test crash to finish setup
특정 버튼을 클릭했을 때 Exception 이 발생하도록 간단한 코드를 추가한다.
binding.button.setOnClickListener {
throw RuntimeException("Test Crash")
}
특정 Activity 에 버튼을 추가하여, 해당 버튼을 클릭했을때 RuntimException 이 발생하도록 추가하였다. 위 코드를 추가한 후에 빌드를 하여 앱을 실행시킨다. 그리고, 그 전에 Crashlytics 페이지를 켜본다.
현재 리포트가 한번도 들어온 적이 없는 상태의 페이지이다. 이제 버튼을 클릭하여 Exception 이 발생했을 때, 페이지가 어떻게 변하는지 관찰해보자.
페이지가 실시간으로 바뀌어, 대쉬보드 형태가 된 것을 확인할 수 있다. 하단의 문제 내역을 통해 어디에서 어떤 문제가 나타났는지, 그리고 해당 문제가 처음 있는 일인지 한눈에 파악할 수 있다. 위 사진에서 리포트된 문제를 클릭하면, 상세 페이지로 이동된다. 그리고 스크롤을 조금만 내려보면, 평소에 Logcat 에서 볼 수 있던 로그 내용을 확인할 수 있다.
Exception 내용 뿐만 아니라, 문제가 발생한 기기의 Android OS 버전 및 기기 모델 정보도 볼 수 있다. 그리고 "스택 추적", "키", "로그", "데이터" 탭을 통해서 다양한 정보도 확인할 수 있다.
지금까지 Android App 과 Firebase Crashlytics 를 연동하는 방법에 대해 정리해보았다. 사실 개발하는 단계에서는 실제 기기나 에뮬레이터로 Logcat 을 통해 바로바로 확인할 수 있어서 문제가 없다. 그러나, 릴리즈 단계로 넘어가면, 연결하지 못한 많은 기기들에서 발생하는 에러들을 볼 수 없기 때문에, Crashlytics 연결을 해주는 것이 좋다. 이제 배포 단계에서 에러가 발생했다는 보고를 받아도, 당황할 필요 없이 Crashlytics 를 통해 확인할 수 있다.
'Android Developer' 카테고리의 다른 글
[Android] Duplicate class androidx.lifecycle.ViewModelLazy found... (2) | 2022.11.21 |
---|---|
화면 자동 꺼짐 없이 항상 화면을 키고 싶을 때 (0) | 2022.11.03 |
Firebase Console 과 Android App 연동 (0) | 2022.11.02 |
RadarChartView 직접 만들기 : 3. Custom Drawing (0) | 2022.10.14 |
RadarChartView 직접 만들기 : 2. Creating a View Class (0) | 2022.10.12 |