Wednesday, July 19, 2023

Firebase Crashlytics: 안정적인 앱 운영을 위한 필수 리포팅 시스템 구축

오늘날 모바일 애플리케이션 시장에서 성공은 단순히 뛰어난 기능을 제공하는 것만으로 결정되지 않습니다. 사용자는 매끄럽고 안정적인 경험을 기대하며, 잦은 충돌(Crash)이나 오류는 사용자의 이탈을 유발하는 가장 큰 원인 중 하나입니다. 단 한 번의 부정적인 경험만으로도 사용자는 앱을 삭제하고 경쟁 앱으로 돌아설 수 있습니다. 따라서 개발팀에게 있어 앱의 안정성을 선제적으로 관리하고, 문제가 발생했을 때 신속하게 원인을 파악하여 해결하는 능력은 비즈니스의 성패를 좌우하는 핵심 역량이 되었습니다.

이러한 배경 속에서 Firebase Crashlytics는 단순한 충돌 보고 도구를 넘어, 앱의 '건강'을 진단하고 관리하는 필수적인 플랫폼으로 자리매김했습니다. 실시간으로 발생하는 모든 충돌과 처리되지 않은 예외(Unhandled Exception)를 자동으로 수집하고, 이를 의미 있는 데이터로 가공하여 개발자에게 제공합니다. 이를 통해 개발자는 더 이상 "제 폰에서는 잘 되는데요?"와 같은 막연한 상황에 의존하거나, 불완전한 사용자 피드백에 기대지 않고도 문제의 핵심에 접근할 수 있습니다. Crashlytics는 어떤 기기, 어떤 운영체제 버전, 어떤 상황에서 문제가 발생했는지에 대한 상세한 컨텍스트를 제공함으로써 디버깅 프로세스를 획기적으로 단축시키고, 개발팀이 보다 중요한 가치 창출에 집중할 수 있도록 돕습니다.

본 글에서는 Firebase Crashlytics의 기본적인 설정 방법부터 시작하여, 수집된 데이터를 분석하고 문제 해결의 실마리를 찾는 고급 활용법, 그리고 다른 Firebase 서비스와의 연계를 통해 시너지를 극대화하는 전략까지 심도 있게 다룰 것입니다. Crashlytics를 단순한 오류 수집기를 넘어, 제품의 품질을 한 단계 끌어올리는 강력한 무기로 활용하는 여정을 함께 시작하겠습니다.

1. Firebase Crashlytics 연동: 첫걸음 떼기

Crashlytics의 강력한 기능을 활용하기 위한 첫 단계는 앱에 SDK를 정확하게 설치하고 설정하는 것입니다. 이 과정은 Android와 iOS 플랫폼에서 약간의 차이가 있지만, 기본적인 흐름은 Firebase 프로젝트 생성, 앱 등록, SDK 추가, 그리고 초기화로 동일합니다. 각 플랫폼별로 상세한 과정을 살펴보겠습니다.

1.1. Firebase 프로젝트 생성 및 앱 등록

모든 작업은 Firebase 콘솔에서 시작됩니다. 기존에 사용하던 Firebase 프로젝트가 없다면 새로 생성해야 합니다.

  1. Firebase 콘솔 접속: Firebase 콘솔로 이동하여 Google 계정으로 로그인합니다.
  2. 프로젝트 추가: '프로젝트 추가' 버튼을 클릭하고 프로젝트 이름을 입력합니다. 이 이름은 사용자에게 표시되지 않는 내부 식별자입니다.
  3. Google Analytics 설정: 이 단계에서 Google Analytics를 사용 설정하는 것을 강력히 권장합니다. Analytics를 연동하면 '비정상 종료 없는 사용자' 비율과 같은 중요한 안정성 측정항목을 추적할 수 있으며, 특정 사용자 행동과 충돌 간의 연관성을 파악하는 데 큰 도움이 됩니다.
  4. 앱 등록: 프로젝트 생성이 완료되면 프로젝트 개요 페이지에서 플랫폼(iOS, Android, Web 등) 아이콘을 클릭하여 앱을 등록합니다. 각 플랫폼에 맞는 패키지 이름(Android) 또는 번들 ID(iOS)를 정확하게 입력해야 합니다. 이 값은 앱의 고유 식별자이므로 오타가 없도록 주의해야 합니다.

