Android 프로젝트 모듈화 고려
게시 됨: 2019-08-21프로젝트가 특정 규모에 도달하면 단일 모듈에서 추가 작업이 덜 효과적입니다. 모듈화하면 효과적인 솔루션이 됩니다.
모듈화의 4가지 장점
모듈화를 결정하기 전에 관련 내용을 정확히 파악하는 것이 좋습니다. 모듈식 Android 프로젝트 구조의 장점은 다음과 같습니다.
더 나은 코드 격리
모든 모듈은 공개 API 인터페이스를 노출하고 구현 세부 정보를 숨길 수 있습니다. 단일 모듈을 사용하면 구현이 잘 숨겨져 있는지 완전히 확신할 수 없습니다(특히 패키지 가시성 수정자를 사용할 수 없는 Kotlin에서).
보다 쉬운 신기술 평가
모듈을 생성할 때 다른 모듈에 영향을 주지 않고 새로운 아키텍처 패턴이나 새로운 라이브러리를 확인할 수 있습니다.
프로젝트 빌드 시간 단축
모듈을 수정하려면 해당 모듈과 이에 의존하는 다른 모듈을 다시 빌드해야 합니다. Android 개발자 문서를 읽고 정확히 어떻게 작동하는지 확인하세요. 종속성 구성
더 편리한 작업
더 작고 격리된 코드 조각을 분석/디버그/리팩터링하는 것이 더 쉬워집니다. 또한 신규 개발자 온보딩이 더 빨라질 것입니다.
이러한 이점은 모듈화 프로세스를 시작하도록 설득하기에 충분할 것 같지만 어떻게 시작합니까?
#1: 모듈과 그 관계 식별
모듈을 인식하는 방법에는 기능과 레이어의 두 가지가 있습니다.
기능 모듈에서 사용자가 사용할 수 있는 앱의 일부 영역(예: 로그인, 대시보드, 프로필 등)을 이해할 수 있습니다. 이러한 영역은 단일 화면 또는 일부 프로세스를 다루는 화면의 흐름으로 구성될 수 있습니다. 이 유형의 모듈은 동일한 유형의 모듈에 종속될 수 없습니다.
기능을 식별한 후에는 일부 또는 모든 모듈에 필요한 공통 기능을 확실히 추출 해야 합니다. 이러한 모듈은 별도의 아키텍처 계층(예: 영구 저장소, 네트워킹, 탐색, UI 구성 요소 등) 또는 기능 집합에서 사용하는 일부 데이터를 처리하는 비즈니스 논리를 담당할 수 있습니다. 이러한 종류의 모듈을 일반적으로 라이브러리 라고 합니다. 라이브러리 모듈은 종속성 트리를 작성할 수 있습니다.
기능 및 라이브러리 모듈 외에도 다른 모듈 간의 수평 연결을 관리하기 위해 하나의 모듈이 필요합니다(이에 대한 자세한 내용은 다음 항목 참조). 이 모듈에는 사용자 정의 애플리케이션 클래스와 종속성 주입 설정이 포함됩니다. 다른 모듈은 이 모듈에 종속될 수 없지만 이 모듈은 프로젝트의 다른 모든 모듈에 종속됩니다.

위의 정의를 고려하면 모듈 계층 구조는 다음과 같습니다.
#2: 의존성 주입 설정
프로젝트 모듈 간의 종속성에도 불구하고 단검 종속성도 설정해야 합니다. Dagger는 종속성을 선언하는 두 가지 방법인 하위 구성 요소 와 구성 요소 종속성 을 제공합니다.
Dagger 하위 구성 요소 종속성은 부모가 모든 종속 자식을 선언해야 합니다. 프로젝트 모듈 간에 이러한 종류의 관계는 작동하지 않습니다. 프로젝트 모듈 종속성의 방향을 반대로 하기 때문입니다. 그러나 별도의 프로젝트 모듈 내에서 사용할 수 있습니다.
Dagger 구성 요소 종속성은 자식이 부모에 종속됨을 선언할 수 있기 때문에 더 유연합니다. 이를 통해 별도의 프로젝트 모듈 간에 이러한 종류의 종속성을 사용할 수 있습니다.
어느 시점에서 하나 이상의 모듈에 다른 모듈에 대한 제한된 지식이 필요하다는 것을 알 수 있습니다. 이것의 아주 좋은 예는 기능 모듈 사이의 탐색일 수 있습니다. 이러한 종류의 관계를 제공하는 것을 수평 종속성 이라고 합니다. 별도의 모듈 간에 이 통신 채널을 생성하려면 이 통신을 설명하는 인터페이스가 있는 추가 모듈과 선언된 인터페이스에 구현을 바인딩할 모듈이 필요합니다.
수평 종속성을 관리하기 위한 프로젝트 모듈 종속성 설정은 아래 이미지에 나와 있습니다.

이러한 관계에 대한 예제 코드는 기사 끝에 있는 프로젝트에서 제공됩니다.

#3: Gradle 설정
모든 프로젝트 모듈에는 추가된 종속성과 플러그인이 적용된다는 점을 제외하고는 거의 동일한 gradle.build가 있습니다. 따라서 프로젝트 디렉토리 루트에 있는 하나의 gradle 파일에 반복적인 구성을 추출하는 것이 좋습니다. 이러한 파일은 정적 코드 분석을 실행하거나 단위 테스트를 실행하기 위해 일반적인 gradle 작업을 등록할 수도 있습니다.
이러한 일반적인 설정의 코드 스니펫은 다음에서 찾을 수 있습니다.
afterEvaluate { project -> def isAndroid = project.plugins.hasPlugin('com.android.library') || project.plugins.hasPlugin('com.android.application') setupModule(isAndroid) setupCommonTestDependencies(isAndroid) setupCommonTasks(isAndroid) } def setupModule(isAndroid) { if (isAndroid) { android { compileSdkVersion projectCompileSdk defaultConfig { minSdkVersion projectMinSdk targetSdkVersion projectTargetSdk } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } lintOptions { abortOnError true checkReleaseBuilds false checkAllWarnings true warningsAsErrors true def lintBaseline = file("quality/lint-baseline.xml") if (lintBaseline.exists()) baseline lintBaseline } } } else { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } } def setupCommonTestDependencies(isAndroid) { dependencies { testImplementation "junit:junit:${junitVersion}" testImplementation "org.assertj:assertj-core:${assertJVersion}" testImplementation "org.mockito:mockito-core:${mockitoVersion}" testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:${mockitoKotlinVersion}" if (isAndroid) { androidTestImplementation "androidx.test.ext:junit:${axTestJUnitVersion}" androidTestImplementation "androidx.test.espresso:espresso-core:${axEspressoLibVersion}" } } } def setupCommonTasks(isAndroid) { if (isAndroid) { tasks.register("unitTest") { task -> task.dependsOn(testDebugUnitTest) } } else { tasks.register("unitTest") { task -> task.dependsOn(test) } } }
결론
이 문서는 Android 프로젝트를 모듈화하기 위한 완전하거나 완전한 가이드가 아닙니다. 그러나 프로젝트 모듈화를 시작할 때 고려해야 할 측면을 다룬다고 생각합니다.
수평적 종속성을 보여주는 코드는 링크에서 찾을 수 있습니다.
Android용 기본 애플리케이션을 구축하고 싶으십니까? 미키도를 선택하세요!