핵심 요점
App Lifecycle은 모바일 애플리케이션의 모든 가능한 상태와 상태 간 전환을 설명하는 유한 상태 기계입니다. 각 상태는 앱이 코드를 실행할 수 있는지, UI를 표시할 수 있는지, 리소스를 소비할 수 있는지를 결정합니다. iOS와 Android는 유사하지만 동일하지 않은 모델을 가지고 있습니다 — 차이는 OS 아키텍처에서 비롯됩니다: iOS는 엄격한 메모리 제어(suspended 상태)를 사용하고, Android는 onSaveInstanceState를 통한 유연한 프로세스 관리를 사용합니다.
iOS에서 생명주기는 포그라운드/백그라운드에 밀접하게 연결됩니다: 앱은 활성 상태이거나 일시 중단됩니다. Android에서는 생명주기가 더 세분화되어 있습니다 — Activity/창은 6개 이상의 상태를 거치며, 프로세스 자체는 ProcessLifecycleOwner를 통해 추가 생명주기를 가집니다. 최신 애플리케이션(2024년부터)은 점차 생명주기 기반 아키텍처 패턴을 사용하고 있습니다: SwiftUI Lifecycle(iOS 16+)은 .scenePhase 환경을 통해, Compose Lifecycle은 LifecycleEventObserver를 통해 사용됩니다. 이러한 패턴은 컨텍스트 전환을 자동으로 처리하고 메모리 누수를 방지합니다. Google에 따르면, Android에서 올바른 생명주기 처리는 백그라운드 작업 중 메모리 소비를 30–40 % 줄입니다.
iOS에서 앱 생명주기는 UIApplicationDelegate 클래스(AppDelegate)를 통해 관리됩니다. 주요 메서드: application(_:didFinishLaunchingWithOptions:) — 첫 실행 시 초기화; applicationDidBecomeActive — 앱이 표시되고 상호 작용 준비; applicationWillResignActive — 비활성 상태로 전환(수신 전화, 알림 창 닫기); applicationDidEnterBackground — 앱이 숨겨짐, 데이터 저장에 ~5초; applicationWillEnterForeground — 백그라운드에서 복귀; applicationWillTerminate — 종료(suspended 없는 오래된 앱에서만 호출).
iOS 13부터 Apple은 iPad 및 macOS Catalyst에서 멀티윈도우 애플리케이션을 지원하기 위해 SceneDelegate(UISceneDelegate)를 도입했습니다. SceneDelegate는 각 개별 씬(창)의 생명주기를 관리합니다: scene(_:willConnectTo:options:) — 씬 생성; sceneDidBecomeActive — 씬 활성; sceneWillResignActive — 씬 포커스 상실; sceneDidEnterBackground — 씬 숨겨짐. AppDelegate는 전역 이벤트(데이터 로딩, 푸시 알림)를 처리하고, SceneDelegate는 각 창의 UI 상태를 처리합니다. SwiftUI에서 생명주기는 @Environment(\.scenePhase): .active, .inactive, .background 환경 값을 통해 관리됩니다 — 이는 SwiftUI 및 UIKit 애플리케이션 모두에 통합 API를 제공합니다.
Android에서 생명주기는 Application 클래스로 시작됩니다 — 모든 Activity보다 먼저 생성되는 싱글톤입니다. Application.onCreate() 메서드는 첫 번째 진입점으로, 전역 초기화(Analytics, DI, Crash Reporting)에 사용됩니다. 그 후 Activity가 순서대로 시작됩니다: onCreate() → onStart() → onResume(). 최소화 시: onPause() → onStop() → onSaveInstanceState(). 소멸 시: onDestroy(). 복귀 시: onRestart() → onStart() → onResume().
ProcessLifecycleOwner(AndroidX Lifecycle 2.2+부터)는 개별 Activity가 아닌 전체 프로세스의 생명주기를 추적합니다. 두 가지 상태를 제공합니다: ON_RESUME(앱이 포그라운드) 및 ON_STOP(앱이 숨겨짐). ProcessLifecycleOwner는 프로세스 수준에서 “앱이 언제 백그라운드로 갔는지” 문제를 해결합니다 — 이는 SDK, 라이브러리 및 모듈식 애플리케이션에 중요합니다. Jetpack Compose에서 생명주기는 LifecycleEventObserver와 collectAsStateWithLifecycle을 통해 관리되며, 백그라운드 모드에서 자동으로 데이터 수집을 일시 중단합니다. Android 14(API 34)는 Service.onTimeout을 통해 포그라운드 서비스 생명주기 알림을 추가했습니다 — 서비스는 6시간 이내에 완료되어야 하며, 그렇지 않으면 시스템이 강제로 중지합니다.
| iOS | Android | 설명 |
|---|---|---|
| Not Running | — | 앱이 실행되지 않음 |
| didFinishLaunching | Application.onCreate | 실행 시 첫 초기화 |
| Active | onResume | 앱이 표시되고 입력을 받음 |
| Inactive | onPause | 일시적인 포커스 손실(전화, 알림 창) |
| Background | onStop | 앱이 숨겨짐, 코드 실행 가능 |
| Suspended | — | 코드 실행 안 됨, 메모리 예약됨 |
| Will Terminate | onDestroy | 앱이 종료 중 |
주요 차이점: iOS는 백그라운드 진입 후 5-10초 후에 앱을 강제로 Suspended(코드 실행 중지)로 전환합니다. Android는 더 긴 시간 동안 백그라운드 작업(서비스, WorkManager)을 허용하지만 Doze 모드(Android 6+) 및 백그라운드 제한(Android 12+)의 제한이 있습니다. Android의 ProcessLifecycleOwner는 iOS의 UIApplicationDidEnterBackgroundNotification과 동일하며, 포커스 손실 후뿐만 아니라 백그라운드로의 모든 전환에서 실행된다는 차이점이 있습니다.
모든 생명주기 상태를 처리하는 기본 AppDelegate 구현. 백그라운드 전환 시 데이터 저장 및 복귀 시 UI 업데이트.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
Analytics.shared.initialize()
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
NotificationCenter.default.post(name: .appDidBecomeActive, object: nil)
}
func applicationWillResignActive(_ application: UIApplication) {
NotificationCenter.default.post(name: .appWillResignActive, object: nil)
}
func applicationDidEnterBackground(_ application: UIApplication) {
CoreDataManager.shared.saveContext()
UserDefaults.standard.synchronize()
}
func applicationWillEnterForeground(_ application: UIApplication) {
NetworkManager.shared.refreshSession()
}
}
AppDelegate는 생명주기의 중심 지점입니다. applicationDidEnterBackground는 중요한 메서드입니다: iOS는 저장에 약 5초를 제공합니다. applicationDidBecomeActive — UI를 업데이트하고 애니메이션을 다시 시작합니다. SceneDelegate(iOS 13+)의 경우 생명주기 로직이 씬에 분산됩니다: sceneDidBecomeActive / sceneDidEnterBackground가 개별 창을 관리합니다.
멀티윈도우 iPad 애플리케이션용 SceneDelegate. 각 씬은 다른 창과 독립적으로 자체 생명주기를 가집니다.
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
}
func sceneDidBecomeActive(_ scene: UIScene) {
PlayerManager.shared.resume()
}
func sceneDidEnterBackground(_ scene: UIScene) {
PlayerManager.shared.pause()
CoreDataManager.shared.saveContext()
}
func sceneDidDisconnect(_ scene: UIScene) {
PlayerManager.shared.cleanup()
}
}
SceneDelegate는 개별 씬의 생명주기를 관리합니다. sceneDidBecomeActive / sceneDidEnterBackground는 특정 창에 대한 AppDelegate 메서드의 해당 항목입니다. sceneDidDisconnect는 씬이 닫힐 때 호출됩니다(사용자가 iPad에서 창을 닫음). iOS 12 및 이하와의 호환성을 위해 AppDelegate는 SceneDelegate 로직을 복제해야 합니다. iOS 17+에서는 @Observable을 사용하여 scenePhase에 따라 작업을 자동으로 일시 중지/재개할 수 있습니다.
ProcessLifecycleOwner는 프로세스 수준에서 앱이 포그라운드/백그라운드에 있을 때를 추적합니다. 이는 전역 생명주기 관리에 최적의 접근 방식입니다.
import android.app.Application
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleObserver())
}
}
class AppLifecycleObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onEnterForeground() {
Analytics.shared.onForeground()
NetworkMonitor.shared.start()
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onEnterBackground() {
Analytics.shared.onBackground()
NetworkMonitor.shared.stop()
}
}
ProcessLifecycleOwner는 애플리케이션 수준에서 백그라운드 전환을 추적하는 유일한 올바른 방법입니다. ON_START는 앱이 표시될 때(포그라운드), ON_STOP은 완전히 숨겨졌을 때(백그라운드) 실행됩니다. Activity 생명주기와 달리 ProcessLifecycleOwner는 특정 화면에 연결되지 않습니다. Jetpack Compose의 경우 수동 관찰 대신 LifecycleResumeEffect 또는 collectAsStateWithLifecycle을 사용하세요.
ViewModel + Lifecycle은 생명주기 관리를 위한 올바른 Android 아키텍처입니다. ViewModel은 onDestroy에서 자동으로 정리되어 누수를 방지합니다.
import androidx.lifecycle.ViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
class TimerViewModel : ViewModel() {
private val _time = MutableLiveData<Long>()
val time: LiveData<Long> = _time
private var startTime = System.currentTimeMillis()
init {
updateTime()
}
private fun updateTime() {
_time.value = System.currentTimeMillis() - startTime
}
override fun onCleared() {
super.onCleared()
Logger.d("ViewModel cleared — 리소스 해제")
}
}
// Activity에서
class TimerActivity : AppCompatActivity() {
private val viewModel: TimerViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Logger.d("Activity created")
}
override fun onResume() {
super.onResume()
viewModel.time.observe(this) { Logger.d("Time: $it") }
}
override fun onPause() {
super.onPause()
Logger.d("Activity paused — 애니메이션 일시 중지")
}
override fun onDestroy() {
super.onDestroy()
Logger.d("Activity destroyed — 정리")
}
}
ViewModel은 화면 회전에서 살아남고 Activity가 종료될 때만 소멸됩니다. onCleared()는 리소스를 해제하고 스트림 구독을 취소하는 곳입니다. LiveData는 onPause에서 자동으로 관찰을 일시 중지하고 onResume에서 재개합니다 — 이는 백그라운드 작업 중 누수 및 충돌로부터 보호합니다. Compose에서는 동일한 목적으로 collectAsStateWithLifecycle()을 사용하세요.
자주 묻는 질문
최소화하면 앱이 활성에서 비활성(잠시), 그다음 백그라운드로 전환됩니다. 몇 초 후 시스템이 앱을 suspended로 전환할 수 있습니다 — 코드가 실행되지 않고 메모리가 예약됩니다. 메모리가 부족하면 시스템이 일시 중단된 앱을 종료합니다(willTerminate가 호출되지 않음). applicationDidEnterBackground에서 데이터를 저장하세요 — 이것이 저장을 위한 마지막 보장된 지점입니다.
최소 필수: applicationDidEnterBackground — 사용자 데이터 저장; applicationWillEnterForeground — UI 업데이트; Android의 onPause — 애니메이션/센서 일시 중지. ProcessLifecycleOwner는 처리를 간소화합니다: onResume — onPause(포그라운드), onStart — onStop(가시성). 필요에 따라 다른 상태를 처리하세요 — 모든 상태가 모든 애플리케이션에 중요하지는 않습니다.
AppDelegate는 전체 애플리케이션의 전역 델리게이트입니다(시작, 백그라운드 전환). SceneDelegate(iOS 13+)는 개별 창(씬)의 생명주기를 관리합니다 — 멀티윈도우 iPad 애플리케이션에서 각 씬은 자체 생명주기를 가집니다. iOS 12 및 이하에서는 모든 생명주기가 AppDelegate를 통해 처리됩니다. iPadOS 16+에서는 Stage Manager에 SceneDelegate가 필요합니다.
ProcessLifecycleOwner는 AndroidX Lifecycle의 구성 요소로, 개별 Activity가 아닌 전체 애플리케이션(프로세스)의 생명주기를 추적합니다. 두 가지 주요 이벤트를 제공합니다: ON_RESUME(앱이 포그라운드) 및 ON_STOP(앱이 백그라운드). SDK, 분석, 네트워크 모니터의 초기화/정리에 사용됩니다 — 현재 화면에 관계없이 앱의 백그라운드 전환에 반응해야 하는 모든 것에 사용됩니다.
Jetpack Compose에서는 LocalLifecycleOwner.current.lifecycle 또는 collectAsStateWithLifecycle() 함수를 통해 LifecycleEventObserver를 사용하세요. Compose 화면의 경우 LifecycleResumeEffect를 사용하세요 — DisposableEffect와 유사하게 onResume에서 실행되고 onPause에서 정리됩니다. lifecycleScope.launchWhenResumed — 백그라운드에서 자동으로 일시 중단되는 코루틴입니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.