[Android] WorkManager와 Doze 모드: 백그라운드 작업은 언제 실행될까

SSY·2026년 7월 1일

AndroidFramework

목록 보기
9/9
post-thumbnail

개요

Android에서 백그라운드 작업을 다루다 보면 WorkManager, expedited work, Doze mode, App Standby Bucket 같은 용어를 자주 만나게 된다. 각각은 따로 보면 단순해 보이지만, 실제 앱에서는 서로 연결되어 동작한다.

이 글은 아래에 대한 Android 백그라운드 진행 개념을 다룬다.

  • "WorkManager를 쓰면 백그라운드 작업이 항상 실행되는가?"
  • "expedited work는 Doze에서도 바로 실행되는가?"
  • "Light Doze와 Deep Doze는 무엇이 다른가?"

1. WorkManager는 무엇을 보장하는가

WorkManager는 앱이 바로 실행 중이지 않아도 언젠가 처리되어야 하는 작업을 예약하는 Jetpack 라이브러리다. 예를 들어 로그 업로드, 서버 동기화, 이미지 업로드, 캐시 정리처럼 "앱 프로세스가 죽더라도 나중에 다시 시도되어야 하는 작업"에 적합하다.

val request = OneTimeWorkRequestBuilder<SyncWorker>()
  .setConstraints(
    Constraints.Builder()
      .setRequiredNetworkType(NetworkType.CONNECTED)
      .build()
  )
  .build()

WorkManager.getInstance(context).enqueue(request)

이 코드는 네트워크가 연결되었을 때 SyncWorker를 실행해 달라고 예약하는데, 중요한 점은 WorkManager가 "즉시 실행"을 보장하지 않는다는 것이다. 또한 WorkManager가 보장하는 것은 '즉시 실행'이 아니라, '작업의 지속'이다. 즉, 조건이 맞고 시스템이 허용하는 시점에 작업을 실행하고, 실패하면 정책에 따라 재시도할 수 있게 해 준다.

[App]
  |
  v
[WorkManager]
  |
  v
[JobScheduler / AlarmManager / Foreground Service]
  |
  v
[System decides execution time]

Android 12 이상에서는 WorkManager의 백그라운드 작업이 주로 JobScheduler 위에서 동작한다. 그래서 WorkManager 작업도 시스템의 배터리 정책, Doze, App Standby Bucket, Job quota의 영향을 받는다.

2. 일반 Work와 Expedited Work의 차이

WorkManager 작업은 크게 Normal Job과 Expedited Job으로 나눠 생각할 수 있다. 일반 작업은 "나중에 해도 되는 작업"이고, expedited 작업은 "사용자 경험상 지금 바로 처리해야 하는 짧은 작업"이다.

val request = OneTimeWorkRequestBuilder<UploadWorker>()
  .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
  .build()

WorkManager.getInstance(context).enqueue(request)

setExpedited()를 호출하면 시스템에 이 작업을 우선 실행해 달라고 요청한다. 하지만 이것도 "무조건 즉시 실행"은 아니다. Expedited Job도 별도의 쿼터를 적용받으며, 쿼터가 남아 있을 경우 Normal Job보다 빠르게 실행될 수 있지만, 없으면 OutOfQuotaPolicy에 따라 동작이 달라진다.

[Expedited Work Request]
        |
        v
[Expedited quota available?]
        |
   +----+----+
   |         |
  yes        no
   |         |
   v         v
[Run soon] [Fallback policy]
             |
       +-----+------+
       |            |
 [Run as normal]  [Drop]

RUN_AS_NON_EXPEDITED_WORK_REQUEST를 선택하면 쿼터가 없을 때 Normal Job으로 내려간다. 반대로 DROP_WORK_REQUEST를 선택하면 작업 자체가 버려진다. 따라서 setExpedited()를 호출하려할 땐 아래를 생각해야 한다.

"즉시 실행을 최대한 보장하되, 쿼터를 넘으면 Normal Job으로 실행되거나, Drop될 수 있다.

실 사용 예시론 아래와 같이 쓸 수 있다.

상황적합한 선택
로그 업로드, 캐시 정리, 주기적 동기화일반 Work
사용자가 버튼을 누른 직후 서버 반영Expedited Work
알림 액션 처리Expedited Work
오래 걸리는 대용량 업로드/다운로드Foreground Service 또는 User-initiated data transfer 검토

3. Job quota는 왜 필요한가

