๊นจ์•Œ ๊ฐœ๋… ๐Ÿ“‘/Android

[Android] ViewModel - ๊ธฐ์ดˆ

interfacer_han 2024. 1. 13. 16:22

#1 View Model์˜ ํ•„์š”์„ฑ

#1-1 ์˜ˆ์ œ

๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด TextView์˜ text๊ฐ€ 1์”ฉ ์ฆ๊ฐ€ํ•˜๋Š” ์˜ˆ์‹œ ์•ฑ์ด๋‹ค. MainActivity.kt ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
 

// package com.example.viewmodelbasics

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import com.example.viewmodelbasics.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private var count = 0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.countText.text = count.toString()
        binding.countButton.setOnClickListener {
            count++
            binding.countText.text = count.toString()
        }
    }
}

 

#1-2 ๋ฌธ์ œ

์ด ๋•Œ, ํ™”๋ฉด์„ ํšŒ์ „์‹œ์ผœ๋ณด๋ฉด count ์ˆ˜์น˜๊ฐ€ ์ดˆ๊ธฐ ์ˆ˜์น˜์ธ 0์ด ๋˜์–ด๋ฒ„๋ฆฐ๋‹ค. ์•ˆ๋“œ๋กœ์ด๋“œ ์‹œ์Šคํ…œ์€, ํ™”๋ฉด ํšŒ์ „๊ณผ ๊ฐ™์€ ํ™˜๊ฒฝ์˜ ๋ณ€ํ™”(configuration change)๊ฐ€ ์ผ์–ด๋‚˜๋ฉด Activity๋ฅผ ํŒŒ๊ดด(destory)ํ•˜๊ณ  ์žฌ์ƒ์„ฑ(recreate)ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ MainActivity์˜ count ๋ณ€์ˆ˜ ๋˜ํ•œ ํŒŒ๊ดด๋˜๊ณ  ์žฌ์ƒ์„ฑ๋œ ๊ฒƒ์ด๋‹ค.
 
์˜ˆ๋ฅผ ๋“ค์–ด REST API๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„๋“ค์—ฌ View์— ํ‘œ์‹œํ•˜๋Š” ์–ด๋–ค ์•ฑ์„ ๊ฐ€์ •ํ•ด๋ณด์ž. ํ™”๋ฉด ํšŒ์ „ ๋“ฑ์˜ ์ด์œ ๋กœ Activity๊ฐ€ ํŒŒ๊ดด๋œ ํ›„ ์žฌ์ƒ์„ฑ๋˜๋ฉด ๊ฐ™์€ Data๋ฅผ REST API์—๊ฒŒ ์š”์ฒญํ•˜๋ฉฐ ์ž์› ๋‚ญ๋น„๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค. ์‚ฌ์šฉ์ž ์ž…์žฅ์—์„œ๋„ ๋ฉ€์ฉกํžˆ ๋‹ค์šด๋กœ๋“œํ–ˆ๋˜ Data๊ฐ€ ๋ˆˆ ์•ž์—์„œ ๋‚ ์•„๊ฐ€๊ณ  ๋‹ค์‹œ ๋‹ค์šด๋กœ๋“œํ•˜๋Š” ๊ฑธ ๊ธฐ๋‹ค๋ ค์•ผํ•˜๋‹ˆ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜๋„ ์ €ํ•˜๋œ๋‹ค. ๋”ฐ๋ผ์„œ ํƒ์›”ํ•œ ์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๊ธฐ ์œ„ํ•ด์„  ์ด๋Ÿฌํ•œ configuration change๋ฅผ ๊ณ ๋ คํ•œ ์„ค๊ณ„๋ฅผ ํ•  ์ค„ ์•Œ์•„์•ผ ํ•œ๋‹ค.
 

#1-3 ViewModel

 

ViewModel ๊ฐœ์š”  |  Android ๊ฐœ๋ฐœ์ž  |  Android Developers

