본문 바로가기
Android Application/앱 설계

안드로이드 MVVM 패턴 예시

by sdchjjj 2023. 4. 1.
728x90

안녕하세요.

이번 포스팅에서는 소프트웨어 디자인 패턴 중 하나인 mvvm 패턴을 안드로이드 앱을 통해 알아보겠습니다.

 

MVVM 패턴에서 M은 Model, V는 View, VM은 ViewModel을 의미합니다.

쉽게 말해 V는 UI, Model은 UI가 그려지는데 필요한 데이터 혹은 UI에 표시되어야 하는 데이터입니다.

이 model과 view의 사이에서 view model이 중간 다리 역할을 하여 view와 model을 분리시켜 주는 것입니다.

 

view(ui) - view model - model(data)

 

위와 같은 형태가 될 수 있으며, view model은 view에서 버튼 클릭 등의 사용자의 동작을 받아 필요한 기능을 수행하고 view에 알려줄 데이터를 가공합니다. 

 

예시를 통해 알아보겠습니다.

 

언어: 코틀린

sdk vsersion

  - compile: 33

  - min: 21

  - target: 33

 

강아지와 고양이 버튼이 있는 PetFragment에서 버튼을 클릭하면 그에 맞는 정보가 표시되도록 하겠습니다.

 

예제 작성 시 hilt를 통해 의존성을 주입해 주었습니다. hilt에 관한 건 아래 포스팅을 확인해 주세요.

https://it-of-fortune.tistory.com/26

 

안드로이드 힐트(hilt) 의존성 주입 구현

안녕하세요. 이번 포스팅에서는 hilt를 사용한 dependency injection을 간단하게 구현해 보겠습니다. 언어: 코틀린 sdk vsersion - compile: 33 - min: 21 - target: 33 저는 아래의 포스팅에서 사용한 소스에 적용해

it-of-fortune.tistory.com

 

Model 입니다.

 

이름과 나이를 포함하고 있는 데이터 클래스를 생성합니다.

 

Pet.kt

data class Pet(
    val name: String,
    val age: Int
)

 

각각의 데이터를 return 해주는 가상의 DataSource를 생성하겠습니다.

 

ExampleDataSource.kt

class ExampleDataSource @Inject constructor() {

    fun getCatData(): Pet {
        return Pet(
            name = "키티",
            age = 7
        )
    }

    fun getDog(): Pet {
        return Pet(
            name = "빙고",
            age = 4
        )
    }
}

이 데이터 소스(로컬 혹은 서버)에서 강아지와 고양이의 정보를 가져올 수 있습니다.

 

ViewModel입니다.

AAC ViewModel을 상속받은 PetViewModel 클래스를 MVVM의 view model로 사용하겠습니다.

두 뷰모델은 다른 종류의 뷰모델인데, 이에 관해서는 다른 포스팅에서 다루겠습니다.

 

PetViewModel.kt

@HiltViewModel
class PetViewModel @Inject constructor(
    private val exampleDataSource: ExampleDataSource
): ViewModel() {

    private val _pet = MutableLiveData<Pet>()
    val pet: LiveData<Pet> get() = _pet

    fun onCatClicked() {
        setCatName()
    }

    fun onDogClicked() {
        setDogName()
    }

    private fun setCatName() {
        _pet.postValue(exampleDataSource.getCatData())
    }

    private fun setDogName() {
        _pet.postValue(exampleDataSource.getDog())
    }
}

 

View입니다.

fragment_pet.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="viewModel"
            type="com.example.laboratory.PetViewModel" />

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".PetFragment">

        <Button
            android:id="@+id/button_cat"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="고양이"
            android:onClick="@{() -> viewModel.onCatClicked()}"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toStartOf="@id/button_dog" />

        <Button
            android:id="@+id/button_dog"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="강아지"
            android:onClick="@{() -> viewModel.onDogClicked()}"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@id/button_cat" />

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent">

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="5dp"
                    android:text="이름:"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@{viewModel.pet.name}"/>

            </LinearLayout>

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="5dp"
                    android:text="나이:"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@{Integer.valueOf(viewModel.pet.age).toString()}"/>

            </LinearLayout>

        </LinearLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

레이아웃에 data binding을 적용해 주었습니다.

https://it-of-fortune.tistory.com/23

 

안드로이드 데이터바인딩(dataBinding) 사용

안녕하세요. 이번 포스팅에서는 저번 포스팅에서 사용했던 프래그먼트와 뷰모델을 이용해 데이터바인딩을 구현해 보도록 하겠습니다. 아래 링크에서 이전에 구현한 내용을 참고해 주세요. 새

it-of-fortune.tistory.com

 

PetFragment.kt

@AndroidEntryPoint
class PetFragment : Fragment() {

    private var _binding: FragmentPetBinding? = null
    private val binding get() = _binding!!

    private val viewModel: PetViewModel by viewModels()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentPetBinding.inflate(layoutInflater, container, false).apply { 
            viewModel = this@PetFragment.viewModel
            lifecycleOwner = viewLifecycleOwner
        }
        return binding.root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

 

viewBinding을 통해 view를 참조합니다.

https://it-of-fortune.tistory.com/21

 

안드로이드 ViewBinding(뷰바인딩) 구현

안녕하세요. 이번 포스팅에서는 안드로이드 프래그먼트에서의 뷰바인딩 구현을 진행해 보겠습니다. 언어: 코틀린 sdk vsersion - compile: 33 - min: 21 - target: 33 바인딩 진행 전 사전 준비 작업입니다.

it-of-fortune.tistory.com

 

에뮬레이터에서 동작을 확인하겠습니다.

figure1. run

의도한 대로 동작을 수행합니다.

 

동작의 순서를 간략하게 표현해 보겠습니다.

 1. 버튼 클릭

 2. PetViewModel에 클릭 이벤트 전달

 3. 이벤트에 맞는 model 요청

 4. 받아온 model을 live data에 post

 5. 이를 관찰하던 view가 변화된 데이터를 ui에 표시

 

이상 포스팅을 마칩니다.

 

감사합니다.

 

728x90

댓글