일반적으로 안드로이에서 앱구성요소에는 4대 컴포넌트
(액티비티, 서비스, 브로드캐스트 리시버, 콘텐트 프로바이더)와 더불어 프래그먼트
, 액티비티
등의 요소가 포함된다.
개발자는 앱 매니페스트에서 이러한 구성요소의 대부분을 선언하며, Android OS에서 이 파일을 사용하여 기기의 전반적인 사용자 환경에 앱을 통합하는 방법을 결정한다.
사용자는 짧은 시간 내에 여러 앱과 상호작용을 할때도 많다는 점을 고려하면, 앱은 사용자 중심의 다양한 워크플로 및 작업에 맞게 조정되어야 한다.
앱 구성요소는 개별적이고 비순차적으로 실행될 수 있으며, 운영체제나 사용자가 언제든지 앱을 종료할 수있다.
즉 앱의 환경 조건은 개별적이고, 비순차적으로 실행될 수 있으며, 운영체제나 사용자가 언제든지 앱 구성요소를 제거할 수 있다. 또한 이러한 이벤트는 직접 제어할 수 없기 떄문에, 앱 구성요소에 어플리케이션 데이터나 상태를 저장해서는 안 되며 앱 구성요소가 서로 종속되면 안된다.
어플리케이션을 실행하는데 데이터와 상태를 저장하는 데 앱 구성요소를 사용할 수 없다? 그렇다면 앱을 어떻게 설계해야 할까?
Android 앱을 확장할때 앱의 견고성을 높이며, 앱을 더 쉽게 테스트할 수 있는 아키텍처
를 정의해야한다.
앱 아키텍처는 앱의 부분과 그 각 부분에 필요한 기능 간의 경계를 정의한다. 위에 언급된 요구사항을 충족하려면 몇 가지 특정 원칙을 준수하도록 설계해야 한다.
따라야 할 가장 중요한 원칙 중 하나는 관심사분리이다.
Activity
나 Fragment
에 모든 코드를 작성하는 것 아주 나쁜 예이다. 이러한 UI 기반의 클래스는 UI 및 운영체제 상호작용을 처리하는 로직만을 포함해야 한다. 이러한 클래스를 최대한 가볍게 유지하여 구성요소 수명 주기와 관련된 많은 문제를 피하고, 테스트에 용이하게 만들자.
Activity
및 Fragment
는 Android OS와 앱 사이를 이어주는 클래스일 뿐이다. OS는 사용자와의 상호작용을 기반 또는 메모리 부족과 같은 시스템 조건으로 인해 언제든지 클래스를 제거할수 있다. 만족스러운 사용자 경험을 위해서라면 이러한 클래스에 대한 의존성을 최소화 하는 것이 좋다.
또 하나의 중요한 원칙 중 하나는 데이터 모델에서 UI를 도출해야 한다. 라는 것이다.
가급적 지속적인 모델을 권장하며, 여기서 데이터모델은 앱의 데이터를 나타낸다. 이들은 앱의 UI 요소 및 기타 구성요소와 독립되어 있다.
즉 이들은 UI 및 앱 구성요소의 수명주기와는 관련이 없다. 하지만 OS가 메모리에서 앱의 프로세스를 삭제하기로 결정하면 여전히 삭제된다.
이러한 지속적인 데이터 모델은 앱의 테스트를 용이하게 하고, 앱의 견고성을 더 높인다.
그러면 안드로이드 디벨로퍼에서 권장하는 아키텍처는 무엇이 있을까??
이전의 일반적인 아키텍처 원칙을 고려하였을 때, 각 어플리케이션에는 레이어가 두 개 이상 있어야 한다.
ViewModel
)Repository
)UI 및 데이터 레이어 간의 상호작용을 간소화하고 재사용하기 위한 도메인 레이어
라는 레이어도 추가할 수 있다.
UI 레이어(또는 프레젠테이션 레이어)의 역할은 화면에 애플리케이션 데이터를 표시하는 것이다.
버튼 눌르기와 같은 사용자 상호작용 또는 네트워크 응답같은 외부 입력으로 인해 데이터가 변할 때마다 변경사항을 반영하도록 UI가 업데이트되어야 합니다.
UI 레이어는 다음 두 가지로 구성된다.
Jetpack Compose 또는 뷰에서 빌드하여 화면에 직접 데이터를 렌더링하는 UI 요소
데이터를 보유하고 이를 UI에 노출하며 로직을 처리하는 상태 홀더(State holders) -> ViewModel 클래스
상태홀더에서는 LiveData 또는 StateFlow와 같이 관찰 가능한 데이터 홀더에 UI 상태를 노출가능한 상태로 반환해야 한다. 자세한 아키텍처 및 구조는 UI 레이어 - 안드로이드 디벨로퍼를 참조하자.
앱의 데이터 레이어에는 비즈니스 로직이 포함되어 있습니다. 리즈니스 로직은 앱에 가치를 부여하는 요소로, 앱의 데이터 생성, 저장, 변경 방식을 결정하는 규칙으로 구성된다.
데이터 레이어는 뷰모델에 제공할 여러 개의 데이터 소스를 포함해야 하며, 다양한 유형의 데이터마다 저장소(repository
) 클래스를 만들어야 한다.
저장소 클래스에서 담당하는 작업은 다음과 같다.
데이터 저장소에서 사용되는 데이터 소스 클래스는 데이터 작업을 위해해 어플리케이션과 시스템 간의 가교역할을 수행한다.(DB 작업, 네트워킹 작업 등등..)
class ExampleRepository(
private val exampleRemoteDataSource: ExampleRemoteDataSource, // 네트워크
private val exampleLocalDataSource: ExampleLocalDataSource // DB
) { /* ... */ }
데이터 레이어에 대한 자세한 설명은 데이터 레이어 - 안드로이드 디벨로퍼를 참고하자.
도메인 레이어는 UI 레이어와 데이터 레이어 사이에 있는 선택적 레이어다.
그래서 여기서 뭘하냐?
복잡한 비즈니스 로직
, 여러 ViewModel 에서 재사용되는 간단한 비즈니스 로직의 캡슐화
를 담당한다. 복잡성을 완화하거나, 재사용성을 선호하는 등 필요한 경우에 도메인 레이어를 사용한다.
예를 들어 여러개의 ViewModel에서 시간대를 활용하는 로직이 필요할 때, 적절하게 시간을 불러오는 클래스 GetTimeZoneUseCase 등이 있을 수가 있다.
자세한 내용은 도메인 레이어 - 안드로이드 디벨로퍼를 참고하자.
앱의 클래스는 올바른 작동을 위해 다른 클래스에 종속된다. -> 즉 하나의 클래스에서 다른 클래스의 객체가 생성되어야 한다.
이러한 의존성 종속관계는 앱이 커졌을 때 테스트 및 관리를 힘들게 한다. 이러한 문제를 해결하기 위해 다음의 패턴 중 하나를 사용할 수 있다.
이러한 패턴을 사용하면 코드를 중복하거나 복잡성을 추가하지 않아도 종속 항목을 관리하기 위한 명확한 패턴을 제공하며, 손쉽게 코드를 확장할 수 있다. 또한 테스트와 프로덕션 구현을 신속하게 전환할 수 있다.
안드로이드 디벨로퍼에서는 의존성 주입 패턴을
Hilt
라이브러리를 사용하여 관리할 것을 추천한다.
서비스, 액티비티, 브로드캐스트 리시버 등 앱의 엔트리 포인트인 앱의 구성요소에 데이터를 저장하지 말라. 각 앱 구성요소는 매우 단기간만 지속되기 때문에
앱의 구성요소는 Context
및 Toast
와 같은 Android 프레임워크 SDK API를 사용하는 유일한 클래스여야 한다.
데이터 캐시와 데이터 결합 등 여러 관계없는 책임을 동일한 클래스에 정의하면 안된다. 관심사에 따라 앱 간의 모듈을 잘 분리하자.
각 모듈의 내부 구현 세부정보를 노출하여서는 안된다. 코드가 발전함에 따라 기술적 문제가 발생할 수 있다.
네트워크에서 데이터를 가져오기 위해 API를 잘 정의하면 해당 데이터를 로컬 데이터베이스에 보존하는 모듈을 더 쉽게 테스트할 수 있다.
두 모듈의 로직을 한 위치에 혼합하거나, 네트워크 코드를 전체 코드에 분산하면 효과적인 테스트가 어려워지고 복잡해진다.
안드로이드 AAC(Android Architecture Component) = Android 아키텍처 구성요소는 강력하고 테스트와 유지관리가 쉬운 앱을 디자인하도록 돕는 라이브러리의 모음이다.
이러한 AAC의 구성요소 중 하나는 ViewModel이며 이는 Android Jetpack의 구성요소 중 하나이자, 생명주기에 관련없이 데이터를 유지할 수 있게 해준다.
이 외에도 LiveData, Room, Paging 등등...
이러한 라이브러리들은 위에서 설명한 안드로이드 권장 아키텍처인
UI 레이어 -> 도메인 레이어 -> 데이터 레이어를 유지할 수 있게 해준다.
안드로이드 디벨로퍼에서 제공하는 Android 아키텍처 구성요소에서나 앱 아키텍처에서는 MVVM
이라는 단어를 특정하여 말하고 있지 않다. 안드로이드 권장 아키텍처를 말하고 있을 뿐이고, AAC를 적절하게 이용해 MVVM패턴을 구현해 사용하고 있었을 뿐이다.
안드로이드 디벨로퍼에서는 MVVM모델이 아닌 UI레이어, 데이터레이어 +)추가적인 도메인레이어로 로직을 나누어서 개발을 진행하라고 한다.
MVVM에 대한 이야기는 없지만 이런 구조자체가 MVVM모델에 기반을 둔 것이라 생각하며 이러한 구조를 따르면서 개발을 하기에 MVVM 아키텍처의 앱을 개발한다고 할 수 있을 것 같다.
안드로이드 아키텍처 컴포넌트(AAC)와 안드로이드 권장 아키텍처를 보았을 때, Android는 Jetpack Compose를 활용하는 형태로 나아가고 있는 듯 하다.