- 소프트웨어 개발은 복잡한 문제를 해결하기 위해 코드를 작성하는 것 이상을 필요하다. 단순히 결과론으로 화면을 보여주는 것만 생각하고 개발한다면, 코드는 점차 커지며 나중에는 손볼 수 없을 정도로 난폭한(?) 코드로 변하게 된다.
- 그렇기에 개발한다는 것은 유지보수성, 확장성, 테스트 용이성 등의 요구 사항을 충족하기 위한 고민이 필요하며, 적절한 아키텍처 설계가 필수이다.
Android Clean Architecture
- Presentation(UI) Layer, Domain Layer, Data Layer 총 3가지 레이어로 구성되어 있다.
- 각 레이어들은 *단방향 데이터 흐름으로 통신한다.
- 단방향 데이터 흐름 : 데이터가 오로지 한 방향으로만 흐르는 것으로 *Up Stream 과 *Down Stream 방식으로 구성되어 있다.
- Up Stream : 사용자가 클릭 등의 이벤트를 발생시키면, UI → Domain → Data 레이어로 전달함으로써 상위 레이어로 전달하는 방식
- Down Stream : Remote or Local Server 로부터 받은 데이터를 Data → Domain → UI 레이어로 전달함으로써 하위 레이어로 전달하는 방식
- 단방향 데이터 흐름 : 데이터가 오로지 한 방향으로만 흐르는 것으로 *Up Stream 과 *Down Stream 방식으로 구성되어 있다.
- 시스템의 각 부분을 독립적으로 개발하고 테스트할 수 있는 환경을 조성할 수 있다.
- 시스템의 변경이나 업그레이드가 필요할 때 전체 시스템을 다시 작성하지 않아도 되며, 특정 부분만 수정하는 것이 가능하다.
- 유지보수 용이성 : 각 계층이 분리되어 있기 때문에 한 계층을 변경해도 다른 계층에 영향을 미치지 않아 유지보수가 쉽다.
- 의존성 관리 및 모듈화를 통해 애플리케이션의 유지 보수성이 MVVM 보다 향상된다.
- MVVM 은 뷰와 뷰 모델 간의 관계에 중점을 두는 반면 Clean Architecture 는 시스템의 전반적인 구조를 고려한다.
- 의존성 관리 및 모듈화를 통해 애플리케이션의 유지 보수성이 MVVM 보다 향상된다.
- 테스트 용이성 : 의존성을 주입하여 유닛 테스트 및 통합 테스트를 수행하기 용이하다.
- 유지 보수성 향상과 동일 이유로 각 계층이 독립적으로 테스트 가능하도록 설계되어 있어 테스트 용이성을 높일 수 있다.
- 유닛 테스트, 통합 테스트 등을 보다 쉽게 테스트를 진행할 수 있다.
- 모듈 간의 분리 : 각 계층이 자체 역할을 가지며, 이로 인해 코드의 재사용성이 높아진다.
- 새로운 기능을 추가하거나 기존 기능을 변경하려는 경우, 변경의 범위를 최소화하며 애플리케이션을 확장하기 유용하다.
- 데이터베이스나 UI 프레임워크 변경 용이성 : 중요한 비즈니스 로직은 외부 프레임워크와 분리되어 있어 해당 프레임워크를 변경하더라도 기존에 작성한 전체 코드를 다시 작성할 필요가 없다.
- MVVM 도 비즈니스 로직을 ViewModel 에서 처리하여 독립적으로 운영할 수 있다. 하지만 프로젝트가 커짐에 따라 비즈니스 로직을 ViewModel 에서 관리하기에는 어려울 수 있다.
주요 원칙
- 의존성 역전 원칙(DIP) : 고수준 모듈은 저수준 모듈에 의존해서는 안되며, 양쪽 모듈 모두 추상화에 의존해야 한다. 이를 통해 느슨한 결합을 유지할 수 있다.
- 경계의 분리 : 시스템을 여러 영역으로 나누고, 각 영역 사이의 인터페이스를 정의하여 각 영역의 독립성을 보장한다.
- 경계는 소프트웨어 요소를 서로 분리하고, 경계 한편에 있는 요소가 반대편에 있는 요소를 알지 못하도록 막는다.
- 화살표의 방향은 의존성을 뜻한다. 클린 아키텍처의 의존성은 밖에서 안으로 향하고, 바깥 원은 안쪽 원에 영향을 미치지 않는다.
- 경계의 바깥으로 갈수록 덜 중요하고 세부적인 영역으로 표현되며, 안으로 갈수록 고수준(좀더 추상회된 개념)으로 표현된다.
- 인터페이스 분리 원칙 : 클라이언트가 자신이 사용하지 않는 메서드에 의존하지 않아야 한다. 즉, 인터페이스는 클라이언트의 요구에 딱 맞는 형태로 분리되어야 한다.
Presentation Layer
- 사용자 인터페이스(UI)와 비즈니스 로직 간의 중간 계층으로, UI 에서 발생한 이벤트를 처리하고 필요한 데이터를 비즈니스 로직에 전달한다.
- 화면과 입력에 대한 처리 등 UI 와 관련된 부분을 담당한다.
- Domain 계층에 대한 의존성을 가지고 있다.
- 뷰(View) : 직접적으로 플랫폼 의존적인 구현, 즉 UI 화면 표시와 사용자 입력을 담당한다. 단순하게 프레젠터가 명령하는 일만 수행한다.
- 프레젠터(Presenter) : MVVM 의 ViewModel 과 같이, 사용자 입력이 왔을 때 어떤 반응을 해야 하는지에 대한 판단을 하는 영역이다. 무엇을 그려야 할지도 알고 있는 영역이다.
- UpStream 관점에서 ViewModel 은 '비즈니스 로직의 출발 지점'으로, Domain 레이어나 Data 레이어에 이벤트를 전달하며 비즈니스 로직의 시작을 트리거하게 된다.
- DownStream 관점에서 ViewModel 은 Domain 레이어나 Data 레이어로부터 UI 에 바인딩될 데이터 구조를 응답 받는다. 그리고 전달받은 응답을 통해 UI 에 데이터 바인딩을 진행한다.
Domain Layer
- Presentation, Data 계층에 대한 의존성을 가지지 않고 독립적으로 분리되어 있다.
- 안드로이드의 의존성을 갖지 않고 java 및 kotlin 코드로만 구성하며 다른 애플리케이션에서도 사용할 수 있다
- 아이폰이나 웹 전용으로도 개발해야 하는 경우, 내부 동작이 거의 동일하므로 순수 Kotlin 으로 공통 로직을 모듈로 분리하여 설계하면 여러 플랫폼에서 활용할 수 있다.
- 비즈니스 로직을 아이폰과 웹 개발자들이 각각 구현하더라도, 다른 플랫폼 개발자는 Domain 모듈만 보고 이 서비스의 비즈니스 로직을 빠르게 파악하고 개발할 수 있다.
- 유스케이스(UseCase) : 애플리케이션의 실제 비즈니스 로직을 포함하는 부분으로, 프레젠터나 뷰모델로부터 전달된 요청을 처리하고 데이터를 가공하여 반환한다.
- 애플리케이션의 특화된 업무 규칙을 포함한다.
- 시스템의 모든 유즈케이스를 캡슐화하고 구현한다.
- 엔티티로 들어오고 나가는 데이터 흐름을 조정하고 조작한다.
- 네이밍 규칙 : 현재 시제의 동사 + 명사/대상(선택사항) + UseCase
- ViewModel 에서 UseCase 를 파라미터로 전달 받아서 사용하게 된다. ViewModel 이 어떤 것을 하고자 하는지 직관적으로 파악할 수 있다.
- UseCase 를 사용하지 않으면 ViewModel 에서는 Repository 를 전달 받아서 사용하게 되는데, 전달 받은 Repository 가 수정이 된다면, Repository 를 사용하는 많은 부분에서 수정이 이루어져야 할 가능성이 높다.
- 하지만, UseCase 를 사용하게 되면 영향이 있는 UseCase 를 사용하는 부분에서만 수정을 하면 되기 때문에 의존성이 줄어든다.
- 엔티티(Entity, 모델) : 앱의 실질적인 데이터
- 핵심 업무 규칙을 캡슐화한다.
- 메서드를 가지는 객체, 일련의 데이터 구조와 함수의 집합이다.
- 가장 변하지 않으며 외부로부터 영향을 받지 않는 영역이다.
Data Layer
- Domain 계층에 대한 의존성을 가지고 있다.
- Domain 계층의 Repository 구현체, 데이터베이스, 서버와의 통신을 포함하고 있다.
- mapper 클래스를 통해 Data 계층의 모델을 Domain 계층의 모델로 변환해주는 역할을 한다.
- 레포지토리(Repository) : 데이터베이스나 외부 데이터 원본과의 상호 작용을 담당하는 부분으로, 데이터를 가져오고 저장하는 작업을 수행한다. 유스케이스는 리포지토리를 통해 데이터를 얻어온다.
- 유즈 케이스가 필요로 하는 데이터의 저장 및 수정 등의 기능을 제공하는 영역으로, 데이터 소스를 인터페이스로 참조하여, 로컬 DB 와 네트워크 통신을 자유롭게 할 수 있다.
- UpStream 관점에서 앱 내에서 필요한 데이터를 DataSource 에 요청하는 역할을 맡고 있다.
- DownStream 관점에서 DatsSource 에서 받아온 데이터를 새로운 모델로 가공하여 하위 레이어(Domain 또는 UI)에 전달하는 역할을 맡고 있다.
- 데이터의 출처에 관계 없이 동일한 인터페이스로 데이터 접근할 수 있도록 한다. ViewModel 에서 직접 데이터에 접근하여 데이터를 가져오는 것이 아니라, ViewModel 에서는 Repository 만 접근을 하게 된다.
- 즉, Data Layer 가 캡슐화가 되고, ViewModel 이 포함되어 있는 Presentation Layer 에서 Data Layer 를 직접 호출하지 않고 Repository 를 통해서만 접근이 가능하게 되는 것이다.
- Data Layer 에 대한 의존성을 줄일 수 있다. 즉, Presentation Layer 와 Data Layer 간의 결합도가 줄어든다.
- Presentation Layer 에서 Data Layer 에 직접 접근하지 않으므로, 새로운 데이터의 추가가 쉽다.
- Presentation Layer 에서는 Repository 에 데이터 요청만 하면 되므로, 일관된 인터페이스로 데이터를 요청할 수 있다.
- Unit Test 를 통한 검증하기가 쉬워진다.
- 즉, Data Layer 가 캡슐화가 되고, ViewModel 이 포함되어 있는 Presentation Layer 에서 Data Layer 를 직접 호출하지 않고 Repository 를 통해서만 접근이 가능하게 되는 것이다.
- 데이터 소스(Data Source) : 실제 데이터의 입출력이 실행되는 영역
- UpStream 관점에서 Repository 가 필요한 데이터를 Remote or Local Server 에 요청한다.
- DownStream 관점에서 Repository 에 데이터를 제공한다.
- 엔티티(Entity, 모델) : 네트워크나 로컬 DB 에서 받아온 DTO
'안드로이드 > Android' 카테고리의 다른 글
[Android] MVI (0) | 2025.01.26 |
---|---|
[Android] LiveData, Flow (0) | 2024.12.22 |
[Android] 프로그램(Program), 프로세스(Process), 스레드(Thread) (0) | 2024.12.11 |