모듈이란?
모듈(module)이란 앱을 구성하는 요소로, 관련된 소스 코드나 리소스 등을 하나로 묶는 단위다.
최근에는 앱 모듈에 모든 코드를 작성하지 않고, data나 domain 등의 모듈로 세분화 시켜 개발한 뒤 최종적으로 프로덕트를 만들 때 이러한 모듈들을 합쳐서 하나의 애플리케이션을 만들게 된다. 이런 방식을 모듈화 프로그래밍이라고도 한다.
모듈은 다음과 같은 특징을 갖는다
- 모듈은 논리적 또는 기능적으로 분리되어 격리되고 독립적인 일을 수행한다.
- 통상 모듈 하나가 하나 이상의 완전한 기능을 수행할 수 있다.
- 작개 쪼개어 다루므로 단순하고 간단명료 하다. 예) 테스트, 재사용 및 유지보수가 쉬움
단일 모듈 VS 다중 모듈
단일 모듈 앱과 다중 모듈 앱 어떤 전략이 더 좋은 전략일까? 각각의 특징을 알아보고 어느 편이 더 나은지 생각해보자.
단일 모듈 앱
하나의 앱 모듈로만 구성된 앱을 의미한다. 개인 개발자, 소규모 개발사들이 앱을 만들 때 취하는 가장 이해하기 쉽고, 간편한 형태이다. 하지만 단일 모듈로 구성된 앱은 자연스레 소스코드 간의 결합도가 높아지기 쉽다. 시간이 지난뒤 확인해보면 거대한 하나의 덩어리 형태로 되어 있어 수정이 어렵게 된다.
다중 모듈 앱
여러 모듈로 구성된 앱을 의미 한다. 앱의 코드베이스가 큰 경우에 다중 모듈 구성을 적용하는 것이 적합하다. 시작하는 사람들에게 다중 모듈 프로젝트는 이해하기 어렵고, 가독성이 떨어질 수 있다. 하지만 코드의 결합도가 낮기 때문에 앱을 확장하거나 수정할 때, 훨씬 더 유연하게 대응할 수 있다.
다중 모듈을 구성하는 기준
모든 프로젝트에 알맞는 단 하나의 다중 모듈화 전략은 존재하지 않는다. 회사의 비즈니스, 고객의 요구사항 및 기타 환경에 알맞게 전략을 세워 멀티 모듈 프로젝트를 꾸려야 한다.
안드로이드 공식 문서에서 안내하고 있는 내용을 기반으로 설명하자면, 일반적으로 모듈을 구성하는 기준은 다음과 같이 3가지 항목이 있다.
- 응집도(Cohesion)
- 결합도(Coupling)
- 세분성(Granularity)
응집도(Cohesion)
응집도는 소프트웨어 공학에서 모듈 내부의 구성요소들이 하나의 목적을 위해 얼마나 밀접하게 관련되어 있는지를 나타내는 개념이다.
높은 응집도를 갖는 모듈은 해당 모듈의 내부 구성요소들이 동일한 목적을 가지고 서로 밀접하게 관련되어 있어서 모듈의 기능을 구현하고 유지보수하기 쉽고, 반면에 낮은 응집도를 갖는 모듈은 다양한 목적을 가지고 있는 구성요소들이 서로 무관하게 연결되어 있어서 모듈의 기능을 파악하기 어렵고 유지보수하기 어렵다
응집도는 소프트웨어 설계 단계에서 고려되어야 하는 중요한 요소 중 하나이며, 모듈의 기능과 목적을 명확하게 파악하고, 모듈 내부의 구성요소들을 효과적으로 조직화하여 모듈의 응집도를 높이는 것이 좋은 소프트웨어 개발 방법론의 핵심 원칙 중 하나다.
결합도(Coupling)
결합도는 소프트웨어 공학에서 다른 모듈들과의 상호작용의 정도를 나타내는 개념이다.
높은 결합도를 갖는 모듈은 다른 모듈들과 밀접하게 상호작용하므로, 한 모듈의 변경사항이 다른 모듈들에게 영향을 미칠 가능성이 크다. 이는 모듈간 의존성이 강하고, 유지보수가 어려워질 수 있는 반면에 낮은 결합도를 갖는 모듈은 다른 모듈들과의 상호작용이 적어서, 한 모듈의 변경사항이 다른 모듈들에게 영향을 덜 주게 된다.
결합도 또한 소프트웨어 설계 단계에서 고려되어야 하는 중요한 요소 중 하나이며, 모듈 간의 관계를 최대한 느슨하게 유지하면서 기능을 구현하는 것이 좋은 소프트웨어 개발 방법론의 핵심 원칙 중 하나다. 모듈 간의 결합도를 낮추기 위해서는 인터페이스 사용 등을 통해 코드를 추상화 할 수 있다.
세분성(Granularity)
Granularity는 소프트웨어 설계에서 모듈의 크기와 복잡도에 대한 개념으로, 모듈이 나누어지는 기준, 크기 및 세부화 정도를 나타낸다.
세분화 정도는 다음과 같은 식으로 표현할 수 있다.
Granularity = 모듈 수 / 코드 베이스
모듈의 세분화 정도가 적절하지 않으면 코드의 가독성이 떨어지고 유지보수가 어려워지는 문제가 발생할 수 있다. 따라서, 적절히 모듈을 나누고 구성하는 것이 중요하다.
처음부터 완벽하게 기능과 목적에 맞게 모듈을 나눈 다는 것은 쉽지 않다. 끊임없이 모듈로 분리하고 통합하는 과정을 겪으면, 진행하고 있는 프로젝트에 알맞은 기준을 찾을 수 있다.
계층별로 모듈을 나눠보기
위 다이어그램은 이전에 안드로이드 공식문서에서 가이드했던 일반적인 아키텍처 다이어그램으로 다음과 같이 3가지 모듈로 구성된다.
- UI Layer : UI계층으로 ViewModel, View, Composables 등이 포함되며, 일반적으로 app 모듈 또는 presentation 모듈이 된다.
- Domain Layer : Use case가 포함되어 복잡한 비즈니스의 캡슐화를 담당한다.
- Data Layer : Repository, Data Source 등이 포함되며, 앱에서 처리하는 다양한 유형의 데이터를 다룬다.
3가지 모듈로 나누는 이러한 패턴은 각 모듈별로 적절히 정의된 책임을 갖고 있으며, 실제로 잘 동작한다. 그렇기 때문에, 응집도를 높이는 측면에서는 이러한 패턴이 좋다고 말 할 수 있다.
서비스가 성장함에 따라 코드베이스가 증가하게 되고, 결국 각각의 모듈도 거대해져 모듈화의 이점을 잃게 된다. 예를 들어 Domain 계층에서 작은 변화가 생길 때 이를 의존하고 있는 UI 계층에도 영향을 미치기 때문에 새로 컴파일이 필요하고, 이는 빌드시간 지연으로 이어진다. 즉, 코드의 결합도가 높아지는 문제가 발생한다고 할 수 있다.
이때는 모듈을 더 많이 세분화 하여 다시 나눠보는 것을 고려할 수 있다.
기능별로 모듈을 나누기
책과 관련 된 앱을 만든다고 가정한다. 이 앱은 책(:books), 저자(:authors),리뷰(:reviews) 기능들로 구성되어 있으며, 기능별로 모듈을 구성해서 앱을 만든다면 다음과 그림과 같이 설계 할 수 있다.
하지만 기능별로 모듈을 구성하면 코드 재사용을 위해 기능 모듈끼리 의존하게 된다. 예를 들면 :books 모듈이 :authors 또는 :reviews에 있는 코드를 참조하기 위해 해당 모듈을 다음 그림과 같이 의존해야 한다.
이렇게 되면 각 모듈간에 강하게 결합 되므로 위에서 설명한 기준과 원칙을 위반하게 된다. 그렇다고 각 모듈별로 동일한 코드를 갖고 있게 되면 DRY(Don’t Repeat Yourself)원칙을 위반하게 된다. (아무도 코드를 복사해서 붙여 넣고 쓰고 싶어 하지 않는다.)
기능 + 계층별로 나눠보자
앞에서 언급한것 처럼 계층별로 모듈을 나눌 때 그리고 기능별로 모듈을 나눌 때 모두 각각의 장단점이 있다.
계층별로 나눌 때는 코드의 응집도는 높다는 장점이 있지만, 결합도가 높고 모듈의 세분화 정도가 떨어지는 단점이 있다.
기능별로 나눌 때는 세분화가 잘되는 장점이 있지만, 결합도가 높아지기 쉽다는 단점이 있었다.
두 솔루션을 결합하여 서로의 단점을 보완해보도록 하자. 다음 그림은 그 예를 나타낸다.
이 다이어그램은 상위 계층에서 하위계층으로만 의존 한다는 전제 조건을 갖는다. (수평적으로도 의존하면 안된다)
두 솔루션을 합치면 다음과 같은 장점을 얻는다.
- 각 모듈은 고도로 전문적이며, 독립적이기 때문에 코드 응집도를 높일 수 있다.
- 각 모듈은 느슨하게 결합되므로 추후에 시스템을 손쉽게 수정할 수 있다.
- 모듈이 세분화되어 있으므로 쉽게 재사용할 수 있다.
모듈화 된 프로젝트의 앱 빌드
앞에서 언급한 것과 같이 기능 + 계층별 모듈로 구성된 프로젝트는 앱을 만들 때 전체 모듈을 빌드할 필요가 없으므로 빌드시간을 줄이는데 유리해진다.
앱의 일부기능만 실행가능한 데모 앱을 빌드 하고 싶다면, 위 그림처럼 필요한 모듈만 의존하여 빌드할 수 있다.
마치며
앞에서 설명한 그림들은 모두 :domain -> :data 형식의 의존관계를 갖고 있다. 일반적으로 안드로이드 진영에서 소개된 클린아키텍처를 구현하는 방식과는 다르게 도메인 계층이 데이터 계층을 의존하는 것으로 표현하고 있다.
안드로이드 공식문서 및 유투브 채널에서도 일관되게 설명하고 있으므로 아마도 이것이 모듈과 아키텍처에 대한 구글의 공식적인 결정인 것 같다. 그렇다면 지금까지 국내, 해외 블로그에서 소개된 아키텍처에 관련된 내용은 모두 틀린것일까?
그건 아니다. 구글은 범용적인 가이드 문서를 제공해야 하는 입장이니 주니어 개발자들도 이해할 수 있는 문서를 만들어야 했고, 그렇기 때문에 별도의 부연설명이 필요 없는 단순한 :ui->:domain->:data 의존관계를 예를 들어 설명한 것이라고 추정한다.
클린아키텍처에서는 모듈간 의존방향성이 :ui -> :domain <- :data 이므로 :ui와 :data가 상호작용하기 위해서는 결국 DIP와 IoC 개념에 대한 이해가 필요하다. 그리고 이 부분은 일반적으로 DI 라이브러리인 Hilt의 도움을 받게 된다. 구글은 이러한 부분까지 설명하기엔 내용이 방대해지기 때문에 의존 방향을 단순히 단방향으로 바꾸고 가이드 문서를 만든 것 같다.
2개의 댓글
로너 · 2023년 6월 15일 4:41 오후
글 잘 읽었습니다 오늘도 많이 배워갑니다!
쥬니어 · 2024년 7월 11일 5:33 오후
클린아키텍쳐와 안드로이드 공식 예제의 모순에 대해 이렇게 코멘트 해주시니 헷갈리지 않아서 좋습니다!