LiveData -> Flow로 변환해보기

Darby·2024년 2월 27일

기존에 개발했던 메이플스토리 검색앱에서 사용하던 LiveData를 Flow로 변환하면서 알게된 내용입니다. 틀린 부분이 있으면 알려주세요!

LiveData의 문제점

처음 MVVM구조를 만들면서 ViewModel을 만들고 그거를 이제 observe하면서 데이터가 바뀐 것을 읽는 형식으로 만들었습니다.

LiveData의 장점

  • LiveData는 수명 주기를 자동으로 인식하고, 활성 상태인 관찰자만 업데이트를 수신합니다. 이를 통해 메모리 누수나 UI 갱신 오류를 방지할 수 있습니다.
  • 데이터가 변경될 때마다 관찰자에게 알림을 제공하여 UI를 업데이트할 수 있습니다. 이는 데이터의 실시간 업데이트를 지원하며, 사용자 경험을 향상시킵니다.
  • LiveData는 구성 변경(예: 화면 회전)에 대한 데이터 손실을 방지하고 앱 상태를 보존하는 데 도움이 됩니다.

보통 이런식으로 계층구조를 만들어서 사용을 하는데 LiveData 같은 경우 Domain Layer에서 사용하는걸 추천하지 않습니다. 보통은 Flow로 하는걸 추천을 하더라구요. 이유는 Domain Layer는 Java와 Kotlin 코드로만 구성하는 것이 좋으며 LiveData를 사용하게 되면 그 순간부터 Android에 의존적일수 있기에 단위테스트를 따로해야하는 경우가 생길 수 있습니다.

구글 공식 문서에서도 Flow로 만든 다음 asLiveData()로 변환해서 사용하는걸로 작성이 되어있습니다.

데이터 영역 클래스에서 LiveData 객체를 작업하고 싶을 수 있지만 LiveData는 비동기 데이터 스트림을 처리하도록 설계되지 않았습니다. LiveData 변환과 MediatorLiveData를 사용하여 LiveData 객체 작업을 할 수는 있지만 이 접근 방식에는 단점이 있습니다. 즉, 데이터 스트림을 결합하는 기능이 매우 제한적이고 변환을 통해 만들어진 객체를 포함하여 모든 LiveData 객체가 기본 스레드에서 관찰됩니다.

간단히 말하면 LiveData는 비동기에서 쓰기에 적절하지 않다.

그래서 저는 제가 개발했던 앱을 LiveData로 되어있던 Domain Layer 계층을 Flow로 바꿀 생각을 하고 있었습니다. 그래서 Coroutine에 대해 알아보고 Flow에 대해 알아보던 중 StateFlow에 대해 알게 되었습니다. 그 전에 Flow에 대해 간단하게 알아보고 가겠습니다.

Flow가 뭔가요??

Flow는 말 그대로 데이터의 흐름들 속에서 데이터를 처리, 변환 할 수 있으며 비동기 데이터 스트림을 처리할 수 있습니다.

기본적으로 데이터를 생산(emit)하고 소비(collect)하는데 사용됩니다.

특징

  • 비동기성(Asynchronous): Coroutine Flow는 비동기적으로 데이터를 처리하며, 데이터가 생산되는 즉시 소비될 수 있습니다.
  • 확장성(Scalability): 여러 개의 연산자(operator)를 조합하여 데이터를 변환하고 처리할 수 있으며, 비동기적으로 실행됩니다.
  • 취소 가능성(Cancellable): 코루틴의 취소 기능을 활용하여 데이터의 생산과 소비를 취소할 수 있습니다.
  • 배압 제어(Backpressure): Flow의 생산자와 소비자 간의 속도 차이를 조절하여 메모리 부하를 방지하고 안정적인 데이터 흐름을 유지할 수 있습니다.

Flow는 기본적으로 Cold Stream 입니다.

Cold Stream이 뭔데?

