Unidirectional data flow

201·2022년 1월 23일
3

앱 아키텍쳐

목록 보기
4/4

최근에 Google Codelab에서 제공하는 Jetpack Compose 강의를 듣던 중 Unidirectinal data flow를 알게되었습니다. 이 개념이 viewmodel과 liveData를 사용하는 이유에 대해 잘 설명해주는 것 같아 정리해봅니다.

1. State

Android UI에서 State란 시간에 따라 변화가 가능한 값입니다. 예를 들자면 EditText의 텍스트는 유저가 입력할때마다 값이 바뀌기 때문에 State라고 볼 수 있습니다.

State를 이용하면 안드로이드의 UI Update 과정을 아래 그림처럼 생각할 수 있습니다.

2. Unstructured State

EditText를 통해 입력한 값을 다른 Text에 나타내기 위해서는 보통 콜백을 이용합니다.

코드랩에서 제공하는 예제를 살펴봅시다.

class HelloCodelabActivity : AppCompatActivity() {

   private lateinit var binding: ActivityHelloCodelabBinding
   var name = ""

   override fun onCreate(savedInstanceState: Bundle?) {
       /* ... */
       binding.textInput.doAfterTextChanged {text ->
           name = text.toString()
           updateHello()
       }
   }

   private fun updateHello() {
       binding.helloText.text = "Hello, $name"
   }
}

textInput.doAfterTextChanged에서 name에 값을 할당하고 text를 업데이트하는 updateHello()함수를 호출합니다.

이 코드에는 State, View, Event, State Updatre, View update가 모두 acitvity 클래스 안에 혼합되어 있습니다.

이러한 코드는 테스트하기 어려울 뿐더러 state(name 변수)의 변화가 자동적으로 view update를 발생시키지 않기 때문에 버그가 발생할 가능성이 높아집니다.

이러한 코드를 Unstructured State라고 부릅니다.

3. Unidirectional Data Flow

Unstructured State에서 벗어나기 위해서는 State를 View와 분리해야 합니다. 그래서 ViewModel을 사용하게 됩니다.

아래는 코드랩에서 제공하는 ViewModel, Activity 코드 입니다.

class HelloCodelabViewModel: ViewModel() {

   // LiveData holds state which is observed by the UI
   // (state flows down from ViewModel)
   private val _name = MutableLiveData("")
   val name: LiveData<String> = _name

   // onNameChanged is an event we're defining that the UI can invoke
   // (events flow up from UI)
   fun onNameChanged(newName: String) {
       _name.value = newName
   }
}

class HelloCodeLabActivityWithViewModel : AppCompatActivity() {
   private val helloViewModel by viewModels<HelloCodelabViewModel>()

   override fun onCreate(savedInstanceState: Bundle?) {
       /* ... */

       binding.textInput.doAfterTextChanged {
           helloViewModel.onNameChanged(it.toString()) 
       }

       helloViewModel.name.observe(this) { name ->
           binding.helloText.text = "Hello, $name"
       }
   }
}

State에 해당하는 name 변수를 Activity에서 ViewModel로 옮겼습니다.

그리고 activity는 viewModel에 event를 전달하고 State인 name의 변화를 observe패턴을 통해 받아옵니다.

  • event 전달 코드
binding.textInput.doAfterTextChanged {
           helloViewModel.onNameChanged(it.toString()) 
}
  • state 받는 코드
helloViewModel.name.observe(this) { name ->
           binding.helloText.text = "Hello, $name"
}

전체적인 flow를 그림으로 나타내면 아래와 같습니다.

State를 View와 분리하면서 각 컴포넌트를 테스트 하기 쉬워졌습니다. 또한 Observe패턴을 통해 activity에서 state를 받으면 바로 Ui update가 발생합니다.

profile
간단하게 개발하고 싶습니다

0개의 댓글