Android 앱 배터리 최적화 완벽 가이드 — Doze, App Standby, WorkManager와 백그라운드 전력 관리

Android 앱을 개발할 때 CPU 성능이나 메모리 사용량은 비교적 쉽게 확인할 수 있습니다.

하지만 배터리 사용량은 다릅니다.

앱이 정상적으로 실행되고 화면 전환도 빠르더라도, 백그라운드에서 불필요한 작업이 반복되면 사용자의 배터리를 지속적으로 소모할 수 있습니다.

예를 들어 다음과 같은 기능이 있다고 가정하겠습니다.

  • 15분마다 서버에서 데이터를 가져오는 기능

  • 일정한 간격으로 위치를 확인하는 기능

  • 앱을 사용하지 않을 때도 SQLite 데이터를 동기화하는 기능

  • 네트워크 연결 상태를 반복적으로 확인하는 기능

  • 주기적으로 알림을 생성하는 기능

  • Foreground Service를 이용한 장시간 작업

이러한 기능을 잘못 구현하면 앱이 사용되지 않는 동안에도 CPU와 네트워크, GPS가 반복적으로 활성화될 수 있습니다.

Android는 이러한 문제를 줄이기 위해 Doze, App Standby, 백그라운드 실행 제한 등의 전력 관리 기능을 제공합니다.

이번 글에서는 Android의 전력 관리 구조를 이해하고, 실제 앱에서 배터리 사용량을 줄이는 방법을 살펴보겠습니다.

개발 환경: Kotlin, XML, MVVM, WorkManager, Coroutine, Retrofit2, SQLiteOpenHelper, minSdk 24 이상. Android 버전과 기기 제조사의 전력 관리 정책에 따라 실제 동작은 달라질 수 있습니다.


1. Android 앱은 언제 배터리를 소모할까?

배터리 소모는 단순히 앱 화면이 켜져 있을 때만 발생하지 않습니다.

대표적으로 다음과 같은 작업이 전력을 사용합니다.

작업주요 전력 사용 요소
데이터베이스 처리CPU, 저장장치
네트워크 통신Wi-Fi, 모바일 데이터, CPU
위치 추적GNSS, Wi-Fi, 셀룰러 위치
이미지 처리CPU, GPU, 메모리
백그라운드 동기화CPU, 네트워크
알림 처리작업 실행, 화면 및 시스템 자원
Foreground Service서비스가 수행하는 실제 작업

여기서 중요한 점은 백그라운드에서 실행된다는 사실 자체보다 어떤 작업을 얼마나 자주 수행하는지가 중요하다는 것입니다.

예를 들어 1분마다 네트워크 요청을 보내는 앱과 하루 한 번 데이터를 동기화하는 앱은 전력 사용 특성이 다릅니다.


Android 전력 관리 구조

2. Doze 모드란?

Doze는 사용자가 기기를 일정 시간 사용하지 않을 때 Android가 백그라운드 작업을 제한하는 전력 관리 기능입니다.

기기가 유휴 상태에 들어가면 시스템은 다음 작업을 제한하거나 지연할 수 있습니다.

  • 일반적인 백그라운드 네트워크 접근

  • 일부 Alarm 실행

  • JobScheduler 작업

  • WorkManager 작업

  • 백그라운드 동기화

Doze 중에는 시스템이 주기적으로 유지관리 구간을 제공할 수 있습니다.

이 구간에서 지연된 작업 일부가 실행될 수 있습니다.

따라서 앱 개발자는 Doze 상태에서도 작업이 정확한 시간에 실행된다고 가정해서는 안 됩니다.

3. Doze 모드에서 발생할 수 있는 문제

예를 들어 다음과 같은 기능을 구현했다고 가정하겠습니다.

오전 9시
  ↓
백그라운드 동기화 예약
  ↓
기기가 장시간 유휴 상태
  ↓
Doze 제한
  ↓
작업 실행 지연 가능

이 경우 앱의 동기화 작업은 예약 시각보다 늦게 실행될 수 있습니다.

이것은 반드시 앱 오류를 의미하지 않습니다.

Android의 전력 관리 정책에 따른 정상적인 동작일 수 있습니다.

