미분류 - 3주차 공부1

업데이트:

카테고리:

태그:


App Architecture

Android 앱은 크기가 커지기 때문에 앱을 확장하고 앱의 견고성을 높이며 앱을 더 쉽게 테스트할 수 있도록 아키텍처를 정의하는 것이 중요하다. 이를 위해 몇가지 원칙을 준수하여 아키텍쳐를 설계해야 한다.

일반 아키텍처 원칙

관심사 분리

Activity나 Fragment같은 UI 기반의 클래스는 UI 및 운영체제 상호작용을 처리하는 로직만 포함해야 한다. 이러한 클래스를 최대한 가볍게 유지하여 컴포넌트 수명 주기와 관련된 많은 문제를 피하고 그러한 클래스의 테스트 가능성을 개선할 수 있다.

데이터 모델에서 UI 도출하기

데이터 모델에서 UI를 도출해야 한다. 가급적 지속적인 모델을 권장하고 있다. 데이터 모델은 앱의 데이터를 나타내며, 앱의 UI 요소로부터 독립되어 있다. 즉, UI 및 앱 컴포넌트 수명 주기와는 관련이 없다.

지속 모델이 이상적인 이유는 다음과 같다.

  • Android OS에서 리소스를 확보하기 위해 앱을 제거해도 사용자 데이터가 삭제되지 않는다.
  • 네트워크 연결이 취약하거나 연결되어 있지 않아도 앱이 계속 작동한다.

데이터 모델 클래스를 기반으로 앱 아키텍처를 구축하면 앱의 테스트 가능성과 견고성이 더 높아진다.

단일 소스 저장소(SSOT)

앱에서 새로운 데이터 유형을 정의할 때는 데이터 유형에 단일 소스 저장소(SSOT)를 할당해야 한다. SSOT는 데이터의 소유자이며, SSOT만 데이터를 수정하거나 변경할 수 있다. SSOT는 이를 위해 불변 유형을 사용하여 데이터를 노출하며, 다른 유형이 호출할 수 있는 이벤트를 수신하거나 함수를 노출하여 데이터를 수정한다.

  • 특정 유형 데이터의 모든 변경사항을 한곳으로 일원화한다.
  • 다른 유형이 조작할 수 없도록 데이터를 보호한다.
  • 데이터 변경사항을 더 쉽게 추적할 수 있도록 합니다. 따라서 버그를 발견하기가 쉬워진다.

단방향 데이터 흐름(UDF)

Android에서 상태 또는 데이터는 일반적으로 계층 범위의 상위 범위 유형에서 하위 범위 유형으로 흐른다. 이벤트는 보통 하위 범위 유형에서 트리거되어 상응하는 데이터 유형의 SSOT에 도달한다. 예를 들어 애플리케이션 데이터는 보통 데이터 소스에서 UI로 흐릅니다. 버튼 누르기와 같은 사용자 이벤트는 UI에서 SSOT로 흐르며, SSOT에서는 애플리케이션 데이터가 불변 유형으로서 수정 및 변경된다.

이 패턴은 데이터 일관성을 강화하고, 오류가 발생할 확률을 줄여 주며, 디버그하기 쉽고, SSOT 패턴의 모든 이점을 제공한다.

권장 앱 아키텍처

일반 아키텍처 원칙에 따라 각 애플리케이션에는 최소한 다음 두 가지 레이어가 포함되어야 한다.

  • 화면에 애플리케이션 데이터를 표시하는 UI 레이어
  • 앱의 비즈니스 로직을 포함하고 애플리케이션 데이터를 노출하는 Data 레이어 UI와 Data 레이어 간의 상호작용을 간소화하고 재사용하기 위한 도메인 레이어라는 레이어를 추가할 수 있다.

image

