ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 4. Compose Side Effect
    안드로이드 학습/Compose 2025. 3. 27. 15:56

    SideEffect(부수효과)란?

    Compose에서 Side Effect(부수효과)는 Composable 함수 범위 밖에서 상태 변경이 필요하면 Effect API를 사용해야한다.

     

    Effect API에서의 작업은 UI와 관련되어 있어야 하며, Compose의 기본적인 단방향 흐름을 방해하면 안된다.

     

    Side Effect를 사용하지 않는 것이 좋지만 필요할 때가 있다.

    • 특정 상태 조건에 따라 다른화면으로 이동 (예: Splash 화면 > Main 화면 이동)
    • Toast 메시지 같은것을 표시
    • 이와 같은 일회성 이벤트

    Compose에서는 부수효과를 처리하기 위해 제공되는 3가지의 Effect API와 다양한 State 함수가 함께 제공되고 있습니다.

    • LaunchedEffect
    • DisposableEffect
    • SideEffect
    • rememberCoroutineScope
    • rememberUpdatedState
    • produceState
    • derivedStateOf
    • snapshotFlow

    LaunchedEffect

    • LaunchedEffect의 첫 번째 인자로 전달된 키 값이 변경될 때마다 내부 코드 블록이 실행됨.
    • 코툴틴을 사용하기 때문에 네트워크의 호출이나 데이터 로깅 같은 비동기 작업을 처리하는데 유용하다.
    • Composable이 제거될 때 자동으로 취소됨.
    @Composable
    fun LaunchedEffectExample() {
        var count by remember { mutableStateOf(0) }
        var message by remember { mutableStateOf("버튼을 눌러보세요!") }
    
        LaunchedEffect(count) {  // count가 변경될 때마다 실행됨
            delay(1000) // 1초 후 실행
            message = "Count: $count"
        }
    
        Column(
            modifier = Modifier.fillMaxSize(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(text = message, fontSize = 20.sp)
            Spacer(modifier = Modifier.height(16.dp))
            Button(onClick = { count++ }) {
                Text("증가")
            }
        }
    }

     

    LaunchedEffect에 Unit이 들어갈 경우 한번만 실행된다.

    @Composable
    fun LaunchedEffectOnceExample() {
        var message by remember { mutableStateOf("초기 메시지") }
    
        LaunchedEffect(Unit) { // Composable이 최초 실행될 때만 실행됨
            delay(2000)
            message = "2초 후 변경됨!"
        }
    
        Box(
            modifier = Modifier.fillMaxSize(),
            contentAlignment = Alignment.Center
        ) {
            Text(text = message, fontSize = 20.sp)
        }
    }

     

    SideEffect

    • Compose에서 recomposition이 발생할 때 마다 해야 하는 작업이 있을때 사용한다. 
    • 주로 로깅, 분석, 외부 상태 업데이트 등 UI에 직접적인 영향을 주지 않는 작업을 수행한다. 
    • Composable의 상태나 속성에 의지하지 않는 작업을 실행할 때 유용
    @Composable
    fun AnalyticsExample() {
        var userAction by remember { mutableStateOf("None") }
    
        SideEffect {
            sendAnalyticsEvent("UserAction: $userAction")
        }
    
        Column(
            modifier = Modifier.fillMaxSize(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(text = "Current Action: $userAction", fontSize = 20.sp)
            Spacer(modifier = Modifier.height(16.dp))
            Button(onClick = { userAction = "Button Clicked" }) {
                Text("버튼 클릭")
            }
        }
    }
    
    fun sendAnalyticsEvent(event: String) {
        Log.d("Analytics", "이벤트 전송: $event")
    }

     

     

    LaunchedEffect와 SideEffect의 차이

    • 비동기는 LaunchedEffect와 사용
    • SideEffect는 특정 key의 변경될 때만 실행, SideEffect는 key 상관없이 매번 수행.
    • LaunchedEffect는 작업이 중첩되면 이전 작업이 취소되고 새 작업이 수행된다.

     

    DisposableEffect의 사용법

    • DisposableEffect는 컴포저블 생명 주기에 맞춰 생성하고 소멸이 필요 한 특정 작업을 처리할때 필요하다.
    • 리소스를 할당하고 해제를 관리하는데 유용하다. 
    @Composable
    fun DisposableEffectExample(context: Context) {
        var isAirplaneModeOn by remember { mutableStateOf(false) }
    
        Column(
            modifier = Modifier.fillMaxWidth(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(
                text = if (isAirplaneModeOn) "비행기 모드: 켜짐" else "비행기 모드: 꺼짐",
                fontSize = 20.sp,
                fontWeight = FontWeight.Bold
            )
    
            DisposableEffect(context) {
                val receiver = object : BroadcastReceiver() {
                    override fun onReceive(context: Context?, intent: Intent?) {
                        if (intent?.action == Intent.ACTION_AIRPLANE_MODE_CHANGED) {
                            val state = intent.getBooleanExtra("state", false)
                            isAirplaneModeOn = state
                            Log.d("DisposableEffect", "Airplane mode changed: $state")
                        }
                    }
                }
                val filter = IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)
                context.registerReceiver(receiver, filter)
                Log.d("DisposableEffect", "Receiver registered")
    
                onDispose {
                    context.unregisterReceiver(receiver)
                    Log.d("DisposableEffect", "Receiver unregistered")
                }
            }
        }
    }
    

     

    BroadcastReceiver와 같은 것들인 해제가 필요하기 때문에 DisposableEffect와 함께 사용하는게 좋다.

     

     

     

    ... 다른 부분은 추후 더 학습 예정 ...

     

Designed by Tistory.