앱 등록 과정에서 플랫폼별 구성 파일(google-services.json for Android, GoogleService-Info.plist for iOS)을 다운로드하게 됩니다. 이 파일들은 앱이 Firebase 프로젝트와 통신하는 데 필요한 모든 정보를 담고 있으므로, 정확한 위치에 추가해야 합니다.

1.2. Android 연동 상세 가이드 (Kotlin 기준)

Android 스튜디오에서 Gradle을 사용하여 Crashlytics를 연동하는 과정은 다음과 같습니다.

1.2.1. 구성 파일 추가

Firebase 콘솔에서 다운로드한 google-services.json 파일을 Android 스튜디오의 프로젝트 뷰에서 app 모듈의 루트 디렉토리(app/)에 복사합니다.

1.2.2. Gradle 파일 설정

두 개의 build.gradle.kts (또는 build.gradle) 파일을 수정해야 합니다.

1. 프로젝트 수준 build.gradle.kts (<project>/build.gradle.kts)

플러그인 블록에 Google 서비스와 Crashlytics Gradle 플러그인을 추가합니다.


// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    id("com.android.application") version "8.2.1" apply false
    id("org.jetbrains.kotlin.android") version "1.9.22" apply false
    // Firebase Crashlytics 플러그인 추가
    id("com.google.firebase.crashlytics") version "2.9.9" apply false
    // Google Services 플러그인 추가
    id("com.google.gms.google-services") version "4.4.1" apply false
}

2. 앱 수준 build.gradle.kts (<project>/<app-module>/build.gradle.kts)

플러그인 블록과 의존성 블록을 수정합니다.


plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    // 플러그인 적용
    id("com.google.gms.google-services")
    id("com.google.firebase.crashlytics")
}

dependencies {
    // ... 다른 의존성들
    
    // Firebase BoM(Bill of Materials)을 사용하여 라이브러리 버전 관리를 권장
    // BoM을 사용하면 호환되는 라이브러리 버전을 자동으로 관리해줍니다.
    implementation(platform("com.google.firebase:firebase-bom:32.7.4"))
    
    // Firebase Crashlytics 라이브러리 추가
    implementation("com.google.firebase:firebase-crashlytics-ktx")
    
    // Google Analytics 연동을 위해 추가 (권장)
    implementation("com.google.firebase:firebase-analytics-ktx")
}

Firebase BoM을 사용하는 것이 좋습니다. BoM은 여러 Firebase 라이브러리 간의 버전 호환성을 보장해주므로, 각 라이브러리의 버전을 개별적으로 명시할 필요 없이 `platform`으로 BoM만 추가하면 됩니다.

1.2.3. 네이티브 코드(NDK) 충돌 보고 설정 (선택 사항)

만약 앱이 C++과 같은 네이티브 코드를 사용한다면, NDK 충돌 보고를 활성화해야 합니다. build.gradle.kts 파일에 다음 설정을 추가합니다.


android {
    // ...
    buildTypes {
        getByName("release") {
            // ...
            // 네이티브 심볼 자동 업로드 활성화
            firebaseCrashlytics {
                nativeSymbolUploadEnabled = true
            }
        }
    }
}

이렇게 설정하면 빌드 시 네이티브 디버그 심볼이 자동으로 Crashlytics 서버에 업로드되어 네이티브 코드에서 발생한 충돌도 정확하게 분석할 수 있습니다.

1.3. iOS 연동 상세 가이드 (Swift 기준)

iOS에서는 Swift Package Manager(SPM) 또는 CocoaPods를 사용하여 연동할 수 있습니다. 최신 Xcode 환경에서는 SPM 사용이 권장됩니다.

1.3.1. 구성 파일 추가

Firebase 콘솔에서 다운로드한 GoogleService-Info.plist 파일을 Xcode 프로젝트 네비게이터의 루트에 드래그 앤 드롭합니다. 이때 'Copy items if needed' 옵션이 체크되어 있는지 확인해야 합니다.

