[Android] 구글 권장 앱 아키텍처 중 UI Layer

명지·2024년 2월 8일

Android

목록 보기
1/2
post-thumbnail

UI 레이어

  • 애플리케이션 데이터를 표시하는 것
  • 사용자의 상호작용 또는 외부 입력으로 인해 데이터가 변할 때마다 변경사항을 반영해서 UI가 업데이트 되도록 해야 함.
  • 앱 데이터 변경사항을 UI가 표시할 수 있는 형식으로 변환 후에 표시하는 파이프라인
  • UI Layer 에는 구성 요소 두 가지로 나눠짐
    1. UI elements : 화면에 데이터를 렌더링 하는 UI 요소 → 뷰 !
    2. State holders : 데이터를 보유하고 이를 UI에 노출하며 로직 처리하는 상태 홀더 → 뷰 모델 !

UI : 사용하는 API(뷰 or Jetpack Compose)와 관계없이 데이터를 표시하는 활동 및 프래그먼트와 같은 UI 요소

UI elements : 각종 뷰 요소들 + 속성 등

UI State : 뷰 요소에 들어갈 데이터 + 상태

💡 MVVM 패턴에서 UI 상태 관리는 중요한 역할을 함
  • ViewModel에서 UI 상태 관리하고, View 에서는 ViewModel 옵저버 패턴을 이용해서 적절한 화면을 사용자에게 보여줌

불변성(immutable)

data class NewsUiState(
    val isSignedIn: Boolean = false,
    val isPremium: Boolean = false,
    val newsItems: List<NewsItemUiState> = listOf(),
    val userMessages: List<Message> = listOf()
)

data class NewsItemUiState(
    val title: String,
    val body: String,
    val bookmarked: Boolean = false,
    ...
)
  • UI 상태 정의는 변경할 수 없음 → 변경 불가능한 객체가 순간의 앱 상태를 보장함
  • 이로써, UI는 상태를 읽고 이에 따라 UI 요소를 업데이트하는 한 가지 역할에 집중할 수 있음.
  • UI 자체가 데이터의 유일한 소스인 경우 제외하고 UI 에서 UI 상태 직접 수정해서는 X
💡 UI 상태 클래스 네이밍 컨벤션 기능 + UiState

UI 의 역할과 복잡성

  • UI의 기본 역할은 UI 상태를 사용자에게 표시하는 것으로, UI가 데이터의 소유, 생성, 변환 등의 추가적인 역할을 맡게 되면, 애플리케이션의 복잡성이 증가함.
  • 코드의 명확한 경계가 흐려지고, 각 컴포넌트 간의 결합도가 높아져, 결과적으로 코드의 테스트 가능성 떨어짐

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

State Holders : UI 상태 생성, 생성 작업에 필요한 로직 포함하는 클래스 → ViewModel

예시 UI )

  1. ViewModelData Layer 에서 현재의 앱 데이터 가져옴 (여기서는 이미지 / 타이틀 / 작성자 / 작성 시간 / 북마크 체크 여부)
  2. View 에서 현재의 UI State 를 옵저빙해서 UI 요소에 적용
  3. View 에서 북마크를 해제했을 때(UI 이벤트 발생) ViewModel 에게 알림 (여기서 알린다는 뜻은 ViewModelView 를 참조하고 있다는 것이 아닌, View 에서 ViewModel 의 메서드를 호출함. 즉, MVVM 패턴에서 “ViewModelView 를 몰라야 한다” 라는 것에 위배되지 않음)
  4. ViewModelData Layer 에게 데이터 수정을 요청함
  5. Data Layer 에서는 data를 업데이트하고, 앱 데이터를 수정함
  6. ViewModel 에서는 새로운 데이터를 받아옴
  7. 무한 반복

그렇다면 UDF를 사용하는 이유

  • 데이터 일관성 : UI 용 정보 소스가 하나 → SSOT
  • 테스트 가능성 : 상태가 분리되어 있어 UI 와 별개로 테스트 가능
  • 유지 관리성 : 상태 변화가 일관된 방식으로 처리되어 코드를 쉽게 수정, 이해, 확장할 수 있음

UI State 노출

  • LiveData
  • StateFlow

→ 관찰 가능한 데이터 홀더에 UI 상태 노출

→ 그 이유로는 ViewModel 에서 데이터를 직접 가져오지 않고도 UI가 상태 변경사항에 반응할 수 있도록 하기 위함 (+ 항상 최신 버전의 UI 상태 캐싱한다는 이점)

💡 UiState 만드는 일반적인 방법 : mutable한 스트림을 immutable한 스트림으로 노출하는 것
  • Backing property BackingProperty : 특정 프로퍼티의 값을 저장하고 관리하기 위해 내부적으로만 사용되는 프라이빗 프로퍼티
class NewsViewModel(...) : ViewModel() {
		// 내부에서 사용되는 mutable한 프로퍼티
    private val _uiState = MutableStateFlow(NewsUiState())
		// 외부에서 사용되는 immutable한 프로퍼티
    val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()

		private val _name = MutableLiveData()
		val name: LiveData<String> = _name
    ...
}

ViewModel 은 상태를 내부적으로 변경하는 메서드 노출해서 UI에 사용되도록 함. 비동기 viewModelScope 사용해 코루틴 실행 및 완료 시 mutable한 상태 업데이트

class NewsViewModel(
    private val repository: NewsRepository,
    ...
) : ViewModel() {

    private val _uiState = MutableStateFlow(NewsUiState())
    val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()

    private var fetchJob: Job? = null

    fun fetchArticles(category: String) {
        fetchJob?.cancel()
        fetchJob = viewModelScope.launch {
            try {
                val newsItems = repository.newsItemsForCategory(category)
                _uiState.update {
                    it.copy(newsItems = newsItems)
                }
            } catch (ioe: IOException) {
                // Handle the error and notify the UI when appropriate.
                _uiState.update {
                    val messages = getMessagesFromThrowable(ioe)
                    it.copy(userMessages = messages)
                 }
            }
        }
    }
}

UI State 사용

  • UI에서 관찰 가능한 데이터 홀더 사용 시에는 생명 주기를 고려해야 함!

    • 사용자에게 뷰가 표시되지 않을 때 UI가 UI 상태를 관찰해서는 안 되기 때문
    • LiveData 는 생명주기를 인식해서 LifecycleOwner 에게 업데이트 전송함
    • Flow 는 비동기 데이터 스트림 처리 시 사용됨 → 생명주기 직접 관리해야 함. lifecycleScope or repeatOnLifecycle 함께 사용하는 것이 좋음
      • collect() 메서드는 flow가 방출하는 각 값을 받아 처리하는 터미널 연산자
        • → 스트림 처리를 시작하고, 스트림에 대한 모든 중간 연산을 실행하여 결과 도출하는 것
      • collect() 메서드는 코루틴 컨텍스트 내에서 비동기적으로 실행되며, 백그라운드에서 flow 를 수행될 수 있음 의미함
  • 더 자세한 내용은 아래 참고 자료 중 UI 레이어를 읽어보시면

참고 자료

앱 아키텍처 가이드  |  Android 개발자  |  Android Developers

UI 레이어  |  Android 개발자  |  Android Developers

profile
멍멍멍멍멍지 ~

0개의 댓글