lifecycleScope는 androidx.lifecycle 라이브러리의 내장 CoroutineScope로, Activity, Fragment 또는 모든 LifecycleOwner의 수명 주기에 바인딩되며 구성 요소가 소멸되면 자동으로 코루틴을 취소합니다. Google Android Developers, 2025에 따르면 lifecycleScope를 사용하면 Activity나 Fragment가 소멸된 후 코드가 실행될 위험 없이 UI 계층과 관련된 코루틴을 안전하게 실행할 수 있습니다. LifecycleOwner가 DESTROYED 상태로 전환되면 Scope가 자동으로 취소됩니다.
주요 내용
lifecycleScope는 LifecycleOwner 인터페이스(Activity, Fragment, Service)의 확장 속성으로, 구성 요소의 전체 수명 주기에 바인딩된 즉시 사용 가능한 CoroutineScope를 제공합니다. LifecycleOwner가 DESTROYED 상태에 도달하면 lifecycleScope는 활성 코루틴을 모두 자동으로 취소합니다.
// In Fragment or Activity
lifecycleScope.launch {
delay(1000)
showSnackbar("안녕하세요!")
}
viewModelScope와 달리 lifecycleScope는 LifecycleOwner가 소멸될 때마다 취소됩니다(화면 회전 포함). 따라서 특정 화면이 표시되는 동안만 유지되어야 하는 작업에 이상적입니다.
lifecycleScope는 LifecycleOwner가 있는 모든 곳에서 사용할 수 있습니다:
lifecycleScope의 자동 취소 메커니즘은 Lifecycle 이벤트 구독을 기반으로 합니다. Lifecycle이 CREATED 아래로 DESTROYED로 떨어지면 Scope가 취소됩니다.
| 상태 | 설명 | Scope 활성 |
|---|---|---|
| CREATED | LifecycleOwner 생성됨, onCreate 실행됨 | 예 |
| STARTED | LifecycleOwner 표시됨 (onStart) | 예 |
| RESUMED | LifecycleOwner 포그라운드 (onResume) | 예 |
| DESTROYED | LifecycleOwner 소멸됨 (onDestroy) | 아니요 (Scope 취소됨) |
lifecycleScope는 CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)로 생성되어 Lifecycle 내부에 저장됩니다. Lifecycle이 DESTROYED 상태로 전환되면 scope.cancel()이 호출됩니다. 이 메커니즘은 Scope에 처음 액세스할 때 수명 주기 이벤트를 구독하는 LifecycleEventObserver를 통해 구현됩니다.
화면이 회전되면 Activity가 소멸(onDestroy)되고 다시 생성됩니다. lifecycleScope는 이전 Activity와 함께 취소되고 새 Activity를 위해 새 Scope 인스턴스가 생성됩니다. 이는 회전 시 유지되는 viewModelScope와의 근본적인 차이점입니다.
lifecycle 라이브러리는 lifecycleScope를 통해 코루틴을 실행하는 여러 방법을 제공합니다. 더 이상 사용되지 않는 메서드에서 최신 API로의 진화를 살펴보겠습니다.
가장 간단한 방법은 lifecycleScope.launch { ... }입니다. 코루틴이 즉시 시작되고 DESTROYED에서 취소됩니다. 그러나 UI가 표시되지 않는 경우에도 코드를 실행할 수 있습니다(onStop 후 백그라운드 등). 이것이 항상 바람직한 것은 아닙니다.
이 메서드들은 Lifecycle이 지정된 상태 아래로 떨어지면 코루틴 실행을 일시 중지하고 돌아오면 재개했습니다. 그러나 lifecycle-runtime-ktx 2.6.0에서 @Deprecated로 표시되었습니다. 이유는:
repeatOnLifecycle은 Google이 권장하는 수명 주기와 동기화된 코루틴 실행 방식입니다. Lifecycle이 지정된 상태에 도달할 때마다 코루틴을 취소하고 다시 시작합니다.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state ->
updateUI(state)
}
}
}
repeatOnLifecycle에 전달된 코루틴은 Lifecycle이 STARTED에 도달하면 시작되고 STARTED 아래로 떨어지면 취소됩니다. STARTED로 돌아오면 코루틴이 처음부터 다시 시작됩니다. 이는 안전하고 효율적입니다 — 일시 중단된 코루틴이 남지 않습니다.
수명 주기를 인식하며 Flow에서 데이터를 수집하려면 flowWithLifecycle 연산자가 있습니다. Lifecycle 상태가 변경되면 자동으로 수집을 중지하고 재개합니다:
viewModel.uiState
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach { state -> updateUI(state) }
.launchIn(lifecycleScope)
flowWithLifecycle 연산자는 UI 계층에서 Flow를 안전하게 구독하는 가장 간결한 방법입니다.
Kotlin을 사용한 Android 애플리케이션에서 lifecycleScope의 세 가지 실제 시나리오를 살펴보겠습니다.
class MapFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
locationProvider.observeLocation().collect { loc ->
updateMapMarker(loc)
}
}
}
}
}
코루틴은 프래그먼트가 표시될 때(STARTED) 시작되고 화면을 떠날 때(STOPPED) 취소됩니다. 사용자가 다른 앱으로 전환하면 위치 업데이트가 배터리를 소모하지 않습니다.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
animateFadeIn(titleView)
delay(200)
animateSlideUp(contentView)
}
}
애니메이션은 프래그먼트가 포그라운드에 있을 때만(RESUMED) 실행됩니다. 애니메이션 중에 사용자가 앱을 최소화하면 코루틴이 취소되고 돌아오면 애니메이션이 다시 시작됩니다.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
while (isActive) {
syncData()
delay(30_000L)
}
}
}
데이터는 30초마다 동기화되지만 화면이 표시될 때만 동기화됩니다. isActive는 코루틴이 취소되었는지 확인하여 화면을 떠날 때 루프에서 안전하게 빠져나올 수 있는 방법을 제공합니다.
두 Scope 모두 수명 주기에 바인딩되어 있지만 다른 측면에 바인딩되어 있습니다. 차이점을 이해하는 것은 올바른 Android 애플리케이션 아키텍처에 중요합니다.
viewModelScope는 화면 회전 시 유지되는 ViewModel에 바인딩됩니다. lifecycleScope는 회전 시 소멸되고 다시 생성되는 LifecycleOwner(Activity/Fragment)에 바인딩됩니다. 이에 따라 사용 시나리오가 결정됩니다.
실제로는 두 Scope의 조합이 일반적입니다: viewModelScope가 데이터를 로드하고 상태를 관리하며 lifecycleScope가 수명 주기를 인식하여 ViewModel의 Flow를 구독합니다. 이러한 책임 분리는 현대 Android 개발에서 모범 사례로 간주됩니다.
lifecycleScope 사용 시 개발자들이 흔히 저지르는 네 가지 실수를 살펴보겠습니다.
lifecycleScope.launch에서 데이터 로딩을 시작하면 화면 회전 시 코루틴이 취소되어 데이터를 다시 로드해야 합니다. 장기 실행 작업에는 viewModelScope를 사용하세요. lifecycleScope는 UI에 바인딩된 작업에만 사용합니다.
lifecycleScope.launch 내에서 직접 viewModel.someFlow.collect { ... }를 호출하면 화면이 표시되지 않을 때도 데이터 수집이 계속됩니다. 이로 인해 백그라운드에서 UI 업데이트와 불필요한 오버헤드가 발생할 수 있습니다. 항상 repeatOnLifecycle 또는 flowWithLifecycle을 사용하세요.
lifecycleScope는 DESTROYED에서 취소되지만 갑작스러운 취소 시 일시 중단 지점 이후의 코드가 실행되지 않을 수 있습니다. NonCancellable을 사용하지 않는 한 일시 중단 호출 후의 코드 실행에 의존하지 마세요.
launchWhenStarted와 그 유사 메서드는 코루틴을 취소하지 않고 일시 중지만 합니다. 화면이 포그라운드와 백그라운드를 여러 번 전환하면 코루틴이 지연된 호출을 축적합니다. repeatOnLifecycle로 전환하세요 — 이것이 Lifecycle과 동기화하는 유일한 올바른 방법입니다.
자주 묻는 질문
lifecycleScope는 LifecycleOwner가 소멸되면 자동으로 취소됩니다. GlobalScope는 애플리케이션 실행 내내 유지됩니다. lifecycleScope의 코루틴은 구성 요소 소멸 후 UI를 업데이트할 수 없지만 GlobalScope에서는 업데이트할 수 있어 충돌이 발생합니다. UI 계층에서는 항상 lifecycleScope를 사용하세요.
아니요, ViewModel은 LifecycleOwner가 아닙니다. 따라서 lifecycleScope를 사용할 수 없습니다. ViewModel은 viewModelScope를 사용합니다. 두 컨텍스트에서 코드를 실행해야 하는 경우 suspend 함수를 사용하여 use case나 repository로 로직을 추출하세요.
repeatOnLifecycle을 호출할 때마다 새 코루틴이 생성되어 지정된 Lifecycle 상태에 도달하면 블록을 실행합니다. 동일한 상태에 대해 repeatOnLifecycle을 두 번 호출하면 두 블록이 독립적으로 실행됩니다. 일반적으로 onViewCreated에서 한 번 호출하면 충분합니다.
lifecycleScope의 디스패처를 직접 변경할 수 없습니다 — Dispatchers.Main.immediate를 사용합니다. 코루틴 블록 내에서 withContext를 통해 다른 디스패처로 전환할 수 있습니다. 테스트 시 LifecycleOwner와 함께 TestDispatcher를 사용하세요.
lifecycleScope는 LifecycleOwner가 DESTROYED 상태(onDestroy 후)로 전환될 때 취소됩니다. 단순한 lifecycleScope.launch 호출은 onPause나 onStop에서 취소되지 않습니다. 백그라운드로 이동할 때 일시 중지하려면 repeatOnLifecycle(STARTED) 또는 repeatOnLifecycle(RESUMED)를 사용하세요.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.