본문 바로가기
Development/Android

[Android] Dependency Injection (a.k.a DI) - 2. MVVM으로 기반 코드 작성

by du.it.ddu 2020. 10. 24.
반응형

자, 우선 기반 코드를 작성하도록 하겠다.

MVVM 아키텍처를 활용할 것이며, 구글의 아래 이미지를 참고하자.

https://developer.android.com/jetpack/docs/guide?hl=ko

 

앱 아키텍처 가이드  |  Android 개발자  |  Android Developers

이 가이드에는 고품질의 강력한 앱을 빌드하기 위한 권장사항 및 권장 아키텍처가 포함되어 있습니다. 이 페이지는 Android 프레임워크 기본을 잘 아는 사용자를 대상으로 합니다. Android 앱을 처

developer.android.com

더 자세한 내용은 위 문서를 보고, 아래부터는 코드를 통해 DI를 확인 할 것이다.


- 기반 코드 패키지 구조

패키지 구조는 위와 같다. 여기서 fake는 실제로 데이터를 받아오기엔 공수가 크니 임의로 데이터를 생성 해 두었다.

- 기반 코드

# MainActivity

class MainActivity : AppCompatActivity() {

    private var isRemote: Boolean = false
    private lateinit var dataListAdapter: DataListAdapter
    private lateinit var viewModel: MainViewModel

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

    private fun initView() {
        dataListAdapter = DataListAdapter()
        rv_data_list.run {
            adapter = dataListAdapter
            setHasFixedSize(true)
            layoutManager = LinearLayoutManager(this@MainActivity)
        }

        bt_add_data.setOnClickListener {
            viewModel.saveData(Data("This is new added data"))
        }
    }

    private fun initViewModel() {
        val dataSource = if (isRemote) {
            RemoteDataSourceImpl()
        } else {
            LocalDataSourceImpl()
        }

        val repository = DataRepository(dataSource)

        viewModel = ViewModelProvider(this, MainViewModelFactory(repository)).get(MainViewModel::class.java)

        viewModel.dataList.observe(this) {
            Log.e("Datalist", "$it")
            dataListAdapter.dataList = it
        }

        viewModel.loadDataList()
    }
}

MainActivity는 위와 같다. isRemote는 실제로 네트워크의 상태 유무에 따라 처리하는 식이겠지만, 샘플 코드이므로 생략한다.

여기서 봐야 할 것은 initViewModel 함수다. isRemote에 따라 DataSource를 생성하고 repository를 생성한 뒤, ViewModelProvider를 통해 MainViewModel을 생성한다.

ViewModelFactory를 사용하는 이유는 MainVIewMode이 DataRepository를 생성자로 부터 주입받기 때문이다.

# MainViewModel

class MainViewModel(private val repository: DataRepository) : ViewModel() {

    private val _dataList: MutableLiveData<List<Data>> = MutableLiveData()
    val dataList: LiveData<List<Data>> = _dataList


    fun loadDataList() {
        _dataList.postValue(repository.loadDataList() ?: emptyList())
    }

    fun saveData(data: Data) {
        repository.saveData(data).let {
            if(it) {
                loadDataList()
            }
        }
    }
}

class MainViewModelFactory(private val repository: DataRepository) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return MainViewModel(repository) as T
    }
}

VIewModel은 위와 같다. Lifecycler의 ViewModel을 상속받는다. 내부 코드는 설명을 생략하고, 맨 아래 MainViewModelFactory를 참고한다.

파라미터가 있는 ViewModel을 ViewModelProvider를 통해 생성하기 위해서는 ViewModelProvider.Factory를 사용해야 한다.

나머지 코드는 앞선 포스팅을 보았다면 어떤 느낌인지 알 것이라 생각하고 생략하겠다.

이 코드의 결과물은 대략적으로 아래 이미지와 같다.

심플하다.


- 문제점

자, 기반코드를 보거나 타이핑했다면 어떤 문제점이 있을 것이다.

ViewModel을 생성할 때 마다 ViewModelProvider.Factory를 만들고 생성자에 넣어 줄 Repository, DataSource 같은 객체를 생성해야 한다.

Repository, DataSource는 한번 생성하면 변할 이유가 없으므로 한번만 생성해도 되는데, 그럼 다른 액티비티들에서 쓸때 어떻게 해야할까? Singletone 패턴으로 구현해야 할까?

여간 귀찮고 똑같은 코드가 반복된다. 앞서 말했듯, 이러한 부분을 DI Framework로 해결해보자.

반응형