따라서 정확한 시각이 필요하지 않은 작업은 지연 가능성을 고려해 설계해야 합니다.


4. App Standby란?

App Standby는 앱의 사용 패턴에 따라 백그라운드 실행 기회를 제한하는 기능입니다.

Android는 앱의 최근 사용 여부와 사용자 상호작용 등을 고려해 앱을 여러 대기 버킷으로 분류할 수 있습니다.

대표적인 버킷은 다음과 같습니다.

버킷일반적인 의미
Active최근 활발하게 사용한 앱
Working set정기적으로 사용하는 앱
Frequent비교적 자주 사용하는 앱
Rare드물게 사용하는 앱
Restricted백그라운드 실행이 강하게 제한되는 앱

실제 분류와 제한은 Android 버전 및 기기 정책에 따라 달라질 수 있습니다.

중요한 점은 앱이 오랫동안 사용되지 않으면 백그라운드 작업 실행 기회가 줄어들 수 있다는 것입니다.

5. Doze와 App Standby 차이

구분DozeApp Standby
판단 기준기기 유휴 상태앱 사용 패턴
적용 범위기기 상태 중심개별 앱 중심
주요 영향작업·네트워크 지연백그라운드 실행 할당량 제한
개발 시 고려예약 시간의 불확실성사용 빈도에 따른 실행 제한

두 기능은 서로 다른 개념이며 함께 영향을 줄 수 있습니다.


백그라운드 작업 설계

6. 모든 작업을 주기적으로 실행해야 할까?

배터리 최적화에서 가장 먼저 검토할 부분은 작업의 필요성입니다.

예를 들어 조류 도감 앱에서 사용자가 직접 검색하는 데이터가 SQLite에 저장되어 있다면, 앱을 사용하지 않는 동안 검색 기능을 위해 주기적인 작업을 실행할 필요가 없습니다.

반면 서버에서 새로운 데이터를 받아야 하는 앱이라면 동기화가 필요할 수 있습니다.

하지만 그 경우에도 다음 질문을 먼저 검토해야 합니다.

  • 데이터가 얼마나 자주 변경되는가?

  • 사용자가 앱을 열었을 때만 동기화해도 되는가?

  • 서버에서 변경 여부를 확인할 수 있는가?

  • 네트워크 연결이 없으면 작업을 미뤄도 되는가?

  • 여러 요청을 하나로 묶을 수 있는가?

가장 효과적인 배터리 최적화는 불필요한 백그라운드 작업을 만들지 않는 것입니다.


WorkManager 활용

7. WorkManager가 적합한 작업

WorkManager는 실행 시점이 다소 지연되더라도 최종적으로 수행해야 하는 지속성 작업에 적합합니다.

대표적인 예는 다음과 같습니다.

  • 서버 데이터 동기화

  • 로그 업로드

  • 백업 작업

  • 이미지 업로드

  • 일정 조건에서 데이터 정리

WorkManager는 Android의 백그라운드 실행 정책을 고려해 작업을 예약합니다.

하지만 WorkManager를 사용한다고 해서 Doze나 App Standby 제한을 우회할 수 있는 것은 아닙니다.

8. Worker 구현

다음은 서버 데이터 동기화 작업의 기본 예제입니다.

class BirdSyncWorker(
    appContext: Context,
    workerParams: WorkerParameters
) : CoroutineWorker(
    appContext,
    workerParams
) {

    override suspend fun doWork(): Result {

        return try {

            // 서버 동기화 수행
            // 실제 구현에서는 Repository 호출

            Result.success()

        } catch (e: IOException) {

            Result.retry()

        } catch (e: Exception) {

            Result.failure()
        }
    }
}

CoroutineWorker를 사용하면 suspend 함수를 활용할 수 있습니다.

다만 모든 오류에 대해 무조건 재시도하는 것은 좋지 않습니다.

네트워크 일시 오류와 영구적인 데이터 오류를 구분해야 합니다.


9. PeriodicWorkRequest 사용

주기적인 동기화 작업은 PeriodicWorkRequest로 예약할 수 있습니다.

val syncRequest =
    PeriodicWorkRequestBuilder<BirdSyncWorker>(
        12,
        TimeUnit.HOURS
    ).build()