ViewModel์„ ์‚ฌ์šฉํ•˜๋ฉด ์ˆ˜๋ช… ์ฃผ๊ธฐ๋ฅผ ์ธ์‹ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ UI ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

developer.android.com

์•ˆ๋“œ๋กœ์ด๋“œ ์ œํŠธํŒฉ(JetPack)์˜ View Model Architecture Component๋Š” ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ์ˆ˜๋‹จ์ด๋‹ค. ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ์—์„œ  View๋Š” ํ™”๋ฉด์— ๋ณด์ด๋Š” UI ๋ถ€๋ถ„, Model์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ถ€๋ถ„์„ ์˜๋ฏธํ•œ๋‹ค. View Model์ด๋ž€, ๋ง ๊ทธ๋Œ€๋กœ View์— ๊ด€๋ จ๋œ Model๋กœ ์œ„์˜ ๊ตฌ๊ธ€ ๊ณต์‹ ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด, "UI์— ํ‘œ์‹œํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๊ด€ํ•˜๊ณ  ๊ด€๋ฆฌ"ํ•˜๋Š” Model๋กœ์„œ ๋„์ž…๋˜์—ˆ๋‹ค. View Model์€ View๊ฐ€ ์•„๋ฌด๋ฆฌ ํŒŒ๊ดด์™€ ์žฌ์ƒ์„ฑ์„ ๋ฐ˜๋ณตํ•ด๋„, ๊ทธ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ ๋ณด๊ด€์†Œ๋‹ค.
 

#1-4 ViewModel์˜ ์ƒ๋ช…์ฃผ๊ธฐ

https://developer.android.com/topic/libraries/architecture/viewmodel?hl=ko

Activity๋Š” ์ƒ๋ช…์ฃผ๊ธฐ๋Š” ์œ„ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด ๊ต‰์žฅํžˆ ์„ธ๋ถ€์ ์œผ๋กœ ๋‚˜๋‰œ๋‹ค. ์•„๊นŒ ์˜ˆ์‹œ ์•ฑ์˜ ํ™”๋ฉด์„ ํšŒ์ „์‹œ์ผฐ์„ ๋•Œ ํ˜ธ์ถœ๋˜์—ˆ๋˜ Activity ํด๋ž˜์Šค์˜ onDestroy()์™€ onCreate() ๋ฉ”์†Œ๋“œ๋„ ๋ณด์ธ๋‹ค. ๋ฐ˜๋ฉด, ์˜†์— ์žˆ๋Š” View Model์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋Š” ๊ต‰์žฅํžˆ ๋‹จ์ˆœํ•˜๋‹ค. ์ƒ์„ฑ ํ›„ ์ƒํƒœ๋ณ€ํ™” ์—†์ด ์ญ‰ ์‚ด์•„์žˆ๋‹ค๊ฐ€, ๋งจ ๋งˆ์ง€๋ง‰์— onCleared()๋˜๋ฉด ๊ทธ์ œ์„œ์•ผ ๋น„๋กœ์†Œ ์ƒ๋ช…์ฃผ๊ธฐ๊ฐ€ ๋๋‚œ๋‹ค. onCleared()๊ฐ€ ๋˜๋Š” ์‹œ์ ์€, ๋ทฐ ๋ชจ๋ธ์ด ๋” ์ด์ƒ ํ•„์š”ํ•˜์ง€ ์•Š์€ ์‹œ์ ์œผ๋กœ ์•ฑ์ด ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋Œ์•„๊ฐ€๋Š” ์ƒํ™ฉ์—์„œ ์•ˆ๋“œ๋กœ์ด๋“œ ์‹œ์Šคํ…œ์ด ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ™•๋ณดํ•˜๊ธฐ ์œ„ํ•ด ํ•ด๋‹น ์•ฑ์„ ์ข…๋ฃŒ์‹œํ‚ฌ ๋•Œ๋ฅผ ์˜ˆ๋กœ ๋“ค ์ˆ˜ ์žˆ๋‹ค. ํ˜น์€ ์‚ฌ์šฉ์ž๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ์•ฑ์„ ์ข…๋ฃŒ์‹œํ‚ค๊ฑฐ๋‚˜, ๋’ค๋กœ๊ฐ€๊ธฐ ๋ฒ„ํŠผ ๋“ฑ์œผ๋กœ ์ด์šฉํ•ด Activity์—์„œ ๋ฒ—์–ด๋‚  ๋•Œ(์˜ˆ๋ฅผ ๋“ค์–ด, ์นด์นด์˜คํ†ก ์‡ผํ•‘ํ•˜๊ธฐ ์ฐฝ์—์„œ ๋’ค๋กœ๊ฐ€๊ธฐ ๋ฒ„ํŠผ์œผ๋กœ ์นด์นด์˜คํ†ก ๋ฉ”์ธ ํ™”๋ฉด์œผ๋กœ ๋Œ์•„์™”์„ ๋•Œ)๋‹ค.
 
