UI(presentation) Layer

John·2023년 6월 23일
0

원문

https://developer.android.com/topic/architecture/ui-layer?hl=ko
생각보다 잘 읽히는 파트여서, 원문만 봐도 충분합니다.

TLDR;

UI Layer 에서는 UiStateHoler(ex. ViewModel) 이 UiState 생성을 책임집니다.
UI elements 는 UiState 를 화면에 그리고, 사용자와 상호작용합니다.
이벤트가 위로 전파해갈 때, 데이터가 아래로 내려올 때,
단방향성을 지향해야, 유지/보수가 수월하고 가독성도 좋습니다.

UI 의 역할

  • 사용자와 상호작용
  • 데이터 변경사항을 화면에 반영

UI 데이터

  • 데이터 레이어(데이터 소스)에서 가져온 데이터 중, 화면에 표시해야하는 정보로 가공
  • UI 는 렌더링을 하는데 필요한 모든 정보에만 관심있고, 그 외에는 관심 없음
  • 정리하면, UI Layer 에서는 애플리케이션 데이터를 UI 에 맞게 변환 및 표시의 책임을 진다

UI Layer Architecture

  • 앱 데이터(from datasource) -> 렌더링 데이터
  • 렌더링 데이터 -> UI 요소(뷰)
  • UI 요소를 통해 사용자와 상호작용

UI 상태 정의

  • UI 상태 = 사용자가 봐야한다고 앱에서 정의한 항목
  • UI = 사용자가 보는 항목

불변성

  • UiState 는 불변 속성으로 정의
  • UI 는 UiState 를 읽어서 렌더링에만 집중

블로그 포스팅을 보여주는 화면의 경우,
Datasource 에서 title, content 등을 받아서
UI Layer 에서는 이를 UiState 로 변환합니다.

data class PostingUiState {
	val title: String,
    val content: String,
    val favoriteCount: int
}

UI 는 UiState 를 보고 화면에 타이틀과 콘텐츠를 보여주겠죠.
만약, 이 포스팅에 '좋아요' 를 클릭한다면,
뷰에서 발생한 이벤트가 Datasource 까지 올라가게되고(Upstream),
좋아요+1 을 원격에 반영하고, 결과가 다시 내려와서(Downstream)
UiState 를 만들어서 화면에 반영하게 됩니다.

UI 자체가 데이터의 유일한 소스인 경우

UI 자체가 데이터의 유일한 소스인 경우를 제외하고 UI에서 UI상태를 직접 수정해서는 안 됩니다.

이 블로그 포스팅 화면에 글씨크기 +,- 버튼이 있다고 가정해볼게요.
서비스 스펙상 글씨크기는 디폴트 크기가 기본이고, 키우거나 줄인다고 해도,
이를 원격에서 설정 값으로 관리해주지는 않는다고 해볼게요.
이런 경우에, "디폴트 글씨 크기를 갖는 글씨크기조절 Widget" 이 유일한 소스라고 볼 수 있겠죠.
사용자는 +,-버튼으로 상호작용을 하게 되겠지만, 이 이벤트는 DataSource 까지 올라갈 일이 없습니다.
그렇다고 UiState 에서 글씨크기의 속성을 가변으로 생각하면 안되겠어요.
상호작용에 따라 UI Layer 내에서 글씨크기를 변경해주고 새로운 UiState 를 만들어주는 것이 좋겠습니다.

단방향 데이터 흐름으로 상태 관리

단방향 데이터 흐름(UDF)

  • 상태는 아래로
  • 이벤트는 위로

상태 홀더 State holders

  • UiState 생성 담당(로직 포함)
  • ViewModel 이 일반적이지만, 간단한 클래스로도 가능

로직의 유형

비즈니스 로직

  • 도메인 또는 데이터 레이어에 위치
  • 제품 요구사항에 대한 구현

UI 로직

  • UiState 변경사항을 UI 에 그리는 방법
  • ex) Android Resources 에서 텍스트 가져오기, 화면 이동, 토스트, 스낵바
  • 즉, Android Context 를 활용하는 경우, ViewModel(상태 홀더) 이 아닌 UI 의 로직으로 봐야함
  • 이 경우, UI 가 비대해질 수 있기 때문에 별도의 클래스를 만들면, UI 로직을 한 곳으로 집중시켜서 가독성 및 테스트 가능성을 높일 수 있음
  • ex) ResourceGetter 인터페이스를 생성하고, ViewModel 은 UiState 를 만들 때, 이 인터페이스를 통해 필요한 리소스를 가져오도록 하면, Context 를 사용하는 ResourceGetter 구현체는 상태홀더(ViewModel) 과 분리해둘 수 있겠죠.
    ViewModel 의 테스트가 더 용이해질 수 있습니다.

UI 상태 노출

ViewModel 즉, 상태 홀더가 UiState 를 책임지고 만들어 주면,
UI 는 이 UiState 를 책임지고 그려줘야해요.
보통, LiveData 또는 StateFlow 처럼 관찰 가능한 데이터 홀더가 이 다리 역할을 합니다.
UI 에서 '관찰 가능한' 데이터 홀더를 사용하기 때문에, ViewModel 이 직접 UI 에 Rendering 을 지시하지 않아도 됩니다. 즉, ViewModel 은 UI elements 에 의존성을 갖지 않고 단순히 UiState 를 만드는 일에만 집중할 수 있어요.
MVP 에서 MVVM 패턴으로 넘어가는 이유도 바로 여기에 있죠.

ViewModel 에서 변경 불가능한 스트림을 노출하여, 이를 구독하는 UI elements 에서 UiState 를 변경할 수 없도록 제한하는 것도 자주 사용되는 방법입니다.

UiState 를 잘 구분

  • 완전히 분리된 두개의 UI elements 가 있다면, UiState 가 방출되는 흐름을 나누는 것이 맞지만, 관련성이 있는 UiState 는 하나로 묶어야 오류가 적어요.
    - 단, UiState 가 너무 많은 필드를 갖게 되면, 사소한 변화로도 UiState 전체가 화면에 업데이트될 수 있어요.

UI 상태 사용

  • observe()
  • collect()

Threading and concurrency

TBD

TBD

Paging

TBD

Animations

TBD

profile
아직 멀었습니다

0개의 댓글