Skip to content

feat: Crashlytics 추가 및 에러 추적 환경 개선#287

Merged
jihun32 merged 16 commits into
developfrom
feat/#286/TWI-82
May 14, 2026
Merged

feat: Crashlytics 추가 및 에러 추적 환경 개선#287
jihun32 merged 16 commits into
developfrom
feat/#286/TWI-82

Conversation

@clxxrlove
Copy link
Copy Markdown
Member

🔗 관련 이슈

📙 작업 내역

  • Crashlytics 의존성 추가
  • build phase에 dSYM 업로드 스크립트 추가
  • Core -> Domain -> Feature / 계층 간 오류 전달 환경 개선
  • 오류가 report 되지 않는 기능들 파악 및 오류를 삼키고 넘어가는 부분들 개선
  • 버전업 (이 사유로 버전이 1.1.1 PR들은 워크플로우 실패함 / CD -> Testflight)

🎨 스크린샷 또는 시연 영상 (선택)

스크린샷 2026-05-11 12 29 24

💬 추가 설명 or 리뷰 포인트 (선택)

  • Firebase Console에서 확인 가능 / Debug 빌드에서는 불가능!! Release/실기기 환경에서 테스트 가능
  • GA가 붙으면 Crashlytics breadcrumbs라는게 붙어서 더 추적이 쉽다고 함

@linear
Copy link
Copy Markdown

linear Bot commented May 11, 2026

TWI-82

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Review Change Stack

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a40ad4e2-8629-4668-aae0-75170cf708bc

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Crashlytics를 Core 모듈로 추가하고 CrashlyticsClient 인터페이스 및 Firebase 구현을 정의합니다. 여러 에러 타입을 CustomNSError로 준수시키고, AppCoordinator, Auth, Onboarding, ProofPhoto 기능에서 에러 및 사용자 추적을 Crashlytics로 전송하도록 통합합니다.

Changes

Crashlytics 통합 및 에러 기록