WorkManager.getInstance(context)
    .enqueueUniquePeriodicWork(
        "bird_sync",
        ExistingPeriodicWorkPolicy.KEEP,
        syncRequest
    )

이 예제는 12시간 주기의 반복 작업을 예약합니다.

하지만 정확히 12시간마다 실행된다는 의미는 아닙니다.

실제 실행 시각은 Doze, App Standby, 작업 제약 조건, 시스템 상태 등에 따라 달라질 수 있습니다.

WorkManager의 주기적 작업은 최소 반복 간격 제한이 있으며, 일반적으로 최소 15분입니다.

15분은 정확한 실행 보장 간격이 아니라 예약 가능한 최소 반복 간격이라는 점을 이해해야 합니다.


10. 작업 중복 방지

앱이 시작될 때마다 다음 코드를 실행한다고 가정하겠습니다.

WorkManager.getInstance(context)
    .enqueue(syncRequest)

작업 예약 방식에 따라 동일한 목적의 작업이 여러 개 생성될 수 있습니다.

이를 방지하려면 고유 작업 이름을 사용하는 것이 좋습니다.

WorkManager.getInstance(context)
    .enqueueUniquePeriodicWork(
        "bird_sync",
        ExistingPeriodicWorkPolicy.KEEP,
        syncRequest
    )

KEEP은 동일한 이름의 미완료 주기 작업이 이미 존재하면 기존 작업을 유지하도록 하는 정책입니다.

주기나 제약 조건을 변경해야 하는 경우에는 업데이트 정책을 검토할 수 있습니다.


Constraints 활용

11. 네트워크 연결 조건

네트워크가 필요한 작업이라면 실행 조건을 지정할 수 있습니다.

val constraints =
    Constraints.Builder()
        .setRequiredNetworkType(
            NetworkType.CONNECTED
        )
        .build()

이를 WorkRequest에 적용합니다.

val request =
    PeriodicWorkRequestBuilder<BirdSyncWorker>(
        12,
        TimeUnit.HOURS
    )
        .setConstraints(constraints)
        .build()

네트워크 연결 조건을 설정하면 연결이 없는 상황에서 무의미한 요청을 반복하는 문제를 줄일 수 있습니다.

다만 연결되어 있다는 사실이 서버 접근 가능성을 보장하지는 않습니다.


12. 배터리 부족 상태에서 작업 제한

대량 데이터를 동기화하는 작업이라면 배터리 상태를 고려할 수 있습니다.

val constraints =
    Constraints.Builder()
        .setRequiredNetworkType(
            NetworkType.CONNECTED
        )
        .setRequiresBatteryNotLow(true)
        .build()

이렇게 하면 시스템이 배터리가 부족하지 않다고 판단하는 조건에서 작업을 실행하도록 요청할 수 있습니다.

단, 이 조건이 모든 기기에서 동일한 배터리 잔량 기준으로 동작한다고 가정해서는 안 됩니다.

13. 충전 중에만 실행하기

대용량 데이터 처리에는 충전 조건을 활용할 수 있습니다.

val constraints =
    Constraints.Builder()
        .setRequiresCharging(true)
        .build()

예를 들어 수천 장의 이미지 파일을 정리하거나 대규모 데이터베이스를 재구성하는 작업에 활용할 수 있습니다.

하지만 사용자가 즉시 결과를 기대하는 작업에 충전 조건을 적용하면 기능이 지나치게 지연될 수 있습니다.

작업의 중요도와 사용자 기대를 함께 고려해야 합니다.


네트워크 전력 최적화

14. 짧은 간격의 반복 요청 피하기

다음과 같은 구조는 주의해야 합니다.

while (true) {

    fetchServerData()

    delay(60_000)
}

이 코드는 Coroutine이 실행되는 동안 1분마다 서버 요청을 시도합니다.

화면이 종료된 뒤에도 장시간 유지되는 Scope에서 실행된다면 불필요한 네트워크 사용으로 이어질 수 있습니다.

또한 Coroutine이 종료되거나 프로세스가 종료되면 반복 작업도 중단될 수 있으므로 안정적인 백그라운드 예약 방법도 아닙니다.

