본문 바로가기
아키텍처

[앱 아카텍처] UI 레이어

by ftbd 2024. 1. 30.

이번 가이드에서는 다음과 같은 작업과 개념을 설명하겠습니다.

 

  1. UI 상태를 정의하는 방법
  2. UI 상태를 생성하고 관리하기 위한 단방향 데이터 흐름(UDF)
  3. UDF 원칙에 따라 관찰 가능한 데이터 유형으로 UI 상태를 노출하는 방법
  4. 관찰 가능한 UI 상태를 사용하는 UI를 구현하는 방법

 

UI 상태 정의

앱에서 사용자에게 표시하는 이 정보가 UI 상태입니다. 즉, 사용자가 보는 항목이 UI라면 UI 상태는 앱에서 사용자가 봐야 한다고 지정하는 항목입니다.

 

불변성

UI 상태 정의는 변경할 수 없습니다. 불변성의 주요 이점은 변경 불가능한 객체가 순간의 애플리케이션 상태를 보장한다는 점입니다. 덕분에 UI는 상태를 읽고 이에 따라 UI 요소를 업데이트하는 한 가지 역할에 집중할 수 있습니다. 따라서 UI 자체가 데이터의 유일한 소스인 경우를 제외하고 UI에서 UI 상태를 직접 수정해서는 안 됩니다. 이 원칙을 위반하면 동일한 정보가 여러 정보 소스에서 비롯되어 데이터 불일치와 미세한 버그가 발생합니다.

 

이 가이드의 이름 지정 규칙(Android)

기능 + UiState

 

 

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

상태 홀더

UI 상태를 생성하는 역할을 담당하고 생성 작업에 필요한 로직을 포함하는 클래스를 상태 홀더라고 합니다.

 

  • ViewModel이 UI에 사용될 상태를 보유하고 노출합니다. UI 상태는 ViewModel에 의해 변환된 애플리케이션 데이터입니다.
  • UI가 ViewModel에 사용자 이벤트를 알립니다.
  • ViewModel이 사용자 작업을 처리하고 상태를 업데이트합니다.
  • 업데이트된 상태가 렌더링할 UI에 다시 제공됩니다.
  • 상태 변경을 야기하는 모든 이벤트에 위의 작업이 반복됩니다.

 

로직의 유형

  • 비즈니스 로직은 앱 데이터에 대한 제품 요구사항의 구현입니다. 비즈니스 로직은 일반적으로 도메인 또는 데이터 레이어에 배치되지만 UI 레이어에는 배치되지 않습니다.
  • UI 동작 로직 또는 UI 로직은 화면에 상태 변경사항을 표시하는 방법입니다. 예를 들어 사용자가 버튼을 클릭할 때 특정 화면으로 이동하거나, 토스트 메시지 또는 스낵바를 사용하여 화면에 사용자 메시지를 표시합니다.

특히 Context 같은 UI 유형의 경우 UI 로직은 ViewModel이 아닌 UI에 있어야 합니다. 테스트 가능성을 높이고 문제 구분에 도움이 되도록 UI 로직을 다른 클래스에 위임하고자 하며 UI가 점점 복잡해지는 경우 간단한 클래스를 상태 홀더로 만들 수 있습니다. UI에서 생성된 간단한 클래스는 UI의 수명 주기를 따릅니다.

 

UDF를 사용하는 이유

  • 데이터 일관성: UI용 정보 소스가 하나입니다.
  • 테스트 가능성: 상태 소스가 분리되므로 UI와 별개로 테스트할 수 있습니다.
  • 유지 관리성: 상태 변경은 잘 정의된 패턴을 따릅니다. 즉, 변경은 사용자 이벤트 및 데이터를 가져온 소스 모두의 영향을 받습니다.

 

UI 상태 노출

UDF를 사용하여 상태 생성을 관리하므로 생성된 상태를 스트림으로 간주할 수 있습니다. 즉, 시간 경과에 따라 여러 버전의 상태가 생성됩니다. 관찰 가능한 데이터 홀더에 UI 상태를 노출하여 데이터 홀더에서 데이터를 직접 가져오지 않고도 UI가 상태 변경사항에 반응할 수 있도록 합니다. 이러한 유형은 항상 최신 버전의 UI 상태를 캐시한다는 이점도 있습니다.

 

UiState 스트림을 만드는 일반적인 방법은 데이터 홀더에서 지원되는 변경 가능한 스트림을 변경 불가능한 스트림으로 노출하는 것입니다. 

 

추가 고려사항

UI 상태: 단일 스트림인지 여러 스트림인지. UI 상태 노출 대상을 단일 스트림과 여러 스트림 중에서 선택할 때 주요 원칙은 이전 글머리기호에서 설명한 내보낸 항목 간의 관계입니다. 단일 스트림 노출의 가장 큰 장점은 편의성과 데이터 일관성입니다. 즉, 상태 사용자자가 언제나 즉시 최신 정보를 확인할 수 있습니다. 하지만 다음과 같이 ViewModel 상태의 스트림이 별개일 때 적합한 경우가 있습니다.

  • 관련 없는 데이터 유형: UI를 렌더링하는 데 필요한 일부 상태는 서로 완전히 별개일 수 있습니다. 이때 서로 다른 상태를 함께 번들로 묶는 데 드는 비용이 이점보다 더 클 수 있으며 이는 상태 중 하나가 다른 상태보다 더 자주 업데이트되는 경우에 특히 그렇습니다.
  • UiState 비교(diff): UiState 객체에 필드가 많을수록 필드 중 하나를 업데이트하면 스트림이 내보내질 가능성이 큽니다. 뷰에는 연속적으로 이루어지는 내보내기가 같은지 다른지 파악하는 비교(diff) 메커니즘이 없으므로 내보내기할 때마다 뷰가 업데이트됩니다.

 

UI 상태 사용

UI에서 관찰 가능한 데이터 홀더를 사용할 때는 UI의 수명 주기를 고려해야 합니다. 수명 주기를 고려해야 하는 이유는 사용자에게 뷰가 표시되지 않을 때 UI가 UI 상태를 관찰해서는 안 되기 때문입니다.