- 모듈은 앱을 구성하는 요소로, 관련된 소스 코드나 리소스 등을 하나로 묶는 단위이다.
- 프로젝트에는 여러 모듈을 포함할 수 있으며, 각 모듈은 다른 모듈을 종속성으로 사용할 수 있다. 각 모듈은 독립적으로 빌드, 테스트, 디버그가 가능하다.
- 안드로이드 스튜디오에서 새 프로젝트를 만들 때, 자동으로 생성되는 'app'도 하나의 모듈이다.
멀티 모듈이란?
멀티모듈 아키텍처는 애플리케이션을 여러 개의 독립적인 모듈로 나누어 개발하는 방법으로, 애플리케이션을 모듈이라는 독립적인 단위로 분리하여 각각의 모듈이 독립적으로 기능을 수행하도록 하는 것이다.
생성 하는 법
안드로이드 스튜디오 상단에서 File - New - New Module
을 선택한다.
Android Library
탭에서 생성할 모듈의 이름을 Module name
에 입력하고 Finish 한다.
settings.gradle.kts
파일에서 'domain'
모듈이 추가되었고, 해당 모듈을 포함시키기 위해 include(":domain")
구문이 추가되어 app과 domain 두 개의 모듈이 함께 사용할 수 있게 되었다.
멀티 모듈 설계
레이어별로 모듈을 분리한 구조
Clean Architecture를 기반으로 Presentation, Domain, Data, App으로 구성할 수 있다.
Presentation Layer
- 사용자 인터페이스 관련 로직을 처리하고, 사용자 입력을 수신하며 Domain Layer와 상호작용
- 구성
- View: Activity, Fragment, Jetpack Compose와 같은 UI 요소
- ViewModel: UI 데이터를 가져와 처리하고 UI로 전달하는 역할
- ViewState: UI의 상태를 나타내는 데이터 클래스로 LiveData나 StateFlow와 함께 사용
Domain Layer
- 핵심 비즈니스 로직을 처리하며 Presentation Layer와 Data Layer 간의 중재 역할
- 구성
- UseCase: 특정 비즈니스 로직을 처리하는 단위로, 애플리케이션의 핵심 로직을 포함
- Repository Interface: 데이터 소스와 상호작용하는 인터페이스로, Domain Layer에서는 구현체가 아닌 인터페이스만 참조
- Entity: 비즈니스 로직에서 사용되는 핵심 데이터 모델
Data Layer
- 외부 데이터 소스(API, DB 등)와의 통신을 담당하고, Domain Layer에 데이터를 제공
- 구성
- Repository 구현체: Repository Interface를 구현한 클래스. 데이터를 실제로 가져오고 가공
- Data Source: API, 데이터베이스 등 외부 데이터 소스와의 상호작용을 처리
- Model (DTO/DB Entity): 데이터 소스(API, DB 등)에서 사용하는 데이터 구조로, 데이터 전송 객체(DTO)나 데이터베이스 엔티티를 포함
App Layer
- 애플리케이션의 전반적인 설정과 DI(Dependency Injection) 및 애플리케이션의 생명주기를 관리
- 구성
- Application Class: 애플리케이션의 초기 설정 및 전역 상태를 관리
- DI 모듈: 의존성 주입을 위한 설정을 포함하며, 각 레이어의 구성 요소를 연결하는 역할
의존 방향
Presentation → Domain ← Data
Presentation Layer → Domain Layer
: Presentation Layer는 사용자 인터페이스와 관련된 로직을 처리하며, 비즈니스 로직을 수행하기 위해 Domain Layer의 UseCase나 서비스를 호출한다.
Domain Layer ← Data Layer
: Domain Layer는 비즈니스 로직을 처리하며, 필요한 데이터를 가져오기 위해 Data Layer의 Repository를 호출한다.
Core 모듈 하에 여러 레이어가 분리된 구조
core
라는 하나의 모듈 안에 여러 레이어(Data, Domain, Local, Remote 등)가 하위 폴더로 나뉘어져 있다.
- 모든 코드가 하나의 모듈에 포함되지만, 폴더 구조를 통해 기능별로 구분한다.
일반적으로 다음과 같은 하위 폴더들이 포함된다.
common (혹은 util)
- 공통적으로 사용되는 유틸리티 클래스들(StringUtils, DateUtils...), 확장 함수, 상수 등이 위치한다.
- 여러 레이어에서 사용할 수 있는 코드가 포함되며, 다른 레이어에 종속되지 않는 독립적인 기능들이 주를 이룬다.
core/common/
├── utils
├── extensions
└── constants
domain
- 비즈니스 로직을 포함하는 계층으로, Entity, UseCase, Repository Interface가 정의된다.
- Data Layer나 Presentation Layer에 의존하지 않으며, Domain 계층이 독립적으로 동작할 수 있도록 설계된다.
core/domain/
├── model
├── usecase
└── repository
data
- 데이터 소스와 관련된 모든 로직이 포함되며, 원격 서버와의 통신(API), 로컬 데이터베이스(Room, SharedPreferences)와 상호작용하며, 이를 위한 Repository 구현체가 위치한다.
- Data 계층은 Domain 계층에서 정의된 Repository 인터페이스를 구현하며, 데이터를 어떻게 제공할지를 결정한다.
core/data/
├── remote
├── local
└── repository
remote
- API와 같은 네트워크 통신을 담당하는 모듈로, Retrofit이나 OkHttp를 사용한 네트워크 호출 코드, 원격 데이터 모델(DTO), API 인터페이스 등이 포함된다.
core/remote/
├── api
└── dto
local
- 로컬 데이터베이스(Room), 캐시 데이터, 파일 저장소와 같은 로컬 저장소와의 상호작용을 담당한다.
- 데이터베이스 엔티티, DAO, SharedPreferences와 관련된 코드가 위치한다.
core/local/
├── dao
├── database // Room 데이터베이스 설정
└── entity // 로컬 데이터베이스 엔티티
oauth (또는 auth)
- 인증 및 인가와 관련된 로직을 담당한다.
- OAuth2, JWT 토큰 관리, 로그인/로그아웃 처리와 관련된 코드가 위치한다.
core/oauth/
├── auth
└── token
구조 비교
구조 | 장점 | 단점 |
---|
모듈별 분리 | - 높은 독립성과 재사용성 - 의존성 역전 원칙 준수 - 유연한 확장성 - 명확한 의존성 관리 | - 복잡한 구조 - 작은 프로젝트에 오버헤드 발생 |
Core 모듈 통합 구조 | - 코드 재사용성 증가 - 의존성 관리 간소화 - 모듈 간 결합도 감소 - 프로젝트 확장성 | - 모듈의 커짐에 따른 복잡도 증가 - Core 모듈이 지나치게 커질 경우 기능 추가 및 분리시 어려움 발생 |
멀티 모듈의 장점
- 기능을 독립적인 모듈로 나눔으로써 코드의 재사용과 유지보수성을 높일 수 있다.
- 변경된 모듈만 빌드하므로 전체 빌드 시간이 줄어들 수 있다.
- 단, 모듈간 종속성이 복잡해지고 모듈의 수정이 많다면 빌드 시간이 증가될 수 있다.
- 모듈 간의 의존성을 명확하게 정의하고 관리할 수 있다.
- 각 모듈을 독립적으로 테스트할 수 있어 버그를 조기에 발견하고 해결할 수 있다.
- 기능별로 코드를 분리하여 프로젝트의 구조를 이해하기 쉽게 유지할 수 있다.
멀티 모듈의 단점
- 모듈 간의 의존성과 설정이 복잡할 수 있어 초기 설정과 관리가 어려울 수 있다.
- 각 모듈의 빌드 설정을 별도로 관리해야 하므로 관리 오버헤드가 증가할 수 있다.
- 모듈이 많아지면 프로젝트 구조가 복잡해져 이해하기 어려울 수 있다.
🤔 멀티 모듈을 적용하면 좋은 프로젝트는?
멀티 모듈은 대규모 프로젝트, 여러 팀이 협업하는 프로젝트나 재사용성이 높은 프로젝트에 적합하다고 볼 수 있다. 또한, 테스트가 중요한 경우나 확장 가능성이 큰 프로젝트에서 모듈화를 통해 관리와 유지보수가 쉬워지고 빌드 속도가 개선될 수 있다.
참조
안드로이드 [Kotlin] - Clean Architecture / 모듈화