주기적 동기화에는 WorkManager 등 목적에 맞는 시스템 API를 사용하는 것이 좋습니다.


15. 변경된 데이터만 가져오기

서버가 변경 여부를 제공한다면 매번 전체 데이터를 다운로드할 필요가 없습니다.

예를 들어 HTTP 조건부 요청을 활용할 수 있습니다.

GET /api/birds HTTP/1.1
If-None-Match: "birds-v12"

서버의 리소스가 변경되지 않았다면 304 Not Modified 응답을 받을 수 있습니다.

이 경우 응답 본문을 다시 다운로드하지 않아도 됩니다.

단, ETag 및 조건부 요청은 서버가 지원해야 하며 클라이언트의 캐시 정책도 올바르게 구성해야 합니다.


16. 여러 요청을 묶기

서버에서 여러 종류의 데이터를 가져오는 경우 다음과 같은 요청 구조를 생각해 볼 수 있습니다.

조류 목록 요청
조류 이미지 메타데이터 요청
분류 정보 요청
공지사항 요청

각 요청을 서로 다른 시각에 반복 실행하면 네트워크 활성화가 잦아질 수 있습니다.

가능하다면 서버 API를 설계할 때 관련 데이터를 하나의 동기화 요청으로 묶는 방법을 검토할 수 있습니다.

다만 응답이 지나치게 커지면 오히려 다운로드 비용이 증가할 수 있습니다.

실제 데이터 크기와 변경 빈도를 기준으로 판단해야 합니다.


위치 추적과 배터리

17. GPS는 배터리 사용량에 영향을 줄까?

위치 추적 기능은 설정에 따라 상당한 전력을 사용할 수 있습니다.

특히 다음과 같은 경우 주의해야 합니다.

  • 매우 짧은 위치 갱신 간격

  • 높은 위치 정확도 요구

  • 장시간 연속 추적

  • 화면이 꺼진 상태에서 위치 수집

  • 필요하지 않은 상황에서도 위치 요청 유지

예를 들어 지도에서 현재 위치를 한 번 표시하는 기능과 몇 시간 동안 이동 경로를 기록하는 기능은 요구사항이 다릅니다.

따라서 같은 위치 요청 설정을 사용하지 않는 것이 좋습니다.


18. 위치 정확도와 갱신 간격

Fused Location Provider를 사용하는 경우 다음과 같이 위치 요청을 구성할 수 있습니다.

val locationRequest =
    LocationRequest.Builder(
        Priority.PRIORITY_BALANCED_POWER_ACCURACY,
        30_000L
    )
        .setMinUpdateIntervalMillis(
            15_000L
        )
        .build()

이 예제는 전력 균형을 고려한 위치 요청 설정입니다.

다만 요청한 간격은 실제 위치 전달을 정확하게 보장하지 않습니다.

위치 갱신은 기기 상태, 권한, 시스템 정책, 사용 가능한 위치 신호에 영향을 받습니다.

또한 위 코드는 Google Play services Location 라이브러리의 API를 사용하는 예제입니다.


19. 고정밀 경로 기록은 별도 설계

정원 내부의 좁은 길이나 지그재그 경로를 세밀하게 기록해야 한다면 더 높은 위치 정확도가 필요할 수 있습니다.

이 경우에는 다음 사항을 고려해야 합니다.

  • 위치 정확도

  • 갱신 간격

  • 위치 오차

  • GNSS 수신 환경

  • 배터리 사용량

  • 장시간 기록 시 서비스 생명주기

고정밀 추적은 전력 사용량을 줄이는 것과 정확도를 높이는 것 사이에 절충이 필요합니다.

또한 스마트폰 GPS만으로 항상 수십 센티미터 수준의 정확도를 확보할 수 있다고 가정해서는 안 됩니다.


Foreground Service

20. Foreground Service란?

Foreground Service는 사용자가 인지할 수 있는 중요한 작업을 수행하는 서비스입니다.

예를 들어 다음과 같은 기능에 사용될 수 있습니다.

  • 사용자가 시작한 위치 추적

  • 음악 재생

  • 일부 장시간 연결 작업

  • 사용자에게 명확하게 표시되는 진행 중 작업

