[안드로이드 - 코틀린] ViewModel 역할 및 사용법

2023. 4. 14. 12:14Android

예전 프로젝트는 하나의 View에 모든 기능이 들어가 있어 View와 기능의 결합도가 매우 높은 스파게티 코드로 짜인 프로젝트였다.

요즘 공부하는 MVVM 아키텍처를 적용해보고자 코드 변경을 하고 있는데 그중 중요한 부분인 ViewModel에 대해 설명하는 시간을 가지겠다.

 

ViewModel - 사용 이유

viewModel은 Activity Fragment와 같은 View들의 데이터를 보관하고 관리하는 역할을 수행합니다. 또한 데이터를 ViewModel에서 관리하기 때문에 UI와 데이터 간의 응집도 또한 낮출 수 있게 된다.

 

Activity와 Frgament의 View들에 데이터가 담겨있다면 해당 View가 재생성될 때 초기화가 되는 상황이 발생하게 되고 그러면 데이터의 손실이 발생하게 됩니다.  하지만 ViewModel을 사용하게 된다면 데이터의 손실을 방지하고 유지할 수 있다.

ViewModel은 생명주기가 Activity Fragment와 독립적으로 존재하기 때문이다.

 

ViewModel 생명주기

 

안드로이드의 ViewModel 생명주기는 기본적으로 Activity와 Fragment보다 긴 것을 알 수 있다. 위에서 설명했듯이 이러한 특징 때문에 데이터를 관리하고 유지하는데 더 유용하다는 것을 알 수 있다. ViewModel은 Activity와 Fragment들이 완전히 종료되면 onCleared() 실행이 되며 종료된다.

또한 ViewModel은 싱글톤 객체로 사용이 가능해 같은 데이터를 사용하는 경우 데이터 공유가 수월하다는 이점이 있다.

 

이번 코드를 바꾸며 사용한 코드이다.  ViewModel은 데이터를 보관하고 관리하는 역할이다. 때문에 데이터를 직접적으로 호출해 사용하는 코드는 다음 글에 작성할 Repository에서 담당을 하게 된다. ViewModeld은 LiveData를 이용해 해당 데이터들의 변화를 감지하고 UI에 전송하는 역할 수행한다.

 

SocketViewModel()

class SocketViewModel(application: Application) : AndroidViewModel(application) {

    private val repository = SocketRepository()

    val sensorData: MutableLiveData<Sensor_data?> = repository.sensorData
    val sensorBeforeData: MutableLiveData<Array<Sensor_data>?> = repository.sensorBeforeData

    private val user_key = Loginkey.getUserKey(application).toInt()

//    소켓 호출 초기화
    init {
        repository.initSocket(user_key)
        Log.d("SocketViewModel", "SocketViewModel_init")
    }

//    연결 해제
    override fun onCleared() {
        super.onCleared()
        repository.disconnectSocket()
        Log.d("SocketViewModel", "SocketViewModel_clear")
    }
 }

 

MainFragment()

private lateinit var socketViewModel: SocketViewModel

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View {

    _binding = FragmentMainBinding.inflate(inflater, container, false)

    socketViewModel = ViewModelProvider(this)[SocketViewModel::class.java]
    
    socketViewModel.sensorBeforeData.observe(viewLifecycleOwner, Observer {data ->
            data?.let {
                lifecycleScope.launch {
                    beforeData(data)
                }
            }
        })
        
   return binding.root
   
 }

 

socketViewModel을 Fragment와 결합해 갱신된는 소켓 데이터를 observe를 통해서 갱신되는 데이터를 UI에 전달해 갱신하는 역할을 한다.

느낀 점

 

이번 코드 수정을 통해 느낀 점은 여태 사용한 방식은 너무 무식한 방식으로 하나의 코드에서 오류가 발생하면 해당 View 전체에 다운이 되는 경우가 발생하게 되고 View의 코드 복잡도가 높아지는 경우를 방지하고 코드의 가독성을 높여준다는 것을 느끼게 되었다 또한 생명주기에 대한 처리, 메모리 누스 방지등의 이점들을 가지게 된다는 점도 알게 되었다. 실은 해당 방식에 대서는 이미 알고 있었지만 안드로이드와 코틀린 언어에 대한 이해도가 당시에는 부족하여 적용하지 못한 방식이었다.