💡 Intro
3주차 데모데이 권장 요구사항은 Firebase 연동과 Firebase Analytics, Crashlytics 등 모니터링 환경 구축, Log 설계 이렇게 세 개가 주어졌다.
지난 글에서 Firebase 연동에 대해 정리했으니 이번 글에서는 Analytics, Crashlytics를 이용한 모니터링 환경 구축하는 방법과 Log 설계에 대해 정리하려고 한다. Analytics, Crashlytics를 이용하는 방법은 크게 어렵지 않다. 이것보다는 Log를 어떻게 설계해야 하는지가 굉장히 고민되었다. 그래서 처음부터 로그를 완벽하게 설계한다는 것은 불가능하다고 생각하기 때문에 어느정도 기반만 잡아놓고 앱을 운영하면서 필요성을 느끼면 반영하고 수정하기로 했다.
❓ 로깅
이벤트는 Firebase에서 미리 정의해놓은 이벤트를 사용해도되고, 직접 정의해서 사용해도 된다. 로깅을 하고싶은 곳에서 메서드를 호출하여 간편하게 로깅을 작성할 수 있다. 여기서 작성한 로깅은 프로젝트를 등록하면서 사용한다고 했던 Firebase Analytics와 안드로이드 스튜디오에서 확인할 수 있다.
앞에서도 이야기 했지만 이번에 Firebase Analytics를 처음 사용해보았고 어떤 정보를 로깅해야 유용할까에 대한 의문을 해결하지 못했다. 그래서 우리는 다음과 같은 간단한 로깅 전략을 세웠고 앱을 운영하면서 보완하기로 했다.
- 사용자가 어떤 버튼을 얼마나 클릭하는지 로깅함으로써 사용자 행동을 모니터링하고 파악하여 앱 발전 방향에 도움
- 서버 연결에서 발생할 수 있는 예외 상황에 대해 로깅함으로써 앱의 유지보수성 향상
그럼 로깅하는 방법에 대해 알아보도록 하자.
먼저 로깅을 원하는 뷰에서 FirebaseAnalytics 인스턴스 참조를 통해 획득한다. (Firebase는 object로 선언되어있다.)
lateinit var firebaseAnalytics: FirebaseAnalytics
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityBeginAdventureBinding.inflate(layoutInflater)
setContentView(binding.root)
...
firebaseAnalytics = Firebase.analytics
}
그리고 로깅을 원하는 시점에 logEvent() 메서드를 사용하여 로깅이 가능하다. 다음의 코드는 미리 정의되어 있는 이벤트 로그를 발생시키는 코드다.
binding.ivBeginAdventureRank.setOnClickListener {
firebaseAnalytics.logEvent(FirebaseAnalytics.Event.SELECT_ITEM) {
param(FirebaseAnalytics.Param.ITEM_ID, "iv_beginAdventure_rank")
param(FirebaseAnalytics.Param.ITEM_NAME, "GO_RANK")
param(FirebaseAnalytics.Param.CONTENT_TYPE, "BUTTON")
}
}
위 코드는 ivBeginAdventureRank 버튼을 클릭했을 때 “SELECT_ITEM” 이벤트를 로깅하는 코드다. "SELECT_ITEM" 이벤트는 사용자가 특정 항목을 선택했을 때 호출된다. 그리고 이 이벤트에 여러 파라미터를 추가하여 이벤트와 관련된 추가 정보를 기록할 수 있다.
우리는 일단 공식문서에서 제공하는 id, name, content-type을 그대로 가져와 적용하였다. 추후에 어떻게 기록을 확인할 수 있는지 어떤 것이 유용한 정보인지 파악하여 수정하기로 했다.
파라미터를 설정하는 함수에 대한 설명은 다음과 같다.
- param(FirebaseAnalytics.Param.ITEM_ID, id): 이벤트에 "ITEM_ID" 파라미터를 추가하고, 해당 항목의 고유 식별자인 "id" 값을 설정한다.
- param(FirebaseAnalytics.Param.ITEM_NAME, name): 이벤트에 "ITEM_NAME" 파라미터를 추가하고, 해당 항목의 이름인 "name" 값을 설정한다.
- param(FirebaseAnalytics.Param.CONTENT_TYPE, "image"): 이벤트에 "CONTENT_TYPE" 파라미터를 추가하고, 항목의 컨텐츠 유형을 "image"로 설정한다.
이벤트들은 여기서 이벤트 파라미터에 대한 설명은 여기서 확인할 수 있다.
그럼 이번에는 커스텀 이벤트 로그를 작성해보자. 커스텀 로그를 작성하는 방법은 미리 정의되어 있는 이벤트 로깅을 하는 것과 크게 다르지 않다.
firebaseAnalytics.logEvent("SERVER_ERROR") {
param("API_NAME", apiName)
param("HTTP_STATUS_CODE", httpCode)
param("ERROR_MESSAGE", errorMessage)
}
"SERVER_ERROR"라는 이벤트를 주고, 원하는 파라미터들을 추가할 수 있다.
위 로깅은 API 요청이 실패했을 때 어떤 이유 때문에 실패했는지 기록하기 위해 사용하려고 한다.
커스텀 이벤트에 대한 내용은 여기서 추가로 확인할 수 있다.
그리고 로깅을 하지 않아도 Firebase Analytics에서 자동으로 수집되는 이벤트들도 있다.
- screen_view : 화면 전환이 있는 경우
- first_open : 앱 설치 혹은 재설치 후 처음으로 앱을 실행할 경우
- app_update : 앱 업데이트 후 실행될 경우
그 외의 자동수집 이벤트는 여기서 확인할 수 있다.
❓ 로깅 확인
우리가 로깅한 데이터들은 어디서 어떻게 확인할 수 있을까? 확인하는 방법은 크게 2가지가 있는 것 같다. 먼저 Firebase Console 대시보드에서 확인하는 방법, 안드로이드 스튜디오에서 로그로 확인하는 방법.
1️⃣ Firebase Console 이벤트 대시보드
먼저 Firebase Console의 이벤트 대시보드에서 확인하는 방법을 알아보자.
Firebase Console에서 [출시 및 모니터링] 탭을 클릭하여 [Events] 탭으로 들어가면 다음과 같은 화면이 나온다. Firebase Console의 이벤트 대시보드에서는 앱에서 로깅된 이벤트 유형별로 자동 생성된 이벤트 보고서가 표시된다. 그래서 원하는 이벤트 유형이 얼마나 기록되었는지도 볼 수 있고 원하는 이벤트 유형을 클릭하여 더 자세한 내용도 확인할 수 있다.
버튼을 클릭하는 로깅에서 기록한 이벤트 타입은 select_content 이다. [기존 이벤트]에서 [select_content]를 클릭하면 다음과 같이 해당 이벤트에 대해 조금 더 자세한 내용을 확인할 수 있다.
더 자세하다고 했지만 정말 조금이다. 파라미터로 등록해주었던 자세한 정보들은 확인할 수 없다. 기본 설정에서는 이벤트에 대한 디테일 정보(파라미터로 넣어준 추가 정보)들을 얻기 어렵다. Firebase를 Blaze 요금제로 업그레이드 한 뒤 BigQuery라는 유로 서비스를 사용하여 쿼리문을 통해 확인할 수 있다고 한다.
2️⃣ 안드로이드 스튜디오에서 확인
다음으로 안드로이드 스튜디오에서 로그로 확인하는 방법에 대해 알아보자. (이를 위해 ADB를 설치하고 연동했다!!)
터미널에 다음의 명령어를 입력해주자.
adb shell setprop log.tag.FA VERBOSE
adb shell setprop log.tag.FA-SVC VERBOSE
adb logcat -v time -s FA FA-SVC
- adb shell setprop log.tag.FA VERBOSE: Firebase Analytics의 FA 태그의 로그 레벨을 VERBOSE로 설정한다. 이 명령어는 FA 태그의 모든 로그를 출력하도록 설정한다.
- adb shell setprop log.tag.FA-SVC VERBOSE: Firebase Analytics의 FA-SVC 태그의 로그 레벨을 VERBOSE로 설정한다. 이 명령어는 FA-SVC 태그의 모든 로그를 출력하도록 설정한다.
- adb logcat -v time -s FA FA-SVC: 위의 두 명령어로 설정한 FA와 FA-SVC 태그의 로그를 로그캣으로 출력한다. v time 옵션은 각 로그 메시지에 시간 정보를 포함하여 출력하도록 지정한 것이다.
실행 후 해당 디바이스에서 발생하는 Firebase Analytics 로그를 확인할 수 있게 된다. 하지만 로그 레벨을 VERBOSE로 설정하면 로그가 굉장히 많이 출력될 수 있으니 주의하자.
단순히 안드로이드 스튜디오에서 Firebase로 보내는 로그를 확인하고 싶은 것이라면 아래 두 줄의 명령어만 입력해도 된다.
adb shell setprop log.tag.FA VERBOSE
adb shell setprop log.tag.FA-SVC VERBOSE
확인한 로그는 다음과 같다.
name=select_content
item_name=GO_RANK
content_type = BUTTON
으로 넣어준 파라미터들이 잘 들어갔다는 것을 확인할 수 있다. 그 외에도 어떤 스크린 클래스에서 발생했는지, 그 스크린 클래스의 id가 무엇인지 등의 정보도 나온다.
하지만 앞에서도 본 것처럼 이러한 자세한 정보는 Firebase Console의 이벤트 대시보드에서는 보이지 않는다. 그럼 좀 더 자세히 보고싶다면 어떻게 해야할까?
❓ DebugView
로깅을 처음 해보는 관계로 우리가 짠 코드가 정상적으로 Firebase Analytics로 넘어가고 있는지 확인이 필요했다. 하지만 Firebase Analytics 대시보드에서는 어떠한 정보도 나오지 않았다.
여기서 한 가지 더 문제점(?)이라고 할 수 있는 것이 있는데, 이벤트나 사용자 사용내역이 바로 반영되지 않는 것이었다. 어떻게 확인할 수 있을까 찾아보다 Firebase Analytics DebugView를 발견하게 되었다.
DebugView는 Firebase 콘솔을 통해 제공되는 도구로, 앱 개발 중 Firebase Analytics 이벤트가 제대로 작동하는지 확인하는 데 도움을 주는 기능이다. 이 기능을 사용하면 앱에서 발생하는 Firebase Analytics 이벤트를 거의 실시간으로 모니터링하고 확인할 수 있다. 이 기능은 개발 중 계측 단계에서 검증하는 데 매우 유용하며, 애널리틱스 구현 시의 오류와 실수를 발견하고 모든 이벤트와 사용자 속성이 정상적으로 로깅되는지 확인하는 데 도움이 될 수 있다.
일반적으로 앱이 로깅하는 이벤트는 약 1시간 동안 취합된 후 일괄 업로드되는데, 이렇게 하는 이유는 최종 사용자 기기의 배터리를 절약하고 네트워크 데이터 사용량을 줄이기 위함이라고 한다. 그러나 개발 기기에서 디버그 모드를 사용 설정하여 이벤트를 업로드하는 지연 시간을 최소화하면 애널리틱스 구현을 검증하고 DebugView 보고서에서 결과를 조회할 수 있다.
Android 기기에서 애널리틱스 디버그 모드를 사용하기 위해서는 다음 명령어를 터미널에 입력하면 된다. PACKAGE_NAME에 본인 앱의 패키지명을 입력하면된다.
adb shell setprop debug.firebase.analytics.app PACKAGE_NAME
ex) adb shell setprop debug.firebase.analytics.app com.now.naaga
위 명령어를 입력한 뒤 앱을 실행하고 Firebase Console → [애널리틱스] → [DebugView] 로 들어가 새로고침을 하면 디버그 기기가 추가된 것을 확인할 수 있을 것이다.
DebugView에서는 개발자가 사용하는 에뮬레이터(혹은 실기기)에서 하는 대부분의 동작들을 위처럼 GUI를 통해 거의 실시간으로 확인할 수 있다.
그리고 만약 디버그 모드를 중지하고 싶다면 다음 명령어를 터미널에 입력해주면 된다. 이 명령어를 입력하여 디버그 모드를 명시적으로 중지할 때까지 디버그 모드는 유지된다.
adb shell setprop debug.firebase.analytics.app .none.
❓Crashlytics
Firebase Crashlytics는 가벼운 실시간 비정상 종료 보고 도구로 앱 품질을 저하하는 안정성 문제를 추적하고 우선순위를 지정하고 문제를 해결하는 데 도움이 된다. Crashlytics는 비정상 종료를 그룹화하고 이러한 비정상 종료를 유발하는 상황을 강조하여 보여주므로 문제 해결 시간이 절약된다.
특정 비정상 종료가 여러 사용자에게 영향을 미치는지 확인할 수 있고, 문제의 심각도가 급격히 증가하면 알림이 전송된다. 그리고 어떤 코드 라인이 비정상 종료를 일으키는지 파악할 수 있다.
이미 프로젝트에 Firebase가 추가되어 있는 상태라면 Crashlytics를 연결하는 방법은 굉장히 간단하다.
모듈(앱 수준) Gradle 파일에 다음과 같은 코드를 추가한다.
dependencies {
// Import the BoM for the Firebase platform
implementation(platform("com.google.firebase:firebase-bom:32.2.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")
}
만약 Firebase Analytics가 추가되어 있다면 implementation("com.google.firebase:firebase-crashlytics-ktx") 한 줄만 추가해도 될 것이다.
다음으로 루트 수준(프로젝트 수준) Gradle 파일에 Crashlytics Gradle 플러그인을 plugins 블록에 추가한다.
plugins {
id("com.android.application") version "7.2.0" apply false
// ...
// Make sure that you have the Google services Gradle plugin dependency
id("com.google.gms.google-services") version "4.3.15" apply false
// Add the dependency for the Crashlytics Gradle plugin
id("com.google.firebase.crashlytics") version "2.9.8" apply false
}
다음으로 다시 모듈(앱 수준) Gradle 파일에 다음과 같은 코드를 추가한다.
plugins {
id("com.android.application")
// ...
// Make sure that you have the Google services Gradle plugin
id("com.google.gms.google-services")
// Add the Crashlytics Gradle plugin
id("com.google.firebase.crashlytics")
}
이러면 Firebase Crashlytics를 사용할 준비는 끝났다.
val crashButton = Button(this)
crashButton.text = "Test Crash"
crashButton.setOnClickListener {
throw RuntimeException("Test Crash") // Force a crash
}
addContentView(crashButton, ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT))
공식 문서에서 제공하는 위 코드를 onCreate() 메서드 안에 붙여넣어 넣어주면 다음과 같이 뷰에 “Test Crash”라는 이름을 가진 버튼이 생성된다. 그리고 해당 버튼을 누르면 crash가 발생하는 구조다.
그리고 Firebase Console [출시 및 모니터링] → [Crashlytics]로 들어가면 비정상 종료된 것들을 확인할 수 있다. 그리고 자세히 보면 어떤 이유로 앱이 비정상 종료되었는지도 나온다.
참고