부수 효과 (Side Effect)
부수 효과는 Composable 함수의 범위 밖에서 일어난 앱의 상태(State) 변경을 처리하는 데 필요합니다.
일반적으로 Composable에는 부수 효과가 없는 것이 이상적이지만, 부수 효과가 필요한 경우들이 있습니다.
- 단일 실행 이벤트 (변경사항이 State로 관리될 필요가 없는 경우)
- 애니메이션 처리
- 외부 시스템과의 상호작용
- 컴포넌트 라이프사이클 관리
- 데이터 로드 및 네트워크 요청
이때, Effect API를 사용하여 부수 효과가 예측 가능하게 실행되도록 보장해야 합니다.
LaunchedEffect
LaunchedEffect는 컴포저블이 최초로 구성되는 Composition이 이루어질 때, Coroutine의 suspend 함수를 실행해야 할 경우 사용합니다.
이는 키(key) 값이 변경될 때마다 다시 실행됩니다.
주로 비동기 작업을 처리하거나 초기화 작업을 수행할 때 사용됩니다.
@Composable
fun LaunchedEffectExample() {
val snackbarHostState = remember { SnackbarHostState() }
LaunchedEffect(snackbarHostState) {
snackbarHostState.showSnackbar("Hello LaunchedEffect!")
}
Scaffold(
snackbarHost = {
SnackbarHost(hostState = snackbarHostState)
}
) {
...
}
}
DisposableEffect
DisposableEffect는 컴포저블이 최초로 구성될 때 한 번 실행되고, 컴포저블이 사라질 때 정리되어야 할 리스너가 있거나 처리해야 할 이벤트가 있는 경우에 사용됩니다.
이는 키(key) 값이 변경될 때마다 기존 효과를 정리하고 새로운 효과를 실행합니다.
주로 리소스를 할당하고 해제하는 작업에 사용됩니다.
@Composable
fun DisposableEffectExample(
lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current
) {
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_START -> {
Log.d("DisposableEffect", "ON_START")
}
Lifecycle.Event.ON_RESUME -> {
Log.d("DisposableEffect", "ON_RESUME")
}
Lifecycle.Event.ON_PAUSE -> {
Log.d("DisposableEffect", "ON_PAUSE")
}
Lifecycle.Event.ON_STOP -> {
Log.d("DisposableEffect", "ON_STOP")
}
else -> {
Log.d("DisposableEffect", "ELSE")
}
}
}
// 블록 내에서 옵저버 추가
lifecycleOwner.lifecycle.addObserver(observer)
// onDispose에서 옵저버 제거
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
}
SideEffect
SideEffect는 컴포저블이 Recomposition 될 때마다 실행되는 효과를 정의할 때 사용합니다.
주로 컴포저블 내에서 발생하는 상태 변화를 Compose 외부 시스템과 상호작용하는 데 사용됩니다.
@Composable
fun SideEffectExample() {
val counter = remember { mutableStateOf(0) }
SideEffect {
// Recomposition 될 때마다 호출됨
println("Counter value: $counter")
}
Column {
Button(onClick = { counter.value++ }) {
Text("증가")
}
// text 내용이 변경되면서 Recomposition 됨
Text("Counter : $counter")
}
}
rememberCoroutineScope
rememberCoroutineScope는 컴포저블 내에서 코루틴 스코프를 만들고 이를 사용하여 코루틴을 실행할 때 사용됩니다.
이는 컴포저블의 lifecycle 동안 살아있는 코루틴 스코프를 제공합니다.
@Composable
fun RememberCoroutineScopeExample() {
val coroutineScope = rememberCoroutineScope()
Button(onClick = {
coroutineScope.launch {
// 비동기 작업 실행
...
}
}) {
Text("클릭")
}
}
rememberUpdatedState
rememberUpdatedState는 Compose에서 상태 변화를 안전하게 기억하고 업데이트하는데 사용됩니다.
이는 컴포저블이 Recomposition 될 때 안전하게 최신 상태를 사용할 수 있습니다.
@Composable
fun RememberUpdatedStateExample(onTimeout: () -> Unit) {
val updatedOnTimeout by rememberUpdatedState(onTimeout)
LaunchedEffect(Unit) {
delay(1000)
updatedOnTimeout()
}
}
produceState
produceState는 Compose 내에서 상태를 생성하고 관리할 때 사용됩니다.
이는 주어진 값에서 상태를 생산하고 Recomposition을 유발합니다.
@Composable
fun ProduceStateExample(): State<Int> {
return produceState(initialValue = 0) {
while (true) {
delay(1000)
value += 1
}
}
}
derivedStateOf
derivedStateOf는 다른 상태로부터 파생된 상태를 생성할 때 사용됩니다.
이는 파생된 상태가 변경될 때만 Recomposition을 유발합니다.
@Composable
fun DerivedStateExample() {
val counter = remember { mutableStateOf(0) }
val isEven = remember { derivedStateOf { counter.value % 2 == 0 } }
Column {
Text(text = "Counter: ${counter.value}")
Text(text = "Is Even: ${isEven.value}")
Button(onClick = { counter.value++ }) {
Text("증가")
}
}
}
snapshotFlow
snapshotFlow는 Compose의 상태를 Flow로 변환할 때 사용됩니다.
이는 상태가 변경될 때 Flow에서 새로운 값을 방출합니다.
@Composable
fun SnapshotFlowExample() {
val counter = remember { mutableStateOf(0) }
LaunchedEffect(Unit) {
snapshotFlow { counter.value }
.collect { value ->
println("Counter value: $value")
}
}
Button(onClick = { counter.value++ }) {
Text("증가")
}
}
'안드로이드 > Compose' 카테고리의 다른 글
[Android] Compose 성능 최적화 (0) | 2024.06.11 |
---|---|
[Android] Compose - 아키텍처 (0) | 2024.06.05 |
[Android] Compose - Animation (0) | 2024.05.25 |
[Android] Compose - ConstraintLayout (0) | 2024.05.17 |
[Android] Compose - Lazy layout (0) | 2024.05.15 |