본문 바로가기

Android

[디자인패턴] MVVM 패턴(3)

반응형

1. 프로젝트 설정 및 구조

먼저 안드로이드 프로젝트를 생성하고 MVVM 아키텍처를 적용해보겠습니다. 프로젝트 구조는 다음과 같습니다.

android-design-pattern-mvvm/
├── app/
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/
│   │   │   │   ├── com/
│   │   │   │   │   ├── wookgenie/
│   │   │   │   │   │   ├── android_design_pattern_mvvm/
│   │   │   │   │   │   │   ├── ui/
│   │   │   │   │   │   │   │   ├── BaseActivity.kt
│   │   │   │   │   │   │   │   ├── BaseViewModel.kt
│   │   │   │   │   │   │   │   │   ├── main/
│   │   │   │   │   │   │   │   │   │   ├── MainActivity.kt
│   │   │   │   │   │   │   │   │   │   ├── MainViewModel.kt
│   │   │   ├── res/
│   │   │   │   ├── layout/
│   │   │   │   │   ├── activity_main.xml
│   │   │   │   ├── values/
│   ├── build.gradle
│
├── gradle/
├── .gitignore
├── settings.gradle
└── build.gradle

프로젝트 설정의 build.gradle 파일에서 다음과 같이 buildFeaturesviewBinding = truedataBinding = true를 추가해야 합니다.

// build.gradle
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}

android {
    namespace 'com.wookgenie.android_design_pattern_mvvm'
    compileSdk 33

    defaultConfig {
        // ...
    }

    buildTypes {
        // ...
    }
    compileOptions {
        // ...
    }
    kotlinOptions {
        // ...
    }
    buildFeatures {
        viewBinding = true
        dataBinding = true
    }
}

dependencies {
    // ...
}

2. BaseActivity와 BaseViewModel

BaseActivityBaseViewModel은 MVVM 아키텍처에서 공통된 로직을 추출하고 재사용성을 높이기 위해 사용되는 기반 클래스입니다. 각각의 클래스를 자세히 살펴보겠습니다.

BaseActivity

BaseActivity는 모든 액티비티의 기반 클래스로 사용됩니다. 다양한 액티비티에서 공통된 작업을 처리하고, 액티비티의 생명주기를 관리합니다. 또한 데이터 바인딩을 초기화하고 필요한 뷰 요소를 설정합니다.

요소 설명
binding 해당 액티비티의 데이터 바인딩을 관리하는 객체입니다. TViewDataBinding 타입으로 지정됩니다.
TAG 액티비티의 로그 태그로 사용됩니다. 각 액티비티마다 고유한 로그 태그를 정의할 수 있습니다.
layoutId 해당 액티비티에서 사용할 레이아웃 리소스 ID를 지정합니다. 예를 들어, R.layout.activity_main과 같이 리소스 ID를 설정할 수 있습니다.
viewModel 해당 액티비티에서 사용할 뷰모델 객체를 지정합니다. RBaseViewModel의 하위 클래스로 지정됩니다.
initView() initView() 메서드는 레이아웃을 띄운 직후에 호출되며, 액티비티 내의 뷰나 액티비티의 속성을 초기화하는 작업을 수행합니다.
initDataBinding() initDataBinding() 메서드는 initView() 이후에 호출되며, 데이터 바인딩 설정을 수행합니다.
initAfterBinding() initAfterBinding() 메서드는 데이터 바인딩 이후에 호출되며, 바인딩 이후에 추가적인 작업을 수행할 수 있습니다.
생명주기 메서드 onCreate(), onRestart(), onStart(), onResume(), onPause(), onStop(), onDestroy() 등의 생명주기 메서드는 액티비티의 생명주기에 따라 호출되며, 각각의 생명주기 단계에서 필요한 작업을 수행할 수 있습니다.
// BaseActivity.kt
abstract class BaseActivity<T : ViewDataBinding, R : BaseViewModel> : AppCompatActivity() {

    lateinit var binding: T
    abstract val TAG: String
    abstract val layoutId: Int
    abstract val viewModel: R
    abstract fun initView()
    abstract fun initDataBinding()
    abstract fun initAfterBinding()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.i(TAG, "onCreate")
        binding = DataBindingUtil.setContentView(this, layoutId)

        binding.lifecycleOwner = this@BaseActivity