1.3.2. Swift Package Manager (SPM)를 이용한 SDK 추가

  1. Xcode에서 프로젝트를 열고 File > Add Packages... 로 이동합니다.
  2. 우측 상단의 검색창에 Firebase Apple platforms GitHub 저장소 URL을 입력합니다: https://github.com/firebase/firebase-ios-sdk
  3. 'Dependency Rule'을 'Up to Next Major Version'으로 설정하고 'Add Package'를 클릭합니다.
  4. 패키지 목록이 나타나면 `FirebaseCrashlytics`와 `FirebaseAnalytics`를 선택하고 'Add Package'를 클릭합니다.

1.3.3. 초기화 코드 추가

앱의 진입점인 App 구조체 또는 AppDelegate 파일에 Firebase 초기화 코드를 추가합니다.

SwiftUI App Lifecycle 사용 시 (YourApp.swift):


import SwiftUI
import FirebaseCore

@main
struct YourApp: App {
    init() {
        FirebaseApp.configure()
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

UIKit AppDelegate 사용 시 (AppDelegate.swift):


import UIKit
import FirebaseCore

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    var window: UIWindow?

    func application(_ application: UIApplication,
                   didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        FirebaseApp.configure()
        return true
    }
}

1.3.4. 디버그 심볼(dSYM) 업로드 스크립트 설정

이 단계는 매우 중요합니다. Crashlytics가 난독화된 충돌 로그를 사람이 읽을 수 있는 형태로 변환(Symbolication)하기 위해서는 디버그 심볼 파일(dSYM)이 필요합니다. 이 파일을 빌드 시마다 자동으로 업로드하도록 설정해야 합니다.

  1. Xcode에서 프로젝트 네비게이터의 최상단 프로젝트 파일을 선택합니다.
  2. TARGETS에서 메인 앱 타겟을 선택합니다.
  3. Build Phases 탭으로 이동합니다.
  4. 좌측 상단의 '+' 아이콘을 클릭하고 'New Run Script Phase'를 선택합니다.
  5. 새로 생성된 'Run Script' 섹션을 열고, 셸 스크립트 입력창에 다음 스크립트를 붙여넣습니다.
    • SPM 사용 시: "${BUILD_DIR%Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run"
    • CocoaPods 사용 시: "${PODS_ROOT}/FirebaseCrashlytics/run"
  6. Input Files 섹션을 열고 '+' 버튼을 눌러 다음 두 경로를 추가합니다.
    • $(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)
    • $(SRCROOT)/$(BUILT_PRODUCTS_DIR)/${WRAPPER_NAME}.dSYM/Contents/Info.plist (이 경로는 dSYM 파일이 생성되는 빌드에서만 유효합니다.)

이 스크립트는 앱이 빌드될 때마다 dSYM 파일을 찾아 Crashlytics 서버로 업로드하는 역할을 합니다. 이 설정이 누락되면 Crashlytics 대시보드에서 의미 있는 충돌 리포트를 볼 수 없게 됩니다.

1.4. 연동 확인: 테스트 충돌 발생시키기

설정이 올바르게 완료되었는지 확인하기 위해 의도적으로 충돌을 발생시켜 리포트가 Firebase 콘솔에 정상적으로 전송되는지 확인해야 합니다.

주의: 테스트 충돌 코드는 디버거가 연결된 상태에서는 Crashlytics가 리포트를 수집하지 않으므로, Xcode나 Android 스튜디오에서 앱을 실행한 후 디버거 연결을 해제(Stop 버튼 클릭)하고, 앱 아이콘을 직접 탭하여 실행한 상태에서 테스트를 진행해야 합니다.

Android (Kotlin):

버튼 클릭 리스너 등에서 아래 코드를 실행합니다.


Button(onClick = {
    throw RuntimeException("Test Crash") // 강제 충돌 발생
}) {
    Text("Crash!")
}

iOS (Swift):

마찬가지로 버튼의 액션 핸들러 등에서 아래 코드를 호출합니다.


import SwiftUI

struct ContentView: View {
    var body: some View {
        Button("Crash!") {
            fatalError("Test Crash") // 강제 충돌 발생
        }
    }
}

충돌을 발생시킨 후, 앱을 다시 실행하면 Crashlytics SDK가 수집된 충돌 리포트를 서버로 전송합니다. 보통 몇 분 내에 Firebase 콘솔의 Crashlytics 대시보드에서 'Test Crash' 리포트를 확인할 수 있습니다. 리포트가 보인다면 성공적으로 연동이 완료된 것입니다.

2. Crashlytics 대시보드 분석: 데이터에서 통찰력 얻기

Crashlytics 연동 후 데이터가 수집되기 시작하면 Firebase 콘솔의 Crashlytics 대시보드는 앱 안정성을 위한 관제 센터가 됩니다. 대시보드의 각종 지표와 리포트를 효과적으로 분석하는 방법을 이해하는 것은 문제 해결의 효율성을 극대화하는 데 매우 중요합니다.

2.1. 대시보드 핵심 지표 이해하기

대시보드 상단에는 앱의 전반적인 안정성 상태를 요약하는 핵심 지표들이 표시됩니다.

