배경
이번에 devfest에서 안성용 개발자님께서 발표하셨던 Android Modularization Recipe에 대해 보게 되었습니다.
기존에 제가 아는 Modularization은 아래 그림같은 형태로 app, feature, data, core 정도로 나눠서 구성했었는데
feature 단위가 커짐에 따라 어떻게 모듈을 구성해야할지, 재사용성과 빌드속도가 향상된 것인지 의아했던 부분이 있었는데
이번 기회에 저도 따라가면서 모듈 구조를 완성해보자 라는 생각에 학습하게 되었습니다.
기존 구성했던 방식과의 차이점
대부분의 오픈소스 프로젝트를 보면 위의 사진과 같이 큰 묶음으로 모듈을 묶고 있는걸 볼 수 있었습니다. (ex. pokedex, nowinandroid)
하지만 이렇게 개발했을 때 1개의 모듈에서 통합적으로 개발할 때보다 빌드속도가 향상되는 것은 맞지만
참조당하는 부분이 많은 모듈에 수정사항이 생겼을 때 참조하고 있는 모든 모듈들이 재빌드되는 것은 효율적이지 않습니다.
그래서 Android 공식 홈페이지에서도 종속항목역전이라는 항목으로 모듈 안에서 추상화(api)모듈과 구현(impl)모듈을 만드는 걸 권장하고 있습니다.
추가적으로 공식 홈페이지에는 feature에 관한 내용이 나와있지 않지만 Modularization Recipe 발표에서는 feature 모듈 역시도 추상화와 구현으로 나눠는 걸 권장하고 있습니다.
제가 기존 모듈화 구조처럼 개발하면서 느꼈던 애매한 점들은 다음과 같습니다.
• A모듈과 B모듈에서 공통으로 사용되지만 C모듈에선 사용되지 않는 UI를 공용 컴포넌트 모듈로 빼기엔 애매함 (디자인 시스템에 속하지 않는 컴포넌트)
⸰ 이런 경우 A, B의 공통 모듈을 생성하는 케이스도 존재하지만 매번 그렇게 만들다보면 모듈화의 기준과 관리가 애매해짐
• UseCase의 경우에도 애플리케이션 개발단위가 커질수록 domain이 비대해짐
반대로 feature-api, impl을 적용했을 때 장점은 다음과 같습니다.
• 관심사 분리를 기준으로 모듈을 잡고 다른 feature 모듈에서 필요로 할 때 해당 api 모듈만 사용하면 추가적인 모듈 생성이 필요없고 생성 기준이 명확해짐
• runtimeOnly로 App모듈 역시도 컴파일 시점에 어떤 모듈이 있는지 알 수 없으며, 이에 따라 개발 환경에서 각각의 개발자들이 개발할 때 Conflict 등의 협업 문제가 발생하지 않고 개발 효율성 증대
추상, 구현 모듈 구조 잡기
feature와 data 모듈 간의 관계는 다음과 같습니다.
기존 모듈 구조와 동일하지만 상위 모듈에서는 api 모듈만 참조하고 있는 모습입니다.
home 화면을 구성할 때 만약 detail의 Usecase와 home의 Usecase가 동시에 필요하다면 다음과 같은 의존성을 설정할 수 있습니다.
home:impl
• Usecase class
• UI (Composable Screen, Activity, Fragment, ViewModel 등)
home:api
• Usecase interface
• UI (Widget, ViewHolder, 컴포넌트 등)
:data:api
• repository interface
:data:impl
• repository class
• di module class
• test code
app모듈과 feature 모듈 구조 잡기
Android 공식 홈페이지에선 NavHost가 app 모듈에 존재하기 때문에 직접적으로 composable Screen을 명시할 수 있지만 해당 설계에선 runtimeOnly 관계로 의존성을 설정하여 컴파일 시점에 어떤 모듈이 있는지 파악할 수 없습니다.
따라서 navigator 모듈에서 각 feature의 navigator interface를 만들고 feature의 impl 모듈에서 상속받아 구현하는 과정을 거칩니다.
이렇게 상속받아 navigator를 구현받았다면 hilt를 통해 주입해줍니다.
그 다음 Navigator 모듈에 있는 NavHost에 파라미터로 넘겨줍니다.
마무리
모듈구조를 설정해보면서 느낀 점은 올해는 버전관리나 build Config 관련된 부분을 제외한 모듈화에 대해선 학습을 멈춰도 되겠다 싶을 정도로 많은 공부가 되었습니다.
이런 구조로 설계했을 때 앱 초기 개발 단계에서는 오버엔지니어링처럼 보일 순 있지만, 클린 아키텍처와 마찬가지로 앱이 성장할수록 개발 효율성이 증가할 것으로 예측됩니다.
설계하면서 가장 어려웠던 부분은 화면 전환 부분이었는데 hilt를 이용해서 가능하게 만들었습니다.
(만약 di를 사용하시지 않는다면 app이 implementation으로 모든 Feature 의존성을 가져야하지 않을까.. 라는 생각도 해봅니다)
현재 UI 부분이 마무리 되지 않아 지속해서 작업 중이면 빠르게 마무리해보도록 하겠습니다 :)
(브랜치: feat/home)
'Android' 카테고리의 다른 글
[Android Compose] CompositionLocalOf는 언제 쓰는거지? (0) | 2024.06.23 |
---|---|
[Android] Compose PreviewParameter 어디에 쓰는거지? (0) | 2024.06.20 |
GSON과 코틀린을 쓰면서 생겼던 일 (nonnull인데 왜 null 값이 들어갔지?) (0) | 2024.04.16 |
[Android] Kakao Login 구현하기3 - MVVM+UiState (1) | 2023.09.30 |
[Android] Kakao Login 구현하기2 - 프로젝트 설정 (0) | 2023.09.30 |
댓글