
작업 일정
- #39 [config] crashlytics log
- #47 [config] 앱 배포를 위한 메타데이터 설정
사용자가 앱을 실행하다 오류가 발생했을 경우 그 오류 데이터를 수집해 문제를 해결할 수 있도록 하는 게 좋다. 그럴 때 사용하는 게 Firebase Crashlytics다. Firebase 무료 제공 제품군에 포함되어 있어 요금제와 상관 없이 사용할 수 있다. 어디까지나 오류 보고용이기 때문에 저장 공간과 네트워크를 너무 많이 사용하지 않도록 크기 제한이 존재할 뿐이다.
FirebaseCrashlytics.instance.log() 로 남긴 로그는 앱 흐름을 추적하기 위한 것으로, 이것만으로는 그저 데이터가 쌓이다 FIFO 방식으로 덮어씌워질 뿐 서버에 알리지 않는다. 그러다 크래시 발생 시 당시 상황 로그로 함께 첨부된다. FirebaseCrashlytics.instance.recordError() 로 남긴 로그는 비치명적 예외를 추적하기 위한 것으로, Crashlytics 대시보드의 [Non-fatal] 탭에 집계된다. 어디서 얼마나 자주 발생했는지 통계를 확인할 수 있다. 앱이 뻗은 경우에는 프로세스 종료 직전에 minidump로 작성되어 앱이 다시 켜질 때 백엔드로 전송된다.
개발자가 예외 처리를 한 부분에 대해서는 예외 처리를 통해 대처했지만 이슈가 발생하긴 했다는 의미에서 FirebaseCrashlytics.instance.recordError() 로 기록을 남긴다. 때로는 개발자의 예외 처리로 다 잡히지 않는 이슈도 존재할 수 있는데 그 경우 PlatformDispatcher.onError() 를 통해 백엔드로 전송한다. 런타임 버그, 라이브러리 결함 등은 개발자가 전부 예측하여 방어할 수 없기 때문에 최후의 방어선으로 넣어주는 게 좋다고 한다.
firebase_crashlytics package를 사용한다. 사용자가 가입 및 로그인했을 때 누구에게서 발생한 오류인지 식별하기 위한 사용자 식별 바인딩을 하고, 로그아웃하거나 탈퇴했을 때 그것을 해제한다. 그리고 백그라운드에서 발생할 수 있는 이슈와 사용자의 행동에 따라 발생할 수 있는 이슈를 각각 추적하여 모니터링하는 코드를 작성한다. 흐름을 파악하는 데 도움이 될 만한 곳에 FirebaseCrashlytics.instance.log() 를, 문제가 발생한 부분에 FirebaseCrashlytics.instance.recordError() 를 적절히 사용한다.
Crashlytics는 설정한다고 바로 대시보드가 뜨지는 않고 한 번 비정상 종료 테스트를 해 주어야 뜬다. 그리고 아이폰의 경우 dSYM이라는 녀석을 별도로 구성해 주어야 한다. ios/Runner.xcworkspace 을 열어 루트 Runner 에서 [Targets] 에 있는 Runner 를 선택한 후, [Build Phases] 탭에서 [New Run Script Phase] 드롭다운 메뉴를 눌러 쉘 스크립트를 작성한다.
if [ -f "${PODS_ROOT}/FirebaseCrashlytics/run" ]; then "${PODS_ROOT}/FirebaseCrashlytics/run" elif [ -f "${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run" ]; then "${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run" fi
기본적으로 디버그 빌드에서는 dSYM을 사용하지 않도록 되어 있기 때문에 별도로 설정하지 않으면 릴리즈 빌드를 해야 dSYM 번들이 생성된다. 디버그 빌드에서도 dSYM을 사용하고 싶다면 [Build Settings] 탭에서 [Basic]이 아닌 [All] 을 선택하고 [Debug Information Format]의 [Debug] 항목을 [DWARF with dSYM File]로 변경하면 된다.
[Input Files] 섹션에 적절한 파일 경로를 등록해 두면 Xcode 빌드 시스템이 빌드 산출물의 타임스탬프를 추적하여, dSYM 파일이 실제로 새로 생성되었을 때만 업로더를 실행함으로써 불필요한 네트워크 중복 전송 방지 및 증분 빌드 최적화를 보장한다고 한다.
앱 이름을 설정하고 아이콘을 설정하고 스플래시 화면을 설정한다. 아이콘 설정에는 flutter_launcher_icons package를 사용하고 스플래시 화면 설정에는 flutter_native_splash package를 사용한다. 이들은 dependencies가 아닌 dev_dependencies로 추가하여 개발 시에만 사용하고 배포 시에는 포함하지 않는다.
flutter_launcher_icons package를 사용할 땐 pubspec.yaml 에 flutter_launcher_icons: 로 시작하는 설정값을 작성한 뒤 명령어를 실행하여 아이콘을 적용한다.
dart run flutter_launcher_icons
flutter_native_splash package도 마찬가지로 pubspec.yaml 에 flutter_native_splash: 로 시작하는 설정값을 작성한 뒤 명령어를 실행하여 아이콘을 적용한다.
dart run flutter_native_splash:create
현재는 안드로이드 설정이 디버그 키로 되어 있는데 배포를 하기 위해서는 릴리즈 키를 연결해야 한다. 릴리즈 키는 명령어를 통해 생성할 수 있다.
keytool -genkey -v -keystore android/app/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload
릴리즈 키를 생성한 후에는 android/key.properties 파일에 키 정보를 집어넣고 이를 통해 android/app/build.gradle.kts 파일에 키를 등록한다. 설정을 마친 후 프로덕션 배포 규격으로 빌드를 실행하여 정상 서명되는지 검증한다.
flutter build appbundle --release
✓ Built build/app/outputs/bundle/release/app-release.aab (101.4MB) 와 같은 내용으로 마무리되면 잘 된 거다.
개인정보 처리방침 및 이용약관은 직접 작성하면 놓치는 부분이 꽤 있을 것 같아서 AI에게 다음 사이트들을 참고하여 작성해 달라고 하였다.
앱 내 위젯으로 작성하는 것과 별도로 제출용 HTML 파일을 작성하여 Firebase Hosting으로 띄운다. firebase.json 에 특정 디렉토리만 호스팅하도록 작성해 놓으면 그 안에 있는 파일만 띄울 수 있다.
firebase.json// 앞 부분 생략 "hosting": { "public": "public", "ignore": [ "firebase.json", "**/.*", "**/node_modules/**" ] // 뒷 부분 생략
firebase deploy --only hosting