[TIL]Android AAC(MVVM)

mandoofu·2024년 9월 27일

안드로이드

목록 보기
11/20
post-thumbnail
  • Loose Coupling : 시스템의 구성 요소들이 서로 최소한의 의존성을 가지도록 설계하는 것

  • 유지관리, 품질, 견고성, 테스트 등을 개선하고 코드 충돌을 최소화하여 프로젝트에 일관성을 부여해 빠르게 업무를 시작하고 효율을 높일수 있다.

  • MVVM Architecture

  • UI Layer(Presentation Layer)

    • 화면에 앱 데이터를 표시하는 것이 주된 일
      • 데이터를 보유하고 이를 UI에 노출하며 로직을 처리하는 State Holder(ViewModel)
  • Data Layer

    • 앱 전체 CRUD(비지니스로직)를 결정하는 규칙으로 구성
    • 0~N개의 데이터 Repository로 구성
      • 앱 내에는 다양한 유형의 Repository가 존재
      • AgencyRepository, GroupRepository, StatistiicsReposiory 등
    • 역할
      • 앱 내에 데이터 노출
      • 데이터 변경사항을 한 곳에 집중
      • 여러 DataSource(원천지)간의 충돌 해결
      • 앱 내에서 DataSource 추상화
      • 비즈니스 로직(핵심 로직) 포함
    • Data Source
      • 앱에서 접근하는 물리적 저장소
        • Remote, Local DB, Network, File, 다양한 클라우드 접근 등
  • Domain Layer

    • UI 레이어와 Data 레이어 사이에 있는 선택적 레이어
    • 역할
      • 복잡한 비즈니스 로직이나 여러 ViewModel에서 재사용되는 간단한 비즈니스 로직의 캡슐화를 담당
      • 도메인 레이어 Naming Rule
        • 동사 + 명사/대상(선택사항) + UseCase
          • FormatDateUseCase, LogOutUserUseCase, LatestNewsUseCase, MakeLoginRequestUseCase
    • 보통 UseCase has a Repository
    • UseCase는 수명주기가 없음

MVVM

  • 정의

    • Presentation(마틴 파울러) 패턴에서 파생된 디자인 패턴에서 분리
    • 비즈니스 로직과 프레젠테이션 로직을 UI로 부터 분리하는 것이 핵심
  • Model

  • View

  • ViewModel

    • View 을 표현(State 관리)하기 위한 전용 View Model(Presentation Logic, View 전용 Model)을 나타냄
    • ViewModel : View = 1:N 관계
    • Observer Pattern, 생산자/소비자 패턴을 이용해 View와의 의존성을 분리
      • DataBinding, LiveData, StateFlow
    • ViewModel 소멸시 해야 할 일이 있다면onCleared() 에 재정의
    • ViewModel 에서는 절대 위젯 객체를 소유하면 안됨

Jetpack AAC(Android Architecture Component)

  • AAC 핵심 구성요소
    • ViewModel: 앱의UI 관련 데이터 모델과 로직을 코드와 분리하는 것
    • LiveData: ViewModel의 특정한 데이터 항목이 변동될 때 UI 컨트롤러가 통보를 받는 구조(관찰자)를 나타내며 해당 일을 담당하는 것이 LiveData의 역할을 담당하며 안드로이드에서는 데이터바인딩을 통해 엮는다
      • 관찰 가능한 데이터 홀드 클래스
    • Lifecycle Component : 안드로이드 컴포넌트의 생명주기를 관리하는 객체이며 보통 Activity/Fragment/Compose 가 lifeCycleOwner 가 됨

DataBinding

  • 데이터 바인딩의 목적은 앱 레이아웃의 XML(View)를 앱 코드에 저장된 데이터(ViewModel 인스턴스)와 연동시키는데 주목적

  • 코딩 방식이 아닌 선언적 방식을 사용하여 레이아웃의 UI 구성 요소를 앱의 데이터 소스에 바인딩(상호연동) 하여 자동으로 UI의 데이터 변화를 유지하는 목적

  • 구성 (build.gradle)

plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt’ //BR (Binding Resource)를 생성하기 위해 필요
}

buildFeatures {
dataBinding true
//viewBinding true
}
  • layout : layout를 Root View로 두어야 한다
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="mainViewModel"
type="data_binding.ui.main.MainViewModel" />
</data>
<RelativeLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="64dp"
tools:context=".ui.main.MainFragment">
, , , ,
</RelativeLayout>
</layout>
  • Data Binding Expression 방향성
    • 단방향(One-Way)
      • 데이터 모델 값 변경 -> 레이아웃 값 변경
      • 기본적으로 단방향
    • 양방향
      • 데이터 모델 값 변경 <-> 레이아웃 값 변경
      • 표현식을 @= 을 사용하여 구성한다
        - android:text="@={mainViewModel.krwText}"

StateFlow vs LiveData

  • LiveData는 비동기 흐름(rx, Coroutine Flow)을 지원하지 않는다.

    • LiveData는 Activity, Fragment, Compose등의 View 라이프사이클과 연동되며 UI를 지워하기 위한 목적으로 설계되어 Main Thread에서 CRUD작업이 이루어져야한다.
    • Lifecycle과 연동되는 LiveData의 특성상 LiveData.observe()는 관찰을 진행하지 않는다.
  • Coroutine Flow는 값을 순차적으로 방출(emit)

    • Flow는 kotlin의 순수 API이며 안드로이드에 종속적이지 않다.
  • StateFlow의 특징

    • Flow는 Cold Flow이나 StateFlow는 HotFlow
    • StateFlow는 항상 갱신된 값(equals&hashcode 통해 비교)을 보장하며 관찰 가능한 Data State를 유지해야 하는 상황에 적합
    • 반드시 초기화 해야함
    • 생산(발행)된 마지막 데이터를 갖는다
    • 갱신된 값이 그 전과 같다면 emit하지 않는다.
  • Flow -> StateFlow 변경 시 stateln(scope, started, initialValue) 연산자를 사용

    • scope : StateFlow가 Flow로부터 생산(emit)된 데이터를 소비(collect)할CoroutineScope을 명시
      • 보통 소비는 viewModelScope
    • started : Flow로부터 생산된 데이터를 언제부터 소비할지를 명시
    • initialValue : StateFlow에 저장될 초기값 설정(UIState.Loading)
interface SharedFlow<out T> : Flow<T> {
, , ,
}
interface StateFlow<out T> : SharedFlow<T> {
, , ,
}
  • LiveData -> StateFlow 전환
class NewsRemoteDataSource(...,
	private val externalScope: CoroutineScope,
) {//flow 는 기본적으로 Cold
	val latestNews: Flow<List<ArticleHeadline>> = flow { //flow 는 기본적으로 Cold
...
	}.stateIn( //Hot flow 로 변환
		scope = externalScope, //생산된 데이터를 소비할 코루틴(보통 ViewModel Scope)
		replay = 0, //새로운 소비자가 추가되면 그 전 방출한 값을 다시 보낼 것인지 여부
		started = SharingStarted.WhileSubscribed(5000) //소비가 없을 시 5초 간 발행 후 생산을 멈춤
		//started = SharingStarted.Eargly //소비가 없을 시 즉시 생산을 멈춤
		initialValue = Result.Loading //초기값 설정
	)
}
  • SharedFlow(Event Bus)
    • 모든 소비자에게 값을 내보내는 Hot Flow
      • 앱 내 모든 컨텐츠가 주기적으로 동시에 새로 고침 되도록 하고 싶을 때
      • 초기값이 필요없고 Unit도 가능

0개의 댓글