@Composable
@NonRestartableComposable
@ExplicitGroupsComposable
@OptIn(InternalComposeApi::class)
fun SideEffect(
effect: () -> Unit
) {
currentComposer.recordSideEffect(effect)
}
Compose์์ SideEffect๋ ์ปดํฌ์ ๋ธ ํจ์์ ๋ฒ์๋ฅผ ๋ฒ์ด๋๋ ๋ชจ๋ ๋์์ ์๋ฏธํ๋ค. DB ์ ๋ฐ์ดํธ, api call, shared preferences, datatstore ๊ฐ์ UI ์์ ์ด ์๋ ๋์๋ค์ด ์ฌ๊ธฐ์ ํด๋น๋๋ค๊ณ ๋ณผ ์ ์๋ค. ๊ณต์๋ฌธ์์์ ๋ฒ์ญ์ ๋ถ์ํจ๊ณผ๋ผ๊ณ ํด๋จ๋๋ฐ ๊ทธ๋ฅ SideEffect๊ฐ ๋ ๋ซ๋ค.
๋์์๋ฆฌ์ ๋ํด ์์๋ณด๊ธฐ ์ ์, SideEffect๋ ์์ํจ์ ๊ฐ๋ ๊ณผ ๊ฐ์ด ๋์๋ค๋๋๋ฐ ์ด๊ฑธ ๋จผ์ ์ง๊ณ ๊ฐ์.
๊ฐ์ ์ ๋ ฅ์ ๋ํด ํญ์ ๊ฐ์ ์ถ๋ ฅ์ ์์ฑํ๊ณ SideEffect๊ฐ ์๋ ๊ฑธ ์์ํจ์๋ผ๊ณ ํ๋ค. ๊ทธ๊ฒ Composable์ธ๋ฐ, ์๋ ์ฝ๋๋ฅผ ๋ณด์.
@Composable
fun UserCard(user: User) { // ๊ฐ์ User ๊ฐ์ฒด์ ๋ํด ํญ์ ๋์ผํ UI๋ฅผ ์์ฑ
Column {
Text(user.name)
Text(user.email)
}
}
@Composable
fun UserCard(user: User) {
Column {
Text(user.name)
Text(user.email)
LaunchedEffect(Unit) {
database.logUserView(user.id) // SideEffect
}
}
}
UI ์์ ์ด์ธ์ ๋์์ด ๋งค ํธ์ถ๋ง๋ค ๋ค๋ฅธ ์ถ๋ ฅ์ ์์ฑํ๋ค. ๊ทธ๋์ ๋๋ฒ์งธ UserCard๋ ๋น์์ํจ์๋ผ๊ณ ํ ์ ์๊ฒ ๋ค. ๊ทผ๋ฐ ๋ฌด์กฐ๊ฑด ์ฌ์ฉํด์ผํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค. UI๋ ๋์ผํ๊ฑธ ์ถ๋ ฅํ์ง๋ง ์ง๊ธ ์ฝ๋์ฒ๋ผ logging์ด ํ์ํ๊ฑฐ๋ api call์ด ํ์ํ๊ฑฐ๋ ํ ๋๋ SideEffect๋ฅผ ์ ์ธ์๊ฐ ์๋๋ฐ, ์ด๊ฑธ Composition๊ณผ SideEffect์ ์คํ์ ๋ถ๋ฆฌํ๋ฉด ํด๊ฒฐ๋๋ค.
@Composable
fun MyScreen() {
var stateCnt by remember { mutableStateOf(0) }
Column {
Text("Count: $stateCnt")
Button(onClick = { stateCnt++ }) {
Text("์ฆ๊ฐ")
}
}
LaunchedEffect(stateCnt) {
println("์ฆ๊ฐํ์ด์ $stateCnt")
}
}
์ปดํฌ์ ๋ธ์ ์ฌ์ ํ ์์นํ์ง๋ง, UI๋ฅผ ๊ทธ๋ฆฌ๋ ๋ถ๋ถ์ ๋ถ๋ฆฌํ๋ค. ์ปดํฌ์ง์ ์ด ์๋ฃ๋ ๋ค์ SideEffect๊ฐ ํธ์ถ๋๋ ํจํด์ด๋ค.
์ด๋ฌ๋ฉด `LaunchedEffect`๋ก ๋ฑ๋ก๋์์ง๋ง, `stateCnt`๊ฐ ๋ณํํ ๋๋ง print๋ฌธ์ด ์ถ๋ ฅ๋๋ค. ์ด๋ฌ๋ฉด UI๋ ํญ์ ๊ฐ์ ์ ๋ ฅ์ ๋ํด ๊ฐ์ ์ถ๋ ฅ์ ๋ฐํํ๋ค. ์์ํจ์๋ฅผ ์งํจ ๊ฒ์ด๋ค.
์ด์ ๊ฒ์๊ธ์์ ์ผ๋ recomposition์ ๊ฒฐํฉํด๋ณด๋ฉด, stateCnt๊ฐ onClick์ผ๋ก ์ธํด ์ฆ๊ฐํ๊ณ , Text๊ฐ ๊ทธ์๋ฐ๋ผ recomposition๋๊ณ , ๊ทธ๋ฌ๊ณ ๋์ LaunchedEffect๊ฐ ํธ์ถ๋ผ์ print๊ฐ ์ถ๋ ฅ๋๋ค๋ ๊ฒ์ ์ ์ ์๋ค.
SideEffect ์ข ๋ฅ๋ ํฌ๊ฒ 3๊ฐ์ง๋ก ์ก์๋ณผ ์ ์๋ค.
- `SideEffect`: ๋งค ๋ฆฌ์ปดํฌ์ง์ ๋ง๋ค ์คํ๋์ด์ผ ํ๋ ๊ฐ๋จํ ๋๊ธฐ ์์
- `LaunchedEffect`: ๋น๋๊ธฐ ์์ ์ด๋ ์ฝ๋ฃจํด ๊ธฐ๋ฐ์ ์ฅ๊ธฐ ์คํ ์์
- `DisposableEffect`: ์ ๋ฆฌ๊ฐ ํ์ํ ๋ฆฌ์์ค ๊ด๋ฆฌ
SideEffect ๋ถํฐ ๋ณด์.
@Composable
@NonRestartableComposable
@ExplicitGroupsComposable
@OptIn(InternalComposeApi::class)
fun SideEffect(
effect: () -> Unit
) {
currentComposer.recordSideEffect(effect)
}
Composer์ effect๋ฅผ ๊ธฐ๋กํ๊ธฐ๋ง ํ๊ณ , ์ด effect๋ ์ปดํฌ์ง์ ์ด ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋ ํ์ ์คํ๋๋ค. ๋ชจ๋ ๋ฆฌ์ปดํฌ์ง์ ๋ง๋ค ์คํ๋๊ธฐ ๋๋ฌธ์, ๋ง์ recomposition์ด ๋ฐ์ํ ์ ์๋ SideEffect๋ LaunchedEffect๋ก ๊ด๋ฆฌํด์ผ๋๋ค.
LaunchedEffect๋ ๊ฐ์ฅ ๋ณต์กํ SideEffect๋ค.
internal class LaunchedEffectImpl(
parentCoroutineContext: CoroutineContext,
private val task: suspend CoroutineScope.() -> Unit
) : RememberObserver {
private val scope = CoroutineScope(parentCoroutineContext)
private var job: Job? = null
override fun onRemembered() {
// This should never happen but is left here for safety
job?.cancel("Old job was still running!")
job = scope.launch(block = task)
}
override fun onForgotten() {
job?.cancel(LeftCompositionCancellationException())
job = null
}
override fun onAbandoned() {
job?.cancel(LeftCompositionCancellationException())
job = null
}
}
@Composable
@NonRestartableComposable
@OptIn(InternalComposeApi::class)
fun LaunchedEffect(
key1: Any?,
block: suspend CoroutineScope.() -> Unit
) {
val applyContext = currentComposer.applyCoroutineContext
remember(key1) { LaunchedEffectImpl(applyContext, block) }
}
์ด๊ธฐํ๋๋ฉด, ๋ถ๋ชจ ์ฝ๋ฃจํด ์ปจํ ์คํธ๋ฅผ ๋ฐ์ ์๋ก์ด CoroutineScope๋ฅผ ๋ง๋ ๋ค. ์ปดํฌ์ง์ ์ ์ง์ ํ ๋๋ ์ด์ ์ ์คํ ์ค์ด๋ ์์ ์ด ์์๊ฒฝ์ฐ ์ทจ์ํด๋ฒ๋ฆฌ๊ณ ์๋ก์ด ์ฝ๋ฃจํด์ ์์ํ์ฌ ์ฃผ์ด์ง task๋ฅผ ์คํํ๋ค.
remember(key1)์์๋ key๊ฐ ๋ณ๊ฒฝ๋ ๋ ์๋ก์ด LaunchedEffectImpl ์ธ์คํด์ค๊ฐ ์์ฑ๋ฉ๋๋ค. ์ด๋ ์ด์ ์ธ์คํด์ค์ ์์ ์ ์ทจ์๋๊ณ , ์๋ก์ด ์์ ์ด ์์๋๋ค.
์์ SideEffect๋ ์ปดํฌ์ง์ ์ด ๋๋ ๋ค์ ์คํ๋๋ค๊ณ ํ๋๋ฐ, ์ปดํฌ์ง์ ์์ ์ ๊ฑฐ๋ ๋ LeftCompositionCancellationException์ ์ฌ์ฉํ์ฌ ์ฝ๋ฃจํด์ ์ทจ์ํ๊ฒ๋๋ค. ์ด ์์ธ๋ stacktrace๋ฅผ ์ต์ ํํ์ฌ ์ฑ๋ฅ์ ๊ฐ์ ํ๋ค๊ณ ํ๋ค.
LaunchedEffect๋ฅผ ์ฌ์ฉํ ๋, Key๋ฅผ ๊ผญ ์จ์ผํ๋ค.
private const val LaunchedEffectNoParamError =
"LaunchedEffect must provide one or more 'key' parameters..."
@Deprecated(LaunchedEffectNoParamError, level = DeprecationLevel.ERROR)
key ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ์ ํ๋๋ก ๋์๊ธฐ ๋๋ฌธ์ ์ด๊ฑธ ์ง์ผ์ค์ผํ๋ค. key๊ฐ ์์ผ๋ฉด ๋ฌดํ ๋ฆฌ์ปดํฌ์ง์ ์ด ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ด ์๊ธฐ ๋๋ฌธ์, ์ด์ ์ key์์ด ์ฐ๋๊ฑฐ๋ Unit์ key๋ก ๋ฃ์ผ๋ฉด ๋์ผํ ํจ๊ณผ๊ฐ ๋์ฌ ๊ฒ ๊ฐ๋ค.
DisposableEffect๋ LauncedEffect์ ์ ์ฌํ ๋ถ๋ถ์ด ์์ง๋ง, onDispose์์์ ํธ์ถ๋ผ์ผํ๋ค.
private class DisposableEffectImpl(
private val effect: DisposableEffectScope.() -> DisposableEffectResult
) : RememberObserver {
private var onDispose: DisposableEffectResult? = null
override fun onRemembered() { // ์ค์ ์คํ๋จ๊ณ
onDispose = InternalDisposableEffectScope.effect()
}
override fun onForgotten() {
onDispose?.dispose()
onDispose = null
}
override fun onAbandoned() {
// Nothing to do as [onRemembered] was not called.
}
}
@Composable
fun DisposableEffect(
key1: Any?,
effect: DisposableEffectScope.() -> DisposableEffectResult
) {
remember(key1) { DisposableEffectImpl(effect) }
}
effect๊ฐ DisposableEffectScope๋ก ๊ฐ์ธ์ ธ์๊ธฐ ๋๋ฌธ์, onDispose์์์ ์์ ์ ์ํํ๋ฉฐ onRemember์์ ์ปดํฌ์ง์ ์ ๋ค์ด์ฌ ๋ ํธ์ถ๋๋ค.
DisposableEffectScope๋ฅผ ์ข ๋ ๋ด์ผ๋๋ค.
class DisposableEffectScope {
/**
* Provide [onDisposeEffect] to the [DisposableEffect] to run when it leaves the composition
* or its key changes.
*/
inline fun onDispose(
crossinline onDisposeEffect: () -> Unit
): DisposableEffectResult = object : DisposableEffectResult {
override fun dispose() {
onDisposeEffect()
}
}
}
crossline ํค์๋๋ฅผ ์จ์, ๋๋ค๊ฐ ๋น์ง์ญ ๋ฆฌํด์ ์ฌ์ฉํ์ง ๋ชปํ๋๋ก ํด์ dispose ๋ก์ง์ ์์ ์ฑ์ ๋ณด์ฅํ๋ค.
crossline์ inline ํค์๋๋ ๊ฐ์ด ๋ณผ ๋ ์๋ฏธ๊ฐ ์๋๋ฐ, ์๋ ์ฝ๋๋ก ์ดํดํด๋ณด์.
inline fun unsafeOperation(callback: () -> Unit) {
thread {
callback() // ๋ค๋ฅธ ์ค๋ ๋์์ ๋น์ง์ญ ๋ฐํ์ ์ํ
// return ์ด ๊ฐ๋ฅํ๋ค
}
}
inline fun safeOperation(crossinline callback: () -> Unit) {
thread {
callback() // crossinline์ผ๋ก ๋น์ง์ญ ๋ฐํ์ด ๊ฐ์ ๊ธ์ง
}
}
inline ํจ์๋ ์ปดํ์ผ ์์ ์ ํจ์ ํธ์ถ ๋ถ๋ถ์ด ํจ์์ ๋ณธ๋ฌธ์ผ๋ก ๋์ฒด๋๋ ํจ์๋ค. ๊ทธ๋ฆฌ๊ณ ๋น์ง์ญ ๋ฐํ์ด๋ ๋๋ค๋ฅผ ํฌํจํ๋ ์ธ๋ถ ํจ์์์ ๋ฐํํ๋ ๊ฒ์ ์๋ฏธํ๋ค. ๊ทธ๋์ inlineํจ์๋ฅผ ์ฐ๋ฉด return ๊ฐ์ ํธ์ถ๋ถ์์ ๊บผ๋ผ ์ ์๊ฒ๋๋๋ฐ, crossline์ผ๋ก ์ด๊ฑธ ๋ง๋ ๊ฒ์ด๋ค.
๊ทธ๋์ onDispose ํจ์์์ return์ ์ฌ์ฉํ์ง ๋ชปํ๋ค.
# ๊ทธ๋ผ ์ด๋ป๊ฒ ์ฐ๋ ๊ฒ ์ข์๊น?
# > `SideEffect`์ ๊ฒฝ์ฐ
๋จผ์ , state๊ฐ ๋ณํ๋ ์ฝ๋๋ฅผ SideEffect์ ๋ฃ์ผ๋ฉด ์๋๋ค. recomposition์ด ๋ฐ์ํ ๋ ๋ง๋ค SideEffect๊ฐ ํธ์ถ๋๋๋ฐ state๊ฐ ๋ณํ๋ ์ฝ๋๊ฐ SideEffect ๋ด๋ถ์ ์์ผ๋ฉด ๋ฌดํ ๋ฆฌ์ปดํฌ์ง์ ์ด ๋ฐ์ํด๋ฒ๋ฆฐ๋ค.
@Composable
fun UserProfile(user: User) {
var lastSeenTime by remember { mutableStateOf<Long?>(null) }
SideEffect {
lastSeenTime = System.currentTimeMillis()
}
// ์ด๊ฒ ๋ง๋ ๋ฐฉ๋ฒ
var lastSeenTime by remember { mutableStateOf<Long?>(null) }
LaunchedEffect(Unit) {
lastSeenTime = System.currentTimeMillis()
}
}
๋ฐ๋ผ์ LauncedEffect๋ก ์ฒ์ Composition์ด ๋ ๋๋ง ์ ์ฉ๋๋๋ก ์ ์ดํ๊ฑฐ๋, ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์ด์ฉํด์ state๋ฅผ ๊ด๋ฆฌํด์ผ๋๋ค. ์ฌ๊ธฐ์ ๋ ํ์ฅ๋๋ ๊ฒ, ๋น๋๊ธฐ ์์ ๋ํ ๋ฃ์ด๋๋ฉด ์๋๋ค.
์ปดํฌ์ง์ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๋ฐ๋ผ๊ฐ๋ ๋ฐ, ๋น๋๊ธฐ ์์ ์ SideEffect์์ ์ํํ๋ค๋ฉด ์๋ช ์ฃผ๊ธฐ๊ฐ ๋ง์ง ์์ ์์ธ๊ฐ ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋ ๋ค๋ฉด ๋๊ธฐ์์ ๋ง ํด์ผ๋๋๋ฐ, ์ด ๋๊ธฐ์์ ์ด ๋๋ฌด ์ค๋ ๊ฑธ๋ฆฌ๋ฉด UI ์ ๋ฐ์ดํธ์ ์ํฅ์ ์ค ์๋ ์๋ค!
๊ทธ๋์ ์ ๋ฆฌํ์๋ฉด SideEffect๋ ๋๊ธฐ์์ , ๊ฐ๋ฒผ์ด ์์ , state๋ฅผ ์ง์ ๋ฐ๊พธ์ง ์๋ ์์ ๋ง ์ํํด์ผ๋๋ค.
# > `LaunchedEffect, DisposableEffect`์ ๊ฒฝ์ฐ
LaunchedEffect์ ๊ฒฝ์ฐ, ํค๊ฐ ๋ฌด์กฐ๊ฑด ์์ด์ผํ๋ค. ๊ทธ๋ฐ๋ฐ ํ์ํ ํค๋ง ์์ด์ผํ๋ค.
LaunchedEffect(userId) {
viewModel.loadUserData(userId)
}
๊ทธ๋ฆฌ๊ณ ๊ฐ์ฒด๋ฅผ ํค๋ก ์ฐ๊ธฐ๋ณด๋ค๋ ๊ทธ ๊ฐ์ฒด์ key๋ฅผ remember์ ๋ฃ์ด์ค key๋ก ์ฌ์ฉํ๋ ๊ฒ ๋ ์ต์ ํํ ์ ์๋ ๋ฐฉ๋ฒ์ด๋ค.
๋ํ ์๊น ๋ด๋ถ ๊ตฌ์กฐ๋ฅผ ๋ด์ ์๊ฒ ์ง๋ง, ์ด๋ฏธ ์ฝ๋ฃจํด ์ค์ฝํ๊ฐ ์๊ธฐ์ ์ค์ฝํ๋ฅผ ๋ ์ ์ธํด์ค ํ์๊ฐ ์๋ค.
val scope = rememberCoroutineScope()
LaunchedEffect(messages) {
scope.launch { // ๋ถํ์ํ ์ค์ฒฉ๋ ์ฝ๋ฃจํด
processMessages(messages)
}
}
// ๊ทธ๋ฅ ์ฐ๋ฉด๋๋ค.
LaunchedEffect(messages) {
processMessages(messages)
}
DisposableEffect๋ ์ ์ฌํ๋ค. ๋ค๋ง ๋ฆฌ์์ค ํด์ ๋ฅผ ์ฌ๊ธฐ์ ํด์ค ๋ ์ผ๋ถ๋ง ํ์ง๋ง๊ณ ํด์ผ๋ ๊ฑฐ ๋ค ํด์ ์์ผ์ผ ๋ฉ๋ชจ๋ฆฌ ๋์๊ฐ ์๋ค.
DisposableEffect(Unit) {
onDispose {
locationManager.stopTracking()
locationManager.releaseResources()
locationManager.unregisterListeners()
}
}
๊ทผ๋ฐ ํ๋ ์คํดํ ๋งํ ๋ถ๋ถ์ด ์๋ค.
DisposableEffect์ ์คํ์์ ์ ์ธ์ ์ผ๊น? ํ์ฌ Unit์ผ๋ก ๊ฑธ์ด๋ ๊ฑธ ๊ธฐ์ค์ผ๋ก ํ๋ค.
DisposableEffect(Unit) {
Log.d(TAG, "Greeting: outer")
onDispose {
Log.d(TAG, "Greeting: inner")
}
}
๋น์ฐํ outer๋ ๋จผ์ ์ฐํ๋ค. DisposableEffect๊ฐ Decomposition์์ ํธ์ถ๋๋ ๊ฒ ์๋๋ผ, onDispose ๋ด๋ถ ์์ ์ด Decomposition์ดํ์ ์ผ์ด๋๋ค. ์ด๊ฑธ ๊ตฌ๋ถํด์ผ๋๋ค.
๊ทธ๋์ ์๊น ๋ฆฌ์์ค ๊ด๋ จ ์ฝ๋๋ฅผ ์ข ๋ ์ ๋๋ก ์ฐ์๋ฉด ์๋์ ๊ฐ์ด ์ธ ์ ์๋ค.
DisposableEffect(Unit) {
val locationManager = getLocationManager()
locationManager.startTracking()
onDispose {
locationManager.stopTracking()
locationManager.releaseResources()
locationManager.unregisterListeners()
}
}
๋ฆฌ์์ค ์ฌ์ฉ ์์ ๋, ํด์ ์์ ๋ ๋ค DisposableEffect ๋ด๋ถ์ ๊ฑธ์ด๋ฌ์ ์๋์ผ๋ก ์ ๋ฆฌ๋๊ฒ ํ๋ ๋ฐฉ๋ฒ์ด๋ค.
DisposableEffect๋ Back Handler ์ธ ๋๋ ์ ์ฉํ๋ค.
@Composable
fun BackHandler(enabled: Boolean = true, onBack: () -> Unit) {
val currentContext = LocalContext.current
val dispatcher = (currentContext as? ComponentActivity)
?.onBackPressedDispatcher
?: return
DisposableEffect(dispatcher, enabled) {
val callback = object : OnBackPressedCallback(enabled) {
override fun handleOnBackPressed() {
onBack()
}
}
dispatcher.addCallback(callback)
onDispose {
callback.remove()
}
}
}
ํ์ฌ ์กํฐ๋นํฐ์ context๋ฅผ ๊ฐ์ ธ์์ dispatcher๋ฅผ ์ ์ดํ๊ธฐ ๋๋ฌธ์ callback์ ์ค์ ํ๊ณ ํด์ ํด์ฃผ๋ ์์ ์ด ํ์์ ์ด๋ค.
๊ทธ๋์ ์ ์ฝ๋๋ฅผ ๋ณด๋ฉด key๋ก enable, dispatcher ๋๊ฐ๋ฅผ ๋ฐ์ ์ค์ ํ๋๋ฐ ํธ๋ค๋ฌ๊ฐ enabled ๊ฐ์ ์ธ๋ถ์์ ๋ฐ์ on/off๋๋ ๊ฑฐ ํ๋๋ ์ธ๋ถ์์ ์์ฑํ dispatcher๋ฅผ ์ฃผ์ ๋ฐ์ ์ฌ์ฉํ๋ ๊ฑฐ ํ๋๋ค.
ํธ๋ฆฌ๊ฑฐ๋ก ์ฌ์ฉํ key๊ฐ ๊ทธ๋์ enabled ํ๋๊ณ , dispatcher๋ crossline ๋๋ฌธ์ DisposableEffect ๋ด๋ถ์์ ์์ฑํด์ ๋ฐํํ์ง ๋ชปํ๋๊น ์ฃผ์ ํ๋ ๋ฐฉ์์ผ๋ก ์ดํดํ๋ฉด๋๊ฒ ๋ค.
Effect ํ์ ๋ค์ ์ฌ์ฉ ์๋๋ฆฌ์ค๋ ์๋๋ก ์ดํดํ๋ฉด ๋๋ค.
sequenceDiagram
participant C as Composition
participant S as SideEffect
participant L as LaunchedEffect
participant D as DisposableEffect
C->>C: Composition ์์
C->>S: recordSideEffect
C->>L: remember(key)
C->>D: remember(key)
C->>C: Composition ์๋ฃ
C->>S: effect ์คํ
C->>L: onRemembered & launch
C->>D: onRemembered & effect
Note over C,D: ์ปดํฌ์ง์
์ข
๋ฃ/key ๋ณ๊ฒฝ ์
C->>L: onForgotten & cancel
C->>D: onForgotten & dispose
> "Composition ์์"
์ด๋ Compose runtime ์ด UI ํธ๋ฆฌ๋ฅผ ๊ตฌ์ฑํ๊ธฐ ์์ํ๋ค. ๋ชจ๋ @Composable ํจ์๋ค์ด ํธ์ถ๋๋ฉด์ ํ๋ฉด์ด ๊ทธ๋ ค์ง๋ค.
> "remember(key)"
LaunchedEffect์ DisposableEffect ๋ชจ๋ remember๋ฅผ ํตํด ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ ๊ฑธ ์๊น ์ฝ๋๋ก ์ดํด๋ดค๋ค. ์ด๋ key๊ฐ ๋ณ๊ฒฝ๋์ง ์๋ ํ ๊ฐ์ ์ธ์คํด์ค๊ฐ ์ฌ์ฌ์ฉ๋๋ค.
> "effect ์คํ"
SideEffect์ ๊ฒฝ์ฐ, Composition์ด ์๋ฃ๋ ํ recordSideEffect๋ก ๋ฑ๋กํ๋ SideEffect๊ฐ ์คํ๋๋ค.
๋๋จธ์ง ๋๊ฐ๋ onRemembered์์ ์คํ์ด ๋๋๋ฐ
LaunchedEffect์ ๊ฒฝ์ฐ, onRemembered๊ฐ ํธ์ถ๋๋ฉด์ ์ฝ๋ฃจํด์ด ์์๋๋ค. ์ด์ ์ ์คํ ์ค์ด๋ job์ด ์๋ค๋ฉด ์ทจ์๋๋ค.
DisposableEffect์ ๊ฒฝ์ฐ, onRemembered์์ effect๊ฐ ์คํ๋๊ณ DisposableEffectResult๊ฐ ์ ์ฅ๋๋ค.
> "์ปดํฌ์ง์
์ข
๋ฃ/key ๋ณ๊ฒฝ ์"
์ปดํฌ์ ๋ธ์ด ์ปดํฌ์ง์
์ ์ข
๋ฃํ ๋๋ key๊ฐ ๋ณ๊ฒฝ๋์ด ์๋ก์ด effect๊ฐ ํธ์ถ๋ ๋ ์คํ๋๋๋ฐ, DisposableEffect๋ ์ด๋ onDispose๋ฅผ ํ๋ค.
์ญ์ ์๋ช ์ฃผ๊ธฐ ๊ด๋ จ ๋ด์ฉ์ ์ด๋ ค์ด ๊ฒ ๊ฐ๋ค!
๋์์ด ๋๋ค๋ฉด ๋๊ธ์ด๋ ๊ณต๊ฐ ๋ฒํผ ํ ๋ฒ์ฉ ๋๋ฅด๊ณ ๊ฐ์ฃผ์ธ์!
'Android ๐ฅ๏ธ > Compose๐' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
@Immutable (0) | 2025.02.23 |
---|---|
Composition & Recomposition ๋์ ์๋ฆฌ (1) | 2024.12.14 |
Jetpack Compose ๊ธฐ์ด (0) | 2024.08.16 |