반응형

iOS 앱 크래시 발생 시 확인하는 방법

iOS 앱 크래시를 코드로 감지하거나 수집하는 방법은 제한적입니다.

App Store 정책을 지키면서 안정적으로 로그를 남기려면, MetricKit + Exception Handler + Breadcrumb Logger + Crashlytics/Sentry 조합이 가장 효과적입니다.

 

1. MetricKit (공식, iOS 13+)

import UIKit
import MetricKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate, MXMetricManagerSubscriber {
  func application(_ application: UIApplication,
                   didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    MXMetricManager.shared.add(self)
    return true
  }

  func didReceive(_ payloads: [MXDiagnosticPayload]) {
    for payload in payloads {
      if let crashes = payload.crashDiagnostics {
        for crash in crashes {
          let signal = crash.signal
          let exception = crash.exceptionType
          let reason = crash.terminationReason
          let stack = crash.callStackTree.jsonRepresentation()
          print("Crash signal: \(signal), reason: \(String(describing: reason))")
          // JSON 저장 후 다음 실행 시 서버 업로드 가능
        }
      }
    }
  }
}

장점: 시스템 레벨 크래시, OOM, Hang까지 진단 가능

단점: “다음 실행 시” 전달됨 (실시간 아님)

 

2. NSSetUncaughtExceptionHandler (Objective-C 예외)

void UncaughtExceptionHandler(NSException *exception) {
  NSString *summary = [NSString stringWithFormat:@"name=%@ reason=%@ stack=%@",
                       exception.name, exception.reason, exception.callStackSymbols];
  // 최소한의 로그만 남기기
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
  return YES;
}

장점: 간단한 예외 로그 확보

단점: Swift fatalError 등은 잡히지 않음

 

3. POSIX Signal Handler (SIGABRT, SIGSEGV 등)

#include <signal.h>
#include <execinfo.h>

void signalHandler(int sig) {
  void *stack[64];
  int frames = backtrace(stack, 64);
  backtrace_symbols_fd(stack, frames, STDERR_FILENO);
  _exit(1);
}

void installSignals() {
  signal(SIGABRT, signalHandler);
  signal(SIGSEGV, signalHandler);
  signal(SIGILL,  signalHandler);
  signal(SIGBUS,  signalHandler);
}

주의: 신호 핸들러에서는 파일/네트워크 접근 금지.

사용법: Swift에서 브리징 후 installSignals() 호출.

 

4. Breadcrumb 로깅 (비동기 안전 로그)

struct Breadcrumb: Codable { let t: TimeInterval; let msg: String }
final class Bread {
  static let shared = Bread()
  private let url = FileManager.default.temporaryDirectory.appendingPathComponent("crumbs.log")
  private let q = DispatchQueue(label: "crumbs", qos: .utility)

  func add(_ msg: String) {
    let b = Breadcrumb(t: Date().timeIntervalSince1970, msg: msg)
    q.async {
      if let data = try? JSONEncoder().encode(b) {
        try? FileHandle(forWritingTo: self.url).flatMap {
          try? $0.seekToEnd()
          try? $0.write(contentsOf: data + Data([0x0A]))
          try? $0.close()
        } ?? { try? data.write(to: self.url) }()
      }
    }
  }
}

앱 주요 이벤트(화면 진입, 요청 성공 등)를 Breadcrumb으로 남겨 크래시 직전 흐름을 복원.

 

5. OOM(Out Of Memory) 탐지 요령

체크 포인트설명

last_run.json 파일 삭제 여부 비정상 종료 감지
didReceiveMemoryWarning 횟수 증가 직전 메모리 부족 징후
MetricKit OOM 리포트 iOS 14+ 에서 수신 가능

 

6. 서드파티 크래시 SDK

Firebase Crashlytics

import FirebaseCore
import FirebaseCrashlytics

FirebaseApp.configure()
Crashlytics.crashlytics().log("Entered GameScene")
Crashlytics.crashlytics().setCustomValue("stage5", forKey: "level")

장점: 실시간 대시보드, dSYM 자동 업로드, 버전별 그룹핑

단점: 커스텀 전송 제어 어려움

Sentry

  • 장점: 성능 트레이스 + 크래시 통합 수집, breadcrumb 자동 저장
  • 설정: SentrySDK.start { $0.dsn = "YOUR_DSN" }

 

7. 실시간으로 잡을 수 없는 것들

항목설명

Watchdog 종료 런치/백그라운드 제한시간 초과, 감지 불가
SIGKILL OS 강제 종료, 콜백 없음
fatalError() NSSetUncaughtExceptionHandler에 안 잡힘

 

8. 최소 구성 템플릿

  1. MetricKit 구독 (공식 리포트)
  2. Breadcrumb 로거 (앱 이벤트 추적)
  3. NSException / Signal 핸들러 (보조)
  4. Crashlytics 또는 Sentry (실시간 수집)
  5. 다음 실행 시 업로드 파이프라인 구축

 

9. 심볼리케이션

  • dSYM 자동 업로드 (upload-symbols 스크립트)
  • 로컬 테스트: atos -o YourApp.dSYM/Contents/Resources/DWARF/YourApp 0xADDRESS

 

결론

  • 실시간 감지는 불가능하나, “다음 실행 시 + Breadcrumb 조합” 으로 완전한 분석 가능.
  • 공식 MetricKit + Firebase Crashlytics/Sentry 조합이 현실적이며, App Store 정책에도 안전.
  • 크래시 순간에는 I/O, 네트워크 전송을 절대 시도하지 말고, 다음 실행 시 업로드하도록 구성하세요.
반응형
Posted by 까칠코더
,