  • 비정상 종료 없는 사용자 (Crash-free users): 선택한 기간 동안 한 번도 충돌을 경험하지 않은 사용자의 비율입니다. 이 수치는 앱 안정성을 나타내는 가장 중요한 지표이며, 100%에 가까울수록 좋습니다. 새로운 버전을 배포한 후 이 수치가 급격히 떨어진다면 심각한 문제가 발생했음을 의미합니다.
  • 비정상 종료 (Crashes): 발생한 총 충돌 횟수입니다.
  • 사용자 (Users): 앱을 사용한 총 사용자 수입니다.
  • 이슈 (Issues): Crashlytics는 유사한 스택 추적을 가진 충돌들을 하나의 '이슈'로 그룹화합니다. 대시보드에는 해결되지 않은 이슈의 총 개수가 표시됩니다.

이 지표들을 버전별, 기간별로 필터링하여 특정 배포 버전의 안정성을 평가하거나 시간 경과에 따른 안정성 변화 추이를 파악할 수 있습니다.

2.2. 이슈 목록과 우선순위 결정

대시보드 중앙에는 이슈 목록이 표시됩니다. 이 목록은 기본적으로 가장 많은 사용자에게 영향을 미치는 순서(영향을 받은 사용자 수 기준)로 정렬되어 있습니다. 문제 해결 자원은 한정되어 있으므로, 어떤 이슈부터 해결할지 우선순위를 정하는 것이 중요합니다.

  • 영향을 받은 사용자 (Users): 가장 먼저 고려해야 할 지표입니다. 소수의 사용자가 여러 번 겪는 충돌보다, 다수의 사용자가 한 번씩 겪는 충돌이 더 시급한 문제일 수 있습니다.
  • 발생 횟수 (Events): 총 충돌 발생 횟수입니다. 특정 조건에서 반복적으로 발생하는 이슈일 경우 이 수치가 높게 나타날 수 있습니다.
  • 최신 버전에서의 발생 여부: 이슈 목록에는 해당 이슈가 발생한 앱 버전들이 표시됩니다. 가장 최신 버전에서 집중적으로 발생하는 이슈는 즉각적인 대응이 필요합니다.
  • 신규 이슈 (New Issues): 새로운 버전 배포 후 처음 나타난 이슈는 '신규' 태그와 함께 표시됩니다. 이는 새로운 코드 변경 사항으로 인해 발생했을 가능성이 높으므로 주의 깊게 살펴보아야 합니다.

2.3. 이슈 상세 분석: 충돌의 재구성

이슈 목록에서 특정 이슈를 클릭하면 문제의 원인을 파악하는 데 필요한 모든 정보가 담긴 상세 페이지로 이동합니다.

Crashlytics 이슈 상세 페이지 예시

2.3.1. 스택 추적 (Stack Trace) 읽기

가장 핵심적인 정보는 스택 추적입니다. 스택 추적은 충돌이 발생하기까지의 함수 호출 순서를 역순으로 보여줍니다. 스택 추적을 읽을 때는 다음 사항에 주목해야 합니다.