Android는 모든 앱이 마음대로 백그라운드에서 계속 작업하면 배터리가 빠르게 줄어든다. 그래서 시스템은 앱의 최근 사용 빈도와 중요도에 따라 백그라운드 실행 시간을 제한한다. 이때 쓰이는 기준 중 하나가 App Standby Bucket이다.

App Standby Bucket은 앱을 대략 다음과 같은 그룹으로 나눈다.

[Active] -> 방금 사용했거나 자주 쓰는 앱
[Working set] -> 정기적으로 쓰는 앱
[Frequent] -> 자주 쓰지만 매일은 아닌 앱
[Rare] -> 거의 쓰지 않는 앱
[Restricted] -> 시스템이 강하게 제한하는 앱

각 bucket은 일반 job과 expedited job에 서로 다른 실행 쿼터를 가진다. Android 공식 문서의 근사값 기준으로 보면 일반 job은 대략 다음과 같다.

Bucket일반 Job 실행 쿼터
Active60분 rolling window에 최대 20분
Working set4시간 rolling window에 최대 10분
Frequent12시간 rolling window에 최대 10분
Rare24시간 rolling window에 최대 10분
Restricted하루 1회, 최대 10분

Expedited job은 별도 쿼터를 가진다.

BucketExpedited 실행 쿼터
Active24시간 rolling window에 최대 30분
Working set24시간에 최대 15분
Frequent24시간에 최대 10분
Rare24시간에 최대 10분
Restricted24시간에 최대 5분

이 수치는 보장된 실행 시간이 아니라 시스템 동작을 이해하기 위한 근사값이다. OS 버전, 제조사 정책, 충전 상태, 배터리 제한 설정, 시스템 부하에 따라 달라질 수 있다. Android 16부터는 Active bucket에서도 job runtime quota가 더 명확히 적용된다. 그래도 중요한 방향성은 분명하다. 사용자가 최근에 자주 쓴 앱일수록 백그라운드 실행 기회가 많고, 거의 쓰지 않는 앱일수록 기회가 줄어든다.

4. Doze mode는 무엇인가

Doze mode는 기기가 사용되지 않는 동안 배터리를 아끼기 위해 백그라운드 CPU, 네트워크, JobScheduler, alarm 동작을 제한하는 Android의 절전 모드다.

가장 단순하게 표현하면 Doze는 다음 조건에서 시작된다.

[충전 중 아님]
      +
[화면 꺼짐]
      +
[일정 시간 미사용]
      |
      v
[Doze 진입]

Deep Doze까지 가려면 여기에 "기기가 가만히 있음"이 중요해진다.

[충전 중 아님]
      +
[화면 꺼짐]
      +
[오래 미사용]
      +
[기기 정지 상태]
      |
      v
[Deep Doze]

Doze에 들어가면 일반적인 백그라운드 작업은 바로 실행되지 않고 maintenance window라는 짧은 허용 구간으로 밀릴 수 있다. 이 window 동안 시스템은 미뤄둔 작업, sync, alarm을 일부 처리하게 해 준다.

시간 흐름

Normal ----> Doze ----------------------> Maintenance ----> Doze ----> Maintenance
              |                               |
              v                               v
        jobs/network 제한               밀린 작업 일부 처리

그래서 WorkManager 일반 작업은 Doze 중에 바로 실행되지 않을 수 있다. WorkManager가 잘못된 것이 아니라, WorkManager가 시스템의 절전 정책을 따르기 때문이다.

5. Light Doze와 Deep Doze의 차이

Android 7.0 이후로는 Doze가 더 세분화되었다. 화면이 꺼지고 충전 중이 아니면 비교적 빨리 약한 제한이 적용될 수 있고, 기기가 더 오래 사용되지 않고 가만히 있으면 더 강한 제한이 적용된다.

편의상 전자를 Light Doze, 후자를 Deep Doze 또는 Full Doze라고 부를 수 있다.

구분Light DozeDeep / Full Doze
진입 조건화면 꺼짐 + 충전 중 아님 + 짧은 미사용화면 꺼짐 + 충전 중 아님 + 긴 미사용 + 정지 상태
기기 움직임꼭 완전 정지일 필요는 없음정지 상태가 중요
제한 강도상대적으로 약함더 강함
네트워크/Jobbatching 중심 제한더 강하게 지연
maintenance window비교적 자주시간이 갈수록 더 드물어질 수 있음

여기서 "기기가 움직인다"는 것은 단순히 자이로센서 하나만 보는 의미가 아니다. Android는 motion sensor, significant motion sensor, accelerometer 계열의 저전력 센서와 제조사 구현을 통해 기기가 움직였는지 판단한다. 기기가 주머니 안에서 계속 흔들리거나 이동 중이라면 Deep Doze로 들어가기 어렵거나 늦어질 수 있다.

