본문 바로가기

안드로이드/아키텍처

멀티 모듈

모든 클래스는 각자의 역할이 뚜렷하게 존재한다.

대표적인 예로 ViewModel은 UI의 상태를 관리하기 위한 클래스로, UI 상태를 제외한 나머지는 알 필요가 없다. 

그러나 데이터베이스에서 데이터를 가져오기 위해 Repository를 사용해야 하기 때문에 추상화 된 Repository를 DI를 통해 주입받아 사용하곤 한다.

이처럼 각자의 역할을 정의하고 이를 의존성 주입을 통해 관리하게 되는데, 하나의 모듈에서 프로젝트를 구현하다 보면 본인도 모르는 사이에 각 코드간의 의존성이 높아지고, 나중에는 클래스마다 복잡한 관계를 갖게 되는 경우가 생기게 된다.

 

1. 멀티 모듈

멀티 모듈을 사용하면 위와 같은 문제를 어느 정도 개선할 수 있다.

  • 역할 및 기능 별로 모듈을 생성, 관심사 분리를 통해 유지보수 및 재사용성을 향상시킬 수 있다.
    대표적으로 UI(feature), Domain, Data 모듈이 있다.
  • 코드 중복 최소화
  • 빌드 시스템은 수정 된 모듈만 컴파일 할 수 있기 때문에 빌드 속도가 향상된다.

그러나 모듈 설계를 잘못할 경우 역으로 빌드 시간이 증가할 수 있으며, 복잡한 의존성 그래프로 인해 관리가 어려워 질 수 있다.


2. 모듈 생성

모듈 종류

1. Application Module

 

우리가 기존에 사용하던 app 모듈로, APK 파일을 생성한다.

APK: 안드로이드 운영체제에서 애플리케이션을 설치하고 배포하는 데 사용되는 패키지 파일 형식

 

2. Android Library

app 모듈과 동일한 구성을 갖고 있다. 다만 APK 파일이 아닌 AAR 파일을 생성한다.

AAR: 코드뿐만 아니라 레이아웃, 이미지, 매니페스트 파일 등 안드로이드 앱에 필요한 모든 리소스와 코드를 포함, AAR 파일을 다른 앱에서 쉽게 가져다 쓸 수 있다.
ex) 로그인 기능을 AAR로 만들오 다른 앱에서 가져다 사용

 

3. Java or Kotlin Library

순수 Java 또는 Kotlin으로만 구성된 모듈로, JAR 파일을 생성한다.

JAR: 여러개의 자바 클래스 파일과 클래스들이 이용하는 관련 리소스 및 메타데이터를 하나의 파일로 모아서 자바 소프트웨어나 라이브러리를 배포하기 위한 소프트웨어 패키지 파일

implementation vs api

implementation과 api는 비슷하지만 접근성의 차이가 있다.

 

예를 들어 아래와 같은 모듈이 있다고 하면

B 모듈은 C 모듈에 있는 User Class를 사용할 수 있지만, A 모듈은 C 모듈에 있는 User Class를 사용하지 못한다.

(C 모듈의 변경이 일어나면 C 모듈과 C모듈을 의존하고 있는 B모듈이 다시 컴파일 된다.)

 

 

하지만 B 모듈에서 C 모듈을 api를 통해 의존성을 추가하였다면 A 모듈에서도 C 모듈에 있는 User Class를 사용할 수 있게 된다.

(C 모듈의 변경이 일어나면 B 모듈뿐만 아니라 A 모듈까지 다시 컴파일 된다.)


주의 사항

CircularReferenceException

여러 모듈을 만들어 사용하다 보면 다음과 같은 순환 구조가 만들어지는 경우가 있다.

이 경우 CircularReferenceException이 발생하게 된다.

 

본인의 경우 Compose가 아닌 View로 구현 된 프로젝트로, 화면 전환을 위해 Intent로 다른 모듈에 있는 Activity를 참조하기 위해 의존성을 추가하다 발생하였다.

 

Alarm 모듈에서 알람 클릭 시 앱 실행을 위해 MainActivity를 PendingIntent에 추가하기 위해 feature:main 모듈의 의존성을 추가하였고, main은 BottomNavigation 중 하나인 feature:routine 모듈을, feature:routine 모듈에서는 알림 추가를 위해 alarm 모듈을 참조하여 순환 구조가 만들어지게 되었다.

alarm > main > routine > alarm

 

멀티 모듈에서 Intent 사용

Intent의 경우 이를 간단하게 해결할 수 있는데, 해당 모듈을 참조하여 추가하는게 아니라 경로를 추가하면 된다.

 

일반적인 경우 다음과 같이 Intent를 사용하는데

Intent(context, MainActivity::class)

 

위와 같은 방법 대신 다음과 같이 ClassName을 활용하면 된다.

val intent = Intent()
intent.setClassName(context, "com.project.main.MainActivity")

 

'안드로이드 > 아키텍처' 카테고리의 다른 글

Build Logic  (0) 2025.09.09