Replies: 11 comments 25 replies
-
금방 결과가 나온다면 ProgressBar를 안 보여주는 것이 낫지 않을까요?
|
Beta Was this translation helpful? Give feedback.
-
위의 두 상태에 해당되지 않는다는 것은 정상적으로 로딩을 마친 상태를 의미하나요? |
Beta Was this translation helpful? Give feedback.
-
둘 다 네트워크 에러인데 어떤 점이 다른 건가요? |
Beta Was this translation helpful? Give feedback.
-
fetchData()에 궁금한 점이 있습니다.
|
Beta Was this translation helpful? Give feedback.
-
개인적으로 fetchData()에서 Success가 최상단에 있는 것이 좋아 보입니다. |
Beta Was this translation helpful? Give feedback.
-
람다 인자에 기본으로 실행할 함수를 설정하지 않고 nullable하게 설정한 이유가 있나요? |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
이런 이유라면 |
Beta Was this translation helpful? Give feedback.
-
+) 코드를 보면서 느낀 건데, CUD는 READ보다 작업이 짧아서 delayLoading() 을 하는 건가요? |
Beta Was this translation helpful? Give feedback.
-
음 저는 현재로서는 직접 적용해보질 않아서 딱 감이 잡히질 않아서, 내일부터 실제로 적용해보면서, 부족한 부분이 있다면 조금씩 고쳐보는 것도 좋아보입니다. |
Beta Was this translation helpful? Give feedback.
-
사전 지식
NetworkViewModel
의 필요성을 알기 위해선 커디 앱의 공통적인 UI 규칙에 대해 알아야 합니다.커디 앱은 모든 화면에서 아래의 규칙을 따라야 한다고 생각합니다.
처음 화면에 들어갔을 때 데이터를 불러올 땐 항상
ProgressBar
를 보여준다.금방 결과가 나올만한 요청을 할 땐 1초 후
ProgressBar
를 보여준다. (대게 CUD 작업은 1초 후ProgressBar
를 보여주는 게 좋다고 생각합니다.)네트워크 요청을 해야 하는 화면의 상태는 크게 세 가지로 나뉜다.
ProgressBar
를 보여주는 상태(만약 요청에 대한 결과가 발생하면ProgressBar
가 필수적으로 사라져야 한다.)NetworkErrorView
를 보여주는 상태새로고침을 할 수 있는 방법은 두 가지이다.
SwipeRefreshLayout
을 이용하여 새로고침NetworkErrorView
에서 재시도 버튼 클릭따라서 새로고침 시엔 로딩 상태로 변하면 안됩니다.
SwipeRefreshLayout
을 이용할 땐SwipeRefreshLayout
가 로딩 상태를 표현하고,NetworkErrorView
를 이용할 땐 로딩 상태로 변하면 로딩 상태와 네트워크 에러 상태가 공존할 수 없기 때문에 네트워크 문제가 해결되지 않았을 때 재시도 버튼을 클릭하면 사용자는NetworkErrorView
가 깜빡이는 것을 보게 됩니다.만약 서버에서 불러온 데이터가 이미 화면에 그려진 상태라면 최대한 화면을 가리지 않도록 한다.
NetworkErrorView
를 보여주지 않는다.문제
위의 규칙을 지키기 위해서는 대부분의 뷰모델 클래스에 아래의 로직을 작성해야 합니다.
delay()
후) 로딩 상태로 변경순서 또한 동일합니다.
위의 공통적인 요구사항을 만족하려면 공통적인 로직을 순서에 맞게 작성해야 합니다.
또한 간단하지 않기 때문에 실수할 가능성이 있습니다.
1초 뒤 로딩 바를 띄우게 하기 위해서는 로딩 상태로 변경하는 로직을 비동기로 실행해야 합니다. 이 로직은 성공 상태로 변하게 하는 명령어가 실행되기 전 취소되어야 합니다. 그렇지 않으면 성공 상태로 변경 후 로딩 상태로 변경하는 명령어가 수행되어 로딩 바가 사라지지 않을 수 있습니다.
CUD 요청 시 네트워크 에러에 의해 요청이 실패하더라도 네트워크 에러 화면을 보여주지 않아야 하지만 "네트워크 에러가 발생했으니 네트워크 에러 상태로 변경해야지"라고 착각할 수 있습니다.
새로고침을 했을 때 로딩 상태로 변하게 하면 안되지만 잊을 수 있습니다.
이러한 실수를 막기 위해
NetworkViewModel
을 만들었습니다.얻을 수 있는 이점
NetworkViewModel
을 상속한 뷰모델은 화면에 공통적인 UI 상태와 UiEvent를 관리하지 않아도 됩니다. 그 화면과 관련 있는 것들만 관리할 수 있습니다.아직 해결하지 못한 문제
NetworkViewModel
에서 제공하는 기능을 사용할 수 없습니다. 그래서 이전처럼 직접 구현해야 합니다.코드 설명
소스 코드
멤버 변수
NetworkViewModel
을 상속한 뷰모델이 공통적인 화면 상태와 이벤트를 관리할 수 있도록 관련 멤버 변수와 메서드의 공개 범위를protected
로 선언했습니다.screenUiState
: 로딩 상태, 네트워크 에러 상태, 아무것도 아닌 상태baseUiEvent
: 알 수 없는 에러 발생 이벤트, 네트워크 에러에 의한 요청 실패 이벤트메서드
fetchData
데이터를 불러오는 요청을 할 때 사용하기에 좋습니다. 매개 변수는 아래와 같습니다.
fetchData
:suspend () -> ApiResponse<T>
타입입니다. 데이터 요청 콜백 함수를 넣어야 합니다.onSuccess
:(T) -> Unit = {}
타입입니다. 성공 응답을 받으면 수행될 콜백 함수입니다. 만약 인자를 넣지 않는다면 성공 시 아무 일도 하지 않습니다.onFailure
:(code: Int, message: String?) -> Unit = { _, _ -> }
타입입니다. 실패 응답을 받으면 수행될 콜백 함수입니다. 만약 인자를 넣지 않는다면 아무 일도 하지 않습니다.onLoading
:suspend () -> Unit = suspend { changeToLoadingState() }
타입입니다. 로딩 시 수행될 콜백 함수입니다.fetchData
를 호출하기 전 수행됩니다. 만약 인자를 넣지 않는다면 화면의 상태를 로딩 상태로 변경합니다. 불러올 데이터의 성격에 따라 로딩 시 수행할 로직을 다르게 할 수 있습니다. 예를 들어 빠르게 불러올 수 있는 데이터라면 로딩 바를 1초 뒤에 보이도록 할 수 있습니다.onNetworkError
:() -> Unit = ::changeToNetworkErrorState
타입입니다. 네트워크 에러 발생 시 수행될 콜백 함수입니다. 만약 인자를 넣지 않는다면 화면의 상태를 네트워크 에러 상태로 변경합니다. 만약 이 메서드 수행 시 이미 화면에 그려진 데이터가 있다면 네트워크 에러에 의한 요청 실패 이벤트 발생을 알리는 콜백 함수를 넣는 것이 좋습니다.refreshData
refresh()
메서드 구현하기 위해 사용할 수 있습니다. 매개 변수는 아래와 같습니다.refresh
:suspend () -> ApiResponse<T>
타입입니다. 새로고침 요청 콜백 함수를 넣어야 합니다.onSuccess
:(T) -> Unit = {}
타입입니다. 새로고침 성공 시 수행될 콜백 함수입니다. 만약 인자를 넣지 않는다면 성공 시 아무 일도 하지 않습니다.onFailure
:(code: Int, message: String?) -> Unit = { _, _ -> }
타입입니다. 실패 응답을 받으면 수행될 콜백 함수입니다. 만약 인자를 넣지 않는다면 아무 일도 하지 않습니다.로딩 시엔 무조건 아무 일도 하지 않도록 했습니다.
네트워크 에러 시엔 무조건 네트워크 에러에 의한 요청 실패 이벤트를 알리도록 했습니다.
command
CUD 요청 시 사용할 수 있습니다. 매개 변수는 아래와 같습니다.
command
:suspend () -> ApiResponse<T>
타입입니다. CUD 작업을 요청하는 콜백 함수를 넣어야 합니다.onSuccess
:(T) -> Unit = {}
타입입니다. CUD 작업 성공 시 수행될 콜백 함수입니다. 만약 인자를 넣지 않는다면 성공 시 아무 일도 하지 않습니다.onFailure
:(code: Int, message: String?) -> Unit = { _, _ -> }
타입입니다. 실패 응답을 받으면 수행될 콜백 함수입니다. 만약 인자를 넣지 않는다면 아무 일도 하지 않습니다.onLoading
:suspend () -> Unit = suspend { delayLoading() }
타입입니다. 로딩 시 수행될 콜백 함수입니다.command
를 호출하기 전 수행됩니다. 만약 인자를 넣지 않는다면 화면의 상태를 1초 뒤 로딩 상태로 변경합니다. 불러올 데이터의 성격에 따라 로딩 시 수행할 로직을 다르게 할 수 있게 하기 위해 존재합니다. 예를 들어 오래 걸릴 만한 요청이라면 로딩 바를 즉시 보이도록 할 수 있습니다.네트워크 에러 시엔 무조건 네트워크 에러에 의한 요청 실패 이벤트를 알리도록 했습니다.
commandAndRefresh
CUD 요청 후 새로고침하여 화면을 갱신할 때 사용할 수 있습니다. 매개 변수는 아래와 같습니다.
command
:suspend () -> ApiResponse<T>
타입입니다. CUD 작업을 요청하는 콜백 함수를 넣어야 합니다.refresh
:() -> Job = this::refresh
타입입니다. CUD 작업이 성공하면 실행됩니다. 새로고침 및 결과 처리하는 콜백 함수를 넣어야 합니다.onSuccess
:(T) -> Unit = {}
타입입니다. CUD 작업 성공 후refresh
매개 변수 혹은NetworkViewModel
의refresh()
메서드를 호출하고 결과가 성공이면 수행될 콜백 함수입니다. 만약 인자를 넣지 않는다면 성공 시 아무 일도 하지 않습니다.onFailure
:(code: Int, message: String?) -> Unit = { _, _ -> }
타입입니다. 실패 응답을 받으면 수행될 콜백 함수입니다. 만약 인자를 넣지 않는다면 아무 일도 하지 않습니다.onLoading
:suspend () -> Unit = suspend { delayLoading() }
타입입니다. 로딩 시 수행될 콜백 함수입니다.command
를 호출하기 전 수행됩니다. 만약 인자를 넣지 않는다면 화면의 상태를 1초 뒤 로딩 상태로 변경합니다. 불러올 데이터의 성격에 따라 로딩 시 수행할 로직을 다르게 할 수 있게 하기 위해 존재합니다. 예를 들어 오래 걸릴 만한 요청이라면 로딩 바를 즉시 보이도록 할 수 있습니다.네트워크 에러 시엔 무조건 네트워크 에러에 의한 요청 실패 이벤트를 알리도록 했습니다.
혹시 제가 위에서 제시한 UI 규칙에 대해 아쉬운 점이 있다면 알려주세요.
그리고
NetworkViewModel
에 대해 더 나은 의견이 있다면 알려주세요.BaseViewModel을 NetworkViewModel로 변경했습니다.
Beta Was this translation helpful? Give feedback.
All reactions