  • 굵게 표시된 라인: 일반적으로 개발자가 작성한 앱 코드에 해당하는 부분은 굵게 표시됩니다. 문제의 원인이 있을 가능성이 가장 높은 곳입니다.
  • 충돌 원인 (Crashed thread): 어떤 스레드에서 충돌이 발생했는지 확인합니다. 특히 Android에서 ANR(Application Not Responding)이 발생한 경우, 메인 스레드에서 어떤 작업이 장시간 실행되고 있었는지 파악하는 것이 중요합니다.
  • 예외 유형 및 메시지: 스택 추적 상단에는 `NullPointerException`, `IndexOutOfBoundsException` (Android) 또는 `Fatal error: Unexpectedly found nil while unwrapping an Optional value` (iOS)와 같은 예외 유형과 메시지가 표시됩니다. 이는 문제의 성격을 파악하는 첫 번째 단서입니다.

2.3.2. 심볼리케이션(Symbolication) 및 난독화 해제(Deobfuscation) 문제 해결

만약 스택 추적이 `0x10...`과 같은 메모리 주소나 의미 없는 클래스/메소드 이름으로 보인다면, 이는 심볼 파일이 제대로 업로드되지 않았기 때문입니다.

  • iOS: dSYM 파일이 누락된 경우입니다. Build Phases의 Run Script 설정이 올바른지, Bitcode가 활성화된 경우 App Store Connect에서 dSYM을 다운로드하여 수동으로 업로드했는지 확인해야 합니다.
  • Android: ProGuard나 R8을 사용하여 코드 난독화를 적용한 경우, 매핑 파일(mapping.txt)이 필요합니다. Crashlytics Gradle 플러그인은 보통 이 파일을 자동으로 업로드하지만, 빌드 환경에 따라 수동 업로드가 필요할 수 있습니다.
정확한 스택 추적 없이는 디버깅이 거의 불가능하므로, 이 문제를 가장 먼저 해결해야 합니다.

2.3.3. 데이터 탭: 컨텍스트 파악

스택 추적 외에 '데이터', '로그', '키' 탭은 충돌 당시의 상황을 재구성하는 데 매우 유용한 정보를 제공합니다.

