“fat-aar” 플러그인(kezong)을 쓰려다 AGP 7.x까지만 안정 지원이라 포기… 결국 구글 공식 Fused Library로 결정했고, 실제로 래퍼(b.aar) 안에 a.aar +
domain
+data
를 묶어냈다. 이 글은 그 과정의 시행착오와 최소 설정을 정리한 기록이다.
Fused Library
였나kezong/fat-aar-android
는 AGP 7.x 세대까지만 검증되었고, Gradle/AGP 8대에서 이슈가 잦다. 안정성 측면에서 채택 보류. (GitHub, Stack Overflow):a-wrapper
(선택) – a.aar을 로컬 Maven 좌표로 바꿔 담는 래퍼:domain
– Android Library (또는 :domain-core
+ :domain-android
로 분리):data
– Android Library:b
– 최종 fused AAR 모듈 (여기에 Fused Library 플러그인을 적용)포인트: fused 모듈은 “소스 없음”이 기본 전제에 가깝고, 실제 내용물은 포함할(Android) 라이브러리들에서 나온다. 즉,
:b
는 껍데기이자 패키저 역할.
gradle.properties
# Fused Library 켜기 (필수)
android.experimental.fusedLibrarySupport=true
# 기본은 “퍼블리시 전용” 제한. 프로젝트 간 직접 의존으로 쓰고 싶으면 false
# (내 케이스: 메이븐 배포가 목적이 아니라 내부에서 바로 써야 해서 꺼둠)
android.experimental.fusedLibrarySupport.publicationOnly=false
true
를 켜야 DSL과 태스크가 활성화된다.publicationOnly=false
로 내려야 한다.gradle/libs.versions.toml
)[versions]
agp = "8.13.0"
[plugins]
android-fusedlibrary = { id = "com.android.fused-library", version.ref = "agp" }
공식 문서 레시피도 플러그인 alias 사용을 권장한다.
:b
모듈의 최소 Gradle 스크립트핵심은: 절대
com.android.library
를 같이 적용하지 말 것. Fused 모듈은 오직com.android.fused-library
+androidFusedLibrary {}
만 쓴다. (둘 다 적용하면 빌드 에러)
b/build.gradle.kts
:
plugins {
alias(libs.plugins.android.fusedlibrary)
// 필요 시 퍼블리시도 같이: `maven-publish`
}
androidFusedLibrary {
namespace = "com.ex.ample"
minSdk = 26
}
dependencies {
// 중요: implementation이 아니라 include()를 쓴다
include(project(":domain"))
include(project(":data"))
}
android {}
가 아니라 androidFusedLibrary {}
다.implementation
이 아니라 include(...)
로 묶는다. 트랜지티브는 자동 패키징되지 않으므로 필요한 것만 명시적으로 include.로컬에서 합쳐진 산출물을 앱에서 검증하려면, 문서의 샘플처럼 로컬 리포지토리에 퍼블리시해두고 앱 쪽에서 좌표로 붙여 테스트하는 게 깔끔하다. 퍼블리시 시에는 from(components["fusedLibraryComponent"])
를 사용한다.
fused-library
가 붙은 최종 모듈(:b
)은 소스 코드를 가지지 않는 “패키저 전용 모듈”이어야 한다. 실제 노출될 API(파사드/래퍼)는 별도 라이브러리 모듈(예::core
)에 두고,:b
에서include(project(":core"))
로 묶어서 내보낸다.
Fused Library
는 “여러 Android Library를 단일 AAR로 묶는” 플러그인이고, 소스는 포함하지 않고 라이브러리들을 소스로 사용한다. 즉, :b
자체에는 코드가 없어야 한다. 공개 API가 필요하면 코드를 :core
에 두고, :b
에서 include로 합쳐야한다!!:core
– 공개 API/파사드가 들어있는 Android Library (여기서 domain
/data
/a
를 래핑):domain
– Android Library:data
– Android Library:b
– Fused 모듈 (소스 없음, 패키징 전용!!!)include(project(":core"))
, include(project(":domain"))
, include(project(":data"))
, include("group:artifact:a:1.0.0")
core/build.gradle.kts
(일반 라이브러리)
plugins { id("com.android.library"); kotlin("android") }
android {
namespace = "com.ex.ample.core"
compileSdk = 34
defaultConfig { minSdk = 26 }
}
dependencies {
implementation(project(":domain"))
implementation(project(":data"))
// 필요 시 a.aar의 API를 감싸는 어댑터 코드 작성
}
b/build.gradle.kts
(fused – 소스 없음)
plugins { alias(libs.plugins.android.fusedlibrary) }
androidFusedLibrary {
namespace = "com.ex.ample"
minSdk = 26
}
dependencies {
// 핵심: implementation X, include O
include(project(":core"))
include(project(":domain"))
include(project(":data"))
// a.aar은 좌표로 include (파일 직접 포함 불가)
include("com.myco.libs:a:1.0.0")
}
:b
에는 코드 넣지 말 것. “라이브러리 묶개” 역할만 한다. include(...)
로만 묶인다(트랜지티브 자동 패키징 안 됨). .aar
파일 직접 include는 불가 → 좌표화해서 include. gradle.properties
에서 해제 가능동시에 두 플러그인 금지: com.android.fused-library
+ com.android.library
를 같은 모듈에 적용하면 바로 실패. Fused 모듈에는 오직 하나만.
android {}
금지: Fused 모듈은 androidFusedLibrary {}
를 쓴다(네임스페이스/SDK만 지정).
implementation
금지: 반드시 include(...)
로 묶는다. 트랜지티브는 자동 합류 안 됨.
.aar 파일 직접 include 불가: a.aar은 좌표화해서 include("g:a:v")
로 선언. (로컬/사내 Maven OK)
기본은 퍼블리시 전용: 프로젝트 직접 의존으로 쓰려면 android.experimental.fusedLibrarySupport.publicationOnly=false
설정.
AGP 요구사항: AGP 8.12+ 필요. 최신 릴리즈 노트에서 Fused 관련 수정 사항도 확인 권장.
fat-aar 대체 이유: AGP 8대에서 이슈 보고가 누적. 장기 안정성 측면에서 공식 플러그인 채택. (GitHub, Stack Overflow)
gradle.properties
android.experimental.fusedLibrarySupport=true
android.experimental.fusedLibrarySupport.publicationOnly=false
b/build.gradle.kts
plugins { alias(libs.plugins.android.fusedlibrary) }
androidFusedLibrary {
namespace = "com.ex.ample"
minSdk = 26
}
dependencies {
include(project(":domain"))
include(project(":data"))
// a.aar -> 로컬/사내 Maven 좌표로 발행 후:
// include("com.your.group:a:1.0.0")
}