์ด์ œ๋ถ€ํ„ฐ ์ด View Model์„ ํ”„๋กœ์ ํŠธ์— ์ ‘๋ชฉ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณธ๋‹ค.
 

#2 View Model ์‚ฌ์šฉํ•˜๊ธฐ

#2-1 build.gradle.kts (Module: app)

plugins {
    ...
}

android {
    ...
}

dependencies {
    val lifecycle_version = "2.5.1"
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version") // ViewModel

    ...
}

๋จผ์ €, ViewModel ํด๋ž˜์Šค๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. ๋ชจ๋“ˆ ์ˆ˜์ค€ gradle์— ์œ„์™€ ๊ฐ™์€ ๊ตฌ๋ฌธ์„ ์ถ”๊ฐ€ํ•œ๋‹ค. ํ•ด๋‹น ๊ตฌ๋ฌธ์€ ์—ฌ๊ธฐ์—์„œ ์ถ”๋ฆฐ ๊ตฌ๋ฌธ์ด๋‹ค.
 

#2-2 MainActivityViewModel.kt

// package com.example.viewmodelbasics

import androidx.lifecycle.ViewModel

class MainActivityViewModel : ViewModel() {
    private var count = 0

    fun getCurrentCount(): Int {
        return count
    }

    fun getUpdatedCount(): Int {
        return ++count
    }
}

์ด์ œ MainActivity์—์„œ View Model๋กœ ์“ธ MainActivityViewModel๋ฅผ ๋งŒ๋“ ๋‹ค. ์ด ํด๋ž˜์Šค๋Š” ViewModel ํด๋ž˜์Šค์˜ ์ž์‹์ด๋ฏ€๋กœ, ViewModel ํด๋ž˜์Šค๋ฅผ extendsํ•œ๋‹ค.
 
MainActivity์— ์žˆ๋˜ count ๋ณ€์ˆ˜๋ฅผ MainActivityViewModel๋กœ ์˜ฎ๊ธฐ๊ณ  ํ•ด๋‹น ๋ณ€์ˆ˜๋ฅผ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์†Œ๋“œ๋„ ๋งŒ๋“ ๋‹ค.
 

#2-3 MainActivity.kt ์ˆ˜์ •

// package com.example.viewmodelbasics

...

import androidx.lifecycle.ViewModelProvider

...

class MainActivity : AppCompatActivity() {
    ...
    private lateinit var viewModel: MainActivityViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        
        viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
        binding.countText.text = viewModel.getCurrentCount().toString()
        binding.countButton.setOnClickListener {
            binding.countText.text = viewModel.getUpdatedCount().toString()
        }
    }
}

