App Lifecycle: iOS 및 Android에서 앱 상태란

저자: IT Sectr 게시일: 2026-03-02 읽는 시간: 8 분
App Lifecycle(앱 생명주기)은 모바일 앱이 시작부터 종료까지 거치는 일련의 상태입니다. 생명주기를 이해하는 것은 리소스 관리, 데이터 지속성 및 안정성 보장에 중요합니다. iOS에서 앱은 다음 상태를 거칩니다: Not Running → Active → Inactive → Background → Suspended. Android에서는 — onCreate → onStart → onResume → onPause → onStop → onDestroy + onRestart를 통해 진행됩니다. 각 상태는 개발자에게 상태 저장, 리소스 해제 또는 복귀 준비를 위한 기회를 제공합니다. Apple 문서에 따르면, 생명주기 이벤트를 무시하는 것은 앱 최소화 시 충돌의 40 % 원인입니다. IT Sectr에서는 모든 프로젝트에 대해 Android의 ProcessLifecycleOwner와 iOS의 AppDelegate 패턴을 필수 표준으로 구현했습니다 — 이로 인해 백그라운드 관련 버그가 60 % 감소했습니다.

핵심 요점

  • App Lifecycle — 시작부터 종료까지의 앱 상태 집합, 각각 특정 개발자 작업이 있습니다.
  • AppDelegate — 생명주기 처리를 위한 iOS 중앙 클래스: didFinishLaunching, didEnterBackground, willEnterForeground.
  • SceneDelegate — 개별 씬의 델리게이트(iOS 13+), 멀티윈도우 애플리케이션에서 각 창의 생명주기를 관리합니다.
  • ProcessLifecycleOwner — AndroidX Lifecycle의 Android 구성 요소로 전체 프로세스의 생명주기를 추적합니다.
  • Application.onCreate — Android 애플리케이션의 첫 번째 진입점으로, 모든 Activity보다 먼저 호출됩니다.

App Lifecycle이란?

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 생명주기: AppDelegate 및 SceneDelegate

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 및 ProcessLifecycleOwner

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 생명주기 비교

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과 동일하며, 포커스 손실 후뿐만 아니라 백그라운드로의 모든 전환에서 실행된다는 차이점이 있습니다.

코드 예제

iOS: 생명주기 메서드를 포함한 AppDelegate

모든 생명주기 상태를 처리하는 기본 AppDelegate 구현. 백그라운드 전환 시 데이터 저장 및 복귀 시 UI 업데이트.

swift
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가 개별 창을 관리합니다.

iOS: SceneDelegate (iOS 13+)

멀티윈도우 iPad 애플리케이션용 SceneDelegate. 각 씬은 다른 창과 독립적으로 자체 생명주기를 가집니다.

swift
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에 따라 작업을 자동으로 일시 중지/재개할 수 있습니다.

Android: ProcessLifecycleOwner를 사용한 Application

ProcessLifecycleOwner는 프로세스 수준에서 앱이 포그라운드/백그라운드에 있을 때를 추적합니다. 이는 전역 생명주기 관리에 최적의 접근 방식입니다.

kotlin
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을 사용하세요.

Android: ViewModel을 사용한 Activity 생명주기

ViewModel + Lifecycle은 생명주기 관리를 위한 올바른 Android 아키텍처입니다. ViewModel은 onDestroy에서 자동으로 정리되어 누수를 방지합니다.

kotlin
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()을 사용하세요.

자주 묻는 질문

사용자가 iOS 앱을 최소화하면 어떻게 되나요?

최소화하면 앱이 활성에서 비활성(잠시), 그다음 백그라운드로 전환됩니다. 몇 초 후 시스템이 앱을 suspended로 전환할 수 있습니다 — 코드가 실행되지 않고 메모리가 예약됩니다. 메모리가 부족하면 시스템이 일시 중단된 앱을 종료합니다(willTerminate가 호출되지 않음). applicationDidEnterBackground에서 데이터를 저장하세요 — 이것이 저장을 위한 마지막 보장된 지점입니다.

모든 App Lifecycle 상태를 처리해야 하나요?

최소 필수: applicationDidEnterBackground — 사용자 데이터 저장; applicationWillEnterForeground — UI 업데이트; Android의 onPause — 애니메이션/센서 일시 중지. ProcessLifecycleOwner는 처리를 간소화합니다: onResume — onPause(포그라운드), onStart — onStop(가시성). 필요에 따라 다른 상태를 처리하세요 — 모든 상태가 모든 애플리케이션에 중요하지는 않습니다.

SceneDelegate와 AppDelegate의 차이점은?

AppDelegate는 전체 애플리케이션의 전역 델리게이트입니다(시작, 백그라운드 전환). SceneDelegate(iOS 13+)는 개별 창(씬)의 생명주기를 관리합니다 — 멀티윈도우 iPad 애플리케이션에서 각 씬은 자체 생명주기를 가집니다. iOS 12 및 이하에서는 모든 생명주기가 AppDelegate를 통해 처리됩니다. iPadOS 16+에서는 Stage Manager에 SceneDelegate가 필요합니다.

Android에서 ProcessLifecycleOwner란 무엇인가요?

ProcessLifecycleOwner는 AndroidX Lifecycle의 구성 요소로, 개별 Activity가 아닌 전체 애플리케이션(프로세스)의 생명주기를 추적합니다. 두 가지 주요 이벤트를 제공합니다: ON_RESUME(앱이 포그라운드) 및 ON_STOP(앱이 백그라운드). SDK, 분석, 네트워크 모니터의 초기화/정리에 사용됩니다 — 현재 화면에 관계없이 앱의 백그라운드 전환에 반응해야 하는 모든 것에 사용됩니다.

Jetpack Compose에서 생명주기를 처리하는 방법은?

Jetpack Compose에서는 LocalLifecycleOwner.current.lifecycle 또는 collectAsStateWithLifecycle() 함수를 통해 LifecycleEventObserver를 사용하세요. Compose 화면의 경우 LifecycleResumeEffect를 사용하세요 — DisposableEffect와 유사하게 onResume에서 실행되고 onPause에서 정리됩니다. lifecycleScope.launchWhenResumed — 백그라운드에서 자동으로 일시 중단되는 코루틴입니다.

요약

  • App Lifecycle은 리소스 관리, 데이터 지속성 및 안정성을 결정하는 앱 상태의 유한 상태 기계입니다.
  • iOS AppDelegate(applicationDidEnterBackground)는 5초 이내에 백그라운드 전환 시 데이터를 저장하는 중요한 메서드입니다.
  • iOS SceneDelegate(iOS 13+)는 각 씬의 생명주기를 별도로 관리 — 멀티윈도우 iPad 애플리케이션에 필수입니다.
  • Android ProcessLifecycleOwner는 SDK 초기화/정리를 위한 전역 프로세스 생명주기 관찰자입니다.
  • Android의 ViewModel은 화면 회전에서 살아남고 Activity 종료 시 정리 — 메모리 누수를 방지합니다.
  • SwiftUI @Environment(\.scenePhase)는 SwiftUI에서 생명주기에 통합 API를 제공합니다(active, inactive, background).
  • 올바른 생명주기 처리는 메모리 소비를 30-40% 줄이고 백그라운드 작업 중 충돌의 40%를 방지합니다.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기