현재 어떤 프로젝트를 멀티모듈로 개발하고 있다.
BuildType을 `debug`/`staging`/`release`로 나누려고 했는데,
생각해보니 거의 모든 모듈에 같은 BuildType 정의를 써줘야 한다는 문제를 마주쳤다.
모든 build.gradle에 일일히 BuildType을 써줘야 한다..?
분명 공통화 방법이 있을 것이라고 생각하여 시작하게 됐다.
목표
1. BuildType 코드를 한군데로 빼서 공통화
2. 현재 BuildType마다 달라지는 변수 설정. 그런데 BuildConfig를 쓰지 않고.
이 두가지 목표를 이룰려고 한다
BuildType 공통화
`build-logic` 모듈 만들기
참고로 스튜디오의 파일은 Project 모드로 보고있습니다.
공통된 하나의 BuildType 정의를 써놓을 모듈이 하나 필요하다.
`app` 모듈과 같은 수준의 위치에 `build-logic`이라는 모듈을 하나 만들었다
모듈의 종류는 `Java or Kotlin Library`로 선택하면 된다.
MyProject
├- app
├- build-logic <- New!
├- core
│ ├- model
│ └- network
└- data
⚠️주의! 여기서는 모듈의 이름으로 `buildSrc`를 쓰면 안된다.
`buildSrc`는 gradle이 특별하게 취급하는 디렉토리이기 때문에, 지금은 필요가 없다.
`build-logic` 모듈을 만들었다면, 그 안에 `build.gradle.kts` 내용을 이렇게 갈아치운다
build-logic/build.gradle.kts
------------------------------------------------
plugins {
`kotlin-dsl`
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
dependencies {
compileOnly("cohttp://m.android.tools.build:gradle:{AGP버전}")
}
만약 디펜던시에서 버전 카탈로그를 쓰고싶으면,
`settings.gradle.kts`에서 임포트해줘야 한다.
해당 파일이 없다면, 그냥 `build-logic`을 우클릭하면 뜨는 메뉴에서 New>File을 눌러서 만들면 된다.
build-logic/settings.gradle.kts
------------------------------------------------
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
// 버전 카탈로그 임포트
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
rootProject.name = "build-logic"
마지막으로 project 수준의 `settings.gradle.kts`에서 자동으로 include된 `build-logic` 모듈을 지우고 `includeBuild`로 써준다
MyProject/settings.gradle.kts
------------------------------------------------
pluginManagement {
includeBuild("build-logic") <- 여기에 써준다.
repositories {
// ... 생략
}
}
dependencyResolutionManagement {
// ... 생략
}
rootProject.name = "MyProject"
include(":app")
include(":data")
include(":core")
// ... 생략
//include(":build-logic") <- 지우거나 주석처리 한다
공통 BuildType 작성
모듈을 만들고 gradle 설정까지 했으면, 다음은 BuildType 설정 코드를 작성한다.
나는 `BuildTypeConfig`라고 파일 이름을 지었다.
src/main/.../build_logic/BuildTypeConfig.kt
------------------------------------------------
// 모든 타입의 모듈에 적용될 공통 BuildType 설정 메서드
private fun configureCommonBuildTypes(
commonExtension: CommonExtension<*, *, *, *, *, *>
) {
commonExtension.buildTypes {
getByName("debug") {
isMinifyEnabled = false
}
create("staging") {
initWith(getByName("debug"))
matchingFallbacks += listOf("debug")
}
getByName("release") {
isMinifyEnabled = true
proguardFiles(
commonExtension.getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
// App 타입 모듈에만 적용할 BuildType 설정 메서드
fun ApplicationExtension.configureApplicationBuildTypes() {
buildTypes {
configureCommonBuildTypes(this@configureApplicationBuildTypes)
debug {
applicationIdSuffix = ".dev"
manifestPlaceholders["appName"] = "@string/app_name_dev"
}
maybeCreate("staging").apply {
applicationIdSuffix = ".staging"
isDebuggable = true
manifestPlaceholders["appName"] = "@string/app_name_staging"
}
release {
manifestPlaceholders["appName"] = "@string/app_name"
}
}
}
// Android Library 타입의 모듈에만 적용할 BuildType 설정 메서드
fun LibraryExtension.configureLibraryBuildTypes() {
buildTypes {
configureCommonBuildTypes(this@configureLibraryBuildTypes)
// 설정할 게 없으면 따로 써주지 않아도 된다
debug {
// 원하는 설정
}
maybeCreate("staging").apply {
// 원하는 설정
}
release {
// 원하는 설정
}
}
}
이렇게 공통 코드를 작성하고나서, 바로 각 모듈에서 저 메서드를 호출해서 사용할 수 없다.
나는 각 모듈의 gradle에서 직접 사용할 수 있을 줄 알았는데 참조 자체가 안됐다.
Plugin 작성
그래서 Plugin을 만들어서 각 모듈에 적용해야 한다.
// App 모듈 용 플러그인
class MyProjectAndroidApplicationPlugin : Plugin<Project> {
override fun apply(target: Project) {
// App 모듈에 적용할 것이기 때문에 AppPlugin 클래스를 사용
target.pluginManager.apply(AppPlugin::class.java)
// App 모듈에 적용할 것이기 때문에 ApplicationExtension을 사용
target.extensions.configure<ApplicationExtension> {
// 여기서 위에서 작성한 BuildType 설정 메서드를 호출한다
configureApplicationBuildTypes()
}
}
}
// Android 라이브러리 모듈 용 플러그인
class MyProjectLibraryPlugin : Plugin<Project> {
override fun apply(target: Project) {
// Android 라이브러리 모듈에 적용할 것이기 때문에 LibraryPlugin 클래스를 사용
target.pluginManager.apply(LibraryPlugin::class.java)
// Android 라이브러리 모듈에 적용할 것이기 때문에 LibraryExtension을 사용
target.extensions.configure<LibraryExtension> {
// 여기서 위에서 작성한 BuildType 설정 메서드를 호출한다
configureLibraryBuildTypes()
}
}
}
이렇게 작성한 Plugin은 `build-logic` 모듈의 `build.gradle.kts`에 등록해줘야 한다.
build-logic/build.gradle.kts
------------------------------------------------
plugins { ... }
java { ... }
repositories { ... }
dependencies { ... }
gradlePlugin {
plugins {
register("MyProjectAndroidApplicationPlugin") {
id = "myproject.plugin.build.application"
implementationClass = "cohttp://m.myproject.build_logic.plugin.MyProjectAndroidApplicationPlugin"
}
register("MyProjectLibraryPlugin") {
id = "myproject.plugin.build.library"
implementationClass = "cohttp://m.myproject.build_logic.plugin.MyProjectLibraryPlugin"
}
}
}
`register`에 넘겨주는 인자들은 다음과 같다
- `id` : 해당 플러그인의 id를 지어준다
- `implementationClass` : 작성한 플러그인의 클래스 이름
나의 경우, `implementationClass`에 클래스 이름을 넘길때, 해당 클래스의 패키지명까지 다 써줘야 제대로 인식이 됐었다
만든 Plugin 적용
이제 다른 모듈의 build.gradle.kts에서 플러그인에 내가 만든 플러그인의 id를 쓰면된다
각 모듈의 build.gradle.kts
------------------------------------------------
plugins {
// ... 생략
id("myproject.plugin.build.application")
}
이렇게 모든 모듈의 BuildType을 한곳에서 관리하고 간단하게 적용할 수 있게 됐다.
BuildType마다 다른 변수 설정
원래는 `BuildConfig`라는 것을 써서 BuildType마다 서버 주소같은걸 다르게 적용할 수 있다.
그런데 AGP 9.0 버전부터는 `BuildConfig`가 사라질 것이라고? 해서?
`BuildConfig`를 쓰지 않고 해당 기능을 구현하려고 했다.
나는 network 모듈에서 해당 기능이 필요했기 때문에 network 모듈에 설정해놨다.
역시 스튜디오 파일 뷰어 모드는 Project 모드로 보고있습니다.
일단 `network` 모듈의 `src` 폴더 안에 내가 설정한 BuildType의 이름으로 폴더들을 만들어줬다.
나는 `debug`/`staging`/`release` 세가지로 나눴기 때문에 폴더를 세개 만들었다.
그리고 새로 만든 폴더들 밑에 `kotlin`이라고 소스코드를 넣을 폴더를 만들었다.
network
├- src
│ ├- main
│ ├- debug
│ │ └- kotlin
│ ├- release
│ │ └- kotlin
│ ├- staging
│ │ └- kotlin
│ └- ...
└- ...
대략 이렇게 되는 것이다.
그리고 모든 `debug`, `release`, `staging` 폴더의 `kotlin` 폴더 안에 똑같은 `object` 클래스를 만들었다.
object NetworkConfig{
const val SERVER_URL: String = "server url"
}
클래스의 이름과, 변수 명이 다 일치해야 한다.
여기서 이상한 점이 한가지가 있다.
`debug` 폴더만 이름 옆에 `[main]`이라고 써져있고, kotlin 코드가 인식되는데?
나는 대체 왜 `debug` 폴더만 저렇게 표시가 되는지 몰랐는데,
알고보니 현재 선택된 BuildType에 맞춰서 `main`에서 참조하는 폴더가 뭔지 표시되고 있는 것이였다.
나만 몰랐던거면 마루머쓱;
현재 Build Variants를 변경하면, BuildType에 맞는 폴더가 참조되는 것으로 바뀐다.
BuildType을 `staging`으로 변경하니 `staging` 폴더로 참조가 바뀌었다
테스트 하면 각 BuildType마다 설정한 변수 값이 잘 바뀐다~~
끝
빌드 타입 나누고싶어서 하루 웬종일 삽질했다..
추가적으로 url같은걸 `local.properties`를 써서 숨기고싶은데 코드에선 직접적으로 해당 파일에 접근할 수가 없어서 어떻게 해야할지 고민해봐야 할 것 같다.
참조자료
- [Android] Version Catalog + Convention Plugin으로 build.gradle 버전을 관리해보자!
- 나만의 안드로이드 앱 만들기(중급자편) - BuildConfig의 deprecated 대응하기
'Android' 카테고리의 다른 글
[Android] 수제 ImageViewer(feat. Coil) (Compose) (1) | 2025.06.12 |
---|---|
[Android] java.lang.ClassCastException (0) | 2025.06.12 |
[Android] Compose TextField 커스텀 (2) | 2025.06.12 |
[Android] LazyColumn안에 LazyVerticalGrid 넣기(nested scroll) (0) | 2025.06.12 |
[Android] Retrofit2 Multipart사용하기 (Java) (0) | 2025.06.12 |