Grusie 안드로이드 개발 기술 블로그

[Android] StateFlow와 UiState, 병렬처리에 대한 고찰 본문

안드로이드 개발/코틀린

[Android] StateFlow와 UiState, 병렬처리에 대한 고찰

grusie 2024. 4. 5. 17:16
728x90
반응형
SMALL

병렬처리를 진행하며, UiState를 활용해 화면을 구상하고 있었던 중 고민이 생겼다.

userInfo가 필요할 때도 있고, userAddInfo가 필요할 때도 있고, 둘 다 필요할 때도 있다.

기존의 방식대로 각각을 UiState로 구분해서 넣는다면, 둘 다 필요할 때 어떻게 처리하는 게 좋은가?에 대한 고찰이다.

UiState 처리에 관한 고찰

기존에 사용했던 방식이다. userItemVo를 가져오게되면, _userItemVo stateFlow에 넣고, UiState를 SuccessGetUserInfo로 변경해주는 방식이다. 이렇게 했을 경우, 데이터를 하나씩 가져와서 화면에 뿌려줄 때에는 도움이 되겠지만, 여러 통신 결과를 가지고 처리하기에는 무리가 있다고 생각하였다. 그렇다고 파라미터로 필요한 것도 아닌데 동기로 처리하는 것은 말이 안 되는 것 같아 고민하였다.

 

첫 번째 방법 : _userItemVo도 StateFlow로 collect할 수 있도록 되어있고, uiState도 collect를 할 수 있도록 되어있다면, 각각이 필요할 경우에는 _userItemVo, _userAddItemVo 등 각각 처리를 하다가, 둘 다 필요할 경우가 있을 때를 위해, awaitAll로 두 가지를 묶어서 uiState를 SuccessUserInfo 등으로 변경을 해주어 사용하면, 오래 기다릴 필요 없이 각 데이터들이 불러와 질 때 마다 원하는 뷰를 변경 할 수 있겠다는 생각이 들었다.

 

두 번째 방법 : 어차피 뷰를 띄워줄 때 필요한 것이고, 유저정보라는 곳이 한 화면에 들어가는 것이기에 그냥 병렬로 하되, 필요한 값들을 모아서 awaitAll을 해주고, uiState를 변경해주는 방법이 있을 것 같다.

프로필 정보를 불러오는 창

두 가지 방법 중에 어차피 UiState를 쓰는데 굳이 각각을 따로 stateFlow로 생성 할 필요가 없으며, 그렇게 했을 때 collect하는 부분에서의 함수가 너무 길고 복잡해지고, 예시로 유저정보를 보여줄 화면은 정보가 다 넘어왔을 때 화면을 그려주고 스켈레톤뷰를 없앨 예정이기에, 두 번째 방법을 사용하기로 했다.

 

사실 이전 스토리 개편을 할 때 두 번째 방법을 채용했는데, 병렬처리를 고민하면서 생기는 고민이였다.

https://medium.com/@laco2951/android-ui-state-modeling-%EC%96%B4%EB%96%A4%EA%B2%8C-%EC%A2%8B%EC%9D%84%EA%B9%8C-7b6232543f25

 

Android UI State Modeling 어떤게 좋을까?

우리는 안드로이드를 개발 할 때 MVVM 패턴을 사용하면서 ViewModel에서 데이터를 요청하고 결과를 UI로 만들고는 합니다.

medium.com

고민에 도움이 된 블로그 글이 있어 첨부하였다.

저기에서도 말하듯 정답은 없고, 필자는 이렇게 해결하려고 한다는 것 정도만 참고하면 좋을 것 같다.

뷰모델

뷰모델에서의 처리

쓸데 없는 stateFlow를 다 날리고, 반환 값을 UserItemVo?로 처리한 후, error가 생길 수 있으니 errorCode를 전역으로 처리해 넣어준다.

viewModelScope.launch {
    setVrogUiState(if (isRefresh) VrogUiState.Refresh else VrogUiState.Loading)
    try {
        val userInfoDeferred = async { getUserInfo(memId) }
        val userExtendInfoDeferred = async { getUserExtendInfo(memId) }
        val userBackgroundCoverDeferred = async { getUserBackgroundCover(memId) }
        val userContentCountDeferred = async { getUserContentCount(memId) }
        ...
        
        //유저 정보 가져오기 성공
        val getUserInfoDeferred = async {
            setVrogUiState(
                VrogUiState.SuccessGetUserInfo(
                    userItemVo = userInfoDeferred.await(),
                    userAddInfoItemVo = userExtendInfoDeferred.await(),
                    backgroundCover = userBackgroundCoverDeferred.await(),
                    contentCount = userContentCountDeferred.await()
                )
            )
        }
    }

유저 정보에 필요한 데이터 호출들을 async 형태로 호출하고, 하위에서 await를 통해, 성공 상태로 변경한다.

성공상태로 변경하는 과정도 async로 만들어주지 않으면, 하위 코드들이 대기상태에 들어가기에, async형태로 만들어주고

awaitAll(
    getUserInfoDeferred,
    getWriteStoryListDeferred,
    getPopularStoryListDeferred,
    ...
)

if (errorCode != null) {
    setVrogUiState(VrogUiState.Error(errorCode!!))
} else {
    setVrogUiState(VrogUiState.Success)
}

마지막에 전체 통신이 끝났다는 것을 확인하기 위해 await를 사용해준다.

viewModel.apply {
    vrogUiState.collect { state ->
        Logger.d("confirm state : ${state}")
        when (state) {
            is VrogUiState.Loading -> {
                showProgress()
            }

            is VrogUiState.Success -> {
                hideProgress()
            }

            is VrogUiState.Error -> {
                showAlert(state.errorCode)
                hideProgress()
            }

            is VrogUiState.SuccessGetUserInfo -> {
                setUserData(
                    userItemVo = state.userItemVo,
                    userAddInfoItemVo = state.userAddInfoItemVo,
                    backgroundCover = state.backgroundCover,
                    contentCount = state.contentCount
                )
            }
            ...

뷰에서는 기존과 마찬가지로 collect를 활용해서 처리를 하면 될 것이다. async - await로 인해 모든 state 받아야 하기에 collectLatest가 아닌 collect로 처리해 주어야 한다.

 

async-await 상태 관리에 대해 고민한 흔적

async - await를 사용했을 때 상태 관리에 대해 고민한 흔적..

후기

사실 아까도 말했듯 이 방법 자체는 이전에 스토리 개편 프로젝트를 진행할 때 사용해본 방법이다. 얼마 전 이벤트 처리에 대해서도 고민했고, 병렬처리를 하였을 때, State관리가 어떻게 되는지 확실하게 알기 위해 공부를 하게 되었다.

병렬처리에 대해서도 이런저런 생각을 하며 delay를 주어가며 테스트를 직접 해보았고, 결과는 이렇게 냈던 것 같다.

더 나은 방법이 당연히 있을 것이라 생각하지만, 필자의 생각은 여기에 그친 것 같다.

반응형
LIST