Flow 단점

  • Flow는 안드로이드 생명주기에 대해 알 수 없습니다.
  • 생명주기에 대해 알 수가 없다 보니 화면 회전, 화면 이동 등의 이벤트로 인해 화면이 재생성될 때마다 새로운 구독자가 등록될 수 있습니다. 이 경우, 이전 구독자가 여전히 활성화되어 있을 수 있으므로 메모리 누수가 발생할 수 있습니다.
  • Flow는 Cold Stream의 한 형태이기 때문에 새로운 구독자가 등록될 때마다 데이터를 처음부터 다시 생성합니다. 이로 인해 데이터가 불필요하게 캐싱되거나 중복으로 처리되는 문제가 발생할 수 있습니다.

이러한 문제점이 있었습니다. 그래서 Flow로 받아서 asLiveData()로 변환해서 LiveData로 사용하고 있었습니다.

StateFlow의 등장

StateFlow는 SharedFlow 의 한 종류이며 Hot Stream의 성격을 가지고 있습니다.

사용하다보면 LiveData와 비슷하다고 느꼈는데 가장 큰 차이점은 StateFlow는 기본 값을 가지고 있다인 것 같습니다.

StateFlow 특징

  • 항상 값을 가지고 있고, 오직 한 가지 값을 가집니다. 그렇기 때문에 생성할 때 기본값을 넣어줘야합니다.
  • StateFlow는 Hot Stream의 한 형태이므로 여러 구독자가 공유된 데이터 스트림을 동시에 구독할 수 있습니다.
  • StateFlow는 불변성 상태 값을 유지하므로 데이터의 변경을 실시간으로 반영할 수 있습니다.

이러한 특징들로 인해 LiveData를 대체할 수 있습니다.

제가 사용했던 코드를 보면서 마무리 하겠습니다.

MainViewModel.kt

@HiltViewModel
class MainViewModel @Inject constructor(private val userRepository: UserRepository) : ViewModel() {
	//기본값을 넣어줘야한다.
    private val _userData = MutableStateFlow(ResultVO())
    
    val userData: StateFlow<ResultVO> get() = _userData

    private val _errorLiveData = MutableStateFlow("")
    val errorLiveData: StateFlow<String> get() = _errorLiveData
    fun clearErrorData() {
        _errorLiveData.value = ""
    }


    fun getUserData(id: String) {
        viewModelScope.launch {
            try {
            	//가져온 데이터를 발행(emit)
                _userData.emit(userRepository.getUserData(id))
                clearErrorData()
            } catch (e: Exception) {
                _errorLiveData.emit(e.message.toString())
            }
        }
    }
}
MainView.kt

@Composable
fun ParallaxEffect(
    navigateBack: () -> Unit,
    id: String?,
) {
    ...

	val viewModel: MainViewModel = hiltViewModel()
    val likeCharacterViewModel: LikeCharacterViewModel = hiltViewModel()
    val recentSearchViewModel: RecentSearchViewModel = hiltViewModel()

    val userData by viewModel.userData.collectAsState()
    val errorData by viewModel.errorLiveData.collectAsState()

    ...
    
    if (errorData.isNotEmpty()) {
           SearchFailedDialog(
           errorData = errorData.toString(),
           onDismiss = { viewModel.clearErrorData() })
      }
      
      MainAvatar(userData = userData)
    
}

사용방법은 LiveData와 거의 유사합니다.


참고

https://developer.android.com/topic/architecture?hl=ko
https://medium.com/@apfhdznzl/flow%EC%99%80-channel-cold-stream%EA%B3%BC-hot-stream-c42c64cf4996
https://www.youtube.com/watch?v=6z3QjG7vLkE&ab_channel=%EA%B0%9C%EB%B0%9C%ED%95%98%EB%8A%94%EC%A0%95%EB%8C%80%EB%A6%AC

profile
성장하는 개발자가 되자

0개의 댓글