Foreground Service는 일반적인 백그라운드 작업보다 지속적인 실행이 필요한 경우에 사용하지만, 무제한 실행을 보장하는 수단은 아닙니다.

Android 버전별 시작 제한, 서비스 유형, 권한, 실행 시간 제한을 고려해야 합니다.


21. Foreground Service를 남용하면 안 되는 이유

Foreground Service는 사용자에게 표시되는 알림과 함께 실행될 수 있습니다.

하지만 다음과 같은 작업에 무조건 적용하는 것은 적절하지 않습니다.

  • 단순 데이터 갱신

  • 일반적인 로그 업로드

  • 하루 한 번 실행하는 백업

  • 정확한 실행 시각이 필요 없는 동기화

이러한 작업에는 WorkManager가 더 적합한 경우가 많습니다.

또한 Foreground Service를 사용한다고 배터리 소모가 자동으로 줄어드는 것은 아닙니다.

오히려 장시간 CPU, 네트워크, 위치 센서를 사용하는 작업은 상당한 전력을 소비할 수 있습니다.


알림과 배터리

22. 알림을 위해 반복적으로 서버를 확인해야 할까?

새로운 공지사항을 알려주기 위해 앱이 1분마다 서버를 확인하는 구조를 생각해 보겠습니다.

앱
 ↓
1분마다 서버 요청
 ↓
새 공지사항 확인
 ↓
알림 표시

이 구조는 네트워크 요청이 자주 발생합니다.

서버가 푸시 알림을 지원한다면 Firebase Cloud Messaging(FCM)을 활용하는 방법을 검토할 수 있습니다.

서버
 ↓
FCM
 ↓
Android 기기
 ↓
앱 알림

이 방식은 앱이 직접 짧은 간격으로 서버를 조회하는 필요성을 줄일 수 있습니다.

다만 FCM 메시지 전달 시점 역시 시스템 상태와 메시지 우선순위 등에 따라 달라질 수 있습니다.


23. FCM 우선순위 남용 주의

FCM에는 메시지 우선순위 설정이 있습니다.

하지만 모든 메시지를 높은 우선순위로 전송하는 것은 바람직하지 않습니다.

높은 우선순위는 즉각적인 사용자 알림이 필요한 경우에 적합하며, 남용하면 메시지 처리 정책에 영향을 받을 수 있습니다.

일반적인 데이터 갱신은 보통 우선순위로 처리하거나 앱 실행 시 동기화하는 방식을 고려하는 것이 좋습니다.


AlarmManager와 정확한 실행

24. 정확한 시각이 필요한 작업

일부 기능은 정확한 시각이 중요합니다.

예를 들어 다음과 같습니다.

  • 사용자가 직접 설정한 알람

  • 일정 시각에 울리는 타이머

  • 알람 시계의 기상 알림

이 경우 AlarmManager의 정확한 알람 기능을 검토할 수 있습니다.

하지만 정확한 알람은 Android 버전과 앱의 용도에 따라 별도 권한이나 제한이 적용될 수 있습니다.

일반적인 데이터 동기화를 위해 정확한 알람을 사용하는 것은 피하는 것이 좋습니다.

25. WorkManager와 AlarmManager 비교

항목WorkManagerAlarmManager
주요 목적지연 가능한 지속성 작업시각 기반 알람
정확한 실행 시각보장하지 않음API·권한·상태에 따라 가능
작업 조건네트워크·충전 등 지원직접 별도 처리 필요
대표 사례서버 동기화알람 시계
Doze 영향작업 지연 가능알람 유형에 따라 차이

작업의 목적에 맞는 API를 선택하는 것이 중요합니다.


배터리 사용량 측정

26. 배터리 최적화도 측정이 필요하다

코드에서 반복 작업을 제거했다고 해서 실제 배터리 사용량이 얼마나 개선되었는지 바로 알 수 있는 것은 아닙니다.

배터리 사용량은 다음 요소의 영향을 받습니다.

  • 기기 모델

  • 배터리 상태

  • 화면 밝기

  • 네트워크 환경

  • 이동 여부

  • CPU 및 GPU 부하

  • 시스템 백그라운드 작업

따라서 배터리 최적화도 성능 분석과 마찬가지로 재현 가능한 테스트가 필요합니다.


