반응형
Jetpack Compose에서 SideEffect는 컴포저블이 리컴포지션될 때마다 실행되는 코드 블록을 정의하는 데 사용됩니다. 이는 주로 Compose의 상태 관리와 관계없는 작업(예: 로그 출력, 외부 상태 업데이트 등)을 수행할 때 유용합니다.
📌 SideEffect의 동작 방식
- SideEffect는 컴포지션 단계에서 실행됩니다.
- 컴포저블이 리컴포지션될 때마다 호출됩니다.
- Compose 내부에서 상태를 변경하지 않는 부수 효과(Side Effect)를 안전하게 실행할 수 있도록 도와줍니다.
🔍 SideEffect를 사용하는 이유
- 컴포저블의 리컴포지션 확인
- SideEffect를 활용하여 특정 컴포저블이 얼마나 자주 리컴포지션되는지 확인할 수 있습니다.
- Compose 상태와 관계없는 외부 상태 업데이트
- 예를 들어, 네트워크 호출을 트리거하거나, 로그를 남기거나, 특정 이벤트를 추적할 때 사용할 수 있습니다.
- Compose 내부에서 UI 상태를 직접 변경하지 않고 외부 이벤트를 처리
- SideEffect는 UI 상태를 변경하지 않고 외부 시스템과의 연계를 쉽게 해줍니다.
🛠 SideEffect 사용 예제
1️⃣ 리컴포지션 횟수 추적하기
@Composable
fun MyComposable() {
val recompositionCount = remember { mutableStateOf(0) }
SideEffect {
recompositionCount.value++
Log.d("Recomposition", "MyComposable recomposed ${recompositionCount.value} times")
}
Text("리컴포지션 횟수: ${recompositionCount.value}")
}
📌 설명:
- SideEffect 블록 안에서 recompositionCount.value++을 실행하여 컴포저블이 리컴포지션될 때마다 카운트를 증가시킵니다.
- Log.d(...)를 사용하여 리컴포지션 횟수를 확인할 수 있습니다.
- 이 코드 실행 시, 상태가 변경될 때마다 로그가 출력됩니다.
2️⃣ 외부 API 호출 (로깅 시스템과 연동)
@Composable
fun MyComposable(userName: String) {
SideEffect {
Log.d("UserTracking", "User viewed: $userName")
}
Text(text = "Hello, $userName!")
}
📌 설명:
- 이 예제에서는 SideEffect를 사용하여 컴포저블이 리컴포지션될 때마다 로그를 남김으로써 사용자의 화면 방문을 추적할 수 있습니다.
- userName이 변경될 때마다 SideEffect 내부의 코드가 실행됩니다.
3️⃣ 외부 객체 업데이트 (예: ViewModel 상태 변경)
@Composable
fun MyComposable(viewModel: MyViewModel) {
val counter = remember { mutableStateOf(0) }
Button(onClick = { counter.value++ }) {
Text("Click me: ${counter.value}")
}
SideEffect {
viewModel.updateCounter(counter.value)
}
}
📌 설명:
- SideEffect를 이용해 뷰모델(ViewModel)의 상태를 업데이트합니다.
- 버튼 클릭 시 counter.value가 증가하며, 그 값이 SideEffect 내부에서 viewModel.updateCounter()로 전달됩니다.
- 이 방법을 사용하면 Compose의 상태 변화가 외부 객체에 올바르게 반영됨을 보장할 수 있습니다.
🚨 SideEffect 사용 시 주의할 점
- Compose의 상태를 변경하면 안 됨
- SideEffect 내부에서는 Compose 상태를 직접 변경하면 안 됩니다.
- Compose 상태는 remember, mutableStateOf 등을 활용해야 하며, SideEffect는 외부 시스템과의 연동을 위한 역할을 수행해야 합니다.
- 리컴포지션이 잦다면 불필요한 SideEffect 호출을 피해야 함
- SideEffect는 리컴포지션마다 실행되므로, 빈번한 리컴포지션이 발생할 경우 불필요한 API 호출이나 이벤트 발생이 일어날 수 있습니다.
- 이 문제를 해결하려면 불필요한 리컴포지션을 최소화하는 최적화 작업이 필요합니다.
- 예를 들어, remember나 derivedStateOf를 활용하여 리컴포지션을 줄일 수 있습니다.
🎯 SideEffect vs LaunchedEffect 차이점
기능 SideEffect LaunchedEffect
실행 시점 | 매 리컴포지션마다 실행 | 처음 Composition이 생성될 때 실행 |
코루틴 사용 여부 | ❌ 불가능 (단순 블록 실행) | ✅ 가능 (Suspend 함수 실행 가능) |
주 사용 목적 | 상태 변경 없이 외부에 영향 주기 (로그, 이벤트 등) | 비동기 작업 실행 (네트워크, 데이터 로딩 등) |
예제 코드 비교
✅ SideEffect: 매 리컴포지션마다 실행됨
@Composable
fun MyComposable() {
SideEffect {
Log.d("Debug", "Recomposition 발생!")
}
}
✅ LaunchedEffect: 처음 1회만 실행됨
@Composable
fun MyComposable() {
LaunchedEffect(Unit) {
delay(1000)
Log.d("Debug", "1초 후 실행됨")
}
}
📌 정리:
- SideEffect는 리컴포지션이 발생할 때마다 실행되므로, 외부 상태를 업데이트하는 용도로 적합합니다.
- 비동기 작업을 실행해야 한다면 LaunchedEffect를 사용하는 것이 적절합니다.
🎯 결론
- SideEffect는 컴포저블이 리컴포지션될 때마다 실행되는 코드 블록을 정의하는 데 사용됩니다.
- 외부 API 호출, 로깅, 뷰모델 업데이트 등 Compose 내부 상태와 관계없는 작업을 수행할 때 적합합니다.
- LaunchedEffect와는 달리 코루틴을 사용할 수 없으며, 리컴포지션마다 실행되므로 필요하지 않은 경우 사용을 피해야 합니다.
- 불필요한 리컴포지션을 방지하는 것이 성능 최적화의 핵심입니다.
📝 추가로 보면 좋은 문서
반응형