Layer / File(s) Summary
Tuist 프로젝트 구성 및 의존성
Tuist/Package.swift, Tuist/ProjectDescriptionHelpers/Module.swift, Tuist/ProjectDescriptionHelpers/Target/Dependency/TargetDependency+External.swift
Tuist 패키지 설정에 FirebaseCrashlytics를 staticLibrary로 추가하고, Module.Core 헬퍼에 crashlytics 케이스를 등록하며, TargetDependency.External에 FirebaseCrashlytics를 추가합니다.
Crashlytics 빌드 스크립트
Tuist/ProjectDescriptionHelpers/Scripts/CrashlyticsScript.swift, Projects/App/Project.swift
Release 빌드에서 dSYM을 Crashlytics로 업로드하는 스크립트를 정의하고, Release 타겟의 scripts 목록에 crashlyticsUploadSymbols를 추가합니다.
Crashlytics Core 인터페이스 정의
Projects/Core/Crashlytics/Interface/Sources/CrashlyticsClient.swift
CrashlyticsClient 구조체와 네 개의 주입 가능한 클로저(record, log, setUserIdentifier, setCustomValue)를 정의하고, 테스트용 더미 구현과 DependencyValues 확장을 제공합니다.
Crashlytics 커스텀 키 정의
Projects/Core/Crashlytics/Interface/Sources/CrashlyticsKey.swift
사용자, 화면, 네트워크, 이미지 업로드, 카메라, 인증, 키체인 등의 Crashlytics 커스텀 키 상수를 정의합니다.
Crashlytics 모듈 정의
Projects/Core/Crashlytics/Project.swift
ProjectDescription을 사용하여 인터페이스와 구현 타겟으로 나뉜 Core.Crashlytics 모듈을 정의합니다.
Crashlytics Live 구현
Projects/Core/Crashlytics/Sources/CrashlyticsClient+Live.swift
DependencyKey를 준수하는 liveValue를 구현하여 FirebaseCrashlytics 싱글톤과 연결하고, 에러 기록, 로그, 사용자 ID 설정, 커스텀 값 설정을 Firebase로 라우팅합니다.
에러 타입 CustomNSError 준수
Projects/Core/CaptureSession/Interface/Sources/CaptureSessionError.swift, Projects/Core/Network/Interface/Sources/NetworkError.swift, Projects/Core/Push/Sources/PushClient+Live.swift, Projects/Core/Storage/Sources/KeychainTokenStorage.swift, Projects/Domain/Auth/Interface/Sources/Entity/AuthLoginError.swift, Projects/Domain/Onboarding/Interface/Sources/Entity/OnboardingError.swift
CaptureSessionError, NetworkError, PushError, KeychainError, AuthLoginError, OnboardingError에 CustomNSError를 준수시켜 errorDomain, errorCode, errorUserInfo를 제공합니다.
Auth 에러 처리 개선
Projects/Domain/Auth/Sources/AuthClient+Live.swift
AuthLoginError에 caseName 컴퓨티드 프로퍼티를 추가하고, performSignIn과 performRefreshToken에서 네트워크 및 저장소 에러를 명시적으로 처리하여 정확한 에러 타입으로 매핑합니다.
앱 초기화 및 버전 업데이트
Projects/App/Project.swift, Projects/App/Sources/AppDelegate.swift
CFBundleShortVersionString을 1.1.2로 업데이트하고, AppDelegate에서 DEBUG 빌드 설정에 따라 Crashlytics 크래시 수집을 조건부로 활성화합니다. FirebaseRemoteConfig 의존성도 추가합니다.
AppCoordinator Crashlytics 통합
Projects/App/Sources/Reducer/AppCoordinator.swift
AppCoordinator에 crashlyticsClient 의존성을 주입하고, 인증 상태 확인 실패 시 에러를 기록하며, 온보딩 상태 확인 실패 시 Crashlytics에 로그하고, 성공한 로그인 시 사용자 ID를 설정합니다.
Auth 기능 Crashlytics 통합
Projects/Feature/Auth/Project.swift, Projects/Feature/Auth/Sources/Reducer/AuthReducer.swift
Auth 기능 프로젝트에 crashlytics 코어 의존성을 추가하고, AuthReducer에서 로그인 실패 시 DEBUG 빌드가 아닐 때 AuthLoginError.caseName을 이용하여 Crashlytics에 에러를 기록합니다.
Onboarding 상태 및 액션 확장
Projects/Feature/Onboarding/Sources/OnboardingCoordinator.swift
OnboardingCoordinator.State에 toast 필드를 추가하고, Action에 showToast 케이스를 추가하며, 리듀서에서 showToast를 처리하여 토스트 상태를 저장합니다.
Onboarding 기능 Crashlytics 통합
Projects/Feature/Onboarding/Project.swift, Projects/Feature/Onboarding/Sources/OnboardingCoordinator.swift
Onboarding 기능 프로젝트에 crashlytics 의존성을 추가하고, OnboardingCoordinator에서 초대 코드 조회 실패 시 Crashlytics에 기록하며 showToast 액션을 디스패치하여 경고 토스트를 표시합니다.
Onboarding 뷰 토스트 렌더링
Projects/Feature/Onboarding/Sources/OnboardingCoordinatorView.swift
OnboardingCoordinatorView에 .txToast(item: $store.toast) 수정자를 추가하여 coordinator 상태의 토스트를 UI로 표시합니다.
ProofPhoto 기능 Crashlytics 통합
Projects/Feature/ProofPhoto/Project.swift, Projects/Feature/ProofPhoto/Sources/ProofPhoto/ProofPhotoReducer+Impl.swift
ProofPhoto 기능 프로젝트에 crashlytics 의존성을 추가하고, ProofPhotoReducer에서 카메라 캡처 및 이미지 업로드 중 발생한 에러를 Crashlytics에 기록하며 uploadStep과 originalImageBytes 정보를 추적합니다.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Keepiluv/Keepiluv-iOS#90: OnboardingCoordinator 추가 PR와 관련하여, 본 PR이 해당 좌표기자에 Crashlytics 에러 보고 및 토스트 상태/액션을 추가합니다.
  • Keepiluv/Keepiluv-iOS#199: ProofPhotoReducer+Impl.swift를 수정하며 캡처/업로드 완료 처리를 변경하므로 코드 레벨에서 관련됩니다.
  • Keepiluv/Keepiluv-iOS#150: 온보딩 기능의 토스트/UI를 수정하며, 본 PR이 의존하는 txToast 타입 API를 확장합니다.

Suggested labels

✨ feat

Suggested reviewers

  • jihun32
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#286/TWI-82

Comment @coderabbitai help to get the list of available commands and usage tips.

@clxxrlove
Copy link
Copy Markdown
Member Author

@coderabbitai summary

@clxxrlove clxxrlove added the feat label May 11, 2026
@github-actions github-actions Bot requested a review from jihun32 May 11, 2026 03:31
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

✅ Actions performed

Summary regeneration triggered.

Copy link
Copy Markdown
Contributor

@jihun32 jihun32 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생해쓰 GA PR 코멘트에서 너가 요청한 인증샷 퍼널 이벤트 심어놨는데 컨플릭 해결하면서 함 확인해줘~

그리고 지금 crashlytics.log, crashlytics.record 호출할 때 message를 하드코딩으로 넣는데
Analytics 구현했던것처럼 해서 하드코딩 위험도 낮추고, 화면별로 Crashlytics 로그/에러 볼 수 있게 따로 분리하는건 어때?