27. Android Studio와 시스템 도구

Android Studio의 프로파일링 기능과 시스템 추적 도구를 활용해 전력 사용에 영향을 주는 작업을 분석할 수 있습니다.

예를 들어 다음 사항을 확인할 수 있습니다.

  • 백그라운드 CPU 활동

  • 네트워크 요청 빈도

  • 위치 요청 발생 시점

  • 작업 실행 시간

  • 서비스 실행 상태

기기와 Android 버전에 따라 제공되는 전력 분석 기능은 달라질 수 있습니다.

정확한 에너지 소비량을 직접 측정하는 기능과 CPU·네트워크 활동을 간접적으로 관찰하는 기능은 구분해야 합니다.


ADB를 이용한 Doze 테스트

28. Doze 상태 강제 진입

개발 환경에서는 ADB를 이용해 Doze 동작을 테스트할 수 있습니다.

adb shell dumpsys deviceidle force-idle

이 명령은 테스트 기기를 강제로 유휴 상태로 전환하는 데 사용할 수 있습니다.

실행 후에는 앱의 백그라운드 작업이 어떻게 동작하는지 확인합니다.

테스트가 끝나면 다음 명령으로 강제 유휴 상태를 해제할 수 있습니다.

adb shell dumpsys deviceidle unforce

이 명령은 테스트용이며 Android 버전과 기기 정책에 따라 동작 차이가 있을 수 있습니다.


29. App Standby 테스트

ADB를 이용해 앱의 대기 버킷을 변경하는 테스트도 가능합니다.

예를 들어 다음과 같은 명령을 사용할 수 있습니다.

adb shell am set-standby-bucket \
    com.example.birdapp rare

이 명령은 테스트 앱을 Rare 버킷으로 지정하는 예제입니다.

실제 앱의 Application ID로 변경해야 합니다.

이후 WorkManager 작업이 어떻게 실행되는지 관찰할 수 있습니다.

다만 테스트용으로 강제 지정한 상태와 시스템이 실제 사용 패턴을 기반으로 분류한 상태는 완전히 동일하지 않을 수 있습니다.


WorkManager 디버깅

30. 예약된 작업 확인

WorkManager는 내부적으로 작업 상태를 관리합니다.

개발 중에는 다음 상태를 확인하는 것이 중요합니다.

  • ENQUEUED

  • RUNNING

  • SUCCEEDED

  • FAILED

  • BLOCKED

  • CANCELLED

반복 작업은 한 번 실행이 완료되더라도 다음 실행을 위해 다시 예약 상태로 관리될 수 있습니다.

따라서 주기적 작업의 상태를 단발성 작업과 동일하게 해석하지 않도록 주의해야 합니다.


31. WorkInfo 관찰

WorkManager.getInstance(context)
    .getWorkInfosForUniqueWorkLiveData(
        "bird_sync"
    )
    .observe(viewLifecycleOwner) { workInfos ->

        workInfos.forEach { workInfo ->

            Log.d(
                "WorkManager",
                "State: ${workInfo.state}"
            )
        }
    }

이 예제는 Fragment에서 고유 작업의 상태를 관찰하는 방식입니다.

실제 앱에서는 디버깅 목적의 로그를 과도하게 출력하지 않도록 관리하는 것이 좋습니다.


앱 시작 시 동기화 전략

32. 백그라운드 반복 작업 대신 앱 실행 시 동기화

모든 앱에 주기적 백그라운드 동기화가 필요한 것은 아닙니다.

예를 들어 서버의 조류 정보를 하루에 몇 번만 확인하면 되는 앱이라면 다음과 같은 전략을 사용할 수 있습니다.

앱 실행
   ↓
마지막 동기화 시간 확인
   ↓
충분한 시간 경과?
   ├── 아니오 → 로컬 데이터 사용
   └── 예 → 서버 변경 여부 확인
                 ↓
              SQLite 갱신

이 방식은 앱이 사용되지 않는 동안 불필요한 동기화를 줄이는 데 도움이 됩니다.


33. 마지막 동기화 시간 저장

DataStore를 이용해 마지막 동기화 시간을 저장할 수 있습니다.

