
본 포스트는 app 모듈 하나만 사용하고 있던 프로젝트에서, 멀티 모듈 프로젝트로 리팩토링했던 과정을 정리합니다.
[목차]
1️⃣ 멀티 모듈로 전환을 결심한 이유
2️⃣ 멀티 모듈의 장점
3️⃣ 모듈 생성 흐름
4️⃣ 결론
현재 진행중인 프로젝트는 시작한 지 얼마 되지 않았고,
초기 단계부터 빠르게 기획되고 개발하는 흐름을 가지고 있습니다.
그래서 기획의 변경과 화면 디자인의 변경이 잦고,
새로운 화면이 계속 생기거나 이미 만든 화면이 베타 버전에선 필요 없는 상황이 생기는 등의 이슈가 생겼습니다.
이러한 변화에 따라 빠른 개발 속도를 유지하기 위해서 코드가 살짝 복잡해지고 의존 관계가 허술해지더라도 싱글 모듈로 구현하고 있었으며, 디렉토리를 분리하는 방법으로 구현하고 있었습니다.
다만 한 모듈 내에 있었기 때문에 의존 관계가 불분명해지는 경우도 있고, 코드량이 증가하면서 관리의 불편함이 생기기 시작했습니다.
firebase 관련 클래스를 feature, data, activity에서 의존하며,
단일 UI 컴포넌트가 여러 화면에서 사용되는 공통 UI 컴포넌트로 바뀌고,
feature 디렉토리 내 16개의 서브 디렉토리 존재하고 있음
현재 펀딩이 진행 중이고, 관련해서 크게 기획이 변경되며 개발이 잠시 딜레이되고 있습니다.
이 틈을 타 추후 유지보수 및 확장성을 대비해 멀티 모듈로 리팩토링하려고 합니다.
Android 앱 모듈화 가이드를 보면, 여러 장점들을 확인할 수 있습니다.
모듈화를 통해 코드를 공유하고 각각 원하는 기능만 뽑아서 여러 앱을 빌드 가능
internal,private등 키워드를 통해 다른 모듈에서 사용하지 못하도록 캡슐화 가능
관심사 분리 원칙을 사용해 코드의 확장성과 유지보수성을 챙김
모듈별 테스트가 가능해 테스트 용이성이 높음
증분 빌드를 사용해 빌드 성능을 개선할 수 있음
본 프로젝트에서는 특히 다음과 같은 장점을 얻을 수 있습니다.
1. 구현은 했지만 베타버전에선 필요없는 화면들을 베타버전에 제외하고 출시한다.
2. 16개의 feature 들을 internal 키워드로 분리해서 외부 모듈에서 사용을 막아 코드 재사용성과 휴먼 에러를 줄인다.
AI를 통해 기존 프로젝트 구조를 출력하고,
nowinandroid 패키지를 참고해 리팩토링할 모듈 구조를 설계했습니다.
기존 프로젝트 구조
├── app/ # 앱 엔트리 포인트
│
├── core/ # 공통 코어 레이어
│ ├── common/ # 공통 유틸리티 및 컴포넌트
│ │ ├── component/ # 공통 UI 컴포넌트
│ │ ├── exception/ # 예외 처리
│ │ ├── extension/ # 확장 함수
│ │ ├── shape/ # 커스텀 Shape
│ │ ├── type/ # 공통 타입
│ │ └── util/ # 유틸리티 (context, polygon 등)
│ │
│ ├── data/ # 데이터 레이어
│ │ ├── api/service/ # API 서비스
│ │ ├── datasource/ # 데이터소스 인터페이스
│ │ ├── datasourceimpl/ # 데이터소스 구현체
│ │ ├── datastore/ # DataStore (token, user)
│ │ ├── di/ # 의존성 주입
│ │ ├── model/ # 데이터 모델 (request, response, mapper)
│ │ ├── network/ # 네트워크 (인터셉터 등)
│ │ ├── pagination/ # 페이징 처리
│ │ ├── repository/ # 레포지토리 인터페이스
│ │ └── repositoryimpl/ # 레포지토리 구현체
│ │
│ ├── designsystem/ # 디자인 시스템
│ │ └── theme/ # 테마 정의
│ │
│ ├── model/ # 도메인 모델
│ └── navigation/ # 네비게이션
│
└── feature/ # 기능별 모듈
├── addfeed/ # 피드 추가
├── chat/ # 채팅 (list, room)
├── community/ # 커뮤니티 (general, volunteer, shelter)
├── follow/ # 팔로우
├── home/ # 홈 피드 (like, comment, feed, report)
├── location/ # 위치
├── login/ # 로그인
├── main/ # 메인
├── map/ # 지도
├── mypage/ # 마이페이지
├── notification/ # 알림
├── otherprofile/ # 타 사용자 프로필
├── profile/ # 내 프로필
├── sample/ # 샘플
├── signup/ # 회원가입
├── splash/ # 스플래시
└── volunteer/ # 봉사활동
목표 구조
Pure한 Kotiln Library는 :core:model 만 사용합니다.
그 외에는 전부 Android Library를 사용해 생성할 예정입니다.
모듈 생성 흐름은 다음과 같이 설정했습니다.

모듈 명을 작성한 후, 패키지 이름을 작성합니다.
서브모듈인 경우에는 :를 통해 하위 구조를 설계합니다.
이 때 순수 퓨어한 Kotlin 모듈은 경우에는 Java or Kotlin Library를, 아니라면 Android Library로 생성합니다.
(클린 아키텍처라면 domain 모듈이 순수 Kotlin 모듈이 될 것입니다.)
모듈을 생성한다면 프로젝트 단위의 settings.gradle.kts에 생성한 모듈에 대한 구문이 생길 것입니다.

이후 각 모듈별로 필요한 의존성을 추가해주고 관련 코드들을 모듈로 이동해줍니다.
의존성: hilt, ksp, serialization, firebase, compose, datastore 등등...
마지막으로 app 모듈의 build.gradle.kts의 dependency에서 추가한 모듈을 implementation 해줍니다.

이제 코드를 빌드해서, 제대로 실행히 되는 지, 기존 코드대로 잘 동작하는 지(중요!) 확인합니다.
잘 실행되었다면 다음 모듈로 진행합니다.
제 프로젝트 기준으로 다른 모듈을 의존하지 않는 모듈을 정리해드리겠습니다.
core:commoncore:datastorecore:designsystemcore:firebasecore:modelcore:navigationcore:data:apicore:model, core:networkcore:data:implcore:common, core:data:api, core:datastore, core:firebase, core:model, core:networkcore:networkcore:datastore, core:firebasecore:uicore:common, core:designsystem, core:modelfeature 모듈은 다른 feature 모듈을 의존하는 경우가 많습니다.
따라서 그 전에 AI Agent에 코드베이스를 읽게 시켜서 모듈 간 의존성을 확립한 후에 설계하시면 좋을 것 같습니다.
feature 모듈에는 공통적으로 아래 모듈들을 의존시키면 됩니다.
core:common, core:data:api, core:designsystem, core:naivgation, core:ui
모듈 특성에 따라서 core:firebase, core:model, 다른 feature 모듈을 추가하면 되겠습니다.
이렇게 모든 모듈을 생성하고 코드를 이동하는 멀티모듈 리팩토링이 완료가 되었습니다.
다만, 이제 한 가지 개발자로서 불편한 점이 있습니다.
거의 대부분의 모듈들의 build.gradle.kts 파일에 같은 코드들이 중복되어 있는 것을 볼 수 있습니다.
다음 편에서는 이런 코드 중복은 컨벤션 플러그인을 사용해서 줄이고, build.gragle.kts 파일을 어떻게 쉽게 관리하는 지 확인하는 포스팅을 진행하겠습니다.
https://developer.android.com/topic/modularization?hl=ko
https://brunch.co.kr/@purpledev/43
settings.gradle.kts 내에
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")를 통해 Type Safe한 모듈 주입이 가능합니다~AS IS
TO BE