일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- Compose
- textfield
- 쿠링
- Codeforces
- MyVoca
- Rxjava
- AWS
- 암호학
- architecture
- Gradle
- 코루틴
- Coroutine
- activity
- Coroutines
- Hilt
- MiTweet
- relay
- android
- 백준
- androidStudio
- 코드포스
- 프로그래머스
- Python
- Kotlin
- livedata
- pandas
- boj
- ProGuard
- GitHub
- TEST
- Today
- Total
이동식 저장소
[Android] 모듈화 가이드 본문
모듈화란 코드를 여러 부분으로 나누어 느슨하게 의존하도록 만드는 것이다. 각 모듈은 자신이 맡은 기능만을 수행하며, 다른 모듈은 신경쓰지 않는다. 다만 외부에 노출할 인터페이스만을 제공한다.
모듈을 나누는 방법에는 여러 가지가 있겠지만, 나는 맡은 역할에 따라 레이어로 나눈 후 기능별로 다시 나누는 편이다. 예를 들어 데이터를 저장하는 Data 모듈을 정의하고, 저장하는 데이터에 따라 ``:data:meal``과 ``:data:schedule`` 모듈을 정의하는 것이다. Android 팀에서 권장하는 방법이다. (Android Dev Summit '22 영상 참고)
앱을 모듈화하면 코드를 재사용하기 쉬워지고, 외부와의 인터페이스를 제외한 나머지를 숨길 수 있으며, Google Play의 기능을 사용하여 필요한 모듈만 선택적으로 delivery할 수도 있다. 물론 가장 큰 장점은 코드를 관리하기 쉬워진다는 것이다.
간접적으로 캡슐화, testability, 빌드 시간 단축 등의 효과도 누릴 수 있다. 특히 수정되지 않은 모듈을 자동으로 skip하는 Gradle 특성상 빌드 시간 단축에 큰 도움이 된다. 실제로 전혀 모듈화되지 않은 MyVoca의 빌드 시간은 7분 이상 걸리는데, 절반 정도 모듈화한 한빛 캘린더는 5분 30초 내외로 끝난다. 완전히 모듈화되지 않았음에도 불구하고 시간이 크게 줄어든다.
물론 모듈을 어떻게 나눌지는 개발자의 몫이다. 너무 덜 나누면 모듈화의 장점을 누리기 힘들어지고, 그렇다고 너무 많이 나누면 모듈화 자체의 오버헤드가 커지기 때문에 코드의 크기와 역할을 고려하여 적절히 나누어야 한다. 간단한 앱에서는 모듈화가 아예 필요하지 않을 수도 있지만, 데이터를 가져와서 저장하고 보여주기만 하는 한빛 캘린더 정도의 앱조차도 모듈화가 꼭 필요한 걸 보면 웬만한 앱은 모듈화하는 게 맞다.
일반적인 모듈화 패턴
일반적으로 앱을 모듈화할 때는 코드의 결합도와 응집도를 고려한다. 고등학교 글쓰기 시간에는 결합도와 응집도가 모두 높아야 한다고 배우지만, 코드를 모듈화할 때는 결합도를 낮추면서 응집도를 높이는 전략을 취해야 한다. 그 이유는 결합도와 응집도의 의미를 이해하면 알 수 있다.
코드의 결합도란 모듈이 서로 의존하는 정도를 의미한다. 모듈 간의 결합도가 낮다면 모듈 내부가 바뀌어도 외부에는 그 사실이 거의 드러나지 않으며, 다른 모듈이 내부적으로 어떻게 구현되었는지 전혀 알지 못할 것이다. 사실 알 필요도 없다.
코드의 응집도란 모듈로 묶인 코드가 수행하는 역할이 얼마나 비슷한지를 의미한다. 비슷한 기능을 수행하는 코드끼리 묶인 모듈을 응집도 높은 모듈이라고 말한다.
이제 결합도가 낮아야 하는 이유, 응집도가 높아야 하는 이유를 알겠지? Low 결합도, high 응집도를 기본으로 하여 코드를 모듈화하면 된다.
모듈의 종류
앱의 아키텍쳐에 따라 모듈을 나누는 방법이 크게 달라지는데, 여기서는 Android 추천 아키텍쳐에서 사용할 수 있는 모듈을 공부해 보겠다.
Data module
데이터 연산을 처리하는 모듈이다. Data module은 데이터 및 비즈니스 로직을 캡슐화하며, 데이터에 접근할 수 있는 API를 외부에 제공하는 동시에 모듈의 구현 방법을 철저히 숨겨야 한다. SQL이나 MongoDB 등 뭘 쓰던간에 외부와 통신할 때는 data module에서 제공하는 API만을 사용하도록 강제해야 한다.
Data source와 Repository, 실제 데이터베이스 라이브러리 등이 포함된다.
Feature module
독립적인 기능 하나를 담당하는 모듈로, UI와 관련된 코드를 작성하는 모듈이다. Navigation의 각 destination을 하나의 feature module로 구현할 수 있다.
UI 코드와 ``ViewModel`` 등이 포함된다. 모듈 하나에 화면을 하나만 써야 하는 건 아니며, 필요하다면 여러 개의 화면을 구현해도 된다.
Feature module은 data module에 의존한다.
App module
앱의 진입점 역할을 하는 모듈이다. Feature module을 조합하여 앱의 root navigation을 만든다.
아래 그림처럼 build variant를 활용하면 하나의 app 모듈을 여러 개의 바이너리로 컴파일할 수 있다.
앱이 자동차나 wear 등 여러 종류의 디바이스에서 실행될 수 있다면, 디바이스의 종류별로 app 모듈을 만들 수 있다. 모듈을 잘 나눠놨다면 기기마다 다른 기능을 쉽게 포함할 수 있다.
Common(Core) module
다른 모듈에서 자주 사용하는 코드를 정의하는 모듈이다. 아키텍쳐상 어느 레이어에도 속하지 않지만, 코드의 재사용성을 높이기 위해 정의하는 모듈이다.
- UI module: 커스텀 UI element 등을 정의하는 모듈이다. 앱 전체에 통일된 스타일을 적용하고 싶을 때에도 정의할 수 있다.
- Analytics module: 앱의 사용 통계를 분석하는 코드를 정의하는 모듈이다. 보통 분석 작업은 여러 모듈을 참조하므로, 분석 작업만을 담당하는 별도의 모듈을 정의하면 좋다.
- Network module: 네트워크 연결을 담당하는 모듈이다. 여러 모듈에서 공통적으로 사용하는 네트워크 코드를 정의할 수 있다.
- Utility module: 앱에서 자주 사용되는 자잘한 코드를 정의하는 모듈이다. ``LocalDate``를 ``String``으로 변환하는 코드 등 작지만 소중한 코드를 모아놓는 모듈이다.
모듈끼리 통신하는 방법
모듈끼리 데이터를 자주 주고받는 상황에서도 의존성을 최대한 낮추기 위해 노력해야 한다. 정보를 주고받는 모듈 사이에 중개자 모듈을 정의해 보자.
Home 모듈에서 선택한 책의 정보를 checkout 모듈에 전달하고 싶다면, 책의 ID를 navigation argument로 전달할 수 있다. 여기서는 app module이 중개자 역할을 맡았다.
// app 모듈에서 호출
navController.navigate("checkout/$bookId")
Navigation argument에는 최대한 간단한 정보만을 담아야 한다. Home에서 ``Book`` 객체를 직접 보낸다면 home 모듈이 ``Book``의 source가 되므로 single source of truth 원칙이 깨진다. 모든 데이터의 source는 data module이어야 하기 때문이다.
Home 모듈은 책의 ID만을 전달하고, ``Book`` 객체는 checkout 모듈에서 data 모듈에 요청하여 가져오게 한다면 single source of truth 원칙을 지키면서 앱의 결합도를 낮출 수 있다.
그 외의 조언
그 밖에 일반적으로 고려할 만한 내용을 정리해 보았다.
Configuration 관리하기
Configuration이란 모듈에서 사용하는 라이브러리 버전 등의 설정을 의미한다. 모듈이 많아질수록 모듈 자체를 관리하는 일도 복잡해지기 때문이다.
예를 들어 Gradle의 version catalog 기능을 이용하면 앱에서 사용하는 라이브러리를 파일 하나에서 모두 정의할 수 있다. 라이브러리가 업데이트되어도 catalog 파일만 수정하면 된다. Version catalog에 대해서는 별도로 글을 써 보겠다.
최소한으로 노출하기
날씨가 추우니까...... 모듈 외부에 꼭 드러내야 하는 필수 API만 public(또는 internal)으로 선언하고, 나머지는 숨겨야 한다. 내부 구현까지 노출하면 모듈을 선언하는 의미가 퇴색된다.
가급적 Pure Kotlin 모듈을 작성하기
모듈에서 Android API를 참조하지 않는다면 가급적 Kotlin 모듈로 작성해 보자. Android module은 기본적으로 포함하는 라이브러리와 플러그인이 있어 오버헤드가 상대적으로 크기 때문이다.
참고문헌
'Primary > Android' 카테고리의 다른 글
[Android Studio] Flamingo 신기능 정리 (0) | 2022.12.07 |
---|---|
[Android Studio] Electric Eel 신기능 정리 (0) | 2022.12.04 |
[Android] 백그라운드 작업을 하나씩 처리하는 방법 (0) | 2022.11.08 |
[Android] 반응형 앱 구현 방법론 (2) (0) | 2022.10.18 |
[Android] 반응형 UI 구현 방법론 (0) | 2022.10.17 |