Not Running — 그것이 무엇인가, 라이프사이클의 초기 상태

저자: IT Sectr 게시일: 2026-03-03 읽는 시간: 11 분

Not Running — 아직 시작되지 않거나 이미 종료된 모바일 애플리케이션의 라이프사이클 초기 상태입니다. iOS와 Android가 이 상태를 관리하는 방법, 어떤 이벤트가 Not Running으로부터의 전환을 초래하는지, Swift 및 Kotlin에서 애플리케이션 시작 및 종료를 올바르게 처리하는 방법을 알아보세요.

주요 포인트

  • Not Running — 애플리케이션이 메모리에 로드되지 않고 코드를 실행하지 않음; 라이프사이클의 입구이자 출구점
  • 시작 — Not Running으로부터의 전환은 앱 아이콘 탭, 딥 링크 문자 푸시 알림을 통해 발생
  • 종료 — 사용자가 스위프로 앱을 닫고, 시스템이 메모리 부족으로 언로드하거나 충돌이 발생
  • 콜드 스타트 — 애플리케이션이 참여부터 시작, 모든 객체가 새로 생성되며 캐시에서 상태가 복구되지 않음
  • 혹 스타트 — 애플리케이션이 Suspended에 있다가 완전한 초기화 없이 Active로 돌아옴

Not Running — 이 상태는 무엇인가

Not Running은 모바일 애플리케이션이 기기의 RAM에 로드되지 않고 시스템 자원을 소비하지 않는 기본 라이프사이클 상태입니다. iOS와 Android에서 이 상태는 애플리케이션과 관련된 프로세스와 스레드가 완전히 없다는 것을 의미합니다. 사용자는 홈 화면에서 앱 아이콘을 보지만, 애플리케이션 자체는 활성화되지 않고 최근 사용 앱 목록에 없습니다.

사용자가 앱 아이콘을 탭하면, 시스템이 새 프로세스를 만들고, 실행 가능한 코드를 메모리에 로드하고 필요한 모든 데이터 구조를 초기화합니다. 이 과정을 콜드 스타트(cole start)라고 합니다. 로드 시간 측면에서 가장 많은 자원을 소비하는 과정입니다.

시스템은 다른 상태에서도 애플리케이션을 Not Running으로 올길 수 있습니다. 애플리케이션이 백그라운드나 Suspended에 있는 경우, 운영체제는 더 높은 우선순위의 태스크(예: 활성적인 전방 애플리케이션)를 위해 충분한 RAM이 없을 때 이를 언로드할 권리가 있습니다.

개발자는 애플리케이션이 백그라운드에 있을 때 시스템에 의해 언제든지 종료될 수 있다는 사실을 고려해야 합니다. 이는 저장되지 않은 모든 데이터가 손실될 수 있다는 것을 의미합니다. 따라서 Active에서 Background로 전환할 때 키-값 저장소(UserDefaults, SharedPreferences)나 로컬 데이터베이스에 상태를 저장하는 것이 극히 중요합니다.

시스템이 어떤 애플리케이션을 언로드할지 결정하는 방법

iOS는 현재 애플리케이션 상태에 기반한 우선순위를 사용합니다. Active가 가장 높고, 그 다음이 Inactive, Background, Suspended, 그리고 마지막으로 Not Running이 가장 낮습니다. Android는 비슷한 프로세스 계층을 사용합니다: Foreground 프로세스는 OOM_ADJ = 0, Visible 프로세스 = 100, Service 프로세스 = 200, Background 프로세스 = 300, Empty 프로세스 = 400입니다. 값이 높을수록, 메모리가 부족할 때 프로세스가 종료될 가능성이 높습니다.

플랫폼상태언로드 우선순위설명
iOSNot Running가장 높음애플리케이션이 로드되지 않음 — 시스템 자원 소비 없음
iOSSuspended높음메모리에 있지만 코드 미실행 — 언로드 최우선 목표
iOSBackground중간백그라운드 태스크 실행 중 — 타임아웃 후 언로드
iOSActive낮음활성 애플리케이션 — 위기적인 메모리 압박 시에만 언로드
AndroidEmpty Process가장 높음활성 컴포넌트가 없는 프로세스 — 찼 저 제거
AndroidBackground Process높음보이는 Activity가 없는 백그라운드 프로세스
AndroidForeground Service낮음알림이 있는 서비스 — 거의 종료되지 않음
AndroidForeground Process최소활성 Activity — 마지막으로 종료

