[안드로이드] State Hoisting, View Model

말랑돌·2025년 1월 28일

안드로이드 학습

목록 보기
3/17
post-thumbnail

State Hoisting (상태 호이스팅)

일종의 디자인 패턴 중 하나로

Stateful한 컴포저블을 Stateless한 컴포저블로 만들기 위한 방법이다.

Stateless하게 변경하는 이유

  • Stateful한 컴포저블은 재사용성이 떨어진다.
  • Stateless한 컴포저블은 상태를 외부에서 관리하기 때문에 재사용성이 높아진다.

상태 호이스팅 방법

예를 들어 설명하겠다.

  • 컴포저블A에서 Stateful한 컴포저블B를 호출한다.
  • 컴포저블B의 State 변수를 A로 이동시킨다.
  • 이 과정에서 컴포저블B는 Stateless하게 변경된다.

자식 컴포저블의 상태를 부모 컴포저블로 옮기는 것이 상태 호이스팅 방법이다.


예시 코드

@Composable
fun KotlinWorldScreen() {
    var text by rememberSaveable { mutableStateOf("") }
    TextFieldWithTitle(title = "Title", text = text, onTextValueChange = { text = it })
}

@Composable
fun TextFieldWithTitle(title: String, text: String, onTextValueChange: (String) -> Unit) {
    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = title,
            modifier = Modifier.padding(bottom = 12.dp),
            style = MaterialTheme.typography.h6
        )
        OutlinedTextField(
            value = text,
            onValueChange = onTextValueChange
        )
    }
}
출처:
https://kotlinworld.com/244 [조세영의 Kotlin World:티스토리]


View Model

뷰모델은 다음의 특징 덕분에 상태 변수를 더 잘 보관할 수 있다.

  • 뷰모델은 상태 호이스팅을 더 잘 구현하기 위한 방법이다.
  • 별도의 생명주기를 가진다.(어플 실행 ~ 어플 종료)

사용방법

  1. 뷰모델 의존성 추가
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
  1. 뷰모델 생성. 이때 뷰모델 내부에서는 remember을 가져서는 안된다.
class MainViewModel : ViewModel() {
    var count by mutableStateOf(0) //no remember

    fun updateCount(newValue: Int) {
        count.value = newValue + 1
    }
}
  1. 최대한 상위 컴포저블에서 뷰모델을 호출
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)
                }
            }
        }
    }
}
  1. (상태 호이스팅을 위한 방법) 컴포저블의 재사용을 위해서는 해당 컴포저블 내부에서 람다 구현이 아닌, 호출하는 컴포저블에서 람다를 건네주는 식으로 구현해햐한다.
@Composable
fun WellnessScreen(
    modifier: Modifier = Modifier,
    wellnessViewModel: WellnessViewModel = viewModel()
) {
   Column(modifier = modifier) {
       StatefulCounter()
 
       WellnessTasksList(
           list = wellnessViewModel.tasks,
           onCheckedTask = { task, checked ->
               wellnessViewModel.changeTaskChecked(task, checked) 
               //람다 구현을 넘겨줌으로써 하위 컴포저블을 Stateless하게 유지
           },
           onCloseTask = { task ->
               wellnessViewModel.remove(task)
           }
       )
   }
}

0개의 댓글