val LAST_SYNC_TIME =
    longPreferencesKey("last_sync_time")

동기화 성공 후 시간을 저장합니다.

dataStore.edit { preferences ->

    preferences[LAST_SYNC_TIME] =
        System.currentTimeMillis()
}

이후 앱 실행 시 마지막 동기화 시간을 확인해 서버 요청 여부를 결정할 수 있습니다.

다만 System.currentTimeMillis()는 사용자가 기기 시간을 변경하면 영향을 받을 수 있습니다.

장기적인 동기화 정책에서는 서버 시간, 데이터 버전, ETag 등을 함께 고려하는 것이 좋습니다.


이미지와 캐시 최적화

34. 이미지 다운로드도 배터리를 소모한다

이미지가 많은 앱에서는 네트워크 다운로드와 이미지 디코딩이 반복적으로 발생할 수 있습니다.

예를 들어 RecyclerView를 스크롤할 때 같은 이미지를 계속 다운로드한다면 불필요한 네트워크 사용이 증가합니다.

Glide와 같은 이미지 라이브러리의 캐시 기능을 활용하면 이러한 작업을 줄일 수 있습니다.

Glide.with(imageView)
    .load(imageUrl)
    .placeholder(R.drawable.image_placeholder)
    .into(imageView)

다만 캐시 정책은 이미지 변경 빈도와 저장 공간을 함께 고려해야 합니다.

무조건 모든 이미지를 영구 보관하는 것이 좋은 방법은 아닙니다.


실제 앱 적용 사례

35. 조류 도감 앱

조류 도감 앱에서는 다음과 같은 최적화를 고려할 수 있습니다.

기능권장 접근
조류 검색로컬 SQLite 우선
이미지 표시Glide 캐시 활용
데이터 업데이트필요 시 또는 낮은 빈도로 동기화
좋아요 저장로컬 DB 즉시 저장
사용자 메모로컬 저장 우선
공지사항앱 실행 시 확인 또는 FCM
광고화면 생명주기에 맞춰 관리

조류 정보가 자주 변경되지 않는다면 짧은 간격의 백그라운드 동기화는 불필요할 수 있습니다.


36. 위치 기반 앱

위치 기반 앱에서는 다음과 같은 전략을 고려할 수 있습니다.

  • 사용자가 위치 기능을 사용하는 동안에만 요청

  • 필요하지 않으면 위치 업데이트 중지

  • 목적에 맞는 정확도 선택

  • 불필요하게 짧은 갱신 간격 방지

  • 장시간 추적 시 사용자에게 명확히 안내

  • Android 버전별 백그라운드 위치 권한 확인

특히 위치 추적은 기능 정확도와 배터리 사용량 사이의 균형이 중요합니다.


37. 서버 동기화 앱

서버와 데이터를 주고받는 앱에서는 다음과 같은 방법이 유용합니다.

  • WorkManager 사용

  • 네트워크 연결 조건 설정

  • 변경된 데이터만 다운로드

  • 요청 중복 방지

  • 지수 백오프 적용

  • 실패 원인에 따른 재시도

  • 앱 실행 시 동기화 우선 검토

단순히 동기화 주기를 늘리는 것보다 서버 요청 구조 자체를 개선하는 것이 더 효과적인 경우도 있습니다.


배터리 최적화에서 피해야 할 실수

38. 무조건 배터리 최적화 예외 요청하기

일부 앱에서는 사용자가 시스템의 배터리 최적화 제한을 해제하도록 안내하는 경우가 있습니다.

하지만 일반적인 앱에서 이를 기본 해결책으로 사용하는 것은 적절하지 않습니다.

배터리 최적화 예외는 앱의 핵심 기능상 정당한 필요가 있는 경우에만 검토해야 합니다.

Google Play 정책과 Android 플랫폼 제한도 함께 확인해야 합니다.


39. WakeLock 남용

WakeLock은 특정 상황에서 기기가 절전 상태로 전환되는 것을 방지하는 데 사용될 수 있습니다.

하지만 WakeLock을 필요 이상으로 오래 유지하면 배터리 소모가 증가할 수 있습니다.

일반적인 백그라운드 작업에서는 WorkManager처럼 시스템이 실행을 관리하는 API를 우선 검토하는 것이 좋습니다.