로직의 유형

  • 비즈니스 로직
    상태 변경에 따라 진행해야 할 작업, 비즈니스 로직은 일반적으로 도메인 또는 데이터 레이어에 배치되지만 UI 레이어에는 배치되지 않는다.
  • UI 동작 로직 (UI 로직)
    상태 변경사항을 표시하는 방법, 예를 들어 Android Resources를 사용하여 화면에 표시할 올바른 텍스트를 가져오거나, 사용자가 버튼을 클릭할 때 특정 화면으로 이동하거나, 토스트 메시지 또는 스낵바를 사용하여 화면에 사용자 메시지를 표시하는 것 등이 UI 동작 로직에 해당된다.

UI(Presentation) Layer

UI 레이어의 역할은 UI에 데이터를 표시하는 것이다. 버튼 클릭 등의 사용자의 상호작용 또는 네트워크 응답 등의 외부 입력으로 인해 데이터나 상태가 변할 때마다 변경사항을 반영하도록 UI를 업데이트 해야한다.

UI 레이어는 다음의 두 가지로 구성된다.

  • 화면에 데이터를 렌더링하는 UI 요소 (View 또는 Composable 함수 등)
  • 데이터를 보유하고 이를 UI에 노출하며 로직을 처리하는 State Holder (ViewModel 클래스 등)

UI State를 생성하는 역할을 담당하고 생성 작업에 필요한 로직을 포함하는 클래스를 State Holder라고 한다.

단방향 데이터 흐름(UDF)을 이용한 상태 관리

image

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

UDF를 사용하면 다음의 이점이 있다.

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

Data Layer

Data Layer에서는 기본적으로 애플리케이션 데이터 및 비즈니스 로직이 들어간다. 이렇게 관심사를 분리하면 Data Layer을 여러 화면에서 사용하고, 앱의 여러 부분 간에 정보를 공유하고, 단위 테스트를 위해 UI 외부에서 비즈니스 로직을 재현할 수 있다.

image

Data Layer는 0~N개의 DataSource와 DataSource를 포함하는 Repository로 이루어진다. 앱에서 처리하는 다양한 유형의 데이터별로 Repository 클래스를 만들어야 한다.

Repository 클래스에서 담당하는 작업은 다음과 같다.

  • 앱의 나머지 부분에 데이터 노출
  • 데이터 변경사항을 한 곳에 집중
  • 여러 DataSource 간의 충돌 해결
  • 앱의 나머지 부분에서 DataSource 추상화
  • 비즈니스 로직 포함

각 DataSource 클래스는 파일, 네트워크 소스, 로컬 데이터베이스와 같은 하나의 DataSource만 사용해야 한다. (아..?) 다른 레이어는 DataSource에 직접 액세스하면 안 된다. Data Layer의 진입점은 항상 Repository클래스여야 한다. StateHolder 클래스 및 UseCase 클래스에는 데이터 소스가 직접 종속 항목으로 있어서는 안 된다. Repository 클래스를 진입점으로 사용하는 것으로 아키텍처의 다양한 레이어를 독립적으로 확장할 수 있다. 이 레이어에서 노출된 데이터는 변경 불가능해야 한다. 그래야 값이 일관되지 않은 상태로 만들 위험이 없어지고 여러 스레드에서 안전하게 처리될 수 있다.

Domain Layer

Domain 레이어는 UI 레이어와 Data 레이어 사이에 있는 선택적 레이어로 복잡한 비즈니스 로직이나 여러 ViewModel에서 재사용되는 간단한 비즈니스 로직의 캡슐화를 담당한다. 모든 앱에 이러한 요구사항이 있는 것은 아니므로 이 레이어는 선택사항이므로 복잡성을 처리하거나 재사용성을 선호하는 등 필요한 경우에만 Domain 레이어를 사용해야 한다.

Domain 레이어는 다음과 같은 이점이 있다.

  • 코드 중복을 방지
  • Domain 레이어 클래스를 사용하는 클래스의 가독성 개선
  • 앱의 테스트 가능성을 높임
  • 책임을 분할하여 대형 클래스를 피할 수 있음

이러한 클래스를 간단하고 가볍게 유지하려면 각 UseCase에서는 기능 하나만 담당해야 하고 변경 가능한 데이터를 포함해서는 안 된다. 대신 UI 레이어 또는 Data 레이어에서 변경 가능한 데이터를 처리해야 한다.

Reference