일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- NavController
- 클린아키텍처
- cleanarchitecture
- 코딩테스트
- 로그인
- 뷰
- Flow
- 커스텀뷰
- DiffUtil
- sharedFlow
- XML
- 회원가입
- coroutine
- Authentication
- 파이어베이스
- 리사이클러뷰
- Android
- ListAdapter
- MVVM
- Build variants
- 컴포즈
- 안드로이드
- UiState
- NavHost
- 알고리즘
- 코틀린
- Compose
- Jetpack
- Kotlin
- 플레이스토어
- Today
- Total
Grusie 안드로이드 개발 기술 블로그
[Android] 컴포즈 사이드 이펙트 - SideEffect, LaunchedEffect, DisposableEffect 본문
[Android] 컴포즈 사이드 이펙트 - SideEffect, LaunchedEffect, DisposableEffect
grusie 2024. 3. 18. 22:04JetPack의 Compose를 활용하여 UI 개발을 하던 중 사용하는 사이드 이펙트들이 궁금해서 공부 해보려고 한다.
사이드 이펙트란?
- 사이드 이펙드는 UI에서 발생하는 Effect들을 효율적으로 관리 할 수 있는 함수이다.
- JetPack Compose에서 사이드 이펙트의 목적은 제어되고 예측 가능한 방식으로 컴포저블 함수 밖에서 앱의 상태를 변경하는 non-UI 관련 작업을 실행 할 수 있도록 하는 것이다.
- 데이터를 업데이트 하거나, 네트워크 요청을 하는 등의 효과들은 UI 랜더링과 별도로 유지하여 코드의 성능과 유지보수성을 향상시켜야 한다.
JetPack Compose는 UI 랜더링 로직에서 사이드이펙트를 분리하고 별도의 코루틴 스코프에서 실행함으로써 개발자가 효과적으로 부수효과를 관리 할 수 있는 SideEffect, LaunchedEffect, DisposableEffect와 같은 여러 컴포저블 함수를 제공한다.
사이드 이펙트 사용의 주요 장점
- 향상된 성능 : 컴포저블 함수 이외의 non-UI 관련 작업을 실행함으로써 UI 랜더링 로직은 응답과 성능을 유지할 수 있다.
- 코드 가독성 향상 : UI 랜더링 로직에서 non-UI 관련 작업을 분리함으로서, 코드베이스를 이해하고 유지하기 쉬워진다.
- 더 나은 디버깅 : 로깅 및 분석 작업에 부수효과를 사용할 수 있으므로 개발자가 앱의 동작을 더 잘 이해하고 문제를 식별하는 데 도움이 될 수 있다.
SideEffect
- SideEffect은 컴포저블의 상위 컴포저블을 재구성할 때 부수효과를 실행할 수 잇는 컴포저블 함수이다. 보통 로깅, 분석, 외부 상태 업데이트 등 UI에 직접적인 영향을 주지 않는 작업을 수행한다. 이 함수는 컴포저블의 상태나 속성에 의존하지 않는 작업을 실행 할 때 유용하다.
SideEffect의 사용법
@Composable
fun HomeScreen(user: User) {
val count by remember { mutableStateOf(0) }
// composition이 성공할 때마다 호출
SideEffect {
analytics.setUserProperty("userType", user.userType)
}
Button(onClick = {count++}){
Text(text = "count 증가")
}
Text(text = "$count")
}
- recomposition 될 때 마다 실행 된다.
=> 버튼을 누를 때 마다, count가 증가되기에, 최하단의 텍스트뷰가 들어있는 HomeScreen 자체가 recomposition 된다.
LaunchedEffect의 사용법
@Composable
fun HOMEScreen() {
Text(text = "text1")
LaunchedEffect(key1 = Unit) {
delay(2000)
Toast.makeToast(context, "Toast MSG", Toast.LENGTH_SHORT).show()
}
}
- composition 진입 및 key가 업데이트 될 때 실행된다.
- key가 변경될 때마다 다시 실행
- 코루틴 scope 지원
- 위의 예제에서는 key가 Unit이므로, Toast가 한 번만 표시된다.
@Composable
fun HomeScreen() {
val list = remember { mutableStateListOf(1, 2, 3, 4, 5) }
LaunchedEffect(key1 = list.size) {
println("${list.size}")
if (list.isEmpty()) {
println("리스트가 비어있음")
}
}
Button(onClick = { list.removeLast() }) {
Text("삭제")
}
}
- 1회성 이벤트를 호출할 때
- 상태 변경 시 non-composable 코드 호출
DisposableEffect의 사용법
@Composable
fun BroadcastReceiver(intentFilter: IntentFilter, onReceive: (Intent) -> Unit) {
val context = LocalContext.current
DisposableEffect(key1 = context) {
val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
onReceive(intent)
}
}
context.registerReceiver(broadcastReceiver, intentFilter)
onDispose {
context.unregisterReceiver(broadcastReceiver)
}
}
}
- LaunchedEffect와 동일하게 동작하나, clean-up 기능이 있다.
- 코루틴 scope를 지원하지 않는다.
- onDispose
- key가 변경되거나 Composable이 composition을 벗어나는 즉시 호출
- 리소스 해제 관련 로직 수행
컴포저블 함수에서 코루틴 scope를 사용하는 방법
@Composable
fun ShowSnackbar() {
Box(Modifier.fillMaxSize()) {
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
Button(onClick = {
scope.launch {
val result = snackbarHostState.showSnackbar(
"Message deleted", actionLabel = "Undo"
)
when (result) {
SnackbarResult.Dismissed -> {
// nothing to do
}
SnackbarResult.ActionPerformed -> {
// TODO perform undo
}
}
}
}
) {
Text("Delete")
}
SnackbarHost(
hostState = snackbarHostState,
modifier = Modifier.align(Alignment.BottomCenter),
)
}
}
- rememberCoroutineScope를 활용하여, 코루틴을 시작하는 데 사용할 수 있다.
- SideEffect Composable 또는 콜백 외부에서 코루틴을 실행하고 싶지 않을 경우에 사용한다.
- non-composable에서 코루틴을 시작하면, 정의되지 않은 순간에 코루틴을 시작할 위험이 존재한다.
참고
https://velog.io/@beokbeok/Compose-SideEffects%EC%9D%98-%EB%AA%A8%EB%93%A0-%EA%B2%83
https://developer.android.com/jetpack/compose/side-effects?hl=ko
후기
사이드 이펙트를 사용하면서, 세 개의 차이점이 무엇인지 몰랐고, 대충 이름만 들었을 때에는 Launched니까 실행할 때, Dispose니까 끝날때인가? 이런생각을 하였는데, 찾아보니 사용할 곳이 많을 것 같았다.
예를 들어 현재 진행중인 회원가입 부분에서, 임의로 회원가입을 진행시켰다가 삭제하는데, 사용자가 중간에 나갔을 때, 어떻게 감지하고 삭제해야 하는가에 대한 생각을 하였으나 DisposableEffect를 사용해서 onDispose에서 처리하는 형태로 하면 어떨까 싶었다.
하지만 아마 강제종료 시엔 작동하지 않을 것 같아 걱정이다. 그렇게 된다면 아마, 라이프사이클을 감지해서 onStop()에서 처리를 하지 않을까 싶다.
'안드로이드 개발 > 컴포즈' 카테고리의 다른 글
[Android] 안드로이드 컴포즈 Snackbar 사용하기 (0) | 2024.04.02 |
---|