23.12.29

KSang·2024년 1월 1일
0

TIL

목록 보기
22/101

ViewModel

ViewModel 개요|

ViewModel 클래스는 비즈니스 로직 또는 화면 수준 상태 홀더이다

UI에 상태를 노출하고 관련 비즈니스 로직을 캡슐화 한다.

UI는 사용자에게 데이터를 표시하고 사용자의 입력을 처리하는 역할을 담당한다.
하지만 UI 자체가 데이터를 보유하고 있는 경우, 구성 변경이나 화면 전환과 같은 상황에서 데이터가 유실될 수 있다.
또한 UI에 비즈니스 로직(규칙, 계산, 알고리즘, 데이터 처리 등)이 직접 포함되어 있으면 코드의 유지 보수가 어렵고 재사용성이 떨어질 수 있다.

그렇기 때문에 ViewModel클래스를 이용한다

ViewModel클래스는 UI의 데이터를 관리하고 로직을 캡슐화해서 UI랑 분리시킨다

ViewModel의 이점

ViewModel의 대안은 UI에 표시되는 데이터를 보유하는 일반 클래스

는 활동이나 탐색 대상 간에 이동할 때 문제가 될 수 있다

인스턴스 상태 저장 메커니즘을 사용하여 데이터를 저장하지 않을 경우 해당 데이터가 소멸됩니다

ViewModel 클래스의 주요 이점은 기본적으로 두 가지입니다.

  • UI 상태를 유지할 수 있습니다.
  • 비즈니스 로직에 대한 액세스 권한을 제공합니다.

지속성

ViewModel은 ViewModel이 보유하는 상태와 ViewModel이 트리거하는 작업에서 모두 지속성을 허용

이러한 캐싱을 통해 화면 회전과 같은 일반적인 구성 변경에도 데이터를 다시 가져올 필요가 없다

범위

ViewModel을 인스턴스화할 때는 ViewModelStoreOwner 인터페이스를 구현하는 객체를 전달

interface ViewModelStoreOwner

이는 탐색 대상, 탐색 그래프, 활동, 프래그먼트 또는 인터페이스를 구현하는 다른 유형일 수 있다

그러면 ViewModel의 범위가 ViewModelStoreOwner의 수명 주기로 지정됨

ViewModelStoreOwner가 영구적으로 사라질 때까지 메모리에 남아 있다

클래스 범위는 ViewModelStoreOwner 인터페이스의 직접 또는 간접 서브클래스

직접 서브클래스는 ComponentActivity, Fragment, NavBackStackEntry

SavedStateHandle

SaveStateHandle을 사용하면 구성 변경뿐 아니라 프로세스 재생성 전반에 걸쳐서도 데이터를 유지할 수 있다

즉, 사용자가 앱을 닫았다가 나중에 열더라도 UI 상태를 그대로 유지가능

비즈니스 로직에 액세스

대부분의 비즈니스 로직이 데이터 레이어에 있지만 UI 레이어에도 비즈니스 로직이 포함될 수 있다

화면 UI 상태를 만들기 위해 여러 저장소의 데이터를 결합하거나 특정 유형의 데이터에 데이터 레이어가 필요하지 않은 경우

ViewModel은 UI 레이어의 비즈니스 로직을 처리하기에 적합한 위치

Jetpack Compose

선언적 UI 패러다임을 통해 사용자 인터페이스를 간단하고 직관적으로 만들 수 있도록 지원

Jetpack Compose는 UI를 빌드하기 위해 사용하는 함수를 Composable이라고 부르는데, 이 Composable 함수는 UI를 구축하고 UI 상태를 관리하는 데 사용됨. 여기서 상태란, 어플리케이션의 UI가 시간에 따라 변화하는 것을 의미

ViewModel 구현

사용자가 주사위를 굴릴 수 있는 화면을 나타내는 ViewModel의 구현 예

data class DiceUiState(
    val firstDieValue: Int? = null,
    val secondDieValue: Int? = null,
    val numberOfRolls: Int = 0,
)

class DiceRollViewModel : ViewModel() {

    // Expose screen UI state
    private val _uiState = MutableStateFlow(DiceUiState())
    val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()

    // Handle business logic
    fun rollDice() {
        _uiState.update { currentState ->
            currentState.copy(
                firstDieValue = Random.nextInt(from = 1, until = 7),
                secondDieValue = Random.nextInt(from = 1, until = 7),
                numberOfRolls = currentState.numberOfRolls + 1,
            )
        }
    }
}

그 후에 다음과 같이 활동에서 ViewModel에 액세스할 수 있다

import androidx.activity.viewModels

class DiceRollActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same DiceRollViewModel instance created by the first activity.

        // Use the 'by viewModels()' Kotlin property delegate
        // from the activity-ktx artifact
        val viewModel: DiceRollViewModel by viewModels()
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect {
                    // Update UI elements
                }
            }
        }
    }
}

ViewModel과 함께 코루틴 사용

ViewModel에는 Kotlin 코루틴 지원이 포함됩니다. UI 상태를 유지하는 것과 동일한 방식으로 비동기 작업을 유지할 수 있습니다

ViewModel의 수명 주기

ViewModel의 수명 주기는 범위와 직접 연결됩니다. ViewModel은 범위로 지정된 ViewModelStoreOwner가 사라질 때까지 메모리에 남아 있습니다. 이는 다음과 같은 경우에 발생할 수 있습니다.

활동의 경우 완료될 때입니다.
프래그먼트의 경우 분리될 때입니다.
탐색 항목의 경우 백 스택에서 삭제될 때입니다.

일반적으로 시스템에서 활동 객체의 onCreate() 메서드를 처음 호출할 때 ViewModel을 요청합니다. 시스템은 활동 기간 내내(예: 기기 화면이 회전될 때) onCreate() 메서드를 여러 번 호출할 수 있습니다. ViewModel은 처음 ViewModel를 요청한 시점부터 활동이 끝나고 소멸될 때까지 존재합니다

ViewModel 종속 항목 삭제

ViewModel은 수명 주기 과정에서 ViewModelStoreOwner에 의해 ViewModel이 소멸될 때 onCleared 메서드를 호출합니다. 이렇게 하면 ViewModel의 수명 주기를 따르는 모든 작업 또는 종속 항목을 정리할 수 있습니다

cheked()

리스트를 지정한 크기로 나누어 묶는 기능을 제공

fun <T> Iterable<T>.chunked(size: Int): List<List<T>>

chunked 함수는 주어진 리스트를 size 크기로 나누어, 각 묶음을 리스트로 담은 결과를 반환합니다. 만약 원본 리스트의 크기가 size로 나누어 떨어지지 않는 경우, 마지막 묶음은 나머지 요소들로 이루어집니다

chunked(1)을 사용하면 리스트의 각 요소를 1개씩 묶어주기 때문에, 결과적으로 원본 리스트와 동일한 크기의 리스트를 반환합니다

0개의 댓글