MainActivityViewModel(View Model)์€ ์ž์ฒด์ ์ธ ์ƒ์„ฑ์ž๊ฐ€ ์žˆ์Œ์—๋„, MainActivity์—์„  ๊ทธ ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์ธ์Šคํ„ด์Šค(๊ฐ์ฒด)๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค. ๋Œ€์‹ , ViewModelProvider์˜ ์ƒ์„ฑ์ž๋ฅผ ์ด์šฉํ•ด ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์ผ์ข…์˜ ์‹ฑ๊ธ€ํ†ค(์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด์„œ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœ์ด ๋˜๋”๋ผ๋„ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ  ์ตœ์ดˆ ํ˜ธ์ถœ ์‹œ์— ๋งŒ๋“ค์–ด๋‘์—ˆ๋˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์žฌํ™œ์šฉ) ๊ฐœ๋…์œผ๋กœ ViewModel์„ ๊ด€๋ฆฌํ•ด์•ผ๋งŒํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.์™œ ๊ทธ๋ž˜์•ผ๋งŒํ• ๊นŒ?  MainActivity๋Š” ๊ณ„์†ํ•ด์„œ ํŒŒ๊ดด๋˜๊ณ  ์žฌ์ƒ์„ฑ๋œ๋‹ค๊ณ  ํ–ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด, MainActivity์—์„œ ViewModel์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“œ๋Š” ์ฝ”๋“œ๋„ ๊ณ„์† ์‹คํ–‰๋œ๋‹ค๋Š” ์–˜๊ธฐ๋‹ค. ViewModel์€ ๋‹จ์ผ ์ธ์Šคํ„ด์Šค๋กœ ์กด์žฌํ•ด์„œ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ผ๊ด€๋œ UI๋ฅผ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•œ ๋ชฉ์ ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ViewModel์˜ ์ธ์Šคํ„ด์Šค๋Š” ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์œผ๋กœ ๊ด€๋ฆฌ๋˜์–ด์•ผ ํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๊ทธ ์—ญํ• ์„ ViewModel์˜ ์ž์ฒด ์ƒ์„ฑ์ž๋Š” ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†๋‹ค. ๊ทธ๋ž˜์„œ ViewModelProvider๊ฐ€ ๋Œ€์‹  ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
 

#2-4 ์•ฑ ํ…Œ์ŠคํŠธ

Activity๋Š” destroy ๋ฐ recreate๋˜์ง€๋งŒ, ViewModel์€ ๊ทธ๋ ‡์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— count ๋ณ€์ˆ˜๊ฐ€ ๊ณ„์† ์‚ด์•„์žˆ๋Š” ๋ชจ์Šต์ด๋‹ค.

#3 ์š”์•ฝ

Model์ด '์žฌ๋ฃŒ'๊ณ  View๊ฐ€ '์š”๋ฆฌ'๋ผ๋ฉด, View Model์€ 'ํ•„์š”ํ•œ ์žฌ๋ฃŒ๊ฐ€ ์˜ฌ๋ผ๊ฐ€์žˆ๋Š” ๋„๋งˆ'๋‹ค.

 

#4 ์™„์„ฑ๋œ ์•ฑ

 

android-practice/view-model/ViewModelBasics at master · Kanmanemone/android-practice

Contribute to Kanmanemone/android-practice development by creating an account on GitHub.

github.com

 

#5 ์ด์–ด์ง€๋Š” ๊ธ€ 

 

[Android] ViewModel - ๋ทฐ ๋ชจ๋ธ์— ์ธ์ž(Argument) ์ „๋‹ฌ

#1 ViewModelProvider ํด๋ž˜์Šค ๋ถ„์„ #1-1 ์ด์ „ ๊ธ€์˜ ์˜ˆ์ œ ์ˆ˜์ • [Android] View Model - ๊ธฐ์ดˆ #1 View Model์˜ ํ•„์š”์„ฑ#1-1 ์˜ˆ์ œ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด TextView์˜ text๊ฐ€ 1์”ฉ ์ฆ๊ฐ€ํ•˜๋Š” ์˜ˆ์‹œ ์•ฑ์ด๋‹ค. MainActivity.kt ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค

kenel.tistory.com

ViewModel์— ์ธ์ž(Argument)๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.