  • 신호 (Signals): RAM 여유 공간, 디스크 여유 공간, 기기 방향, 근접 센서 상태 등 충돌 직전의 기기 상태 정보를 보여줍니다. 예를 들어, RAM 여유 공간이 지속적으로 낮게 보고되는 충돌은 메모리 누수와 관련이 있을 수 있습니다.
  • 기기 및 OS 분포: 어떤 기기 모델(예: 'Samsung Galaxy S22')이나 OS 버전(예: 'Android 13', 'iOS 16.5')에서 충돌이 집중되는지 확인할 수 있습니다. 특정 기기나 OS에서만 발생하는 벤더별/OS별 버그를 찾아내는 데 결정적인 역할을 합니다.

3. 고급 기법: 충돌 리포트 풍부하게 만들기

기본 설정만으로도 Crashlytics는 강력하지만, 추가적인 정보를 기록하도록 코드를 작성하면 디버깅의 효율성을 몇 단계 더 끌어올릴 수 있습니다. 커스텀 로그, 키, 사용자 식별자를 활용하여 "왜?"라는 질문에 대한 답을 찾아보겠습니다.

3.1. 커스텀 로그 (Custom Logs): 사용자의 발자취 추적하기

커스텀 로그는 충돌이 발생하기 전까지 사용자가 앱에서 수행한 일련의 행동들을 시간순으로 기록하는 '브레드크럼(breadcrumbs)' 역할을 합니다. 어떤 화면으로 이동했는지, 어떤 버튼을 클릭했는지, 어떤 API 요청이 시작되고 완료되었는지를 기록하면, 충돌로 이어진 특정 시나리오를 재현하는 데 큰 도움이 됩니다.

Android (Kotlin):


import com.google.firebase.crashlytics.FirebaseCrashlytics

// 사용자가 특정 화면에 진입했을 때
FirebaseCrashlytics.getInstance().log("MainScreen: onViewCreated")

// 사용자가 특정 버튼을 클릭했을 때
FirebaseCrashlytics.getInstance().log("MainScreen: purchase_button_clicked")

// 네트워크 요청 시작 및 결과
FirebaseCrashlytics.getInstance().log("API: fetch_user_profile started")
// ... API 응답 후
FirebaseCrashlytics.getInstance().log("API: fetch_user_profile success, user_id: ${user.id}")

iOS (Swift):


import FirebaseCrashlytics

// 사용자가 특정 화면에 진입했을 때
Crashlytics.crashlytics().log("DetailView: onAppear")

// 사용자가 특정 버튼을 클릭했을 때
Crashlytics.crashlytics().log("DetailView: add_to_cart_tapped")

// 데이터 처리 과정
Crashlytics.crashlytics().log("DataProcessing: starting heavy computation")
// ... 계산 완료 후
Crashlytics.crashlytics().log("DataProcessing: computation finished")

이렇게 기록된 로그는 이슈 상세 페이지의 '로그' 탭에서 시간순으로 확인할 수 있습니다. 로그는 간결하고 명확하게 작성하는 것이 좋습니다.

3.2. 커스텀 키 (Custom Keys): 상태 정보 기록하기

커스텀 키는 로그와 달리, 충돌 시점의 특정 '상태' 값을 키-값 쌍으로 기록하는 데 사용됩니다. 예를 들어, 현재 사용자의 구독 레벨, A/B 테스트 그룹, 사용 언어, 현재 화면 이름 등과 같은 정보를 기록할 수 있습니다.

Android (Kotlin):


val crashlytics = FirebaseCrashlytics.getInstance()

// 사용자의 구독 상태 설정
crashlytics.setCustomKey("subscription_level", "premium")

// 현재 실행 중인 A/B 테스트 변형 설정
crashlytics.setCustomKey("ab_test_group", "variant_B")

// 처리해야 할 아이템의 개수
crashlytics.setCustomKey("item_count_in_cart", 5)

iOS (Swift):


let crashlytics = Crashlytics.crashlytics()

// 사용자의 현재 위치한 화면 이름 설정
crashlytics.setCustomValue("HomeViewController", forKey: "current_screen")

// 기능 플래그 상태
crashlytics.setCustomValue(true, forKey: "new_feature_enabled")

// 네트워크 연결 타입
crashlytics.setCustomValue("WiFi", forKey: "network_type")

커스텀 키는 이슈 상세 페이지의 '키' 탭에서 확인할 수 있으며, 동일한 키 값을 가진 충돌들을 필터링하여 특정 상태에서만 발생하는 문제를 찾아내는 데 매우 유용합니다. 예를 들어, `new_feature_enabled`가 `true`인 사용자에게서만 특정 충돌이 발생한다면, 새로 추가한 기능에 버그가 있음을 쉽게 추정할 수 있습니다.

3.3. 사용자 식별자 (User Identifier): 특정 사용자 문제 추적

때로는 특정 사용자에게서만 반복적으로 충돌이 발생하는 경우가 있습니다. 사용자 식별자를 설정하면 Crashlytics가 해당 사용자와 관련된 모든 충돌 리포트를 그룹화하여 보여줍니다. 이를 통해 특정 사용자의 충돌 이력을 추적하고, 고객 지원 시나리오에서 "X 고객님께서 겪으시는 문제를 확인하고 있습니다"와 같이 보다 능동적으로 대응할 수 있습니다.

중요: 사용자 식별자로는 이메일, 이름, 전화번호와 같은 개인 식별 정보(PII)를 절대 사용해서는 안 됩니다. 개인정보 보호 규정을 준수하기 위해 앱 내부에서 사용하는 고유하지만 익명화된 사용자 ID(예: 데이터베이스의 사용자 UUID)를 사용해야 합니다.

Android (Kotlin):


// 사용자가 로그인했을 때
FirebaseCrashlytics.getInstance().setUserId("USER_UUID_12345")

iOS (Swift):


// 사용자가 로그인했을 때
Crashlytics.crashlytics().setUserID("USER_UUID_67890")

3.4. 비치명적 오류 (Non-fatal Exceptions) 보고

모든 오류가 앱을 중단시키는 것은 아닙니다. `try-catch` 블록으로 처리된 예외는 앱을 다운시키지는 않지만, 사용자 경험을 저해하거나 잠재적인 버그의 신호일 수 있습니다. 예를 들어, API 요청 실패, 데이터 파싱 오류, 예상치 못한 로직 분기 등이 여기에 해당합니다. Crashlytics는 이러한 '비치명적' 오류도 수동으로 보고하는 기능을 제공합니다.

비치명적 오류를 기록하면, 실제로 앱이 충돌하지는 않더라도 백그라운드에서 얼마나 많은 문제가 발생하고 있는지 파악할 수 있습니다. 이는 앱의 잠재적인 불안정성을 미리 감지하고 선제적으로 품질을 개선하는 데 큰 도움이 됩니다.

Android (Kotlin):


try {
    // 잠재적으로 예외가 발생할 수 있는 코드
    val json = api.fetchData()
    val data = parseJson(json)
} catch (e: JSONException) {
    // 예외를 잡았지만, 앱은 계속 실행됨
    // 이 예외를 Crashlytics에 보고하여 분석
    FirebaseCrashlytics.getInstance().recordException(e)
    // 사용자에게는 안전한 UI를 보여주거나 대체 동작 수행
}

iOS (Swift):


do {
    // 잠재적으로 오류를 던질 수 있는 코드
    let data = try Data(contentsOf: url)
    let result = try JSONDecoder().decode(MyModel.self, from: data)
} catch {
    // 오류를 잡아서 처리
    // 이 오류 정보를 Crashlytics에 보고
    Crashlytics.crashlytics().record(error: error)
    // 사용자에게 오류 메시지 표시 등 후속 처리
}

이렇게 보고된 비치명적 오류는 Crashlytics 대시보드에서 '비정상 종료'와 구분되어 별도로 집계되므로, 치명적인 충돌과 분리하여 관리하고 분석할 수 있습니다.

4. 개발 워크플로우에 Crashlytics 통합하기

Crashlytics를 단순히 문제 발생 후 들여다보는 도구로만 사용한다면 그 잠재력의 절반밖에 활용하지 못하는 것입니다. 개발, 배포, 운영의 전체 사이클에 Crashlytics를 긴밀하게 통합하여 프로액티브한 안정성 관리 문화를 구축해야 합니다.

4.1. CI/CD 파이프라인과의 연동

수동으로 dSYM이나 매핑 파일을 업로드하는 것은 번거롭고 실수가 발생하기 쉽습니다. Jenkins, GitLab CI, GitHub Actions 등 사용하는 CI/CD 도구에 심볼 파일 자동 업로드 단계를 포함시켜야 합니다.

