깨알 개념/Android

[Android] Jetpack Compose - ViewModel에서 State 사용하기

interfacer_han 2024. 7. 23. 02:30

#1 개요

Jetpack Compose에서 ViewModel을 사용해본다. Jetpack Compose를 사용하지 않는 전통적인 방식에서의 ViewModel과 크게 다를 게 없다. Jetpack Compose에 ViewModel을 구현함으로써 State Hoisting 패턴을 극대화시키는 효과도 확인해본다.

 

#2 코드

#2-1 MainActivity.kt

...

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Box(
                modifier = Modifier.fillMaxSize(),
            ) {
                val count = remember {
                    mutableStateOf(0)
                }

                ButtonExample(
                    count.value,
                    Modifier.align(Alignment.Center)
                ) { newValue ->
                    count.value = newValue + 1
                }
            }
        }
    }
}

@Composable
fun ButtonExample(
    currentCount: Int,
    modifierParam: Modifier = Modifier,
    updateCount: (Int) -> Unit,
) {
    Button(
        onClick = { updateCount(currentCount) }, // updateCount()는 ButtonExample()이 아닌, ButtonExample()를 호출한 상위 Composable에서 처리된다.
        modifier = modifierParam

    ) {
        Text(
            text = "Count: $currentCount",
            fontSize = 40.sp
        )
    }
}

State Hoisting 패턴을 구현한 간단한 코드다. 여기에 ViewModel을 추가해보겠다. ViewModel에 담을 데이터는 State와 그 State를 조작하는 이벤트(람다 함수)다.

 

#2-2 MainViewModel.kt 생성

// package com.example.stateinviewmodel

import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel

class MainViewModel : ViewModel() {
    val count = mutableStateOf(0)

    fun updateCount(newValue: Int) {
        count.value = newValue + 1
    }
}

State 및 State 조작 이벤트를 ViewModel에 담는다. State Hoisting에 대해 다룬 이 게시글에서는 하위 Composable에 있던 State를 상위 Composable로 옮기는 State Hoisting 패턴을 적용했었다. 본 게시글에선 Activity에 있던 State를 ViewModel로 옮기고 있다. 이 점에서 Jetpack Compose에서의 ViewModel 구현은 더욱 심화된 State Hoisting 패턴의 구현이라고 할 수 있다.

 

또, remember { ... }로 State를 감싸지 않는 것도 눈여겨 볼만 하다. ViewModel은 View가 아무리 파괴와 재생성을 반복해도 데이터를 잃지 않는다. 즉, Composable의 recomposition이나 Activity의 onCreate() 등에 영향받지 않는다. 따라서 remember { ... }나 rememberSaveable { ... }로 State를 감쌀 필요가 없는 것이다.

 

#2-3 모듈 수준 build.gradle에 라이브러리 추가

plugins {
    ...
}

android {
    ...
}

dependencies {

    ...

    // ViewModel (Compose)
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.3")
}

 

#2-4 MainActivity에 ViewModel 적용하기

...

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val viewModel = viewModel<MainViewModel>() // 간편한 ViewModel 객체 생성

            Box(
                modifier = Modifier.fillMaxSize(),
            ) {
                val count = viewModel.count

                ButtonExample(
                    count.value,
                    Modifier.align(Alignment.Center)
                ) { newValue ->
                    viewModel.updateCount(newValue)
                }
            }
        }
    }
}


@Composable
fun ButtonExample(
    ...
) {
    ...
}

다시 MainActivity로 돌아와서 ViewModel을 사용한다. 기본 전통적인 방식에서의 ViewModel과 당연하게도 똑같이 작동한다. 하지만 Jetpack Compose답게 코드는 훨씬 간편하다.

 

#3 요약

Jetpack Compose의 ViewModel은 State Hoisting 패턴을 극대화한다.

 

#4 완성된 앱

 

android-practice/jetpack-compose/StateInViewModel at master · Kanmanemone/android-practice

Contribute to Kanmanemone/android-practice development by creating an account on GitHub.

github.com