코딩하는 일용직 노동자

LaunchedEffect, DisposableEffect, LifecycleEventEffect, LifecycleStartEffect, LifecycleResumeEffect 정리 본문

안드로이드

LaunchedEffect, DisposableEffect, LifecycleEventEffect, LifecycleStartEffect, LifecycleResumeEffect 정리

bacass 2025. 8. 10. 13:43

안드로이드 Jetpack Compose에서 사용하는 주요 Effect Handler들에 대해 각각의 역할과 사용 사례를 중심으로 설명해 드릴게요.

이 Effect Handler들은 Composable 함수의 생명주기와는 별개로 실행되어야 하는 작업, 즉 \*\*Side Effect(부수 효과)\*\*를 안전하게 관리하기 위해 사용됩니다.
예를 들어 데이터베이스에 접근하거나, 네트워크 요청을 보내거나, 리스너를 등록하는 등의 작업이 여기에 해당합니다.


1. LaunchedEffect

`LaunchedEffect`는 Composable이 처음으로 화면에 그려질 때(Composition 될 때) **코루틴을 실행**하고 싶을 때 사용합니다. 가장 일반적으로 사용되는 Effect Handler 중 하나입니다.

* **핵심 개념**: Composable의 생명주기에 맞춰 시작되고, Composable이 화면에서 사라지면 자동으로 취소되는 코루틴 스코프를 제공합니다.
* **주요 사용 사례**:
* 화면 진입 시 **초기 데이터 로딩** (API 호출)
* **Snackbar나 Toast**를 한 번만 보여주고 싶을 때
* 특정 조건이 만족되었을 때 \*\*화면 이동(Navigation)\*\*을 하고 싶을 때
* **동작 방식**:
* `key`로 지정된 값이 변경될 때마다 기존 코루틴은 **취소**되고, 새로운 코루틴이 시작됩니다.
* `key`로 `true`를 사용하면 Composable이 처음 Composition 될 때 **단 한 번만** 실행됩니다.
@Composable
fun MyScreen(
    scaffoldState: ScaffoldState,
    shouldShowSnackbar: Boolean
) {
    // shouldShowSnackbar 값이 true로 변경될 때마다 스낵바를 보여줍니다.
    if (shouldShowSnackbar) {
        LaunchedEffect(key1 = scaffoldState.snackbarHostState) {
            scaffoldState.snackbarHostState.showSnackbar(
                message = "오류가 발생했습니다.",
                actionLabel = "확인"
            )
        }
    }
}​



2. DisposableEffect

`DisposableEffect`는 리소스를 할당하고, Composable이 화면에서 사라질 때 해당 \*\*리소스를 정리(clean-up)\*\*해야 하는 경우에 사용합니다.

* **핵심 개념**: `onDispose` 블록을 통해 정리 로직을 반드시 실행하도록 보장합니다.
* **주요 사용 사례**:
* `BroadcastReceiver`나 각종 콜백 **리스너를 등록하고 해제**할 때
* Android `LifecycleObserver`를 추가하고 제거할 때
* **동작 방식**:
* `DisposableEffect` 블록 안의 코드는 Composable이 Composition 될 때 실행됩니다.
* `onDispose` 블록은 Composable이 Composition에서 벗어나거나, `key`가 변경되어 Effect가 재시작될 때 호출됩니다.

@Composable
fun HomeScreen(
    onStart: () -> Unit,
    onStop: () -> Unit
) {
    val lifecycleOwner = LocalLifecycleOwner.current

    // lifecycleOwner가 변경되지 않는 한 한 번만 실행됩니다.
    DisposableEffect(key1 = lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_START) {
                onStart()
            } else if (event == Lifecycle.Event.ON_STOP) {
                onStop()
            }
        }

        lifecycleOwner.lifecycle.addObserver(observer)

        // Composable이 사라질 때 observer를 정리합니다.
        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }
}




아래의 Effect들은 Android의 `Lifecycle` 이벤트에 좀 더 특화되어 있어 코드를 간결하게 만들어 줍니다.

3. LifecycleEventEffect

`Lifecycle`의 **모든 이벤트**(`ON_CREATE`, `ON_START`, `ON_RESUME` 등)를 관찰하고, 특정 이벤트가 발생할 때마다 코드를 실행하고 싶을 때 사용합니다.

* **핵심 개념**: `when` 문과 함께 사용하여 각 라이프사이클 이벤트에 맞는 동작을 정의할 수 있습니다.

@Composable
fun AnalyticsLogger(user: User) {
    // ON_RESUME 이벤트가 발생할 때마다 로그를 전송합니다.
    LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
        Analytics.logScreenView("Profile", user.id)
    }
}

4. LifecycleStartEffect & LifecycleResumeEffect

이들은 각각 `ON_START` / `ON_STOP` 과 `ON_RESUME` / `ON_PAUSE` **쌍으로 동작하는 로직**을 처리하는 데 특화된 `DisposableEffect`입니다.

* **핵심 개념**:
* `LifecycleStartEffect`: `ON_START` 시점에 코드를 실행하고, `ON_STOP` 시점에 `onDispose` 블록을 실행합니다.
* `LifecycleResumeEffect`: `ON_RESUME` 시점에 코드를 실행하고, `ON_PAUSE` 시점에 `onDispose` 블록을 실행합니다.
* **주요 사용 사례**: `ON_RESUME`일 때 위치 정보 업데이트를 시작하고, `ON_PAUSE`일 때 중단하는 경우처럼 쌍으로 묶이는 작업에 유용합니다.

@Composable
fun LocationTrackingScreen() {
    // 화면이 사용자에게 보여질 때(ON_RESUME) 위치 추적을 시작하고,
    // 화면이 가려질 때(ON_PAUSE) 추적을 중지합니다.
    LifecycleResumeEffect(Unit) {
        val locationProvider = LocationProvider()
        locationProvider.start()

        onDispose {
        	locationProvider.stop()
        }
    }
}
 
 


대부분의 일회성 비동기 작업은 `LaunchedEffect`로 충분합니다.
리스너 등록/해제와 같은 명시적인 정리가 필요하면 `DisposableEffect`를, Android 라이프사이클과 더 밀접한 연동이 필요하면 `Lifecycle...Effect` 시리즈를 사용하면 됩니다.