애플리케이션의 콜드 스타트와 워밍 스타트

콜드 스타트(cold start)는 애플리케이션이 Not Running에서 곱바로 Active로 전환할 때 발생합니다. 시스템이 새 프로세스를 만들고, 클래스를 로드하고, 정적 필드를 초기화하고, 메인 스레드를 만들고 UI 프레임워크를 시작합니다. iOS에서는 application(_:didFinishLaunchingWithOptions:)를 호출하는 것이고, Android에서는 Application.onCreate()와 Activity.onCreate()를 호출하는 것입니다. 콜드 스타트 시간은 애플리케이션의 복잡성에 따라 200ms에서 몇 초까지 다양합니다.

워밍 스타트(warm start 또는 혹 스타트) — 애플리케이션이 Suspended 상태에 있다가 완전한 재로드 없이 재개되는 것입니다. 시스템이 메모리에서 마지막 UI 스택을 복구하고, 사용자는 같은 위치에서 계속 작업합니다. 워밍 스타트는 콜드 스타트보다 훨씬 빠릅니다. 그 이유는 대부분의 코드가 이미 메모리에 로드되어 있기 때문입니다. iOS에서 워밍 스타트는 application(_:didFinishLaunchingWithOptions:)를 호출하지 않고, applicationWillEnterForeground와 applicationDidBecomeActive만 호출합니다.

콜드 스타트와 워밍 스타트의 차이는 사용자 경험에 중요합니다. 콜드 스타트 동안 개발자는 최대한 빠르게 시작되도록 해야 합니다 — 모듈의 지연 초기화, 무거운 자원의 지연 로드, 시작시 메인 스레드에서의 작업 최소화. Google은 콜드 스타트가 500ms 미만일 것을, Apple은 iOS에서 400ms 미만일 것을 권장합니다.

kotlin
// Android에서 콜드 스타트 시간 측정
class App : Application() {
    private var startTime: Long = 0L

    override fun onCreate() {
        super.onCreate()
        startTime = System.currentTimeMillis()
    }

    fun getStartupTime(): Long {
        return System.currentTimeMillis() - startTime
    }
}

// 지연 초기화로 Activity 시작
class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by lazy {
        ViewModelProvider(this).get(MainViewModel::class.java)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 첫 번째 프레임에 필요한 최소한만
        setupNavigation()
    }

    override fun onPostCreate(savedInstanceState: Bundle?) {
        super.onPostCreate(savedInstanceState)
        // 렌더링 후 무거운 초기화
        initializeHeavyModules()
    }
}

예제는 Android에서 콜드 스타트 시간 측정을 보여줍니다. Application.onCreate()는 Not Running에서 Active로 전환할 때 호출됩니다. 타임스탬프는 프로세스 시작시 기록됩니다. Activity는 첫 번째 프레임을 차단하지 않도록 lazy 위임을 통해 지연 초기화를 사용합니다. onPostCreate는 UI가 이미 렌더링되었기 때문에 무거운 모듈을 초기화하는 최적의 공간입니다.

iOS에서의 Not Running: Swift와 AppDelegate

iOS에서 Not Running은 UIApplicationDelegate 프로토콜을 통해 관리됩니다. 주요 메소드: application(_:didFinishLaunchingWithOptions:)는 콜드 스타트 후에 호출되고, applicationWillTerminate(_:)는 사용자가 애플리케이션을 종료하기 전에 호출됩니다. 그러나 시스템은 applicationWillTerminate를 호출하지 않고 애플리케이션을 종료할 수 있습니다 — 예를 들어, 응급 종료 또는 메모리 압박 상황에서요. iOS는 이 메소드가 호출될 것을 보장하지 않으므로 데이터는 applicationDidEnterBackground에서 저장해야 합니다.

iOS에서 Not Running으로 전환하는 시나리오