직접 WakeLock을 사용하는 경우에는 획득과 해제를 정확하게 관리해야 합니다.


40. 무조건 Foreground Service 사용하기

Foreground Service는 모든 백그라운드 제한을 해결하는 만능 수단이 아닙니다.

Android는 서비스 시작 조건과 유형에 대한 제한을 적용하고 있습니다.

따라서 다음과 같은 작업을 Foreground Service로 처리할 필요가 있는지 검토해야 합니다.

  • 단순 데이터 조회

  • 일반적인 파일 정리

  • 주기적인 서버 확인

  • 짧은 로그 업로드

사용자가 즉시 인지해야 하는 지속적인 작업이 아니라면 다른 API가 더 적합할 수 있습니다.


배터리 최적화 체크리스트

41. 개발 및 배포 전 점검

  • 불필요한 백그라운드 작업 제거

  • 짧은 간격의 반복 네트워크 요청 확인

  • WorkManager 사용 목적 검토

  • 주기적 작업 중복 예약 방지

  • 네트워크 Constraints 적용

  • 배터리 부족 조건 검토

  • 충전 중 실행 조건 검토

  • 재시도 횟수와 백오프 정책 확인

  • Doze 상태에서 작업 지연 확인

  • App Standby 제한 테스트

  • Foreground Service 필요성 검토

  • Foreground Service 유형 및 권한 확인

  • 위치 요청 정확도와 간격 확인

  • 위치 업데이트 중지 처리

  • FCM 우선순위 남용 방지

  • 정확한 알람 사용 필요성 확인

  • 이미지 캐시 활용

  • SQLite 작업의 불필요한 반복 제거

  • 앱 실행 시 동기화 전략 검토

  • 배터리 최적화 예외 요청 최소화

  • WakeLock 사용 여부 점검

  • 실제 기기에서 장시간 테스트

  • Android 버전별 동작 확인


42. 마무리

Android 앱의 배터리 최적화는 단순히 CPU 사용량을 줄이는 작업이 아닙니다.

네트워크, 위치 센서, 백그라운드 작업, 알림, 서비스 실행 등 다양한 요소를 함께 고려해야 합니다.

특히 Android는 Doze와 App Standby를 통해 앱이 사용되지 않는 동안 불필요한 작업을 제한합니다.

개발자는 이러한 제한을 우회하기보다 시스템 정책에 맞춰 앱을 설계하는 것이 중요합니다.

이번 글의 핵심을 정리하면 다음과 같습니다.

  1. 백그라운드 작업이 정말 필요한지 먼저 검토합니다.

  2. Doze와 App Standby에 따라 작업이 지연될 수 있습니다.

  3. WorkManager는 지연 가능한 지속성 작업에 적합합니다.

  4. 주기적 작업은 정확한 실행 시각을 보장하지 않습니다.

  5. Constraints로 불필요한 작업 실행을 줄일 수 있습니다.

  6. 네트워크 요청은 변경된 데이터 중심으로 설계합니다.

  7. 위치 추적은 정확도와 전력 사용량을 함께 고려합니다.

  8. Foreground Service는 필요한 경우에만 사용합니다.

  9. FCM을 활용하면 반복적인 서버 조회를 줄일 수 있습니다.

  10. 실제 기기에서 배터리 관련 동작을 측정하고 검증해야 합니다.

기존 Kotlin + XML 기반 앱이라면 먼저 WorkManager에 등록된 작업 목록과 실행 주기를 점검하는 것부터 시작하는 것이 좋습니다.

그다음 네트워크 요청, 위치 업데이트, 이미지 캐시, SQLite 처리 순서로 확인하면 됩니다.

배터리 최적화의 핵심은 작업을 더 빠르게 실행하는 것이 아니라, 필요하지 않은 작업을 실행하지 않도록 설계하는 것입니다.


참고 자료

태그: Android, Kotlin, 배터리 최적화, Doze, App Standby, WorkManager, Foreground Service, Battery Optimization, Background Work, Android 백그라운드 작업, GPS, 위치 추적, FCM, AlarmManager, Retrofit2, SQLite, Android 앱 개발