앱 크래시 — 프로그램이 응답을 멈추고 종료되는 비정상적인 종료입니다. 모바일 개발에서 크래시는 부정적인 리뷰와 평점 하락의 주요 원인입니다. Firebase (2024)에 따르면, 53%의 경우 사용자는 1–2회 크래시 후 앱을 삭제합니다. 각 크래시는 리텐션을 3–5% 감소시킵니다. Crashlytics 및 Sentry와 같은 모니터링 시스템은 사용자에게 대규모 영향을 미치기 전에 크래시 원인을 신속하게 찾아 수정하는 데 도움을 줍니다.
핵심 요약
크래시 — 코드가 처리하지 않은 예외 상황으로 인해 발생하는 프로그램의 예기치 않은 종료입니다. 모바일 OS에서 크래시는 앱이 즉시 종료되고 “앱이 중지되었습니다” 화면이 표시되거나 홈 화면으로 돌아갑니다.
크래시는 두 가지 큰 클래스로 나뉩니다. 처리된 오류 — try/catch 블록이 예외를 포착하고 앱이 계속 실행되지만 일부 기능이 손실될 수 있습니다. 처리되지 않은 크래시 — 예외가 OS 수준까지 전파되고 시스템이 프로세스를 종료합니다. 두 번째 유형은 사용자가 데이터를 저장할 수 없기 때문에 특히 위험합니다.
200만 사용자와 0.1% 크래시율의 시스템은 각 릴리스에서 2,000명의 사용자를 잃습니다. Google Play Console(2024)에 따르면, 크래시율이 1.5%를 초과하는 앱은 추천에서 제외되고 최대 30%의 유기적 트래픽을 잃습니다.
NullPointerException(NPE) — Java/Kotlin에서 크래시의 왕입니다. null 객체의 메서드를 호출하려고 할 때 발생합니다. Kotlin에서는 null safety 덕분에 NPE가 덜 일반적이지만 !! 연산자를 사용하거나 Java 코드와 상호 작용할 때 여전히 발생할 수 있습니다. Google(2024)은 NPE가 모든 Android 앱 크래시의 25%를 차지하는 것으로 추정합니다.
IndexOutOfBoundsException — 존재하지 않는 인덱스로 목록 요소에 액세스합니다. 일반적인 원인: 서버에서 예기치 않은 형식으로 데이터가 도착하고 UI가 존재하지 않는 위치를 표시하려고 합니다. 해결책 — 인덱스로 액세스하기 전에 항상 컬렉션 크기를 확인합니다.
ANR(Application Not Responding) — Android 특정 문제입니다. UI 스레드가 5초 이상 차단됩니다. 주요 원인: 메인 스레드의 네트워크 요청, 무거운 계산, 데이터베이스 동기화. Android의 StrictMode는 개발 중에 UI 스레드 차단을 감지하는 데 도움이 됩니다.
OutOfMemoryError(OOM) — 앱이 메모리 제한을 초과했습니다. 2–4GB RAM의 모바일 장치에서 큰 이미지나 페이지네이션이 없는 무한 목록을 작업할 때 OOM은 일반적인 문제입니다. 해결책 — 이미지 로딩에는 Glide/Coil, 캐싱에는 LruCache, RecyclerView에는 ViewHolder를 사용합니다.
런타임 예외 — 컴파일러가 빌드 시 확인하지 않는 오류입니다. 특정 장치에서 특정 데이터로 코드가 실행될 때만 나타납니다. Java에서는 RuntimeException과 그 하위 클래스(NullPointerException, IllegalArgumentException, ArithmeticException)입니다.
치명적 오류(FATAL) — 런타임 오류가 아닌 시스템 오류입니다. Signal 11(SIGSEGV) — 네이티브 코드의 메모리 세그먼테이션 위반. Signal 6(SIGABRT) — abort()를 통해 앱 자체에 의해 트리거되는 비정상 종료. 스택 트레이스가 명확한 컨텍스트를 표시하지 않는 경우가 많아 이러한 크래시는 진단하기 어렵습니다.
iOS의 주요 원인은 NSInvalidArgumentException(매개변수의 예기치 않은 nil)과 EXC_BAD_ACCESS(해제된 메모리에 대한 액세스)입니다. Swift는 Objective-C에 비해 크래시 수를 줄였지만 ObjC 런타임 및 C 라이브러리의 오류는 여전히 크래시를 유발합니다.
Firebase Crashlytics — 모바일 앱의 표준입니다. 스택 트레이스를 자동으로 수집하고 로그, 사용자 ID 및 장치 메타데이터를 추가합니다. 서명(오류 클래스 + 라인)으로 크래시를 그룹화합니다. 실시간 알림 — 크래시율이 설정된 임계값(예: 시간당 >0.1%)을 초과할 때 알림.
Sentry — 더 유연한 기능을 제공하는 대안입니다. 사용자 정의 컨텍스트 생성, 브레드크럼(선행 이벤트) 추가, 중요하지 않은 오류를 제외하기 위한 인앱 필터링 구성이 가능합니다. Kotlin 및 Swift용 소스 맵을 사용하면 난독화된 이름 대신 소스 코드를 볼 수 있습니다.
로그 모범 사례: 위험한 작업을 수행하기 전에 주요 메타데이터를 전송합니다 — 이렇게 하면 로그에 크래시 전에 사용자가 무엇을 하고 있었는지 표시됩니다. 사용자 정의 키(API 버전 번호, 마지막 화면, 입력 데이터 크기)를 추가합니다. 이는 쓸모없는 스택 트레이스를 실행 가능한 정보로 바꿉니다.
class PaymentViewModel : ViewModel() {
fun processPayment(amount: Double) {
crashlytics.setCustomKey("last_screen", "payment")
crashlytics.setCustomKey("amount", amount)
try {
api.charge(amount)
} catch (e: Exception) {
crashlytics.recordException(e)
}
}
}
옵셔널 바인딩 및 null safety — Kotlin에서는 null 허용 타입에 `?`를 사용하고, null 안전 처리를 위해 `let` 및 `?:`를 사용합니다. Swift에서는 — 옵셔널 및 guard let을 사용합니다. 최신 Kotlin(2024)에는 Contract 어노테이션이 추가되었습니다: `@ContractsDsl`을 사용하면 함수가 null을 반환하지 않음을 선언할 수 있으며 컴파일러가 이를 확인합니다.
네트워킹 오류 처리 — 모든 네트워크 요청은 타임아웃, 파싱 오류 및 서버 장애를 처리해야 합니다. Result 타입을 사용하는 Retrofit — 오류가 처리되도록 보장하는 sealed 클래스입니다. No Exception 스타일: try/catch 대신 명시적 성공 및 오류 처리를 위해 sealed Result를 사용합니다.
기능 플래그 — 새 버전을 출시하지 않고 문제가 있는 기능을 원격으로 비활성화합니다. 서버 측 작업이 오래된 장치에서 크래시를 유발하는 경우 플래그가 해당 그룹에 대해 기능을 비활성화합니다. Firebase Remote Config를 사용하면 스토어에 게시하지 않고 앱 동작을 변경할 수 있습니다.
점진적 롤아웃 — 사용자의 5%에게 새 버전을 출시하고 크래시율을 모니터링합니다. 비율이 목표(보통 <0.1%) 아래로 유지되면 25%, 그 다음 50%, 그 다음 100%로 확장합니다. Google Play Console 및 App Store Connect는 임계값 초과 시 자동 중지를 위한 단계적 롤아웃을 지원합니다.
1단계: 분류 — 심각도 결정: 심각(사용자의 >1%에서 크래시), 높음(0.1–1%), 중간(<0.1%). 심각한 크래시는 — 즉시 대응. 나머지는 — 현재 스프린트의 표준 버그 수정 프로세스. Google Play Console은 영향을 받는 사용자 수에 따라 크래시를 자동으로 분류합니다.
2단계: 스택 트레이스 분석 — Crashlytics에서 로그를 열고 크래시의 정확한 위치를 확인합니다. 사용자 정의 키 확인: 어떤 화면, 어떤 데이터, OS 버전. 최신 배포와 연관 — 종종 크래시는 예기치 않은 사용 시나리오에 영향을 미친 최근 코드 변경으로 인해 발생합니다.
3단계: 재현 — 유사한 매개변수를 가진 장치 또는 에뮬레이터에서 크래시를 재현해 봅니다. 실패하면 패턴에 대해 크래시 로그를 확인합니다: 특정 모델(Samsung A10), Android 버전(API < 26), 로케일. 해결책 — 시나리오를 포괄하는 방어 조건을 추가합니다.
4단계: 수정 및 모니터링 — 우선순위로 핫픽스를 출시합니다. 릴리스 후 이 유형의 크래시율이 0이 되는지 확인합니다. 크래시 시나리오를 포괄하는 회귀 테스트를 작성합니다. 테스트가 없으면 동일한 버그가 다음 리팩토링에서 다시 발생할 수 있습니다.
자주 묻는 질문
정상 크래시율 — 프로덕션 릴리스의 경우 0.1% 미만. Google Play는 크래시율을 1.5% 미만으로 유지할 것을 권장하지만, 최상위 앱(YouTube, Instagram)은 0.01–0.05%를 유지합니다. 새로운 기능의 릴리스의 경우 핫픽스 후 감소를 조건으로 0.5%까지 일시적 증가가 허용됩니다.
크래시 — 앱이 비정상적으로 종료됩니다. ANR(Application Not Responding) — 앱이 5초 이상 멈추지만 강제로 종료되지는 않습니다. 사용자에게 “앱이 응답하지 않습니다” 대화상자가 표시되고 기다리거나 닫을 수 있습니다. ANR 문제는 크래시만큼 심각하며 스토어 평점에도 영향을 미칩니다.
장치마다 OS 버전, 메모리 크기, 라이브러리 버전, 심지어 프로세서도 다릅니다. 예: 런타임 권한 누락으로 인한 Android 6(API 23)의 크래시는 Android 12에서 발생하지 않을 수 있습니다. 필터(OS 버전, 장치 모델, RAM 용량)로 크래시 로그를 분석합니다. 이는 문제의 특수성을 나타냅니다.
Crashlytics에 사용자 정의 브레드크럼을 추가합니다: 작업을 수행하기 전에 주요 이벤트를 기록합니다. 크래시가 온보딩 3단계에서 발생하면 특정 화면의 문제를 나타냅니다. 디버그 심볼(dSYM, ProGuard 매핑) — 난독화된 이름 대신 실제 함수 이름을 보려면 항상 Crashlytics에 업로드합니다.
프로덕션에서는 — 절대 안 됩니다. 처리되지 않은 크래시는 사용자 경험을 악화시킵니다. 오류 로깅과 함께 try/catch를 사용합니다. 디버그 모드에서는 개발자에게 빠른 피드백을 위해 크래시하는 것이 허용됩니다. 어설션 — 절대 위반되어서는 안 되는 불변 조건 확인용이지만 디버그 빌드에서만 사용합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.