[안드로이드] MVVM 패턴이란?

동현·2023년 1월 23일
0
post-thumbnail

안드로이드 개발을 하다보면 MVVM 패턴이라는 용어를 자주 들을 수 있다. MVVM 패턴이 뭐고 이것이 왜 필요할까?

1. MVVM 패턴이 필요한 이유

예를 들어 메모장 앱을 개발했고 동작에 필요한 모든 코드를 Activity에 때려박았다고 생각해보자. 이 때 사용자로부터 한가지 요청을 받는다.

👩 : 민감한 메모에 비밀번호도 설정할 수 있게 해주세요

요청을 받고 코드를 수정하다보면 이런 생각이 든다.
아니... 이거 추가하다보니 이것도 고쳐야 되고... 이건 어떻게 수정해야 될지 모르겠고...

위의 방식으로는 코드간의 결합도가 너무 높아져 유지보수가 어려워진다. 따라서 유지보수와 확장을 용이하게 하기 위해 관심사를 분리해 코드의 결합도를 낮춰야 한다.

그러기 위해 채택한 디자인 패턴이 MVVM 패턴이다.

2. MVVM 패턴

MVVM 패턴은 Model + View + ViewModel 을 합친 용어이다.

Model

  1. 실제 데이터에 접근하는 데이터 접근 계층에 해당한다.
  2. 주로 백엔드 API 통신이나 내부 DB가 이에 해당한다. (Retrofit2, Room)

View

  1. 사용자가 보는 화면에 해당한다.
  2. Activity, Fragment가 View 역할 담당한다.
  3. 사용자로부터 텍스트 입력, 버튼 클릭과 같은 입력을 받아 ViewModel의 데이터를 관찰해 화면을 갱신한다.

ViewModel

  1. View가 요청하는 데이터를 Model로 요청한 후 이를 받는다.
  2. View는 Model로부터 ViewModel이 받은 데이터를 관측해 UI를 갱신한다.

3. 안드로이드에 적용하기

안드로이드에서는 유지보수하기 용이한 앱을 디자인 하기 돕는 AAC(Android Architecture Component) 가 존재한다.

이 중 LiveData, ViewModel, Room 를 사용해 만든 메모장 앱의 일부를 가져왔다.

위 디자인을 따랐으며, 메모 목록을 불러오는 작업을 예시로 들 것이다.

1. Model

메모가 저장된 데이터베이스에 접근하는 계층이 Model에 해당한다.

@Entity(tableName = "Memos")
data class Memo(
    @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") val id: Long,
    @ColumnInfo(name = "title") var title: String,
    @ColumnInfo(name = "content") var content: String
) {
    constructor(title: String, content: String) :
            this(0, title, content)
}

메모 데이터 그 자체를 나타내는 Entity이다.

class MemoDataSource @Inject constructor(
    private val memoDao: MemoDao,
) {
    suspend fun getAllMemos(): List<Memo> =
        memoDao.getAllMemos()
}

DataSource는 말 그대로 데이터의 출처를 나타낸다. 데이터베이스에 직접적으로 접근해 데이터를 가져오는 역할을 한다.

class MemoRepository @Inject constructor(
    private val memoDataSource: MemoDataSource
) {
    suspend fun getAllMemos() =
        memoDataSource.getAllMemos()
}

Repository는 DataSource의 진입점으로 ViewModel에서 데이터를 요청할 때 Repository를 통해 가져온다. ViewModel이 직접 DataSource에 접근하는 것을 방지해 결합도를 낮출 수 있다.

2. View

사용자에게 메모 목록을 보여주는 Activity가 View에 해당한다.

@AndroidEntryPoint
class MemoListActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMemoListBinding
    private val viewModel: MemoViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_memo_list)

        init()
    }

    private fun init() {
        val adapter = MemoAdapter()
        binding.recyclerView.adapter = adapter
   
   		// viewModel 로부터 메모 목록 요청
        viewModel.loadAllMemos()

		// viewModel의 메모 목록 LiveData를 관측해 이를 RecyclerView에 반영
        viewModel.memos.observe(this) { memos ->
            memos.let { adapter.submitList(memos) }
        }
    }
}

ViewModel의 메모 목록 LiveData를 관측해 변화가 생길 때마다 즉각적으로 UI를 갱신한다.

3. ViewModel

View에 담길 메모목록 데이터를 가지고 있는 ViewModel에 해당한다.

@HiltViewModel
class MemoListViewModel @Inject constructor(
    private val repository: MemosRepository
) : ViewModel() {
    private lateinit var _memos: LiveData<List<Memo>>
    val memos: LiveData<List<Memo>> get() = _memos

    fun loadAllMemos() = viewModelScope.launch {
        _memos = repository.getAllMemos()
    }

Repository를 통해 Model과 통신해 메모 목록을 가져온다. 변화가 생길 때 관측자 (View)에게 알리는 LiveData를 주로 사용한다.

4. 참조

"Android devlopers", 앱 아키텍처 가이드, https://developer.android.com/topic/architecture?hl=ko
"Android devlopers", Android 아키텍처 구성요소, https://developer.android.com/topic/libraries/architecture?hl=ko

profile
https://github.com/DongChyeon

0개의 댓글