개발자 모집공고를 보면 'MVVM 아키텍처'라는 단어를 쉽게 찾아볼 수 있다.
아키텍처는 시스템을 구성하는 서브 시스템, 컴포넌트와 같은 구성요소 간의 관계를 관리하는 시스템 구조이고, MVVM은 아키텍처의 한 기법으로 Model, View, ViewModel의 줄임말이다.
하나의 소프트웨어를 최대한 기능적으로 작은 단위로 나누어(이를 관심사 분리라 한다.) 테스트가 쉽고 큰 프로젝트도 상대적으로 관리하기 쉬워진다.
라는 것이 간단한 검색을 통해 얻은 사전적 지식이다.
이렇게 좋은 SW 설계 기법이지만 나는 아키텍처를 잘 모른다.
이번에 뉴스 앱을 따라 만들어 보면서 다음의 대략적인 '흐름'만 느낄 수 있었을 뿐이다.
View에서 상호작용(예: 버튼 누름) 발생 - ViewModel이 Model에게 변동사항 전달 - Model이 변동사항 반영 및 ViewModel에게 결과 전달 - ViewModel이 결과 수신 및 View에게 전달 - View가 결과를 토대로 UI 업데이트
따라서 정확하게 이해하기 위해서 Android developers에서 제공하는 가이드라인을 읽어 보았다.다만 권장사항이 바뀌어서 많은 블로그 글들에서 앱 아키텍처 다이어그램이 달랐다. MVVM에서 좀 더 정형화된 느낌이다.
1. 모바일 앱 사용자 환경
모바일 앱 사용자 환경에 대한 내용이 첫 문단에 작성되어 있다. 매우 중요하게 다뤄져야 하는 부분으로 받아들여진다.
간단한 시나리오로 위의 내용을 쉽게 풀어쓴 블로그 글을 찾아서 그림으로 표현해보았다.
ausg님의 안드로이드 권장 아키텍처에 대해서 (MVVM 패턴과 비슷!)
SNS 앱에 사진을 업로드하는 과정을 설명한 것으로 번호 순서대로 앱이 전환된다. 앱이 전환되는 순간에도 사용자 환경은 끊임없이 연결되어 있는 상태이다.
여기서 중요한 것은 이 과정에서 언제든지 전화나 알람 등에 의해 이어지던 사용자 환경의 흐름이 끊길 수도 있다는 점이다.
위와 같이 모바일 환경에서는 사용자들이 다양한 앱을 바꾸면서 이용하기 때문에 모바일 앱 사용자 환경의 흐름이 중단되고 다시 이어지는 일이 비일비재하다. 이를 통해 "안드로이드 앱 구성요소는 개별적이고 비순차적으로 실행될 수 있으며, 운영체제나 사용자가 언제든지 앱 구성요소를 제거할 수 있음"을 이해할 수 있다.
또한 "이러한 이벤트는 직접 제어할 수 없기 때문에 앱 구성요소에 애플리케이션 데이터나 상태를 저장해서는 안 되며 앱 구성요소가 서로 종속되면 안 된다."
2. 일반 아키텍처 원칙
앱 아키텍처는 앱의 부분과 그 각 부분에 필요한 기능 간의 경계를 정의한다. 위에 언급된 요구사항을 충족하려면 몇 가지 특정 원칙을 준수해야 한다.
그 첫 번째가 흔히들 SoC라고 불리고 있는 '관심사 분리(Seperation of Concern)'이다.
가이드에서 참고로 달아준 위키백과의 내용을 읽고 '모듈화'를 떠올리게 됐다.
기능적, 요소적 분리를 통해 하나의 기능을 담당하는 모듈로 만들고 코드의 단순화 및 유지보수를 증대시켜 업그레이드, 재사용 및 독립 개발성이 높아진다.
학교 수업을 듣는 것처럼 머리로는 이해해도 체감이 되지 않는 내용이었다. 조금 더 알고 싶어서 구글링을 시작했고 다음의 글들로 도움을 얻었다.
쿠팡에서 작성한 SoC
안드로이드 개발자 Taylor Case님이 작성한 GAA(God Activity Archtechture)
쿠팡의 필자가 말하기를, 당시에 수많은 안드로이드 앱이 GAA라고 불리는 '갓 액티비티 아키텍처'로 개발했다고 한다. GAA는 하나의 Activity에 UI, Model, data 등등 모든 요소를 작성하는 아키텍처이다. 이러한 코드는 Taylor Case님이 설명하는 대로 스파게티 코드가 되기 십상이다. 따라서 Taylor Case님도 만약 아키텍처로 GAA를 선택하거든 "혼자 개발할 때" 쓰라고 글 말미에 강조한다. (혼자 놀 때 쓰라는 의미이다.)
위와 같은 문제를 Google도 인지하고 있었기 때문에 Fragment를 도입해 MVC(Model View Controller)를 시도하지만 그럼에도 많은 문제가 존재했고(쿠팡 SoC 참고) 지금의 아키텍처 구조를 권장하게 된다.
다시 안드로이드 가이드라인을 돌아와서,
Google은 Activity 및 Fragment와 같은 UI 기반의 클래스는 UI 및 OS 상호작용을 처리하는 로직만 포함해야 한다고 설명한다. 즉, GAA로 프로그램을 작성하지 말라는 것이다.
일반 아키텍처 원칙 두 번째는 바로 '데이터 모델에서 UI 도출하기'이다.
북마크 기능을 통해서 두 번째 원칙을 이해할 수 있을 것 같다. 뉴스 앱에서 다시 읽어보고 싶은 기사를 발견한다. 사용자는 북마크 버튼을 누르게 될 텐데 뉴스 앱이 북마크 UI의 색을 바꿈으로써 해당 기사가 저장됐음을 알린다.
내가 아는 선에서 기술적으로 풀어쓰면 OnClickListener가 이벤트를 감지하고 ViewModel에게 알리게 된다. ViewModel은 Model가 협의해서 북마크 method를 수행하고 결과를 받아온다. View는 ViewModel에 있는 결과로 UI를 업데이트한다.
View는 처리된 결과로 동작할 뿐이지 스스로 결정하지 않는 점이 Google이 말하는 데이터 모델에서 UI 도출하기 인듯하다.
데이터 모델에서 UI 도출하기를 준수하면 다음의 이점이 있다고 Google이 설명한다.
- Android OS에서 리소스를 확보하기 위해 앱을 제거해도 사용자 데이터가 삭제되지 않습니다.
- 네트워크 연결이 취약하거나 연결되어 있지 안하도 앱이 계속 작동합니다.
나는 Google이 설명하는 이점을 아래와 같이 이해했다.
- 앱이 강제 종료돼도 데이터는 보존된다.
- Wi-Fi가 없어도 앱이 동작한다. 동작은 한다.
지금까지 권장되는 아키텍처를 사용해야 하는 이유와 원칙에 대해서 살펴보았으며 다음으로 아키텍처 구조를 살펴보겠다.
3. 권장 앱 아키텍처
일반적인 아키텍처 원칙을 고려하여 각 애플리케이션에는 레이어가 두 개 이상 있어야 한다.
- 화면에 애플리케이션 데이터를 표시하는 UI 레이어
- 앱의 비즈니스 로직을 포함하고 애플리케이션 데이터를 노출하는 데이터 레이어
그리고 이들의 상호작용을 위해서 도메인 레이어라는 레이어를 추가할 수 있다.
※ 다이어그램의 화살표는 클래스 간의 종속 항목을 나타낸다. 예를 들어 도메인 레이어는 데이터 레이어 클래스에 의존한다.
- UI 레이어(또는 프레젠테이션 레이어)
UI 레이어는 화면에 애플리케이션 데이터를 표시해준다. 사용자 상호작용(예: 버튼 누르기) 또는 외부 입력(예: 네트워크 응답)으로 인해 데이터가 변할 때마다 변경사항을 반영하도록 UI가 업데이트되어야 한다.
즉, 데이터 모델에서 UI 도출하기를 하자이다.
구성요소는 아래와 같다.
- 화면에 데이터를 렌더링하는 UI 요소. 이러한 요소는 뷰 또는 Jetpack Compose 함수를 사용하여 빌드할 수 있다.
- 데이터를 보유하고 이를 UI에 노출하며 로직을 처리하는 상태 홀더(예: ViewModel 클래스)
앞서 언급한 북마크 예시에서 View는 처리된 결과로 동작한다고 설명했는데 '처리된 결과'가 바로 데이터이며, ViewModel이 상태 홀더가 된다.
- 데이터 레이어
데이터 레이어에는 '비즈니스 로직'이 포함되어 있다.
비즈니스 로직은 앱에 가치를 부여하는 요소로, 앱의 데이터 생성, 저장, 변경 방식을 결정하는 규칙으로 구성된다.
비즈니스 로직이라는 새로운 용어가 나를 혼란하게 했다. 하지만 나에겐 구글링이 있다.
Microsoft가 친절하게 비즈니스 로직은 MVVM에서의 Glue code이며 ViewModel이라고 설명해준다.
데이터 레이어는 0개부터 여러 개의 데이터 소스를 각각 포함할 수 있는 저장소로 구성된다. 앱에서 처리하는 다양한 유형의 데이터마다 저장소 클래스를 만들어야 한다. 예를 들어 영화 관련 데이터에는 MovieRepository 클래스 또는 결제 관련 데이터에는 PaymentsRepository 클래스를 만들 수 있다.
저장소 클래스(Repositories)에서 담당하는 작업은 다음과 같다.
- 앱의 나머지 부분에 데이터 노출
- 데이터 변경사항을 한 곳에 집중
- 여러 데이터 소스 간의 충돌 해결
- 앱의 나머지 부분에서 데이터 소스 추상화
- 비즈니스 로직 포함
각 데이터 소스 클래스는 파일, 네트워크 소스, 로컬 데이터베이스 같은 하나의 데이터 소스만 사용해야 합니다. 데이터 소스 클래스는 데이터 작업을 위해 애플리케이션과 시스템 간의 가교 역할을 한다.
- 도메인 레이어
도메인 레이어는 UI 레이어와 데이터 레이어 사이에 있는 선택적 레이어다.
도메인 레이어는 복잡한 비즈니스 로직, 또는 여러 ViewModel에서 재사용되는 간단한 비즈니스 로직의 캡슐화를 담당한다. 모든 앱에 이러한 요구사항이 있는 것은 아니므로 이 레이어는 선택사항이다. 따라서 복잡성을 처리하거나 재사용성을 선호하는 등 필요한 경우에만 도메인 레이어를 사용해야 한다.
이 레이어 클래스는 일반적으로 사용 사례 또는 상호작용자 라고 한다. 각 사용 사례에서는 하나의 기능을 담당해야 한다. 예를 들어 여러 ViewModel에서 시간대를 사용하여 화면에 적절한 메시지를 표시하는 경우 앱에는 GetTimeZoneUseCase 클래스가 있을 수 있다.