SouZip 안드로이드 프로젝트는 Android App Architecture를 기반으로 한 멀티모듈 구조를 채택하고 있습니다. 각 모듈은 기능 및 레이어별로 명확하게 분리되어 있으며, 이를 통해 코드의 재사용성을 높이고, 빌드 시간을 단축하며, 유지보수를 용이하게 합니다.
.
├── app
├── build-logic
├── presentation
├── domain
├── data
└── core
모듈 간의 의존성 흐름은 아래와 같습니다. 의존성은 항상 바깥쪽 레이어에서 안쪽 레이어로 향하며, Domain 레이어는 다른 레이어에 대한 의존성을 갖지 않습니다.
graph TD
subgraph App
app
end
subgraph Presentation Layer
presentation
end
subgraph Domain Layer
domain
end
subgraph Data Layer
data
end
subgraph Common/Shared
core
end
app --> presentation
app --> data
app --> domain
app --> core
presentation --> domain
presentation --> core
data --> domain
data --> core
- 애플리케이션의 진입점 역할을 하는 최상위 모듈입니다.
presentation,data등 모든 모듈을 통합하고 최종적으로 하나의 Android 애플리케이션(.apk)으로 패키징됩니다.- Dagger Hilt를 사용한 전역 의존성 주입 설정 등 앱 실행에 필요한 초기화를 담당합니다.
- Gradle Convention Plugin을 사용하여 모듈별 Gradle 설정을 중앙에서 관리합니다.
- 이를 통해 여러 모듈에 걸쳐 일관된 의존성 버전 관리와 플러그인 설정을 유지하여
build.gradle.kts파일의 복잡성을 줄입니다.
- UI와 관련된 모든 로직을 담당하는 모듈입니다.
- 화면(Composable UI), 상태 관리(ViewModel), 네비게이션 등을 포함합니다.
- MVI (Model-View-Intent) 아키텍처 패턴을 사용하여 UI 상태를 관리하며,
domain모듈의 Repository 인터페이스의 함수를 호출해 비즈니스 로직을 실행합니다.
- 순수한 Kotlin 모듈로, 애플리케이션의 핵심 비즈니스 로직을 포함합니다.
- Models: 앱의 비즈니스 로직에 사용되는 모델 data class 입니다.
- Repository Interfaces: 데이터 계층에서 구현해야 할 데이터 입출력 명세를 정의합니다.
- 안드로이드 프레임워크에 대한 의존성이 없어 비즈니스 로직의 테스트가 용이합니다.
domain모듈에서 정의한 Repository 인터페이스에 대한 구현체를 포함합니다.- Repository Pattern을 사용하여 데이터 소스를 추상화하고, 데이터의 출처(Remote, Local)를 숨깁니다.
- Remote Data Source: Retrofit을 사용하여 서버 API와 통신합니다.
- Local Data Source: Room DB, DataStore 등을 사용하여 디바이스 내부에 데이터를 저장하고 관리합니다.
- 여러 모듈에서 공통으로 사용되는 유틸리티 및 기반 코드를 포함합니다.
- 자세한 내용은 https://developer.android.com/topic/architecture 와 https://developer.android.com/topic/architecture/recommendations 를 참고하세요.
- 프로젝트 전체의 기반이 되는 아키텍처로, 계층 분리(Separation of Concerns) 와 의존성 규칙(Dependency Rule) 을 통해 유연하고 테스트하기 쉬운 구조를 만듭니다.
presentation모듈은 단방향 데이터 흐름(Unidirectional Data Flow)을 따르는 MVI 패턴으로 구현되었습니다.orbit-mvi라이브러리를 사용하여 상태 관리를 효과적으로 처리합니다.- Intent: 사용자의 액션 또는 입력. 각 화면에서 사용될 Intent는
com.swyp.souzip.presentation.common.base의UiIntent를 상속받아 구현됩니다. - State: UI에 표시될 데이터. State가 변경되면 UI가 반응하여 다시 그려집니다. 각 화면에서 사용될 State는
com.swyp.souzip.presentation.common.base의UiState를 상속받아 구현됩니다. 구현되는 UiState의 프로퍼티들은 모두 Parcelable 하여야 합니다. - Side Effect: Toast, SnackBar, 화면 이동 등 일회성 이벤트를 처리합니다. 각 화면에서 사용될 SideEffect는
com.swyp.souzip.presentation.common.base의SideEffect를 상속받아 구현됩니다. - ViewModel: Intent를 받아 비즈니스 로직(UseCase)을 처리하고, 상태(State)를 업데이트합니다.
- BaseViewModel.kt: MVI 패턴에 따라 orbit-mvi 라이브러리를 적용한 베이스 뷰모델 추상 클래스입니다. presentation 레이어의 모든 뷰모델이 상속받아 구현하여 모든 뷰모델이
orbit이 적용된 mvi 패턴을 따르도록 강제합니다. UI에서 뷰모델의 로직을 호출하는 방법은 오직
onIntent()함수로 유일하며, 각 뷰모델에서 구현되는 다른 함수는 모두 private 접근지정자를 갖습니다.
data모듈은 Repository Pattern을 사용하여 데이터 소스를 추상화합니다.- ViewModel은 데이터가 어디서 오는지(네트워크, DB, 캐시 등) 알 필요 없이
domain의 Repository 인터페이스에만 의존하므로, 데이터 소스의 변경에 유연하게 대처할 수 있습니다.
- 데이터는
Remote(DTO)->Repository->Domain(Model)->Presentation(UI State)순서로 흐르며, 각 계층의 경계를 넘어갈 때 필요한 모델로 매핑됩니다.. 이를 통해 각 레이어는 자신에게 필요한 데이터 형태에만 집중할 수 있습니다.