1. 소개
이 Codelab에서는 Crashlytics의 고급 기능을 사용하여 비정상 종료와 비정상 종료를 일으킬 수 있는 상황을 더 잘 파악하는 방법을 알아봅니다.
샘플 게임인 MechaHamster: Level Up with Firebase Edition에 새로운 기능을 추가합니다. 이 샘플 게임은 기본 Firebase 게임 MechaHamster의 새로운 버전으로, 기본 제공되는 Firebase 기능을 대부분 제거하여 대신 Firebase를 새롭게 사용할 수 있는 기회를 제공합니다.
게임에 디버그 메뉴를 추가합니다. 이 디버그 메뉴는 개발자가 만들 메서드를 호출하고 Crashlytics의 다양한 기능을 실행할 수 있도록 합니다. 이 메서드는 자동 비정상 종료 보고서에 커스텀 키, 커스텀 로그, 심각하지 않은 오류 등으로 주석을 추가하는 방법을 보여줍니다.
게임을 빌드한 후에는 디버그 메뉴를 사용하고 결과를 검사하여 게임이 실제로 어떻게 실행되는지에 관해 제공하는 고유한 뷰를 파악합니다.
학습할 내용
- Crashlytics에서 자동으로 포착하는 오류 유형입니다.
- 의도적으로 기록할 수 있는 추가 오류입니다.
- 오류를 더 쉽게 이해할 수 있도록 오류에 정보를 추가하는 방법
필요한 사항
- 다음 중 하나 또는 둘 다를 포함하는 Unity (최소 권장 버전 2019 이상)
- iOS 빌드 지원
- Android 빌드 지원
- (Android만 해당) Firebase CLI (비정상 종료 보고서의 기호를 업로드하는 데 사용됨)
- 안내에 따라 Firebase CLI를 설치합니다.
CLI를 이미 설치했다면 최신 버전으로 업데이트해야 합니다.
- 안내에 따라 Firebase CLI를 설치합니다.
2. 개발 환경 설정
다음 섹션에서는 Firebase로 레벨 업 코드를 다운로드하고 Unity에서 여는 방법을 설명합니다.
이 Firebase로 레벨업하기 샘플 게임은 다른 여러 Firebase + Unity Codelab에서 사용되므로 이 섹션의 작업을 이미 완료했을 수도 있습니다. 그런 경우 이 페이지의 마지막 단계인 'Unity용 Firebase SDK 추가'로 바로 이동할 수 있습니다.
코드 다운로드
이 Codelab의 GitHub 저장소를 명령줄에서 클론합니다.
git clone https://github.com/firebase/level-up-with-firebase.git
또는 git을 설치하지 않은 경우 저장소를 ZIP 파일로 다운로드할 수 있습니다.
Unity 편집기에서 Level Up with Firebase(Firebase로 레벨 업)를 엽니다.
- Unity Hub를 실행하고 Projects(프로젝트) 탭에서 Open(열기) 옆에 있는 드롭다운 화살표를 클릭합니다.
- 디스크에서 프로젝트 추가를 클릭합니다.
- 코드가 있는 디렉터리로 이동한 후 OK를 클릭합니다.
- 메시지가 표시되면 사용할 Unity 편집기 버전과 대상 플랫폼 (Android 또는 iOS)을 선택합니다.
- 프로젝트 이름 level-up-with-firebase를 클릭하면 프로젝트가 Unity 편집기에서 열립니다.
- 편집기에서 자동으로 열리지 않으면 애셋에서
MainGameScene
를 엽니다. Unity 편집기의 Project(프로젝트) 탭에 있는 Hamster(햄스터)
Unity 설치 및 사용에 관한 자세한 내용은 Unity에서 작업을 참고하세요.
3. Unity 프로젝트에 Firebase 추가
Firebase 프로젝트 만들기
- Firebase Console에서 프로젝트 추가를 클릭합니다.
- 새 프로젝트를 만들려면 원하는 프로젝트 이름을 입력합니다.
이렇게 하면 프로젝트 ID (프로젝트 이름 아래에 표시됨)도 프로젝트 이름에 따라 설정됩니다. 필요한 경우 프로젝트 ID에서 수정 아이콘을 클릭하여 추가로 맞춤설정할 수 있습니다. - 메시지가 표시되면 Firebase 약관을 검토하고 이에 동의합니다.
- 계속을 클릭합니다.
- 이 프로젝트에 Google 애널리틱스 사용 설정 옵션을 선택한 다음 계속을 클릭합니다.
- 사용할 기존 Google 애널리틱스 계정을 선택하거나 새 계정 만들기를 선택하여 새 계정을 만듭니다.
- 프로젝트 만들기를 클릭합니다.
- 프로젝트가 생성되면 계속을 클릭합니다.
Firebase에 앱 등록
- Firebase Console에서 프로젝트 개요 페이지 중앙의 Unity 아이콘을 클릭하여 설정 워크플로를 시작합니다. Firebase 프로젝트에 이미 앱을 추가한 경우 앱 추가를 클릭하여 플랫폼 옵션을 표시합니다.
- Apple (iOS) 및 Android 빌드 대상을 모두 등록하려면 선택합니다.
- Unity 프로젝트의 플랫폼별 ID를 입력합니다. 이 Codelab에서는 다음을 입력합니다.
- Apple (iOS): iOS 번들 ID 필드에
com.google.firebase.level-up
를 입력합니다. - Android: Android 패키지 이름 입력란에
com.google.firebase.level_up
을 입력합니다.
- Apple (iOS): iOS 번들 ID 필드에
- (선택사항) Unity 프로젝트의 플랫폼별 닉네임을 입력합니다.
- 앱 등록을 클릭한 다음 구성 파일 다운로드 섹션으로 이동합니다.
Firebase 구성 파일 추가
앱 등록을 클릭하면 구성 파일 두 개 (빌드 타겟마다 구성 파일 하나씩)를 다운로드하라는 메시지가 표시됩니다. Unity 프로젝트에서 Firebase와 연결하려면 이러한 파일에 Firebase 메타데이터가 필요합니다.
- 사용 가능한 두 구성 파일을 모두 다운로드합니다.
- Apple (iOS): GoogleService-Info.plist를 다운로드합니다.
- Android: google-services.json을 다운로드합니다.
- Unity 프로젝트의 Project 창을 열고 두 구성 파일을 모두 Assets 폴더로 이동합니다. <ph type="x-smartling-placeholder">
- Firebase Console로 돌아가서 설정 워크플로에서 다음을 클릭하고 Unity용 Firebase SDK 추가를 진행합니다.
Unity용 Firebase SDK 추가
- Firebase Console에서 Firebase Unity SDK 다운로드를 클릭합니다.
- 편리한 위치에 SDK의 압축을 풉니다.
- 열려 있는 Unity 프로젝트에서 애셋 > 패키지 가져오기 > 커스텀 패키지.
- Import package 대화상자에서 압축을 푼 SDK가 포함된 디렉터리로 이동하여
FirebaseAnalytics.unitypackage
를 선택한 후 Open을 클릭합니다. - 표시되는 Import Unity Package 대화상자에서 Import를 클릭합니다.
- 이전 단계를 반복하여
FirebaseCrashlytics.unitypackage
을 가져옵니다. - Firebase Console로 돌아가 설정 워크플로에서 다음을 클릭합니다.
Unity 프로젝트에 Firebase SDK를 추가하는 방법에 대한 자세한 내용은 추가 Unity 설치 옵션을 참조하세요.
4. Unity 프로젝트에서 Crashlytics 설정
Unity 프로젝트에서 Crashlytics를 사용하려면 몇 가지 설정 단계를 추가로 수행해야 합니다. 물론 SDK를 초기화해야 합니다. 또한 Firebase Console에서 기호화된 스택 트레이스를 볼 수 있도록 기호를 업로드해야 하며, 테스트 비정상 종료를 강제로 실행하여 Firebase에서 비정상 종료 이벤트를 수신하는지 확인해야 합니다.
Crashlytics SDK 초기화
Assets/Hamster/Scripts/MainGame.cs
에서 다음using
문을 추가합니다.
첫 번째 모듈은 Crashlytics SDK의 메서드를 사용할 수 있도록 해주며 두 번째 모듈에는 C# Tasks API에 대한 확장 프로그램이 포함되어 있습니다.using Firebase.Crashlytics; using Firebase.Extensions;
using
문이 모두 없으면 다음 코드가 작동하지 않습니다.- 계속
MainGame.cs
에서InitializeFirebaseAndStartGame()
를 호출하여 기존Start()
메서드에 Firebase 초기화를 추가합니다.void Start() { Screen.SetResolution(Screen.width / 2, Screen.height / 2, true); InitializeFirebaseAndStartGame(); }
- 다시
MainGame.cs
에서InitializeFirebaseAndStartGame()
을 찾아 앱 변수를 선언한 후 다음과 같이 메서드의 구현을 덮어씁니다.public Firebase.FirebaseApp app = null; // Begins the firebase initialization process and afterwards, opens the main menu. private void InitializeFirebaseAndStartGame() { Firebase.FirebaseApp.CheckAndFixDependenciesAsync() .ContinueWithOnMainThread( previousTask => { var dependencyStatus = previousTask.Result; if (dependencyStatus == Firebase.DependencyStatus.Available) { // Create and hold a reference to your FirebaseApp, app = Firebase.FirebaseApp.DefaultInstance; // Set the recommended Crashlytics uncaught exception behavior. Crashlytics.ReportUncaughtExceptionsAsFatal = true; InitializeCommonDataAndStartGame(); } else { UnityEngine.Debug.LogError( $"Could not resolve all Firebase dependencies: {dependencyStatus}\n" + "Firebase Unity SDK is not safe to use here"); } }); }
여기에 초기화 로직을 배치하면 Firebase 종속 항목이 초기화되기 전에 플레이어 상호작용이 방지됩니다.
처리되지 않은 예외를 심각한 문제로 보고할 때의 이점과 효과는 Crashlytics FAQ에서 설명합니다.
프로젝트 빌드 및 기호 업로드
iOS 앱과 Android 앱은 기호를 작성하고 업로드하는 단계가 다릅니다.
iOS+ (Apple 플랫폼)
- Build Settings(빌드 설정) 대화상자에서 프로젝트를 Xcode 작업공간으로 내보냅니다.
- 앱을 빌드합니다.
Apple 플랫폼의 경우 Firebase Unity 편집기 플러그인이 Xcode 프로젝트를 자동으로 구성하여 각 빌드에 대해 Crashlytics 호환 기호 파일을 생성하여 Firebase 서버에 업로드합니다. Crashlytics 대시보드에서 기호화된 스택 트레이스를 보려면 이 기호 정보가 필요합니다.
Android
- (각 빌드가 아닌 초기 설정 중에만) 빌드를 설정합니다.
- 프로젝트 디렉터리 (즉, Assets 디렉터리의 동위 항목)의 루트에 Builds라는 새 폴더를 만든 후 Android라는 하위 폴더를 만듭니다.
- 파일 > 빌드 설정 > 플레이어 설정 > 구성, 스크립팅 백엔드를 IL2CPP로 설정합니다.
- IL2CPP를 사용하면 일반적으로 빌드가 더 작고 성능이 향상됩니다.
- 또한 IL2CPP는 iOS에서 유일하게 사용할 수 있는 옵션이며, 여기에서 IL2CPP를 선택하면 두 플랫폼이 더 나은 패리티를 제공하고 두 플랫폼 간의 디버깅 차이를 더 간단하게 만들 수 있습니다.
- 앱을 빌드합니다. 파일 > 빌드 설정에서 다음을 완료합니다.
- CreateSymbol.zip이 선택되어 있는지 확인합니다 (드롭다운이 표시되면 디버깅을 선택합니다).
- Unity 편집기에서 바로 APK를 방금 만든 Builds/Android 하위 폴더에 빌드합니다.
- 빌드가 완료되면 Crashlytics 호환 기호 파일을 생성하여 Firebase 서버에 업로드해야 합니다. Crashlytics 대시보드에서 네이티브 라이브러리 비정상 종료에 대한 기호화된 스택 트레이스를 보려면 이 기호 정보가 필요합니다.
다음 Firebase CLI 명령어를 실행하여 이 기호 파일을 생성하고 업로드합니다.firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
<ph type="x-smartling-placeholder">- </ph>
FIREBASE_APP_ID
: 패키지 이름이 아닌 Firebase Android 앱 ID입니다. 앞서 다운로드한google-services.json
파일에서 이 값을 찾습니다.mobilesdk_app_id
값입니다.
Firebase Android 앱 ID 예시:1:567383003300:android:17104a2ced0c9b9b
PATH/TO/SYMBOLS
: 빌드가 완료될 때 Builds/Android 디렉터리에 생성되는 압축된 기호 파일의 경로입니다 (예:Builds/Android/myapp-1.0-v100.symbols.zip
).
테스트 비정상 종료를 강제로 적용하여 설정 완료
Crashlytics 설정을 완료하고 Firebase Console의 Crashlytics 대시보드에 초기 데이터를 표시하려면 테스트 비정상 종료를 강제로 실행해야 합니다.
- MainGameScene 편집기 Hierarchy에서 EmptyObject
GameObject
를 찾고 다음 스크립트를 추가한 후 장면을 저장합니다. 이 스크립트는 앱을 실행하고 몇 초 후에 테스트 비정상 종료를 일으킵니다.using System; using UnityEngine; public class CrashlyticsTester : MonoBehaviour { // Update is called once per frame void Update() { // Tests your Crashlytics implementation by // throwing an exception every 60 frames. // You should see reports in the Firebase console // a few minutes after running your app with this method. if(Time.frameCount >0 && (Time.frameCount%60) == 0) { throw new System.Exception("Test exception; please ignore"); } } }
- 앱을 빌드하고 빌드가 완료된 후 기호 정보를 업로드합니다.
- iOS: Firebase Unity 편집기 플러그인은 기호 파일을 업로드하도록 Xcode 프로젝트를 자동으로 구성합니다.
- Android: Firebase CLI
crashlytics:symbols:upload
명령어를 실행하여 기호 파일을 업로드합니다.
- 앱을 실행합니다. 앱이 실행되면 기기 로그를 확인하고
CrashlyticsTester
에서 예외가 트리거될 때까지 기다립니다.- iOS: Xcode 하단 창에서 로그를 확인합니다.
- Android: 터미널에서
adb logcat
명령어를 실행하여 로그를 확인합니다.
- 예외를 확인하려면 Crashlytics 대시보드로 이동하세요. 이 문제는 대시보드 하단의 문제 표에 표시됩니다. Codelab의 후반부에서 이러한 보고서를 살펴보는 방법을 자세히 알아봅니다.
- 이벤트가 Crashlytics에 업로드되었음을 확인한 후 이벤트를 연결한 EmptyObject
GameObject
를 선택하고CrashlyticsTester
구성요소만 삭제한 다음 장면을 저장하여 원래 상태로 복원합니다.
5. 디버그 메뉴 사용 설정 및 이해
지금까지 Unity 프로젝트에 Crashlytics를 추가하고 설정을 완료했으며 Crashlytics SDK가 Firebase에 이벤트를 업로드하는 것을 확인했습니다. 이제 Unity 프로젝트에서 게임에서 고급 Crashlytics 기능을 사용하는 방법을 보여주는 메뉴를 만듭니다. Firebase로 레벨업 Unity 프로젝트에는 표시하고 기능을 작성할 수 있는 숨겨진 디버그 메뉴가 이미 있습니다.
디버그 메뉴 사용 설정
Unity 프로젝트에 디버그 메뉴에 액세스하는 버튼이 있지만 현재 사용 설정되어 있지 않습니다. MainMenu
프리패브에서 액세스하려면 버튼을 사용 설정해야 합니다.
- Unity 편집기에서
MainMenu
이라는 prefab을 엽니다. - prefab 계층 구조에서
DebugMenuButton
라는 사용 중지된 하위 객체를 찾아 선택합니다. DebugMenuButton
이(가) 포함된 텍스트 입력란의 왼쪽 상단 모서리에 있는 체크박스를 선택하여DebugMenuButton
을(를) 사용 설정하세요.- prefab을 저장합니다.
- 편집기 또는 기기에서 게임을 실행합니다. 이제 메뉴에 액세스할 수 있습니다.
디버그 메뉴의 메서드 본문 미리보기 및 이해
이 Codelab의 후반부에서는 사전 구성된 디버그 Crashlytics 메서드의 메서드 본문을 작성합니다. 그러나 Level Up with Firebase Unity 프로젝트에서는 메서드가 DebugMenu.cs
에서 정의되고 호출됩니다.
이러한 메서드 중 일부는 Crashlytics 메서드를 호출하고 오류를 발생시키지만, Crashlytics가 이러한 오류를 포착하는 기능은 이러한 메서드를 먼저 호출하는 것과는 다릅니다. 오류를 자동으로 포착하여 생성된 비정상 종료 보고서는 이러한 방법으로 추가된 정보를 통해 향상됩니다.
DebugMenu.cs
를 열고 다음 메서드를 찾습니다.
Crashlytics 문제를 생성하고 주석을 추가하는 방법:
CrashNow
LogNonfatalError
LogStringsAndCrashNow
SetAndOverwriteCustomKeyThenCrash
SetLogsAndKeysBeforeANR
디버깅에 도움이 되는 애널리틱스 이벤트를 기록하는 메서드:
LogProgressEventWithStringLiterals
LogIntScoreWithBuiltInEventAndParams
이 Codelab의 이후 단계에서는 이러한 메서드를 구현하고 이러한 메서드가 게임 개발에서 발생할 수 있는 특정 상황을 해결하는 데 어떻게 도움이 되는지 알아봅니다.
6. 개발 시 비정상 종료 보고서 전송 보장
이러한 디버그 메서드를 구현하고 이 메서드가 비정상 종료 보고서에 미치는 영향을 확인하기 전에 이벤트가 Crashlytics에 보고되는 방식을 이해해야 합니다.
Unity 프로젝트의 경우 게임의 비정상 종료 및 예외 이벤트가 즉시 디스크에 기록됩니다. 포착되지 않은 예외 (예: 게임 로직에서 포착되지 않은 C# 예외)의 경우 Unity 프로젝트에서 Crashlytics를 초기화할 때 Crashlytics.ReportUncaughtExceptionsAsFatal
속성을 true
로 설정하여 Crashlytics SDK가 심각한 이벤트로 보고하도록 할 수 있습니다. 이러한 이벤트는 최종 사용자가 게임을 다시 시작할 필요 없이 실시간으로 Crashlytics에 보고됩니다. 네이티브 비정상 종료는 항상 치명적인 이벤트로 보고되며 최종 사용자가 게임을 다시 시작할 때 함께 전송됩니다.
또한 런타임 환경마다 Crashlytics 정보를 Firebase로 전송하는 방식은 다음과 같이 작지만 중대한 차이점에 유의해야 합니다.
iOS 시뮬레이터:
- Crashlytics 정보는 시뮬레이터에서 Xcode를 분리한 경우에만 보고됩니다. Xcode가 연결되면 업스트림 오류를 포착하여 정보 전송을 방지합니다.
휴대기기 (Android 및 iOS):
- Android 관련: ANR은 Android 11 이상에서만 보고됩니다. ANR 및 심각하지 않은 이벤트는 다음 실행 시 보고됩니다.
Unity 편집기:
- 재생 모드 또는 독립형 편집기의 Crashlytics 정보는 기록되거나 Firebase에 업로드되지 않습니다. 또한 Firebase 데스크톱 개발 워크플로는 Crashlytics를 지원하지 않습니다.
CrashNow()
에서 버튼 터치 한 번으로 게임 비정상 종료 테스트
게임에 Crashlytics를 설정하면 Crashlytics SDK가 자동으로 비정상 종료 및 포착되지 않은 예외를 기록하고 분석을 위해 Firebase에 업로드합니다. 보고서는 Firebase Console의 Crashlytics 대시보드에 표시됩니다.
- 이 작업이 실제로 자동임을 입증하려면
DebugMenu.cs
를 열고 다음과 같이CrashNow()
메서드를 덮어씁니다.void CrashNow() { TestCrash(); }
- 앱을 빌드합니다.
- (Android만 해당) 다음 Firebase CLI 명령어를 실행하여 기호를 업로드합니다.
firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
- 지금 비정상 종료 버튼을 탭하고 이 Codelab의 다음 단계로 진행하여 비정상 종료 보고서를 보고 해석하는 방법을 알아봅니다.
7. Firebase Console의 문제 보고서 이해
비정상 종료 보고서를 최대한 활용하는 방법에 대해 알아야 할 몇 가지 사항이 더 있습니다. 작성하는 각 메서드는 Crashlytics 보고서에 다양한 유형의 정보를 추가하는 방법을 보여줍니다.
- 지금 비정상 종료 버튼을 탭하고 앱을 다시 시작합니다.
- Crashlytics 대시보드로 이동합니다. Crashlytics가 근본 원인이 동일한 모든 이벤트를 '문제'로 그룹화하는 대시보드 하단의 문제 표까지 아래로 스크롤합니다.
- 문제 표에 나열된 새로운 문제를 클릭합니다. 이렇게 하면 Firebase로 전송된 각 개별 이벤트에 대한 이벤트 요약이 표시됩니다.
다음과 같은 스크린샷이 표시됩니다. 이벤트 요약에서 비정상 종료를 초래한 호출의 스택 트레이스를 눈에 띄게 표시합니다.
추가 메타데이터
또 다른 유용한 탭은 Unity 메타데이터 탭입니다. 이 섹션에서는 물리적 특징, CPU 모델/사양 및 모든 종류의 GPU 측정항목을 비롯하여 이벤트가 발생한 기기의 속성에 대해 알려줍니다.
다음은 이 탭의 정보가 유용할 수 있는 예입니다.
게임에서 특정 모양을 위해 셰이더를 많이 사용하지만 모든 휴대전화에 이 기능을 렌더링할 수 있는 GPU가 있지는 않다고 상상해 보세요. Unity 메타데이터 탭에 있는 정보를 사용하면 자동으로 사용하거나 완전히 사용 중지할 기능을 결정할 때 앱에서 테스트해야 하는 하드웨어를 더 잘 파악할 수 있습니다.
사용자의 기기에서는 버그나 비정상 종료가 발생하지 않을 수도 있지만, 현재 사용 중인 Android 기기는 매우 다양하므로 특정 '핫스팟'을 더 잘 이해하는 데 도움이 됩니다. 맞춤설정할 수 있습니다.
8. 예외 발생, 포착, 로깅
종종 개발자는 코드가 런타임 예외를 올바르게 포착하고 처리하더라도 그와 어떤 상황에서 그러한 예외가 발생했음을 확인하는 것이 좋습니다. Crashlytics.LogException
는 바로 이러한 용도로 사용할 수 있습니다. 즉, 예외 이벤트를 Firebase로 전송하여 Firebase Console에서 문제를 추가로 디버그할 수 있습니다.
Assets/Hamster/Scripts/States/DebugMenu.cs
에서using
문에 다음을 추가합니다.// Import Firebase using Firebase.Crashlytics;
- 계속
DebugMenu.cs
에서 다음과 같이LogNonfatalError()
를 덮어씁니다.void LogNonfatalError() { try { throw new System.Exception($"Test exception thrown in {nameof(LogNonfatalError)}"); } catch(System.Exception exception) { Crashlytics.LogException(exception); } }
- 앱을 빌드합니다.
- (Android만 해당) 다음 Firebase CLI 명령어를 실행하여 기호를 업로드합니다.
firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
- 심각하지 않은 오류 기록 버튼을 탭하고 앱을 다시 시작합니다.
- Crashlytics 대시보드로 이동하면 이 Codelab의 마지막 단계에서 확인한 것과 비슷한 화면이 표시됩니다.
- 하지만 이번에는 이벤트 유형 필터를 심각하지 않은 오류로 제한하여 방금 기록한 오류와 같이 심각하지 않은 오류만 표시합니다.
9. 프로그램 실행 흐름을 더 잘 이해할 수 있도록 Crashlytics에 문자열을 로깅합니다.
세션당 수백 번은 아니더라도 여러 경로에서 호출되는 코드 줄이 갑자기 예외나 충돌을 일으킬 수 있는 이유를 알아내려고 해 본 적이 있나요? IDE에서 코드를 단계별로 실행하고 값을 더 자세히 살펴보는 것이 좋을 수 있지만, 이렇게 적은 비율의 사용자에게만 이런 일이 일어난다면 어떻게 될까요? 게다가 어떤 행동을 하든 이 비정상 종료를 재현할 수 없다면 어떻게 해야 할까요?
이와 같은 상황에서는 어느 정도 맥락을 알고 있으면 큰 차이를 만들 수 있습니다. Crashlytics.Log
를 사용하면 필요한 컨텍스트를 작성할 수 있습니다. 이러한 메시지를 미래의 자아에 대한 힌트로 생각하세요.
로그는 다양한 방식으로 사용될 수 있지만 일반적으로 통화 순서 또는 부재가 매우 중요한 정보인 상황을 기록하는 데 가장 유용합니다.
Assets/Hamster/Scripts/States/DebugMenu.cs
에서 다음과 같이LogStringsAndCrashNow()
를 덮어씁니다.void LogStringsAndCrashNow() { Crashlytics.Log($"This is the first of two descriptive strings in {nameof(LogStringsAndCrashNow)}"); const bool RUN_OPTIONAL_PATH = false; if(RUN_OPTIONAL_PATH) { Crashlytics.Log(" As it stands, this log should not appear in your records because it will never be called."); } else { Crashlytics.Log(" A log that will simply inform you which path of logic was taken. Akin to print debugging."); } Crashlytics.Log($"This is the second of two descriptive strings in {nameof(LogStringsAndCrashNow)}"); TestCrash(); }
- 앱을 빌드합니다.
- (Android만 해당) 다음 Firebase CLI 명령어를 실행하여 기호를 업로드합니다.
firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
- Log Strings and Crash Now(지금 기록 문자열 및 비정상 종료) 버튼을 탭한 후 앱을 다시 시작합니다.
- Crashlytics 대시보드로 돌아가서 문제 표에 나열된 최신 문제를 클릭합니다. 다시 이전 문제와 비슷한 내용이 표시됩니다.
- 하지만 이벤트 요약 내에서 로그 탭을 클릭하면 다음과 같은 보기가 표시됩니다.
10. 커스텀 키 쓰기 및 덮어쓰기
소수의 값 또는 구성으로 설정된 변수에 해당하는 비정상 종료를 더 잘 파악하고 싶다고 가정해 보겠습니다. 언제든지 변수와 보고 있는 가능한 값의 조합을 기반으로 필터링할 수 있으면 좋을 것입니다.
Crashlytics는 임의의 문자열을 로깅하는 것 외에도 프로그램이 비정상 종료될 때 프로그램의 정확한 상태를 파악하는 것이 유용한 경우 또 다른 디버깅 형식인 커스텀 키를 제공합니다.
세션에 설정할 수 있는 키-값 쌍입니다. 누적되며 완전히 추가되는 로그와 달리, 변수 또는 조건의 최신 상태만 반영하도록 키를 덮어쓸 수 있습니다.
이러한 키는 프로그램의 마지막으로 기록된 상태의 원장이 될 뿐만 아니라 Crashlytics 문제를 위한 강력한 필터로 사용할 수 있습니다.
Assets/Hamster/Scripts/States/DebugMenu.cs
에서 다음과 같이SetAndOverwriteCustomKeyThenCrash()
를 덮어씁니다.void SetAndOverwriteCustomKeyThenCrash() { const string CURRENT_TIME_KEY = "Current Time"; System.TimeSpan currentTime = System.DateTime.Now.TimeOfDay; Crashlytics.SetCustomKey( CURRENT_TIME_KEY, DayDivision.GetPartOfDay(currentTime).ToString() // Values must be strings ); // Time Passes currentTime += DayDivision.DURATION_THAT_ENSURES_PHASE_CHANGE; Crashlytics.SetCustomKey( CURRENT_TIME_KEY, DayDivision.GetPartOfDay(currentTime).ToString() ); TestCrash(); }
- 앱을 빌드합니다.
- (Android만 해당) 다음 Firebase CLI 명령어를 실행하여 기호를 업로드합니다.
firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
- 맞춤 키 및 비정상 종료 설정 버튼을 탭한 후 앱을 다시 시작합니다.
- Crashlytics 대시보드로 돌아가서 문제 표에 나열된 최신 문제를 클릭합니다. 다시 이전 문제와 비슷한 내용이 표시됩니다.
- 하지만 이번에는 이벤트 요약에서 키 탭을 클릭하면
Current Time
:을 포함한 키 값을 확인할 수 있습니다.
커스텀 로그 대신 커스텀 키를 사용하는 이유는 무엇인가요?
- 로그는 순차 데이터를 저장하는 데 적합하지만 최신 값만 원하는 경우 커스텀 키가 더 좋습니다.
- Firebase Console에서 문제 테이블 검색창의 키 값을 기준으로 문제를 쉽게 필터링할 수 있습니다.
하지만 로그와 마찬가지로 커스텀 키에는 한도가 있습니다. Crashlytics는 최대 64개의 키-값 쌍을 지원합니다. 이 기준에 도달한 후에는 추가 값이 저장되지 않습니다. 각 키-값 쌍의 최대 크기는 1KB입니다.
11. (Android만 해당) 맞춤 키와 로그를 사용하여 ANR을 파악하고 진단합니다.
Android 개발자가 디버그하기 가장 어려운 문제 클래스 중 하나는 애플리케이션 응답 없음 (ANR) 오류입니다. ANR은 앱이 5초 넘게 입력에 응답하지 않을 때 발생합니다. 이 경우 앱이 정지되었거나 속도가 매우 느렸다는 의미입니다. 사용자에게 대화상자가 표시되며 사용자는 '대기' 여부를 선택할 수 있습니다. 또는 '앱 닫기'를 선택합니다.
ANR은 좋지 않은 사용자 환경이며 (위의 ANR 링크에서 설명했듯이) Google Play 스토어에서 앱의 검색 가능 여부에 영향을 줄 수 있습니다. ANR은 복잡하고 휴대전화 모델마다 동작이 매우 다른 멀티스레드 코드로 인해 발생하는 경우가 많기 때문에 디버깅 중에 ANR을 재현하기가 거의 불가능하지는 않지만 매우 어려운 경우가 많습니다. 따라서 분석적, 연역적으로 접근하는 것이 일반적으로 가장 좋은 접근 방식입니다.
이 메서드에서는 Crashlytics.LogException
, Crashlytics.Log
, Crashlytics.SetCustomKey
를 조합하여 자동 문제 로깅을 보완하고 추가 정보를 제공합니다.
Assets/Hamster/Scripts/States/DebugMenu.cs
에서 다음과 같이SetLogsAndKeysBeforeANR()
를 덮어씁니다.void SetLogsAndKeysBeforeANR() { System.Action<string,long> WaitAndRecord = (string methodName, long targetCallLength)=> { System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch(); const string CURRENT_FUNCTION = "Current Async Function"; // Initialize key and start timing Crashlytics.SetCustomKey(CURRENT_FUNCTION, methodName); stopWatch.Start(); // The actual (simulated) work being timed. BusyWaitSimulator.WaitOnSimulatedBlockingWork(targetCallLength); // Stop timing stopWatch.Stop(); if(stopWatch.ElapsedMilliseconds>=BusyWaitSimulator.EXTREME_DURATION_MILLIS) { Crashlytics.Log($"'{methodName}' is long enough to cause an ANR."); } else if(stopWatch.ElapsedMilliseconds>=BusyWaitSimulator.SEVERE_DURATION_MILLIS) { Crashlytics.Log($"'{methodName}' is long enough it may cause an ANR"); } }; WaitAndRecord("DoSafeWork",1000L); WaitAndRecord("DoSevereWork",BusyWaitSimulator.SEVERE_DURATION_MILLIS); WaitAndRecord("DoExtremeWork",2*BusyWaitSimulator.EXTREME_DURATION_MILLIS); }
- 앱을 빌드합니다.
- 다음 Firebase CLI 명령어를 실행하여 기호를 업로드합니다.
firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
- 로그 및 키 설정 → ANR이라는 버튼을 탭한 다음 앱을 다시 시작합니다.
- Crashlytics 대시보드로 돌아간 다음 문제 표에서 새로운 문제를 클릭하여 이벤트 요약을 확인합니다. 호출이 제대로 진행되면 다음과 같이 표시됩니다.
보시다시피 Firebase는 스레드의 사용량이 많은 대기를 앱에서 ANR을 트리거한 주된 원인으로 포착했습니다. - 이벤트 요약의 로그 탭에서 로그를 살펴보면 완료로 기록된 마지막 메서드가
DoSevereWork
인 것을 확인할 수 있습니다.
반면 시작으로 표시된 마지막 메서드는DoExtremeWork
입니다. 이는 이 메서드 중에 ANR이 발생했으며 게임이DoExtremeWork
를 기록하기 전에 종료되었음을 나타냅니다.
Why do this?
- ANR을 재현하기는 매우 어렵기 때문에 코드 영역과 측정항목에 관한 풍부한 정보를 얻는 것은 ANR을 연역적으로 발견하는 데 매우 중요합니다.
- 이제 맞춤 키에 저장된 정보를 통해 실행 시간이 가장 긴 비동기 스레드와 ANR을 트리거할 위험이 있는 비동기 스레드를 파악할 수 있습니다. 이러한 종류의 관련 논리 및 숫자 데이터는 코드에서 최적화를 위해 가장 필요한 부분을 보여줍니다.
12. 애널리틱스 이벤트를 삽입하여 보고서 보강
다음 메서드는 디버그 메뉴에서도 호출할 수 있지만, 문제를 자체적으로 생성하는 대신 Google 애널리틱스를 다른 정보 소스로 사용하여 게임 작동을 더 잘 이해할 수 있습니다.
이 Codelab에서 작성한 다른 메서드와 달리 이러한 메서드를 다른 메서드와 함께 사용해야 합니다. 다른 메서드 중 하나를 실행하기 전에 원하는 임의의 순서로 이러한 메서드를 호출합니다 (디버그 메뉴에서 해당하는 버튼을 눌러). 그런 다음 특정 Crashlytics 문제의 정보를 검토하면 정렬된 애널리틱스 이벤트 로그가 표시됩니다. 이 데이터는 게임에서 앱을 계측한 방법에 따라 프로그램 흐름 또는 사용자 입력의 조합을 더 잘 이해하는 데 사용될 수 있습니다.
Assets/Hamster/Scripts/States/DebugMenu.cs
에서 다음 메서드의 기존 구현을 덮어씁니다.
<ph type="x-smartling-placeholder">public void LogProgressEventWithStringLiterals() { Firebase.Analytics.FirebaseAnalytics.LogEvent("progress", "percent", 0.4f); }
</ph>public void LogIntScoreWithBuiltInEventAndParams() { Firebase.Analytics.FirebaseAnalytics .LogEvent( Firebase.Analytics.FirebaseAnalytics.EventPostScore, Firebase.Analytics.FirebaseAnalytics.ParameterScore, 42 ); }
- 게임을 빌드 및 배포한 다음 디버그 메뉴로 이동합니다.
- (Android만 해당) 다음 Firebase CLI 명령어를 실행하여 기호를 업로드합니다.
firebase crashlytics:symbols:upload --app=<FIREBASE_APP_ID> <PATH/TO/SYMBOLS>
- 다음 버튼 중 하나 이상을 한 번 이상 눌러 위 기능을 호출합니다.
- 문자열 이벤트 로그
- 로그인 이벤트
- 지금 비정상 종료 버튼을 누릅니다.
- 비정상 종료 이벤트를 Firebase에 업로드하려면 게임을 다시 시작합니다.
- 다양한 임의 시퀀스의 애널리틱스 이벤트를 로깅한 다음, 게임에서 Crashlytics가 보고서를 생성하는 이벤트를 생성하도록 하면 이는 다음과 같이 Crashlytics 이벤트 요약의 로그 탭에 추가됩니다.
13. 앞으로의 전망
이를 통해 자동으로 생성된 비정상 종료 보고서를 보완할 더 나은 이론적 근거를 마련할 수 있게 되었습니다. 이 새로운 정보를 통해 현재 상태, 과거 이벤트 기록, 기존 Google 애널리틱스 이벤트를 사용하여 이벤트 순서와 결과로 이어지는 로직을 더 잘 분류할 수 있습니다.
앱이 Android 11 (API 수준 30) 이상을 타겟팅한다면 use-after-free
및 heap-buffer-overflow
버그와 같은 네이티브 메모리 오류로 인한 비정상 종료를 디버깅하는 데 유용한 네이티브 메모리 할당자 기능인 GWP-ASan을 통합하는 것이 좋습니다. 이 디버깅 기능을 활용하려면 GWP-ASan을 명시적으로 사용 설정하세요.
다음 단계
원격 구성으로 Unity 게임 계측 Codelab으로 이동하여 Unity에서 원격 구성 및 A/B 테스팅을 사용하는 방법을 알아봅니다.