  • Android: Gradle 플러그인이 대부분의 작업을 자동으로 처리하지만, CI 환경에서는 서비스 계정 인증 등이 필요할 수 있습니다. firebaseCrashlyticsUploadSymbols Gradle 태스크를 CI 스크립트에서 명시적으로 호출하도록 구성할 수 있습니다.
  • iOS: Fastlane과 같은 자동화 도구를 사용하면 dSYM을 다운로드하고 업로드하는 과정을 쉽게 스크립트로 만들 수 있습니다. download_dsymsupload_symbols_to_crashlytics 액션을 활용하면 App Store Connect에 빌드를 올린 후 자동으로 심볼 파일을 Crashlytics에 전송할 수 있습니다.

4.2. 알림 설정 및 외부 서비스 연동

매일 Crashlytics 대시보드를 확인하는 것은 비효율적입니다. 중요한 변화가 있을 때 알림을 받도록 설정하는 것이 중요합니다.

  • Firebase 알림 설정: Firebase 콘솔의 프로젝트 설정 > 통합 탭에서 Crashlytics 관련 알림을 설정할 수 있습니다.
    • 신규 치명적 이슈 발생 시: 새로운 종류의 충돌이 보고되면 즉시 알림을 받습니다.
    • 회귀 이슈 발생 시: '해결됨'으로 표시했던 이슈가 다시 발생하면 알림을 받습니다.
    • 급증 이슈 발생 시: 특정 이슈의 발생 빈도가 비정상적으로 급증할 때(Velocity Alert) 알림을 받아 대규모 장애로 번지기 전에 대응할 수 있습니다.
  • Slack/Jira 연동: 이메일 알림을 넘어 팀이 주로 사용하는 협업 도구와 연동하면 대응 속도를 높일 수 있습니다. Firebase는 Slack 통합을 기본적으로 지원하며, Jira나 다른 프로젝트 관리 도구와는 Cloud Functions for Firebase나 Zapier 같은 중간 서비스를 이용하여 연동할 수 있습니다. 예를 들어, 새로운 Crashlytics 이슈가 발생하면 자동으로 Jira 티켓을 생성하는 함수를 작성할 수 있습니다.

4.3. 이슈 관리 및 추적

Crashlytics 대시보드 내에서도 간단한 이슈 관리가 가능합니다.

  • 이슈 닫기: 해결된 이슈는 '닫기' 처리하여 목록을 깔끔하게 유지할 수 있습니다. 만약 닫힌 이슈가 후속 버전에서 다시 발생하면 '회귀(Regressed)' 상태로 자동 전환되어 다시 목록에 나타납니다.
  • 메모 추가: 특정 이슈에 대한 분석 내용, 담당자, 관련 Jira 티켓 번호 등을 메모로 남겨 팀원들과 공유할 수 있습니다.

4.4. 다른 Firebase 서비스와의 시너지

Crashlytics는 다른 Firebase 서비스와 함께 사용할 때 더욱 강력해집니다.

  • Remote Config & A/B Testing: 새로운 기능을 배포할 때 Remote Config를 이용해 일부 사용자에게만 먼저 활성화하고, 해당 기능 플래그 값을 Crashlytics 커스텀 키로 기록합니다. 만약 이 기능이 활성화된 사용자 그룹에서만 충돌이 급증한다면, 앱 업데이트 없이 즉시 Remote Config에서 해당 기능을 비활성화하여 추가 피해를 막을 수 있습니다.
  • Google Analytics: Analytics와 연동하면 특정 Analytics 이벤트(예: purchase_completed)를 수행한 사용자 그룹의 충돌률을 분석하거나, 충돌을 경험한 사용자들로 잠재고객(Audience)을 만들어 이들의 이후 행동을 추적하는 등 깊이 있는 분석이 가능합니다.
  • Firebase Performance Monitoring: 앱의 성능 저하(느린 네트워크 응답, 긴 프레임 렌더링 시간 등)가 충돌로 이어지는 경우가 많습니다. Performance Monitoring 데이터와 Crashlytics 데이터를 함께 분석하면 문제의 근본적인 원인을 찾는 데 도움이 될 수 있습니다.

마치며

Firebase Crashlytics는 단순히 충돌을 수집하는 도구가 아닙니다. 이는 앱의 안정성을 정량적으로 측정하고, 문제의 원인을 신속하게 진단하며, 개발 워크플로우와 긴밀하게 통합하여 선제적으로 품질을 관리할 수 있게 해주는 강력한 플랫폼입니다. 초기에 정확하게 설정하고, 커스텀 데이터로 리포트를 풍부하게 만들며, 알림 및 다른 서비스와의 연동을 통해 자동화된 모니터링 체계를 구축함으로써 개발팀은 사용자의 신뢰를 얻고 유지할 수 있는 안정적인 애플리케이션을 지속적으로 제공할 수 있습니다.

안정성은 한 번 달성하고 끝나는 목표가 아니라, 끊임없이 관리하고 개선해야 하는 여정입니다. Firebase Crashlytics를 이 여정의 충실한 동반자로 삼아, 사용자에게는 최고의 경험을, 비즈니스에는 지속적인 성장을 가져다주시길 바랍니다.


0 개의 댓글:

Post a Comment