Beeeam

Android MVVM 패턴 본문

Android

Android MVVM 패턴

Beamjun 2023. 3. 26. 23:45

MVVM 패턴

앞에서 포스팅한 MVP 패턴은 View와 Presenter가 1:1 관계라서 View가 추가되면 Presenter도 추가 되야 하는 단점이 있었다. 이러한 단점을 보완한 패턴이 MVVM 패턴이다.

MVVM 패턴은 Model, View, ViewModel로 구성이 된다.

Model과 View는 기존의 패턴들과 동일하고, Presenter 대신에 ViewModel이 사용 된다.

MVVM 패턴도 View가 입력을 받고 해당 요청을 ViewModel로 전달한다. 그러면 그에 맞는 데이터를 Model로 부터 받아온다. 받아온 데이터는 ViewModel에 Livedata에 저장을 한다.

View는 ViewModel에 저장된 Livedata를 관찰하다가 데이터가 변경되면 이를 감지하고, UI를 갱신한다.

MVP와 차이점

Presenter 대신에 ViewModel이 사용된다는 큰 차이점이 있다.

ViewModel?

view와 model의 사이에 있는 요소로 이름의 뜻 그대로 view를 위한 model이다.

view가 관찰(observe) 하고 있기 때문에 관찰 가능한 형태인 Livedata를 사용 해야 한다.

장점

  • View와 ViewModel이 1:n 연결되기 때문에 MVP패턴 처럼 Presenter를 많이 생성하지 않아도 된다.
  • 데이터를 ViewModel을 통해서 참조하기 때문에 Activity / Fragment의 생명주기에 영향을 받지 않는다. (데이터를 ViewModel이 가지고 있기 때문)

단점

  • 규모가 작은 프로젝트에서는 굳이 적용을 안해도 될 듯
  • 구조가 복잡해서 진입 장벽이 높다.

적용 예시

Model (Data.kt)

data class Data(
    val usrName: String? = null,
    val usrPassword: String? = null
) {
    fun login(usrName: String?, usrPassword: String?): String {
        return if (usrName == "abc" && usrPassword == "1234") {
            "성공..!"
        } else {
            "실패..."
        }
    }
}

View (MainActivity.kt)

class MainActivity : AppCompatActivity() {

    private val viewModel: MainViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val edtId = findViewById<EditText>(R.id.edt_id)
        val edtPassword = findViewById<EditText>(R.id.edt_password)
        val btnLogin = findViewById<Button>(R.id.btn_login)
        val tvResult = findViewById<TextView>(R.id.tv_result)

        btnLogin.setOnClickListener {
            viewModel.loadResult(edtId.text.toString(), edtPassword.text.toString())
        }

        viewModel.loginStatus.observe(this) {
            tvResult.text = it.toString()
        }
    }
}

ViewModel (MainViewModel.kt)

class MainViewModel: ViewModel() {

    private val data = Data()

    private val _loginStatus = MutableLiveData<String>()
    val loginStatus: LiveData<String> = _loginStatus

    fun loadResult(usrId: String, usrPasswd: String) {
        if (usrId != null && usrPasswd != null) {
            val res: String = data.login(usrId, usrPasswd)
            res?.let { res ->
                _loginStatus.value = res
            }
        }
    }
}

ViewModel의 데이터는 View가 관찰할 수 있게 하기 위해서 Livedata를 사용해야 하는데 Livedata는 immutable 속성이라서 MutableLiveData에 model로 부터 받아온 데이터를 저장하고, 이를 Livedata에 저장한다.