참고용 예시(입맛대로 변경 ㄱㄱ)

/// CoreCrashlytics Interface
 public protocol CrashlyticsLog {
      var message: String { get }
  }

 public protocol CrashlyticsRecord {
      var dict: [String: String] { get }
  }

  public struct CrashlyticsClient: Sendable {
      public var record: @Sendable (Error, any CrashlyticsRecord) -> Void
      public var log: @Sendable (any CrashlyticsLog) -> Void
  }

/// Feature/ProofPhoto
 enum ProofPhotoCrashlyticsLogEvent: CrashlyticsLogEvent {
      case uploadStep(UploadStep, goalId: Int64, imageBytes: Int?)

      enum UploadStep: String {
          case fetchURL
          case uploadS3
          case createLog
      }

      var message: String {
          switch self {
          case let .uploadStep(step, goalId, imageBytes):
              if let imageBytes {
                  return "upload_step: \(step.rawValue), goalId=\(goalId), size=\(imageBytes)"
              }
              return "upload_step: \(step.rawValue), goalId=\(goalId)"
          }
      }
  }

  enum ProofPhotoCrashlyticsRecordEvent: CrashlyticsRecordEvent {
      case uploadFailed(
          step: UploadStep,
          goalId: Int64,
          originalImageBytes: Int
      )

      var customKeys: [String: String] {
          switch self {
          case let .uploadFailed(step, goalId, originalImageBytes):
              return [
                  CrashlyticsKey.screen: "proof_photo_upload",
                  CrashlyticsKey.uploadStep: step.rawValue,
                  CrashlyticsKey.goalId: "\(goalId)",
                  CrashlyticsKey.originalImageBytes: "\(originalImageBytes)"
              ]
          }
      }
  }
/// 호출부
  crashlytics.log(
      ProofPhotoCrashlyticsLogEvent.uploadStep(
          .fetchURL,
          goalId: goalId,
          imageBytes: nil
      )
  )

  crashlytics.record(
      error,
      ProofPhotoCrashlyticsRecordEvent.uploadFailed(
          step: uploadStep,
          goalId: goalId,
          originalImageBytes: originalSize
      )
  )

Comment on lines +52 to +63
public var caseName: String {
switch self {
case .unsupportedProvider: return "unsupportedProvider"
case .missingCredential: return "missingCredential"
case .userCanceled: return "userCanceled"
case .providerError: return "providerError"
case .serverError: return "serverError"
case .networkError: return "networkError"
case .storageFailed: return "storageFailed"
case .tokenRefreshFailed: return "tokenRefreshFailed"
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

case네이밍이랑 caseName 값 같아서 enum에 String 채택하고 rawValue쓰는건 어때?

@clxxrlove
Copy link
Copy Markdown
Member Author

@jihun32 반영 완료

@jihun32
Copy link
Copy Markdown
Contributor

jihun32 commented May 14, 2026

@jihun32 반영 완료

확인했고 머지할게~

@jihun32 jihun32 merged commit b99dd58 into develop May 14, 2026
2 checks passed
@jihun32 jihun32 deleted the feat/#286/TWI-82 branch May 14, 2026 00:16
clxxrlove added a commit that referenced this pull request May 14, 2026
* chore: FirebaseCrashlytics SPM 의존성 및 모듈 등록 - #286

* feat: Crashlytics dSYM 업로드 스크립트 추가 - #286

* feat: Core/Crashlytics 모듈 추가 - #286

* feat: Core 에러 타입에 CustomNSError 적용 - #286

* feat: Domain 에러 타입에 CustomNSError 적용 및 AuthClient 에러 wrapping - #286

* chore: App 타겟에 Crashlytics 의존성 및 dSYM 스크립트 추가 - #286

* feat: AppDelegate에 Crashlytics 수집 설정 추가 - #286

* feat: AppCoordinator에 Crashlytics 유저 식별자 및 오류 추적 추가 - #286

* feat: Auth Feature에 Crashlytics 로그인 실패 추적 추가 - #286

* feat: ProofPhoto Feature에 Crashlytics 오류 추적 추가 - #286

* feat: Onboarding Feature에 초대 코드 실패 토스트 및 Crashlytics 추적 추가 - #286

* chore: 버전 1.1.2로 업데이트 - #286

* fix: 컴파일 에러 수정 - #286

* fix: CalendarNow 변수명 SwiftLint identifier_name 위반 수정 - #286

* refactor: CrashlyticsClient를 화면별 이벤트 enum 패턴으로 전환 - #286

* refactor: AuthLoginError.caseName 제거 - #286
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants