앱을 여러 Gradle 모듈로 쪼개는 것.
무엇이 무너지길래
단일 모듈이 커지면
- 한 줄만 고쳐도 전체를 다시 컴파일한다 → 빌드가 몇 분씩
- 아무 클래스나 참조할 수 있어 경계가 없다 → 스파게티
- 팀이 같은 파일을 동시에 고쳐 충돌한다
왜 빌드가 빨라지나
-
증분 컴파일 — 바뀐 모듈과 그것에 의존하는 모듈만 다시 컴파일한다
-
병렬 컴파일 — 의존 관계가 없는 모듈은 동시에 컴파일된다
-
빌드 캐시 — 안 바뀐 모듈의 결과를 재사용한다
-
단, 모든 모듈이 core 하나에 의존하면
- core 를 고칠 때마다 전부 다시 컴파일된다
- 이득이 사라진다. core 를 더 잘게 나눠야 한다
경계를 강제한다
// internal 은 같은 모듈 안에서만 보인다
internal class UserRepositoryImpl : UserRepository // 밖에서는 인터페이스만 보인다
단일 모듈에서는 internal이 의미가 없다 — 모듈이 나뉘어야 캡슐화가 컴파일러 수준에서 강제된다.
구조 — feature와 core
- :app — 조립만 한다 (네비게이션 · DI 설정)
- :feature:home
- :feature:profile — 화면 단위. 서로를 참조하지 않는다
- :core:data — Repository 구현
- :core:domain — 모델 · UseCase
- :core:network — Retrofit
- :core:database — Room
- :core:ui — 공용 컴포넌트 · 테마
by feature vs by layer
-
by layer — :ui :domain :data
- 기능 하나를 고치려면 세 모듈을 전부 건드린다
- 팀이 같은 모듈에서 충돌한다
-
by feature :feature:home — :feature:profile
- 기능 하나의 변경이 한 모듈 안에서 끝난다
- 팀이 모듈 단위로 나뉜다
- 나중에 기능을 통째로 떼어내기 쉽다
-
feature 안에서 다시 계층을 나누는 것이 실무의 조합이다
implementation vs api
dependencies {
implementation(project(":core:network")) // 이 모듈만 쓴다. 밖으로 안 새어 나간다
api(project(":core:domain")) // 나를 쓰는 모듈에도 노출된다
}
implementation 을 쓰면
- 이 의존성이 바뀌어도 나를 쓰는 모듈은 다시 컴파일되지 않는다
- 빌드가 빨라진다
기본은 implementation, 공개 API 시그니처에 등장할 때만 api
언제 하지 말까
-
혼자 만드는 작은 앱
-
화면이 열 개 미만
-
아직 도메인 경계가 안 정해졌다
-
모듈 경계를 잘못 그으면 되돌리는 비용이 크다
- 먼저 패키지로 나눠 보고, 경계가 굳으면 모듈로 승격한다
면접 함정
- ❌ "모듈이 많을수록 빠르다" → 모듈 간 의존이 얽히면 오히려 느려진다. Gradle 설정 오버헤드도 있다.
- ❌ "멀티 모듈은 대규모 앱만" → 규모보다 경계가 명확한가가 기준이다.
모듈 간 화면 이동을 어떻게 하나
문제: :feature:home 이 :feature:profile 을 모르는데 어떻게 이동하나
-
방법 1 — 라우트 문자열만 공유한다
- :core:navigation 에 "profile/{id}" 상수를 두고 :app 이 실제 화면을 등록한다
-
방법 2 — 인터페이스를 공유한다
- :core:navigation 에 interface ProfileNavigator
- :feature:profile 이 구현하고 Hilt 가 연결한다
어느 쪽이든 feature 끼리는 직접 참조하지 않는다
빌드 설정 중복을 어떻게 줄이나
// buildSrc 또는 build-logic 에 컨벤션 플러그인을 만든다
plugins { id("myapp.android.feature") } // compileSdk · 코틀린 옵션 · 공통 의존성이 한 줄로
모듈이 20개인데 각 build.gradle.kts 에 같은 설정이 복붙돼 있으면 compileSdk 를 올릴 때 20군데를 고쳐야 한다
- 컨벤션 플러그인이 실질적으로 필수가 되는 지점
얼마나 빨라지나
-
전체 빌드는 오히려 조금 느려질 수 있다 (모듈 설정 오버헤드)
-
이득은 '한 파일 고치고 다시 빌드' 에서 나온다
- 단일 모듈: 전체 재컴파일
- 멀티 모듈: 그 모듈 + 의존하는 모듈만
-
그래서 core 를 자주 고치는 구조면 이득이 거의 없다
-
core 를 안정적으로 유지하는 것이 모듈화 성패를 가른다