사용자는 App Switcher에서 스위프하여 애플리케이션을 수동으로 종료할 수 있습니다. 시스템은 백그라운드에 있는 동안 메모리에서 애플리케이션을 언로드할 수 있습니다. 애플리케이션이 충돌할 수 있습니다. 모든 경우에 시작 중에 만들어진 모든 객체가 파꼴립니다. 저장되지 않은 상태는 영원히 손실됩니다. iOS 13+에서는 상태를 보존하기 위해 NSUserActivity 또는 UIApplication.stateRestorationIdentifier를 통한 상태 복구 메커니즘을 사용하는 것이 권장됩니다.

swift
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    // 콜드 스타트: 애플리케이션이 Not Running에서 전환
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // 최소 서비스 세트 초기화
        setupAnalytics()
        configureAppearance()
        return true
    }

    // 애플리케이션 종료 — 수동 닫기만
    func applicationWillTerminate(
        _ application: UIApplication
    ) {
        saveCriticalData()
    }

    // 백그라운드로 가기 전에 데이터 저장
    func applicationDidEnterBackground(
        _ application: UIApplication
    ) {
        saveApplicationState()
    }

    private func saveCriticalData() {
        UserDefaults.standard.synchronize()
    }

    private func saveApplicationState() {
        let state = ["lastScreen": "main", "timestamp": Date()]
        try? NSKeyedArchiver.archivedData(
            withRootObject: state,
            requiringSecureCoding: true
        )
    }
}

코드는 iOS에서 Not Running을 올바르게 처리하는 것을 보여줍니다. applicationWillTerminate는 사용자가 수동으로 애플리케이션을 종료할 때만 호출됩니다. 중요한 데이터의 저장은 applicationDidEnterBackground에서 중복되는데, 그 이유는 이 메소드가 백그라운드로 이동하기 전에 호출된다는 것이 보장되기 때문입니다. 상태 복구를 통해 콜드 스타트 동안 후에 복구할 수 있도록 UI 스택을 저장할 수 있습니다.

Android에서의 Not Running: Kotlin과 프로세스

Android에서 Not Running은 애플리케이션 프로세스가 존재하지 않는 것을 의미합니다. Android의 기반이 되는 Linux 시스템은 Zygote 메커니즘을 통해 프로세스를 관리합니다. 애플리케이션이 구동되면 Zygote가 새 프로세스를 포크하고, Dalvik/ART를 로드하고 Application.onCreate()를 호출합니다. Android에는 applicationWillTerminate의 직접적인 대응물이 없습니다 — 시스템은 예고 없이 언제든지 프로세스를 종료할 수 있습니다.

Android 프로세스 라이프사이클

Activity가 첫 번째로 호출되면, 시스템이 onCreate → onStart → onResume 체인을 통해 프로세스, Application 그리고 Activity를 만듭니다. 사용자가 뒤로 가기 버튼을 누르면 Activity가 파꼴리고 (onDestroy), 프로세스가 시스템에 의해 종료될 수 있습니다. iOS와의 주요 차이점은 Android에서는 활성 Activity가 없더라도 프로세스가 계속 존재할 수 있다는 것입니다 — 예를 들어 Foreground Service가 실행 중이거나 활성적인 BroadcastReceiver가 있는 경우입니다.

kotlin
// ViewModel에서 SavedStateHandle을 통한 Not Running 처리
class MainViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    companion object {
        private const val KEY_LAST_SCREEN = "last_screen"
        private const val KEY_USER_DATA = "user_data"
    }

    fun saveCurrentState(screen: String, data: String) {
        savedStateHandle[KEY_LAST_SCREEN] = screen
        savedStateHandle[KEY_USER_DATA] = data
    }

    fun restoreState(): AppState? {
        val screen = savedStateHandle.get<String>(KEY_LAST_SCREEN)
        val data = savedStateHandle.get<String>(KEY_USER_DATA)
        return if (screen != null && data != null) {
            AppState(screen, data)
        } else null
    }
}

// Application — Not Running 이후 첫 콜백
class MyApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        initCrashReporter()
        initDependencyInjection()
    }
}

