Background는 앱이 계속 실행되지만 화면에 표시되지 않는 라이프사이클 상태입니다. iOS 및 Android에서 백그라운드 작업의 기초를 설명합니다: 제한 사항, 시간 초과, beginBackgroundTask, WorkManager 및 Service를 통한 백그라운드 작업, 그리고 Background의 올바른 처리를 위한 모범 사례.
핵심 사항
Background는 앱이 운영 체제에 계속 존재하고, 코드를 실행하며 리소스를 소비하지만 기기 화면에 표시되지 않는 상태입니다. 사용자가 홈 화면, 다른 앱에 있거나 기기 화면이 잠겨 있습니다. iOS에서 Background는 Inactive 다음에 옵니다 — 전환 체인: Active → Inactive → Background. Android에서 onStop은 Activity의 Background 전환을 알립니다.
두 플랫폼 모두 백그라운드 작업에 엄격한 제한을 가합니다. iOS는 Background 진입 후 코드 실행을 위해 제한된 시간(보통 30초)을 제공하며, 이후 앱은 Suspended로 전환됩니다. Android는 더 유연합니다: 표시 알림이 있는 Foreground Service는 무제한으로 실행될 수 있지만, 일반 Background Service는 몇 분으로 제한됩니다. 개발자의 주요 작업은 상태를 올바르게 저장하고 시스템 백그라운드 작업 API를 통해 작업을 계속할 수 있도록 예약하는 것입니다.
시스템은 메모리가 부족할 때 언제든지 백그라운드 앱을 종료할 수 있습니다. 종료 시 저장되지 않은 모든 데이터가 손실됩니다. 따라서 applicationDidEnterBackground(iOS) 또는 onStop(Android)에서 상태를 저장하는 것이 매우 중요합니다. 종료 후 다음 실행은 Not Running에서 콜드 스타트로 시작하여 저장된 상태를 복원합니다.
Background와 Suspended를 구분하는 것이 중요합니다. Background — 앱이 활발히 코드를 실행 중입니다. Suspended — 앱이 메모리에 있지만 코드를 실행하지 않습니다 — 중단(frozen) 상태입니다. iOS에서 앱은 백그라운드 작업 완료 후 Background에서 Suspended로 전환됩니다. Android에는 Suspended가 없습니다 — 프로세스가 존재하거나(Background 포함) 종료(Not Running)됩니다. 그러나 Android는 LMK(Low Memory Killer)를 통해 스레드 실행을 일시 중단할 수 있습니다.
| 특성 | iOS Background | Android Background |
|---|---|---|
| 코드 실행 | 예, 최대 30초 | 예, API에 따라 다름 |
| UI 표시 | 아니오 | 아니오 |
| 기본 시간 초과 | ~30초(beginBackgroundTask) | 몇 분(Service) |
| 무제한 작업 | 특별 카테고리만(오디오, VoIP, 내비게이션) | 알림 포함 Foreground Service |
| 실행 보장 | 없음 — 시스템이 언제든지 종료 가능 | WorkManager가 실행 보장 |
| 권한 필요 | 예 — Info.plist의 capabilities | 예 — FOREGROUND_SERVICE 권한 |
| 다음 상태 | Suspended → Not Running | Not Running(또는 재시작) |
iOS에서 Background는 델리게이트 메서드 applicationDidEnterBackground를 통해 처리됩니다. 이 메서드에서 개발자는 사용자 상태를 저장하고, 리소스를 해제하며, 백그라운드 작업을 완료해야 합니다. Background 진입 후 코드를 실행하려면 beginBackgroundTask(expirationHandler:)를 사용합니다 — 시스템에 추가 시간(보통 30초)을 요청하는 API입니다. 이 시간 내에 작업이 완료되지 않으면 expirationHandler가 호출되고 앱이 강제로 Suspended로 전환됩니다.
iOS 13에서 Apple은 BGTaskScheduler를 도입했습니다 — 백그라운드 작업을 예약하기 위한 최신 API입니다. 백그라운드 전환 후 종료 시간만 제공하는 beginBackgroundTask와 달리, BGTaskScheduler는 미래의 작업 실행을 예약할 수 있습니다 — 예를 들어, 한 시간에 한 번 콘텐츠 업데이트 또는 야간 분석 업로드 등입니다. BGTaskScheduler는 새 프로젝트에 권장되는 접근 방식이며, 배터리 효율성이 더 뛰어납니다.
import UIKit
import BackgroundTasks
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
// 앱이 백그라운드로 전환됨 — 백그라운드 작업 시작
func applicationDidEnterBackground(_ application: UIApplication) {
saveAppState()
startBackgroundTask()
}
private func startBackgroundTask() {
backgroundTaskID = UIApplication.shared.beginBackgroundTask { [weak self] in
// 시간 초과 — 강제 종료
self?.endBackgroundTask()
}
// 백그라운드 작업 시뮬레이션(서버에 데이터 저장)
DispatchQueue.global().async { [weak self] in
uploadAnalyticsData()
self?.endBackgroundTask()
}
}
private func endBackgroundTask() {
guard backgroundTaskID != .invalid else { return }
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
// BGTaskScheduler 등록
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.example.refresh",
using: nil
) { task in
handleAppRefresh(task: task as! BGAppRefreshTask)
}
return true
}
func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.example.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 3600)
try? BGTaskScheduler.shared.submit(request)
}
func handleAppRefresh(task: BGAppRefreshTask) {
scheduleAppRefresh()
task.expirationHandler = { task.setTaskCompleted(success: false) }
fetchLatestData { result in
task.setTaskCompleted(success: result)
}
}
}코드는 iOS의 완전한 Background 처리를 보여줍니다. applicationDidEnterBackground가 시간 초과 및 expirationHandler와 함께 beginBackgroundTask를 통해 백그라운드 작업을 시작합니다. 동시에 BGTaskScheduler가 정기적인 콘텐츠 업데이트를 위해 등록됩니다. beginBackgroundTask는 즉시 종료 작업에 사용되고, BGTaskScheduler는 장기 계획에 사용됩니다. 두 API 모두 작업 식별자의 올바른 관리가 필요합니다.
Android에서 Background는 여러 API를 통해 관리됩니다. 전통적인 Service는 백그라운드에서 코드 실행을 허용하지만, Android 8+(API 26)부터 Background Service는 제한됩니다: 앱이 백그라운드로 전환된 후 몇 분 내에 시스템이 종료합니다. 지속적인 알림이 있는 Foreground Service는 무제한으로 실행될 수 있습니다. WorkManager는 기기 재시작 후에도 실행 보장과 함께 백그라운드 작업에 권장되는 솔루션입니다.
Android는 iOS와 달리 장기 실행 백그라운드 프로세스를 지원합니다. Foreground Service는 사용자가 볼 수 있어야 하는 작업(음악 재생, 내비게이션, 운동 추적)에 사용됩니다. JobScheduler와 WorkManager는 지연 가능한 작업(데이터 동기화, 로그 업로드, 캐시 업데이트)에 사용됩니다. 주요 차이점: Android는 조건(Wi-Fi, 충전 중, 기기 유휴 상태)으로 작업을 예약할 수 있어 배터리와 트래픽을 절약합니다.
import android.app.Service
import android.content.Intent
import android.os.IBinder
import androidx.work.*
// 1. 장기 백그라운드 작업을 위한 Foreground Service
class SyncService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = createNotification()
startForeground(NOTIFICATION_ID, notification)
performBackgroundWork()
return START_STICKY
}
private fun performBackgroundWork() {
Thread {
// 서버와 데이터 동기화
syncDataToServer()
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf()
}.start()
}
override fun onBind(intent: Intent?): IBinder? = null
}
// 2. 지연 백그라운드 작업을 위한 WorkManager
class DataSyncWorker(
private val context: Context,
private val params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return try {
// 서버에 분석 업로드
uploadAnalytics()
Result.success()
} catch (e: Exception) {
if (runAttemptCount < 3) Result.retry() else Result.failure()
}
}
}
// WorkManager 작업 예약
fun scheduleBackgroundSync(context: Context) {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
val request = OneTimeWorkRequestBuilder<DataSyncWorker>()
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS)
.build()
WorkManager.getInstance(context).enqueue(request)
}코드는 Android의 백그라운드 작업에 대한 두 가지 접근 방식을 보여줍니다. SyncService — 즉각적이고 장기적인 백그라운드 작업을 위한 알림 포함 Foreground Service. DataSyncWorker — 조건(Wi-Fi, 충전)이 있는 지연 작업을 위한 WorkManager. WorkManager는 기기 재시작 후에도 실행을 보장하고 재시도를 위한 지수 백오프를 지원합니다. Foreground Service는 상태 표시줄에 지속적인 알림이 필요합니다.
두 모바일 플랫폼 모두 지속적으로 백그라운드 작업 규칙을 강화하고 있습니다. iOS에서는 OS의 새 세대마다 백그라운드 실행 시간이 줄어들고 새로운 제한이 추가됩니다. Android에서는 Google이 점점 더 엄격한 절전 모드(Doze, App Standby)를 도입하고 있습니다. 개발자는 현재 제한 사항을 파악하고 있어야 앱이 시스템에 의해 조기에 종료되지 않습니다.
iOS에서는 iOS 13부터 시스템이 백그라운드 시간을 남용하는 앱의 백그라운드 작업을 비활성화합니다. 각 앱은 사용자 행동에 따라 특정 제한을 받습니다. BGTaskScheduler는 최적의 시간(예: 기기가 Wi-Fi에 연결되고 충전 중일 때)에 실행을 예약합니다. BGTaskScheduler를 올바르게 사용하는 앱은 더 많은 백그라운드 시간을 얻습니다.
Android에서는 Android 9(API 28)부터 백그라운드 작업이 Doze 모드에 의해 제한되며, 기기가 유휴 상태일 때 활성화됩니다. Doze 상태의 앱은 백그라운드 작업을 수행할 수 없고, 네트워크가 차단되며, JobScheduler와 WorkManager는 Doze 종료까지 작업을 연기합니다. Foreground Service가 Doze를 우회하는 유일한 방법이지만, 남용 시 사용자에 의한 앱 차단과 권한 취소가 발생합니다.
| 제한 사항 | iOS | Android |
|---|---|---|
| 백그라운드 작업 시간 초과 | ~30초(beginBackgroundTask) | 몇 분(JobScheduler) |
| 무제한 백그라운드 | 오디오, VoIP, 내비게이션, Bluetooth | Foreground Service + 알림 |
| 절전 | Low Power Mode — 백그라운드 작업 비활성화 | Doze, App Standby, 배터리 최적화 |
| 예약 | BGTaskScheduler(iOS 13+) | WorkManager(Android Jetpack) |
| 재시작 후 | 푸시 알림만 | WorkManager가 작업 유지 |
| 최대 실행 시간 | ~30분(오디오) | 무제한(Foreground Service) |
첫 번째 규칙 — 백그라운드에서 리소스 소비를 최소화하세요. 대부분의 백그라운드 작업은 기기가 충전 중이고 Wi-Fi에 연결된 시간으로 연기할 수 있습니다. 조건이 있는 작업 예약에는 BGTaskScheduler(iOS)와 WorkManager(Android)를 사용하세요. 백그라운드에서 무거운 계산을 실행하지 마세요 — 배터리를 소모하고 CPU 스로틀링을 유발합니다.
두 번째 규칙 — beginBackgroundTask에 항상 expirationHandler를 지정하세요. 앱이 할당된 시간 내에 작업을 완료하지 않으면 시스템이 강제로 Suspended로 전환하거나 종료합니다. expirationHandler는 데이터를 저장하고 작업을 올바르게 종료할 마지막 기회입니다. Android에서는 WorkManager에서 setForegroundAsync를 사용하여 더 많은 시간이 필요할 때 일반 작업을 포그라운드로 변환하세요.
세 번째 규칙 — 시작 전에 백그라운드 작업 제한 사항을 확인하세요. iOS에서는 UIApplication.shared.backgroundTimeRemaining을 사용하여 남은 시간을 확인하세요. Android에서는 ActivityManager.isBackgroundRestricted()를 확인 — true인 경우 앱이 백그라운드 작업을 실행할 수 없으므로 설정에서 제한을 해제하도록 사용자에게 제안하세요. 이는 중요한 백그라운드 기능이 있는 앱(알람, 캘린더, 동기화)에 특히 중요합니다.
네 번째 규칙 — 실제 기기에서 백그라운드 작업을 테스트하세요. 시뮬레이터와 에뮬레이터는 실제 백그라운드 작업 제한을 재현하지 않습니다. iOS에서는 Xcode에서 Debug → Simulate Background Fetch를 사용하세요. Android에서는 재시작 후 WorkManager 테스트를 위해 adb shell am broadcast -a android.intent.action.ACTION_BOOT_COMPLETED를 사용하세요. 배터리가 부족한 기기에서의 실제 테스트가 대부분의 백그라운드 작업 문제를 드러냅니다.
import UIKit
final class BackgroundTaskManager {
static let shared = BackgroundTaskManager()
private var tasks: [String: UIBackgroundTaskIdentifier] = [:]
func startTask(name: String, expiration: @escaping () -> Void) {
let remaining = UIApplication.shared.backgroundTimeRemaining
print("백그라운드 남은 시간: \(remaining)초")
let task = UIApplication.shared.beginBackgroundTask { [weak self] in
print("작업 시간 초과: \(name)")
expiration()
self?.endTask(name: name)
}
tasks[name] = task
}
func endTask(name: String) {
guard let task = tasks.removeValue(forKey: name),
task != .invalid
else { return }
UIApplication.shared.endBackgroundTask(task)
}
}코드는 남은 시간을 추적하고 식별자를 관리하는 백그라운드 작업 관리자를 보여줍니다. backgroundTimeRemaining은 강제 종료 전 초 수를 반환합니다 — 값이 무한대이면 앱이 제한 없이 실행 중입니다(오디오, 내비게이션). 관리자는 다른 이름으로 여러 백그라운드 작업을 시작하고 각각을 올바르게 완료할 수 있습니다. 이 접근 방식은 백그라운드 작업 누수를 방지하고 시스템이 종료되지 않은 작업으로 인해 앱을 종료하지 않도록 보장합니다.
자주 묻는 질문
네, 제한된 카테고리에 대해서만 가능합니다: 오디오(AVAudioSession 카테고리 .playback), VoIP(PushKit), 내비게이션(allowsBackgroundLocationUpdates가 있는 CLLocationManager), Bluetooth(central background mode), 백그라운드 업데이트(BGTaskScheduler). 그 외 모든 경우는 최대 30초입니다. iOS 16+에서 Apple은 허용된 카테고리에도 요구 사항을 강화했습니다.
beginBackgroundTask는 백그라운드 전환 후 앱 수명을 약 30초 연장하는 동기 API입니다. applicationDidEnterBackground에서 호출됩니다. BGTaskScheduler는 시스템 트리거(시간, 위치, 콘텐츠 업데이트)를 통해 미래의 작업을 예약하는 비동기 API입니다. BGTaskScheduler는 Apple이 iOS 13+에 권장하는 최신 접근 방식입니다.
Android 8(API 26)부터 Background Service는 앱이 백그라운드로 전환된 후 몇 분 내에 종료됩니다. 해결책: 긴 작업에는 알림이 있는 Foreground Service, 지연 작업에는 WorkManager를 사용하세요. 설정에서 앱의 배터리 최적화를 확인 — 최적화된 경우 시스템이 백그라운드 작업을 연기하거나 취소할 수 있습니다.
Cmd+Shift+H를 눌러 홈 화면으로 이동합니다. Xcode에서 Debug → Simulate Background Fetch를 사용합니다. beginBackgroundTask를 확인하려면 콘솔(Shift+Cmd+C)을 열고 e UIApplication.shared.backgroundTimeRemaining을 호출합니다. Xcode 15+에서는 시뮬레이터의 Diagnostics 탭에서 Background Execution 시나리오를 사용할 수 있습니다.
Process Death는 리소스 부족 또는 백그라운드 유휴 상태에서 시스템이 Android 프로세스를 종료하는 것입니다. iOS와 달리 Android에는 Suspended가 없습니다 — 프로세스는 살아 있거나(백그라운드 가능) 죽어(Not Running) 있습니다. Process Death는 OS의 정상적인 동작이며, 앱은 이후 SavedStateHandle, onSaveInstanceState 또는 DataStore를 통해 상태를 올바르게 복원해야 합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.