Q15) Side Effects
컴포저블 함수 내에서 Side Effects는 어떻게 처리하나요?
Jetpack Compose는 컴포저블의 리컴포지션 스코프 외부에서 작업을 처리할 수 있는 여러 Side Effects Handler API를 제공합니다. 이러한 API는 Android 프레임워크와의 상호 작용, 컴포지션 이벤트 관리, 상태 변화에 기반한 효과 트리거와 같은 시나리오를 처리하는 데 필수적입니다.
세 가지 주요 사이드 이펙트 핸들러 API인 LaunchedEffect, DisposableEffect, SideEffect에 대해 알아보겠습니다.
1. LaunchedEffect: 컴포저블 스코프 내에서 suspend functions 실행
LaunchedEffect는 컴포저블의 컴포지션 내에서 실행되는 Coroutine을 시작하는 데 사용됩니다. LaunchedEffect에 전달된 키가 변경되면 이 Coroutine은 취소되고 다시 시작됩니다. 이는 데이터 가져오기, 애니메이션 시작 또는 컴포저블이 컴포지션에 진입할 때 발생해야 하는 이벤트를 수신하는 등의 작업에 유용합니다.
주요 특징:
컴포저블이 컴포지션에 진입할 때 한 번 실행됩니다.
키가 변경되면 자동으로 취소되고 다시 시작됩니다.
컴포지션 인식: 컴포저블이 컴포지션을 떠날 때 자동으로 취소됩니다. 예를 들어, 사용자가 목록의 항목을 클릭할 때 네트워크에서 추가 데이터를 가져와야 하는 경우, LaunchedEffect를 사용하여 이를 원활하게 처리할 수 있습니다. 이는 LaunchedEffect에 제공된 키 중 하나가 변경될 때마다 작업이 다시 실행될 수 있도록 보장합니다.
또한 LaunchedEffect를 사용하여 Flow를 안전하게 관찰할 수 있습니다. LaunchedEffect에 의해 시작된 Coroutine은 컴포저블 함수가 컴포지션을 떠날 때 자동으로 취소되어 불필요한 리소스 사용을 방지합니다. 또한, Coroutine은 리컴포지션 중에 다시 시작되지 않습니다. Effects가 호출 지점의 생명주기와 일치하도록 하려면 Unit 또는 true와 같은 상수를 키 매개변수로 전달할 수 있습니다. 이는 키가 변경되지 않는 한 효과가 한 번만 실행되도록 보장합니다.
2. DisposableEffect: 정리가 필요한 effects
DisposableEffect는 컴포저블의 컴포지션에 바인딩된 리소스 또는 정리 작업을 관리하는 데 사용됩니다. LaunchedEffect와 달리, 컴포저블이 컴포지션을 떠날 때 리소스를 해제하기 위한 onDispose 람다가 포함된 DisposableEffectScope를 제공합니다.
주요 특징:
리스너, 옵저버 또는 구독과 같은 리소스 관리에 이상적입니다.
onDispose콜백으로 적절한 정리를 보장합니다. 예를 들어, 생명주기 이벤트에 기반하여 분석 이벤트를 전송해야 하는 경우,LifecycleObserver를 사용하여 이를 달성할 수 있습니다. DisposableEffect를 사용하여 컴포저블이 컴포지션에 진입할 때 옵저버를 등록하고, 컴포저블이 컴포지션을 떠날 때 자동으로 등록 해제할 수 있습니다. 이는 적절한 정리를 보장하고 메모리 누수를 방지합니다.
3. SideEffect: Compose 상태를 비 Compose 코드에 발행
SideEffect는 모든 리컴포지션 직후 적용되어야 하는 작업을 실행하는 데 사용됩니다. 이는 컴포저블이 리컴포지션된 후에 실행을 보장하므로, ViewModel 또는 외부 라이브러리에서 UI 상태를 업데이트하는 것과 같이 컴포지션의 일부가 아닌 외부 시스템과 Compose 상태를 동기화하는 데 적합합니다.
주요 특징:
모든 리컴포지션 후에 실행됩니다.
비 Compose 컴포넌트와의 상태 동기화에 유용합니다.
예를 들어, 분석 라이브러리가 사용자 집단을 커스텀 메타데이터(예: "user properties")를 후속 분석 이벤트에 첨부하여 분류하는 것을 지원하는 경우, SideEffect를 사용하여 현재 사용자의 사용자 유형이 원활하게 업데이트되도록 할 수 있습니다. 이 접근 방식은 라이브러리의 상태가 Compose 애플리케이션의 현재 상태와 동기화된 상태로 유지되도록 보장합니다.
SideEffect 사용의 또 다른 예는 아래 예시와 같이 리컴포지션이 완료된 후에만 Lottie 애니메이션을 시작하거나 비 컴포저블 액션을 트리거해야 할 때입니다.
Summary
각 사이드 이펙트 핸들러 API는 고유한 목적을 가집니다.
LaunchedEffect는
Coroutine기반 작업을 시작하거나 키 매개변수 변경에 따라 작업을 다시 시작하는 데 사용합니다.DisposableEffect는 컴포저블의 컴포지션에 연결된 리소스를 관리하고 정리하는 데 사용합니다.
SideEffect는 모든 리컴포지션 직후 적용되어야 하는 작업을 실행하고 외부 시스템을 Compose 상태와 동기화하는 데 사용합니다. 이러한 사이드 이펙트 핸들러 API를 언제 어떻게 사용해야 하는지 이해하면 깔끔하고 선언적인 접근 방식을 유지하면서 Side Effects를 효과적으로 관리하는 데 도움이 될 수 있습니다. 세 가지 주요 사이드 이펙트 핸들링 API의 내부 동작과 작동 방식에 대한 더 깊은 이해를 위해서는 Understanding the Internals of Side-Effect Handlers in Jetpack Compose를 확인하십시오.
- Q) LaunchedEffect는 컴포저블에서 suspend functions를 관리하는 데 어떻게 도움이 되며, 키가 변경되면 어떤 일이 발생하나요?
LaunchedEffect는 컴포저블의 생명주기에 연결된 코루틴 스코프를 제공하여, suspend 함수를 안전하게 실행할 수 있게 해줍니다. key가 변경되면, LaunchedEffect는 기존의 실행 중이던 코루틴을 즉시 취소하고, 새로운 key 값으로 새 코루틴을 시작합니다.
- Q) LaunchedEffect 대신 DisposableEffect를 언제 사용하시겠습니까?
DisposableEffect는 명시적인
onDispose정리 블록이 필요할 때 사용합니다. LaunchedEffect도 코루틴을 취소하는 정리 작업을 하지만, DisposableEffect는 코루틴이 아닌 리스너, 옵저버, 콜백 등을 등록하고, Composable이 사라질 때 이를 수동으로 해제해야 하는 경우에 특화되어 있습니다.예를 들어 LifecyclerObserver 등록 및 해제가 있습니다. onResume, onPause같은 화면 생명주기 이벤트를 받아야할 때, 옵저버를 등록해주고 명시적으로 해제할 수 있습니다.
- Q) SideEffect의 사용 사례와 LaunchedEffect와의 차이점을 설명해 주세요.
SideEffect는 매 리컴포지션이 성공적으로 완료될 때마다 suspend 함수가 아닌 일반 코드를 실행해야 할 때 사용합니다. LaunchedEffect와의 가장 큰 차이점은 실행 시점과 코루틴 스코프의 유무입니다.
간단히 말해, SideEffect는 Compose의 State를 Compose가 아닌 외부 세계와 동기화하기 위한 매우 드물게 사용되는 Effect 입니다.
가장 대표적인 예: 외부 애널리틱스 라이브러리와 연동
Composable 본문에 직접 작성하면 리컴포지션이 취소되어도, 실행될 수 있어 로그가 부정확하게 쌓이게 됩니다.