본문 바로가기

Android

[Android] Crashlytics 전에 StrictMode

 

최근 strict mode라는 것을 알게 되었는데요,

안드로이드에서 제공하는 개발 단계에서 앱의 잘못된 동작 (특히 메인 스레드에서의 과도한 작업이나 자원 누수)을 탐지하고 경고해주는 디버깅 도구라고 합니다.

 

사실 저 역시 최근에 처음 알게 되었는데, 찾아보니 안드로이드 초창기에 (2010년~2011년쯤?) 많이 사용되던 디버깅 도구라고 하네요.

요즘은 워낙 좋은 디버깅 도구가 많이 나와서 저 같은 주니어 개발자들은 접할 기회가 거의 없었던 것 같아요.

 

비교적 옛날(?) 디버깅 도구임에도 불구하고 StrictMode를 주제로 블로그 글을 쓰기로 한 이유는, 이 도구가 안드로이드 앱 성능 문제가 발생하는 원인과 이를 가장 기본적인 수준에서 탐지하는 방법을 이해하는 데 좋은 출발점이 될 수 있다고 생각했기 때문입니다.

 

그럼 Strict Mode에 대해 알아볼까요?

 

 

 

🔎 Strict Mode 란?

StrictMode는 안드로이드 SDK에서 제공하는 개발자용 디버깅 도구입니다.


앱 실행 중에 발생할 수 있는 잘못된 동작을 감지하고, 이를 로그나 경고로 알려줍니다.
예를 들어 메인 스레드에서 네트워크 작업을 실행하거나 닫히지 않은 리소스를 남겨둔 경우 StrictMode가 이를 탐지합니다.

 

목적은 단순합니다

바로 앱 성능을 저하시킬 수 있는 잠재적 문제를 개발 단계에서 미리 찾아내는 것!

 

StrictMode는 크게 ThreadPolicyVmPolicy 두 가지 정책으로 나뉩니다.

 

 

 

🪄ThreadPolicy와 VmPolicy 

1) ThreadPolicy : “이 스레드에서 하면 안 되는 일” 감시

메인(UI) 스레드에서 디스크/네트워크 I/O처럼 느리고 막히는 작업을 했는지, 또는 실수하기 쉬운 호출을 했는지를 잡아냅니다.

  • detectDiskReads() / detectDiskWrites() : 메인 스레드에서 파일·DB 접근을 잡습니다.
  • detectNetwork() : 메인 스레드에서 네트워크를 호출하면 바로 적발합니다
  • detectCustomSlowCalls() : 직접 표시한 “느린 지점”(StrictMode.noteSlowCall("tag"))을 적발합니다.
  • detectResourceMismatches() (API 23+) : 리소스 타입-게터 불일치(암묵 변환으로 비용 증가)를 잡습니다.
  • detectUnbufferedIo() (API 26+) : 버퍼 없이 I/O 하는 비효율을 경고합니다.
  • detectExplicitGc() (API 34+) : Runtime.gc() 강제 호출을 잡습니다(대개 안티패턴).

위반 시에는 아래와 같이 알려줍니다.

  • penaltyLog() 로그캣 기록(기본), penaltyFlashScreen() 화면 깜빡임,
    penaltyDeath() 즉시 크래시, penaltyDeathOnNetwork() 네트워크 위반 즉시 크래시,
    penaltyListener(...) 콜백으로 수집.

 

2) VmPolicy : “프로세스 전반(메모리/보안/리소스) 문제” 감시

닫지 않은 리소스 누수, 잘못된 URI 공유, 평문 통신 등 앱 전역 품질/보안 이슈를 잡습니다. 

  • detectLeakedClosableObjects() : InputStream, Cursor 같은 Closeable 미종료 누수 적발
  • detectLeakedSqlLiteObjects() : SQLiteCursor 등 DB 객체를 닫지 않은 누수.
  • detectLeakedRegistrationObjects() : 해제 안 된 BroadcastReceiver/ServiceConnection 누수.
  • detectActivityLeaks() : Activity 인스턴스가 누수되는 패턴 감시.
  • setClassInstanceLimit(Foo::class.java, N) : 특정 클래스 인스턴스 upper-bound로 누수 힌트 탐지.
  • detectCleartextNetwork() (+ penaltyDeathOnCleartextNetwork()) : TLS 없이 평문 통신을 잡고 필요시 크래시
  • detectFileUriExposure() (+ penaltyDeathOnFileUriExposure()) : file:// URI 외부 노출(보안상 금지; FileProvider로 대체).
  • detectContentUriWithoutPermission() (API 26+) : 외부 앱에 content:// 전달 시 FLAG_GRANT_* 빠뜨린 경우. 
  • detectUntaggedSockets() (API 26+) : 태깅 안 된 소켓 트래픽(디버깅/계측 비활성) 적발.

 

그럼 위에서 설명한 정책들을 사용해보려면?

class App : Application() {
  override fun onCreate() {
    super.onCreate()
    if (BuildConfig.DEBUG) {
      StrictMode.setThreadPolicy(
        StrictMode.ThreadPolicy.Builder()
          .detectAll()              // 디스크/네트워크/느린 호출 등
          .penaltyLog()             // 로그캣에 기록
          .penaltyFlashScreen()     // 눈에 띄게 화면 깜빡임(선택)
          .build()
      )
      StrictMode.setVmPolicy(
        StrictMode.VmPolicy.Builder()
          .detectAll()              // 리소스 누수, file:// 노출, 평문통신 등
          .penaltyLog()
          .build()
      )
    }
  }
}

 

앱 시작 시 Application.onCreate()에서 위와 같이 원하는 디버깅 모드들을 설정해서 사용할 수 있습니다.

다만 디버그 모드에서만 사용해야 합니다!

 

디버그 모드가 아니라 릴리즈 모드에서 사용 시 `penaltyDialog()(경고 다이얼로그)` , `penaltyFlashScreen()` (화면 깜빡임) 같은 패널티는 릴리즈 빌드에서 치명적이기 때문에 UX를 망칠 수 있고, 의도치 않은 크래시 위험이 있습니다.

 

 

 

🤔 최신 디버깅 도구와 어떤 장단점이 있을까?

장점

  • 즉각 탐지 & 빠른 피드백: 메인(UI) 스레드에서의 디스크/네트워크 접근처럼 “하면 안 되는 일”을 바로 적발 (로그·깜빡임·크래시 등). 개발 초기/리팩터링 시 빠른 가이드레일로 유용
  • 설정이 가볍다: `Application.onCreate()` 에 몇 줄로 켜고 끌 수 있음. `detectAll()` , `penaltyLog()` 같은 기본값이 실용적
  • 보안/품질 체크 일부 포함: `detectFileUriExposure()` (file:// 금지), `detectCleartextNetwork()` (평문 통신) 등 기본적인 실수 방지에 도움

단점

  • 범위 한계: 복잡한 메모리 누수나 성능 병목의 원인 분석까지는 어려움(네이티브/프레임워크 내부 경로는 놓칠 수 있음). StrictMode 자체도 “developer tool”로 한계를 명시
  • 오버헤드 & UX 저하: 위반 수집/로깅/크래시 패널티 때문에 릴리스 빌드에는 부적합(디버그 한정 사용 권장)
  • 실사용자 텔레메트리 부재: 실 환경 지표(시작 시간, 느린 프레임, 지역·기기별 분포 등)는 제공하지 않음

 

 

 

📚 참고

https://developer.android.com/reference/android/os/StrictMode

 

StrictMode  |  API reference  |  Android Developers

 

developer.android.com