[OS 트러블 슈팅] Android 15~16 WebView Cold Start 크래시 분석 및 대응

마시마로·2026년 7월 1일

안녕하세요. 최근 Android 16 업데이트 기기(특히 삼성 One UI 8.0 기반 기기)에서 Cold Start 시 WebView 초기화 단계에서 앱이 즉시 종료(FC)되는 크래시 이슈를 마주했습니다.

Play Console Vitals 상에서 크래시 지수가 평소 대비 약 150,000% 급등할 정도로 심각했던 사안이었는데요. 앱 자체의 코드 변경이 없었던 상황에서 어떤 가설을 세우고 원인을 추적했는지, 그리고 제조사 기술 문의와 앱 레벨에서의 방어 코드 작성 과정을 공유합니다.


1. 문제 현상

Android 16 업데이트 기기에서 프로세스가 새로 시작되는 Cold Start 시점에 앱이 즉시 종료되는 현상이 감지되었습니다.

  • 크래시 로그: android.app.ActivityThread.performLaunchActivity 단계에서 InflateExceptionNotFoundException 발생
  • 발생 시점: 레이아웃 XML에 선언된 커스텀 WebView가 인플레이션(Inflation)되어 즉시 초기화될 때 발생
  • 특이 사항: '영향을 받는 사용자'와 '이벤트'가 1:1로 매칭되며, 대개 OS 업데이트 후 최초 1회성으로 집중 발생

2. 가설 수립 및 정밀 분석

처음 로그를 파악했을 때 ResourcesImpl 리다이렉션 키워드가 눈에 띄었습니다. 앱 코드는 동일한 상태에서 OS 버전이나 기기 환경에 따라 현상이 갈렸기 때문에, 내부 로직 결함보다는 OS 프레임워크 및 리소스 매핑의 가설을 세우고 접근했습니다.

🔍 진짜 원인: Android 16의 리소스 참조 위반 정책 강화

구글 및 제조사 기술 문의를 통해 확인한 정확한 메커니즘은 다음과 같았습니다.

  1. GMS(Google Mobile Services) 업데이트가 런타임에 일어날 때, 앱이 갱신된 리소스 경로를 즉각 반영하지 못하고 과거의 경로를 참조하는 일시적인 위반(Violation) 현상이 발생합니다.
  2. Android 15 이전까지는 이런 Race Condition이 발생해도 유연하게 넘어갔으나, Android 16부터는 리소스 참조 위반 정책이 강화되었습니다.
  3. 결과적으로 ResourcesImpl 리다이렉션 실패 시 예외 처리 없이 프레임워크 단에서 앱 프로세스를 즉시 크래시(FC) 처리해 버리는 것이 문제의 본질이었습니다.

3. 단계별 대처 과정

문제 포착부터 제조사 소통, 내부 데이터 검증까지의 대처 흐름입니다.

  • [Phase 1] 이슈 최초 포착 및 리포트 (2025.09 ~ 12)
    Android 16 베타 사용자 유입 시점에 문제를 먼저 감지하여 Google Issue Tracker에 최초 리포트를 등록하고 장기 모니터링에 착수했습니다.
  • [Phase 2] 기술 문의 및 정밀 분석 (2026.01 ~ 02)
    정식 업데이트 기기가 늘어나며 크래시가 급증하자, 삼성 개발자 지원팀에 1차 기술 문의를 진행했습니다. 단순 앱 오류가 아님을 설명하기 위해 과거 관련 QA 리포트 분석 데이터를 근거로 제시했습니다.
  • [Phase 3] 로그 제출 및 이슈 공식화 (2026.03)
    내부에서 의도적으로 발생 시킨 dumpstate 로그를 함께 제출하여 기술 문의를 구체화했고, 플랫폼 단의 리소스 매핑 결함이라는 최종 피드백을 확인받았습니다.
  • [Phase 4] 효과 검증 (2026.03 ~ 현재)
    제조사의 수정 패치(One UI 8.5 이상)가 반영된 기기들을 위주로 Play Console 데이터를 모니터링하여 크래시가 해소된 것을 확인했습니다.

4. 해결 방법 및 방어 코드

제조사 펌웨어 업데이트를 통해 근본적으로 해소되는 문제이지만, 사용자의 OS 업데이트 지연이나 파편화 환경을 고려하여 앱 레벨에서 안전하게 구동될 수 있도록 방어 코드를 추가했습니다.

Application 레벨 방어 코드

Application 클래스의 onCreate() 시점에 GMS 패키지의 런타임 의존성을 강제로 확인하도록 유도하는 방식입니다. 이렇게 하면 GMS 업데이트가 발생하더라도 앱 프로세스가 올바른 리소스 컨텍스트를 즉시 갱신하거나 안정적으로 재시작할 수 있는 환경을 만들어 줍니다.

@SuppressLint("CheckResult")
override fun onCreate() {
    super.onCreate()
    initGmsPackageDependency() // GMS 의존성 컨텍스트 갱신 유도
    // ..기존 초기화 로직
}

/**
 * Android 16 대응 GMS 패키지 의존성 강제 등록
 * 런타임 리소스 참조 위반(Violation)으로 인한 크래시 방지용 방어 코드
 */
private fun initGmsPackageDependency() {
    // Android 16(SDK 36) 환경에서만 작동하도록 제한
    if (Build.VERSION.SDK_INT != Build.VERSION_CODES.V) { 
        DebuggingLog.d("[Application] GMS 의존성 등록 불필요 - SDK: ${Build.VERSION.SDK_INT}")
        return
    }

    try {
        // GMS 패키지 명을 기반으로 컨텍스트를 강제 로드하여 컨텍스트 갱신 유도
        createPackageContext(
            "com.google.android.gms", // GMS_PACKAGE_NAME
            CONTEXT_IGNORE_SECURITY or CONTEXT_INCLUDE_CODE
        )
        DevLog.d("[Application] GMS 패키지 의존성 등록 성공")
    } catch (e: Throwable) {
        DebuggingLog.d("[Application] GMS 패키지 의존성 등록 실패: ${e.message}")
    }
}

마치며
새로운 안드로이드 플랫폼이 출시될 때마다 강화되는 리소스/보안 정책은 종종 예측하기 어려운 사이드 이펙트를 만들어내곤 합니다.

이번 이슈는 원인 불명의 크래시 앞에서 정확한 로그(dumpstate) 분석과 기술 문의 과정을 통해 플랫폼 단의 변화를 빠르게 캐치하고, 앱 레벨에서 유연하게 대응할 수 있는 대응책을 구축해 냈다는 점에서 의미 있는 경험이었습니다. targetSdk 36 전환이나 Android 16 환경을 대응하고 계신 개발자분들께 작은 도움이 되었으면 좋겠습니다.

profile
#Mobile #Android #iOS #Flutter #개발문화

0개의 댓글