SavedStateHandle은 Not Running으로 전환하는 동안 상태를 자동으로 저장하고 콜드 스타트에서 복구하는 Android Architecture Components의 컴포넌트입니다. ViewModelProvider를 통해 만들어진 ViewModel은 화면 회전과 Activity 파괴에서도 살아납니다. 프로세스가 종료되면 SavedStateHandle의 데이터가 Bundle로 직렬화되고 저장된 인스턴스 상태에 저장됩니다.

Not Running으로 전환하는 이유

Not Running여러 가지 이유로 발생합니다. 사용자가 수동으로 애플리케이션을 닫습니다. 시스템이 메모리 부족으로 애플리케이션을 언로드합니다. 애플리케이션이 예외로 충돌합니다. Android에서 시스템은 대규모 앱 업데이트 또는 기기 재부딩 중에 프로세스를 종료할 수 있습니다. iOS는 백그라운드 태스크가 타임아웃될 때(보통 30초) 애플리케이션을 종료할 수 있습니다.

이유iOSAndroid예방 가능
사용자에 의한 수동 닫기App Switcher에서 스위프Recents에서 스위프아니오 — 사용자 작업
메모리 부족메모리 경고 발동onTrimMemory / LMK부분적 — 메모리 최적화
애플리케이션 충돌NSException / 신호UncaughtException / ANR예 — 오류 처리 및 충돌 보고
백그라운드 태스크 타임아웃백그라운드 태스크에 30초JobScheduler에 10분예 — 올바른 태스크 스케줄링
OS 재부딩applicationWillTerminate 호출Broadcast ACTION_SHUTDOWN아니오 — 시스템 이벤트
앱 업데이트발생하지 않음 (iOS 샌드박스)APK 업데이트시 프로세스 종료아니오 — 시스템 업데이트

Not Running으로의 전환 진단 방법

iOS의 경우 applicationWillTerminate와 applicationDidFinishLaunching에서 콘솔 로깅을 사용하세요. 각 실행시 UserDefaults에 플래그를 추가하세요 — 다음 시작시 플래그가 없으면 애플리케이션이 부적절하게 종료된 것입니다. Android에서는 ActivityManager.isBackgroundRestricted()를 사용하여 애플리케이션이 백그라운드 태스크를 실행할 수 있는지 확인하세요. 또한 onTrimMemory(TRIM_MEMORY_COMPLETE)를 모니터링하세요 — 이것은 프로세스가 종료될 것이라는 신호입니다.

Not Running 작업을 위한 최선의 실무

첫 번째 규칙 — applicationWillTerminate나 onDestroy가 호출될 것으로 추정하지 마세요. Active에서 Background로 전환할 때마다 중요한 데이터를 저장하세요. 간단한 설정에는 키-값 저장소를, 구조화된 데이터에는 SQLite/Room을 사용하세요.

둘 번째 규칙 — 콜드 스타트 시간을 측정하고 최적화하세요. 지연 초기화, 메인 스레드에서의 작업 최소화, 자원 선로드, SplashScreen API 사용 — 이 모든 것이 인식된 시작 시간을 향상시킵니다. Google은 우수한 UX를 위해 콜드 스타트가 200ms 미만일 것을 권장합니다.

셋 번째 규칙 — 상태 복구를 구현하세요. iOS에서는 UIApplication.stateRestorationIdentifier와 NSUserActivity를 사용하세요. Android에서는 onSaveInstanceState와 결합된 ViewModel의 SavedStateHandle을 사용하세요. 이를 통해 사용자는 애플리케이션을 재시작한 후에도 같은 위치에서 계속 작업할 수 있습니다.

넣 번째 규칙 — Not Running 이후에 애플리케이션이 시작된 launchOptions와 Intent를 처리하세요. 딥 링크, 푸시 알림, 유니버설 링크 — 이 모든 것이 시작 매개변수를 통해 전달됩니다. 개발자는 이 데이터를 올바르게 추출하고 사용자를 해당 화면으로 안내해야 합니다.

swift
// 콜드 스타트 후 딥 링크 처리
func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    // 알림이 도척했는지 확인
    if let notification = launchOptions?[.remoteNotification] as? [String: Any] {
        handleNotification(notification)
    }
    // 딥 링크 확인
    if let url = launchOptions?[.url] as? URL {
        handleDeepLink(url)
    }
    return true
}

