[챌린지반] MVVM

2024. 7. 16. 10:54[Android] Kotlin 활용 앱 개발

# 개념

- 사용자 인터페이스 개발을 위해 설계된 아키텍쳐 패턴

- 프로그램의 비즈니스 로직과 UI 로직을 명확하게 분리하는 패턴

- Model - View - ViewModel로 구성

 

# 구성 요소

1. Model

- 데이터를 다루는 부분 / 비즈니스 로직 포함

- 데이터를 가져오고 저장하는 역할 수행

- DB, 네트워크 요청, 데이터 소스와 상호작용 등

 

2. View

- 레이아웃과 화면을 보여주는 역할, UI 담당

- 사용자가 보는 화면을 표시, 사용자 입력을 처리

 

3. ViewModel

- Model과 View 사이에서 중재자 역할 수행

- View에서 발생하는 이벤트를 감지, 해당 이벤트에 맞는 비즈니스 로직을 Model에서 수행

- Model과 상호작용해 데이터를 가져오거나 업데이트하고, 업데이트된 데이터를 View에 반영

- View에 표시할 데이터를 가공해 제공

 

# MVVM의 동작 구조

1. 사용자의 이벤트가 View를 통해 들어옴

2. View에서 ViewModel로 이벤트가 전달

3. ViewModel이 Model에게 데이터를 요청

4. Model이 ViewModel에게 요청받은 데이터를 응답

5. ViewModel이 응답받은 데이터를 가공해 저장

6. Data Binding을 통해 ViewModel이 UI를 갱신

 

# 장점

- 뷰 로직과 비즈니스 로직을 구분함으로써 생산성 증대(UI가 나오지 않아도 개발 가능)

- 뷰와 뷰모델이 1:n 관계라서 중복되는 로직을 모듈화해 여러 뷰에 재사용 가능

 

# 단점

- 설계가 복잡함

- 뷰모델이 비대해질 수 있음

- 데이터 바인딩으로 인한 메모리소모가 심함

 

(MVP, MVC, MVVM 비교: https://jminie.tistory.com/168)

 

<예시 코드>

// Model: 데이터 담당(DB), 비즈니스 로직

package com.example.mvvmpractice


data class User(val name: String, val id: String, var pw: String){

}

 

// ViewModel: 뷰와 모델을 연결

package com.example.mvvmpractice

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

// 액티비티나 프래그먼트의 라이프사이클에 구애받지 않고 데이터를 유지할 수 있는 ViewModel()를 상속받은 UserViewModel()를 생성
class UserViewModel: ViewModel() {

    // 데이터가 변경되면 이를 관찰하는 UI 컴포넌트가 자동으로 업데이트되는, 관찰 가능한 데이터 홀더 클래스인 LiveData 클래스의 집합 생성
    private val _user = MutableLiveData<User>()
    
    // LiveData(유저)타입의 user 변수에 _user값을 할당.
    // MutableLiveData에 외부에 직접적으로 접근이 가능할 경우, 원본 데이터의 무결성을 해칠 수 있음.
    // get()을 통해 한 번 거쳐서 원본 데이터인 _user가 아니라 외부에서는 관찰용으로 user만 보게끔
    val user : LiveData<User> get() = _user

    // 유저 데이터 설정 함수
    fun setUser(name: String, id: String, pw: String){
        // setUser함수의 인자로 받은 값을 통해 새로운 유저를 생성, _user의 값으로 지정
        // LiveData타입인 _user의 값이 변경되었으므로 _user를 관찰하는 UI 컴포넌트가 자동으로 업데이트됨.
        _user.value = User(name, id, pw)
    }

}
// View(Activity): 레이아웃 담당, UI

package com.example.mvvmpractice

import android.os.Bundle
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.example.mvvmpractice.databinding.ActivitySignupBinding

// 이름, 아이디, 비밀번호를 입력 후 “회원가입”을 클릭하면 Toast에 이름, 아이디, 비밀번호 노출하기

class SignUpActivity : AppCompatActivity() {

    private lateinit var binding: ActivitySignupBinding
    private lateinit var userViewModel: UserViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()

        // binding에 SignUpActivity 레이아웃 연결
        binding = ActivitySignupBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // ViewModel 초기화
        // ViewModelProvider를 통해 UserViewModel의 인스턴스를 호출 -> 액티비티와 ViewModel이 연결됨
        userViewModel = ViewModelProvider(this).get(UserViewModel::class.java)

		// 레이아웃 파일에서 userViewModel 변수에 ViewModel을 설정하고,
        // 라이프사이클 소유자를 Activity로 설정하여 LiveData가 자동으로
        // UI를 업데이트할 수 있게 설정
        binding.userViewModel = userViewModel
        binding.lifecycleOwner = this

        binding.btnSignUp.setOnClickListener{
            var name = binding.etSignUpName.text.toString()
            var id = binding.etSignUpId.text.toString()
            var pw = binding.etSignUpPw.text.toString()

            userViewModel.setUser(name, id, pw)

            userViewModel.user.value?.let { user ->
                Toast.makeText(this, "이름: ${user.name} / ID: ${user.id} / PW: ${user.pw}",
                Toast.LENGTH_SHORT).show()
            }
        }

        // 사용자 관찰자 설정
        userViewModel.user.observe(this) { user ->
            // 필요시 UI 업데이트
        }
    }
}

'[Android] Kotlin 활용 앱 개발' 카테고리의 다른 글

Android - RecyclerView  (4) 2024.07.16
뷰 바인딩(View Binding)  (0) 2024.07.09
Android - 액티비티 생명 주기  (0) 2024.06.20
Android - 인텐트(Intent)  (0) 2024.06.20
Android - 액티비티(Activity)  (0) 2024.06.20