- 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다. 👍
- Android developers의 앱 아키텍처 가이드 문서를 핵심 요약 정리 및 한글에 더 자연스러운 문맥으로 번역 작성한 글입니다.
- [Android] 앱 아키텍처 가이드 (1) 글을 먼저 보고, 현재 글을 보시는게 좋습니다.
Modern App Architecture (최신 앱 아키텍처)
다음과 같은 기술들을 활용하는 것을 권장하고 있다.
- 반응형 및 계층형 아키텍처
- 모든 레이어에서의 단방향 데이터 흐름(UDF)
- UI 레이어에서 State를 관리하는 State 홀더로 UI 복잡성을 관리
- 코루틴, 플로우 (Coroutines and flows)
- 의존성 주입 (Dependency injection)
UI Layer (Presentation Layer)
화면에 애플리케이션 데이터를 표시하는 역할을 한다. 사용자 상호작용(버튼 누르기) 또는 외부 입력(네트워크 응답)으로 인해 데이터가 변할 때마다 변경사항을 반영하도록 UI가 업데이트되어야 한다.
UI 레이어는 다음 두 가지로 구성된다.
- 화면에 데이터를 렌더링하는 UI Elements (UI 요소) (View 또는 Jetpack Compose 함수를 사용)
- 데이터를 보유하고 이를 UI에 노출하며 로직을 처리하는 State Holder (예: ViewModel 클래스)
Data Layer
비즈니스 로직(Business logic)을 포함한다. 비즈니스 로직은 앱에 가치를 부여하는 요소로, 앱의 데이터 생성, 저장, 변경 방식을 결정하는 규칙으로 구성된다.
데이터 레이어는 데이터 소스가 없을 수도 있고, 여러개의 데이터 소스를 포함할 수 있는 Repository로 구성된다. 앱에서 다루는 각각의 다른 종류의 데이터별로 Repository 클래스를 생성해야 한다. 예를 들어, 영화와 관련된 데이터에 대해서는 MoviesRepository 클래스를 , 결제와 관련된 데이터에 대해서는 PaymentsRepository 클래스를 각각 생성한다.
Repository 클래스의 역할
- 앱의 나머지 부분에 데이터 노출
- 데이터 변경사항을 한 곳에 집중
- 여러 데이터 소스 간의 충돌 해결
- 앱의 나머지 부분에서 데이터 소스 추상화
- 비즈니스 로직 포함
✏️ 각 데이터 소스 클래스는 하나의 데이터 소스만 사용해야 한다. (파일, 네트워크 소스, 로컬 데이터베이스 등)
Domain Layer
UI 레이어와 데이터 레이어 사이에 있는 선택적 레이어로, 복잡한 비즈니스 로직이나 여러 ViewModel에서 재사용되는 간단한 비즈니스 로직의 캡슐화를 담당한다. 복잡한걸 처리하거나, 재사용성을 개선하는 등 필요한 경우에 사용하면 된다.
이 레이어의 클래스는 일반적으로 UseCase 또는 interactors 라고 한다. (체감상 대부분의 안드로이드 개발자들이 UseCase로 표현)
각 UseCase는 하나의 기능을 담당해야 한다. 예를 들어 여러 ViewModel에서 시간대를 사용하여 화면에 적절한 메시지를 표시하는 경우 GetTimeZoneUseCase 클래스를 만들어서, 해당 UseCase가 이 기능을 전담해서 처리하면 된다.
각 컴포넌트간의 의존성 관리하기
앱 내의 클래스들은 서로에게 의존하면서 올바르게 작동하는데, 이 의존성을 관리하기 위해서 의존성 주입(Dependency Injection) 패턴을 따르고 Hilt 라이브러리를 사용하는 것을 권장한다. Hilt는 의존성 트리를 따라 객체를 자동으로 생성하고, 컴파일할 때 의존성 주입에 문제가 없는지 확인하고, Android 프레임워크 클래스를 위한 의존성 컨테이너를 생성하는 역할을 한다.
- 의존성 주입(Dependency Injection) : 클래스가 자체적으로 의존성을 구성하지 않고, 런타임시 다른 클래스가 이러한 의존성을 제공(주입)하는 방법
일반 권장사항
다음의 권장 사항들은 필수적인 것은 아니지만, 대부분의 경우 이를 따르면 코드 베이스가 더 견고하고 테스트 가능하며 장기적으로 유지보수가 가능해진다.
앱 컴포넌트에 데이터를 저장하지 않기
액티비티, 서비스, 브로드캐스트 리시버와 같은 앱의 진입점(entry point)을 데이터 소스로 지정하지 말자. 대신, 해당 진입점에 해당하는 부분 데이터만 검색하기 위해 다른 컴포넌트들과 조정해야 합니다. 각 앱 컴포넌트는 사용자와 기기의 상호작용 및 시스템의 전반적인 현재 상태에 따라 단기간만 지속됩니다.
Android 클래스의 의존성 줄이기
앱 컴포넌트는 Context 또는 Toast 같은 Android 프레임워크 SDK API를 사용하는 유일한 클래스여야 합니다. 앱 컴포넌트와 별도로 앱의 다른 클래스를 추상화하면 테스트 가능성은 높이고 앱 내의 커플링은 줄일 수 있습니다.
앱의 다양한 모듈 간 역할이 잘 정의된 경계 만들기
예를 들어 네트워크에서 데이터를 로드하는 코드를 코드베이스의 여러 클래스나 패키지 전체에 분산하면 안 됩니다. 마찬가지로 데이터 캐시와 데이터 결합 등 여러 개의 관련 없는 기능을 동일한 클래스에 정의하면 안 됩니다.
각 모듈에서 가능하면 적게 노출하기
예를 들어, 모듈의 내부 구현 세부 사항을 노출하는 shortcut을 생성하는 유혹에 빠지지 마세요. 단기적으로 시간을 절약할 수 있지만, 코드베이스가 발전함에 따라 기술적 부채가 많이 발생할 수 있습니다.
핵심 기능에 집중하기
동일한 상용구 코드를 반복하여 작성하느라 시간을 낭비하지 마세요. 대신 앱을 독특하게 만드는 데 시간과 에너지를 집중하고 반복적인 상용구는 Jetpack 라이브러리와 기타 권장 라이브러리가 처리하도록 하세요.
앱 각 부분의 독립적인 테스트 방법 고려하기
예를 들어, 네트워크에서 데이터를 가져오는 데에 대한 잘 정의된 API가 있다면, 해당 데이터를 로컬 데이터베이스에 저장하는 모듈을 테스트하는 것이 더 쉬워집니다. 반면, 이러한 두 모듈의 로직을 한 곳에 혼합하거나, 네트워킹 코드를 코드 베이스 전체에 분산시킨다면, 효과적인 테스트가 어려워질 수 있습니다.
가능한 한 많은 데이터 저장하기
이렇게 하면 사용자가 기기가 오프라인 모드에 있을 때에도 앱의 기능을 즐길 수 있습니다. 모든 사용자가 지속적이고 고속의 인터넷 연결을 즐기는 것은 아닙니다. 또한, 혼잡한 장소에서는 신호가 불안정할 수 있습니다. 사용자들의 편의를 생각하여 가능한 한 많은 데이터를 저장하고 최신 데이터를 유지하는 것이 중요합니다.
'Android' 카테고리의 다른 글
[Android] Kotlin Flows in practice 번역 및 정리 (2) (0) | 2023.04.22 |
---|---|
[Android] Kotlin Flows in practice 번역 및 정리 (1) (0) | 2023.04.21 |
[Android] 앱 아키텍처 가이드 (1) (0) | 2023.04.15 |
[Android] 사용자 데이터 백업 (자동 백업) (0) | 2023.04.13 |
[Android] selector 를 통해 Layout 모든 요소에 터치 효과 만들기 (0) | 2020.03.13 |