[Screen off + unplugged]
          |
          v
   [Light Doze]
          |
          v
[Still unused + stationary]
          |
          v
   [Deep Doze]

정리하면 Light Doze와 Deep Doze의 핵심 차이는 두 가지다.

  • 첫째, 기기가 정지해 있는지가 Deep Doze에서 더 중요하다.
  • 둘째, screen off 이후 미사용 시간이 길수록 더 강한 제한으로 들어간다.

6. Expedited Work는 Doze에서 어떻게 동작할까

Expedited Work는 Doze 상황에서도 일반 Work보다 우선 실행될 수 있다. 사용자가 직접 유발한 중요한 작업이나 high priority FCM 이후의 짧은 후속 처리처럼 즉시성이 필요한 작업에 사용하라고 만들어진 기능이기 때문이다.

하지만 expedited도 시스템 제한을 완전히 무시하지 않는다.

[Device in Doze]
      |
      v
[Expedited Work scheduled]
      |
      v
[Quota available?]
      |
  +---+---+
  |       |
 yes      no
  |       |
  v       v
[Run with priority] [Fallback: normal or drop]

쿼터가 남아 있으면 Doze 중에도 일반 작업보다 빨리 실행될 수 있다. 쿼터가 없으면 일반 Work로 강등되거나 드롭된다. 일반 Work로 강등되면 다시 Doze의 maintenance window를 기다릴 수 있다.

그래서 expedited를 사용할 때는 작업을 짧게 유지해야 한다. "중요한 작업 전체"를 expedited로 밀어 넣는 것이 아니라, 사용자 경험상 즉시 필요한 최소 단위만 expedited로 처리하고 나머지는 일반 Work나 foreground 작업으로 분리하는 편이 안전하다.

7. 백그라운드 작업을 설계할 때의 기준

Android 백그라운드 작업을 설계할 때 가장 먼저 물어야 할 질문은 "이 작업이 정말 지금 실행되어야 하는가"다.

[Background task]
      |
      v
[User-visible and time-sensitive?]
      |
  +---+---+
  |       |
 yes      no
  |       |
  v       v
[Expedited] [Normal Work]

조금 더 나누면 다음처럼 판단할 수 있다.

질문선택
앱이 꺼져도 언젠가 반드시 처리되어야 하는가WorkManager
사용자가 방금 한 행동의 결과인가Expedited Work 검토
몇 분 이상 오래 걸리는가Foreground Service 또는 user-initiated job 검토
주기적으로만 갱신하면 되는가Periodic Work
Doze에서도 즉시 사용자에게 보여야 하는 알림인가High priority FCM 검토
정확한 시각에 울려야 하는가Exact alarm 검토

여기서 중요한 점은 "제한을 우회하는 API"를 찾는 것이 아니라, 작업의 성격에 맞는 API를 고르는 것이다. Android의 백그라운드 제한은 앱을 괴롭히기 위한 장치가 아니라, 여러 앱이 함께 설치된 기기에서 배터리와 성능을 유지하기 위한 운영체제 정책이다.

결과적으로 이해해야 할 것

WorkManager는 백그라운드 작업을 안정적으로 예약하는 도구다. 하지만 시스템의 절전 정책을 벗어나지 않는다. 일반 Work는 Doze나 quota에 의해 지연될 수 있고, expedited Work는 더 빠르게 실행될 수 있지만 별도 쿼터가 있다.

Doze는 화면이 꺼지고 충전 중이 아니며 기기가 사용되지 않을 때 적용되는 절전 모드다. Light Doze는 비교적 빠르게 들어가는 약한 제한이고, Deep Doze는 기기가 오래 사용되지 않고 정지 상태일 때 들어가는 강한 제한이다.

따라서 Android 백그라운드 작업 설계의 핵심은 다음 한 문장으로 정리할 수 있다.

모든 작업을 빨리 실행하려고 하지 말고, 빨라야 하는 작업과 늦어도 되는 작업을 분리해야 한다.

이 분리가 되어 있으면 일반 Work, expedited Work, foreground service, FCM, alarm을 상황에 맞게 고를 수 있다. 반대로 이 분리가 안 되어 있으면 "WorkManager를 썼는데 왜 안 돌지?" 또는 "expedited인데 왜 밀리지?" 같은 문제가 반복된다.

참고 자료

0개의 댓글