        initView()
        initDataBinding()
        initAfterBinding()
    }

    override fun onRestart() {
        super.onRestart()
        Log.i(TAG, "onRestart")
    }

    override fun onStart() {
        super.onStart()
        Log.i(TAG, "onStart")
    }

    override fun onResume() {
        super.onResume()
        Log.i(TAG, "onResume")
    }

    override fun onPause() {
        super.onPause()
        Log.i(TAG, "onPause")
    }

    override fun onStop() {
        super.onStop()
        Log.i(TAG, "onStop")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.i(TAG, "onDestroy")
    }
}

BaseViewModel

BaseViewModel 클래스는 MVVM 아키텍처에서 사용되는 뷰모델의 핵심 기반 클래스입니다. 앱 전체에서 공통으로 사용되는 기능들을 추출하여 모든 뷰모델 클래스에서 재사용할 수 있도록 지원합니다.

이 클래스는 단순한 변수와 메서드 이상으로, 앱의 전반적인 흐름과 사용자 경험을 개선하기 위한 기능을 포함하고 있습니다. 네트워크 요청 중일 때 사용자에게 로딩 상태를 시각적으로 표시하거나, 팝업창을 띄우는 등의 작업을 효과적으로 관리할 수 있는 메커니즘이 내장되어 있습니다.

BaseViewModel을 활용함으로써 각각의 뷰모델 클래스에서 공통된 기능들을 별도의 구현 없이 간편하게 사용할 수 있습니다. 이는 코드의 재사용성을 높이고 개발 작업의 효율성을 높이는 중요한 역할을 합니다.

// BaseViewModel.kt
open class BaseViewModel(application: Application) : AndroidViewModel(application) {

    val AndroidViewModel.context: Context
        get() = getApplication<Application>().applicationContext

    private val _isLoading = MutableLiveData<Boolean>(false)
    val isLoading: LiveData<Boolean> get() = _isLoading

    protected fun showProgress() {
        _isLoading.value = true
    }

    protected fun hideProgress() {
        _isLoading.value = false
    }
}

3. BaseActivity와 BaseViewModel 상속 예시

지금부터는 실제 예시를 통해 BaseActivityBaseViewModel 클래스를 상속하여 액티비티와 뷰모델을 구현하는 방법을 알아보겠습니다. 아래는 MainActivity와 해당 뷰모델인 MainViewModel의 상속 및 구현 예시입니다.

MainViewModel

MainViewModel클래스는 BaseViewModel을 상속하여 구현됩니다. 추가적으로 필요한 로직과 데이터 관리를 구현할 수 있습니다.

요소 설명
MainViewModel 클래스명
상속 BaseViewModel
메서드 및 변수 추가 로직 및 데이터 관리
// MainViewModel.kt
class MainViewModel(application: Application) : BaseViewModel(application) {

}

MainActivity

MainActivity 클래스는 BaseActivity를 상속하여 구현됩니다. MainActivity에서는 액티비티의 초기화, 데이터 바인딩 설정, 추가 작업 등을 구현합니다.

요소 설명
MainActivity 클래스명
상속 BaseActivity
TAG 액티비티 태그
layoutId 레이아웃 리소스 ID
viewModel 뷰모델 객체
initView() 레이아웃 초기화 및 뷰 설정 작업 수행
initDataBinding() 데이터 바인딩 설정 작업 수행
initAfterBinding() 데이터 바인딩 이후 추가 작업 수행
// MainActivity.kt
class MainActivity : BaseActivity<ActivityMainBinding, MainViewModel>() {

    override val TAG: String = MainActivity::class.java.simpleName
    override val layoutId: Int
        get() = R.layout.activity_main
    override val viewModel: MainViewModel by viewModels()

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

    override fun initView() {

    }

    override fun initDataBinding() {

    }

    override fun initAfterBinding() {
        
    }
}

 

위 예시에서는 MainViewModelBaseViewModel을 상속하여 기본 로직을 활용하며, MainActivityBaseActivity를 상속하여 액티비티의 초기화, 데이터 바인딩 설정, 추가 작업 등을 구현합니다. 이렇게 함으로써 공통된 기능을 기반 클래스에서 상속받아 각각의 클래스에서 활용할 수 있습니다.

반응형

'Android' 카테고리의 다른 글

[라이브러리] Retrofit2(3)  (0) 2023.09.19
[라이브러리] Retrofit2(2)  (0) 2023.08.11
[라이브러리] Retrofit2(1)  (0) 2023.08.11
[디자인패턴] MVVM 패턴(2)  (0) 2023.08.10
[디자인패턴] MVVM 패턴(1)  (0) 2023.08.09