private func handleDeepLink(_ url: URL) {
    guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
          let screenId = components.queryItems?.first(where: { $0.name == "screen" })?.value
    else { return }
    openScreen(screenId)
}

코드는 iOS 콜드 스타트에서 시작 매개변수의 처리를 보여줍니다. launchOptions에는 시스템이 애플리케이션을 시작한 데이터가 포함되어 있습니다. 알림, 딥 링크 그리고 유니버설 링크가 이 딕셔너리를 통해 전달됩니다. 개발자는 원활한 사용자 경험을 보장하기 위해 가능한 모든 시작 시나리오를 올바르게 처리해야 합니다.

자주 묻는 질문

Not Running으로 전환할 때 데이터는 어쪌되나요?

영구 저장소(UserDefaults, Core Data, SharedPreferences, Room)에 저장된 데이터는 보존됩니다. RAM의 데이터 — 변수, 캐시, SavedStateHandle이 없는 ViewModel 상태 — 된한 없이 손실됩니다. 따라서 Background로 전환할 때마다 애플리케이션 상태를 저장하는 것이 극히 중요합니다.

iOS에서 콜드 스타트와 혹 스타트를 어떻게 구분하나요?

콜드 스타트 동안에는 application(_:didFinishLaunchingWithOptions:)가 호출됩니다. 혹 스타트(Suspended에서 복귀) 동안에는 이 메소드가 호출되지 않습니다 — applicationWillEnterForeground와 applicationDidBecomeActive만 트리거됩니다. 콜드 스타트에서만 작업을 수행해야 하는 경우 didFinishLaunchingWithOptions에 플래그를 설정하세요.

Android 애플리케이션이 활성 Service와 Not Running에 있을 수 있나요?

그러합니다. 지속적인 알림이 있는 Foreground Service는 모든 Activity가 파꼴되더라도 시스템이 프로세스를 종료하는 것을 막습니다. Background Service(foreground 없이 startService)는 시스템이 언제든지 중단할 수 있습니다. 실행 중인 Service는 프로세스가 존재한다는 것을 의미하고, 이것은 더 이상 Not Running이 아닙니다.

시뮬레이터에서 Not Running을 어떻게 에뮬레이트하나요?

iOS 시뮬레이터에서는 App Switcher(Cmd+Shift+H 두 번, 위로 스위프)를 통해 애플리케이션을 종료하세요. Android 에뮬레이터에서는 adb shell am force-stop com.example.app 또는 Logcat의 Stop 버튼을 사용하세요. 그 후 애플리케이션을 다시 실행하세요 — 이것이 Not Running에서의 깨깋한 콜드 스타트가 됩니다.

Not Running의 문맥에서 kill-switch란 무엇인가요?

Kill-switch는 애플리케이션의 응급 종료를 위한 서버 명령입니다. 은행 및 기업 애플리케이션에서 원격 액세스 차단을 위해 사용됩니다. 애플리케이션이 kill 명령을 받으면 다음 콜드 스타트시 UI를 차단하고 재인증을 요청합니다. iOS에서 kill-switch는 차단 플래그가 있는 원격 알림을 통해 구현됩니다.

요약

  • Not Running — 라이프사이클의 초기 및 최종 상태, 애플리케이션이 메모리에 로드되지 않고 코드를 실행하지 않음
  • 콜드 스타트 — Not Running에서 애플리케이션을 완전히 재시작, 모든 컴포넌트를 참여부터 초기화해야 함
  • 혹 스타트 — Suspended에서 복귀, didFinishLaunchingWithOptions 또는 Application.onCreate을 호출하지 않음
  • 데이터 저장 — Background로 전환할 때 실행하는 것이 극히 중요, Not Running은 언제든지 발생할 수 있음
  • iOS — applicationWillTerminate가 보장되지 않으며, UserDefaults 또는 상태 복구를 통해 상태가 저장됨
  • Android — 프로세스가 언제든지 종료될 수 있으며, ViewModel의 SavedStateHandle이 상태를 자동으로 저장함
  • 시작 최적화 — 지연 초기화, 메인 스레드에서 최소 작업, 빠른 첫 프레임을 위한 SplashScreen API

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

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

프로젝트 논의

더 읽어보기