App/Android

[Android][Kotlin]7. JetPack Compose - ViewModel

앙두딘 2022. 9. 21. 16:53

Text와 Button을 세로로 정렬하여 UI를 구성했다.

버튼을 누르면 Text값이 바뀌는 화면을 만들어 볼 것이다.

setContent {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
    ){
        Text(
            "Hello",
            fontSize = 30.sp,
        )
        Button(onClick={
            
        }){
            Text("변경")
        }
    }
}

일반적인 방법

버튼을 눌렀을 때 Text의 값을 바꿔주려면 어떻게 해야 할지 생각을 해보면,

“Hello”부분을 state로 만들어줘야한다.

setContent {
    val data = remember{mutableStateOf("Hello")}
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
    ){
        Text(
            data.value,
            fontSize = 30.sp,
        )
        Button(onClick={
            data.value = "world"
        }){
            Text("변경")
        }
    }
}

state선언부에 remember를 해주지 않으면, 버튼을 눌러도 값이 변경되지 않는다.

  • 이유
    state의 상태가 변경되면, compose가 다시 빌드가 된다 (다시 **composition**이 일어난다.) 그랬을 때, 다시 “Hello”로 초기화되었던 코드가 실행되면서 바뀌지 않는 것처럼 보이는 것이다.

따라서 **remember를 추가해서, 이전의 값을 기억**해주는 것이다.

ViewModel 사용

ViewModel은 View와 생명주기가 분리되어있다는 특성때문에, remember를 사용해주지 않아도 된다.

class MainViewModel: ViewModel(){
    val data = mutableStateOf("Hello")
}

기존 안드로이드에서도 사용하던 뷰모델 사용법

⇒ 한 번 생성된 뷰모델을 계속해서 재사용할 수 있다.

class MainActivity : ComponentActivity() {
    private val viewModel by viewModels<MainViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Column(
                modifier = Modifier.fillMaxSize(),
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally,
            ){
                Text(
                    viewModel.data.value,
                    fontSize = 30.sp,
                )
                Button(onClick={
                    viewModel.data.value = "world"
                }){
                    Text("변경")
                }
            }
        }
    }
}

Compose에서의 ViewModel사용법

일단 gradle에 다음과 같이 추가해준다.

뷰모델을 간단하게 사용할 수 있도록 하는 라이브러리

//ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1"

compose로 setcontent에 ViewModel을 선언해주고, 동일하게 사용한다.

setContent {
    val viewModel = viewModel<MainViewModel>()
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
    ){
        Text(
            viewModel.data.value,
            fontSize = 30.sp,
        )
        Button(onClick={
            viewModel.data.value = "world"
        }){
            Text("변경")
        }
    }
}

ViewModel을 사용할 때 보통 다음과 같이 데이터를 외부에서 접근할 수 있게 하지 않는다.

class MainViewModel: ViewModel(){
    val data = mutableStateOf("Hello")
}

이렇게 구현하면 view쪽에서 viewmodel의 값을 바꾸는 일이 생길 수 있는데,

ex) viewmodel.data.value = "" 이런 식!

view에서 viewModel을 직접 바꾸는 일은 웬만하면 지양해야 한다. (메소드로 변경해야 함)

따라서 ViewModel을 다음과 같이 수정한다.

class MainViewModel: ViewModel(){
    private val _data = mutableStateOf("Hello")
    val data: State<String> = _data //읽기 전용으로 생성
}

mutable한 데이터를 private화하여 외부에서 접근할 수 없도록 하고,

State로 data를 선언하여 읽기 전용으로 만들었다.

그리고 data를 수정하기 위해 메소드를 선언해서 사용한다.

class MainViewModel: ViewModel(){
    private val _data = mutableStateOf("Hello")
    val data: State<String> = _data //읽기 전용으로 생성

    fun changValue(){
        _data.value = "World"
    }
}

value값을 직접 바꿔주는 대신 changeValue함수를 사용한다.

Button(onClick={
    viewModel.changValue()
}){
    Text("변경")
}

테스트해보면, 회전을 했을 때도 데이터가 초기화되지 않는다.