build.gradle.kts Migration (feat. - Version Catalog, BinaryPlugin)

David·2024년 7월 2일
0
post-thumbnail

🧐 마이그레이션 하게 된 이유


현재 저희 앱의 개발 환경은 멀티 모듈을 적용하고 있지만
초기 단계이고 groovy 문법을 사용하고 있는데요.

앱이 아직 크지 않아서 제가 입사하면서 컴포즈를 도입하고
도입과 동시에 멀티모듈로 분리하였는데요.

시간이 지나면서 레거시 코드(xml, databinding, adapter, etc..)들과
컴포즈의 혼용으로 되어있는 모듈도 있고
기능들이 추가되면서 점점 모듈이 증가했는데요.

🤮 모듈을 추가할 때마다 기존의 모듈에서 비교를 하며 복사 붙여넣기로 넣어야하고
개발을 동료분들과 같이하니 디펜던시 관리가 쉽지 않았습니다.

또한 요새 문서는 거의 .kts로 작성이 되어 있어 groovy 문법으로 변경하는 것도
시간이 걸리는 요소 중 하나였습니다.

이에 우선 kts 로 마이그레이션 하기로 결정했는데요.
:app 모듈 하나를 마이그레이션을 해봤는데
바꿔야 할 부분이 너무 많고 반복 작업이 너무 많아서
공통의 부분들을 묶어서 재사용성을 생각했고

그에 대한 방법은 처음에
스크립트 플러그인으로 공통 부분을 묶어서 처리할려고 하였으나 kts는 타입 세이프하여
사용할 수 없어

BinaryPlugin 의 CustomConventionPlugin 을 참고하여
빌드 환경을 개선해보았습니다.


🏗️ Version Catalog 및 build-logic 셋팅


⚙️ Version Catalog Setting

생성 경로

  • root > gradle > libs.versions.toml

Version Catalog를 생성하면 다음과 같은 섹션으로 나눌 수 있는데요.
저는 다음과 같은 규칙으로 명시했습니다.

  • versions
    • 라이브러리 버전 명시
  • libraries
    • 필요한 라이브러리 명시
    • '#' 으로 같은 관심사끼리 묶어서 명시
    • 다른 관심사는 줄 바꿈(2줄)로 분리
  • bundles
    • 디펜던시 추가 시 같이 묶이는 라이브러리들을 bundles로 명시
  • plugins
    • 추가해야 하는 플러그인을 명시
    • 우리 프로젝트에서 생성한 플러그인은 앞에 prefix로 petnow로 구분하여 명시
[versions]
...


[libraries]
...


[bundles]
...


[plugins]
...

⚙️ build-logic Setting

생성 경로

  • root > build-logic

💁🏻 build-logic > convention 모듈을 생성

  • 위와같은 방법으로 하위에 convention 모듈 생성

💁🏻 build-logic > settings.gradle.kts 설정

🚀 settings.gradle.kts(:build-logic)

  • 중앙 관리를 위한 설정
  • Version Catalog 를 사용할 수 있게 설정

💁🏻 root > settings.gradle.kts 설정

  • 독립적으로 빌드할 수 있게 셋팅


💁🏻 build-logic > convention > build.gradle.kts 설정

📌 위의 플러그인은 우리가 코드에서
Gradle Api 및 필요한 Extension을
import 하여 사용하기 위해 추가해야 합니다.

[versions]
ksp = "2.0.0-1.0.22"
androidGradlePlugin = "8.1.1"

kotlin = "2.0.0"


[libraries]
# Plugin
plgn-android = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
plgn-kotlin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
plgn-ksp = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
plgn-kotlin-compose-compiler = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" }

여기까지 작업이 완료되면 Plugin을 상속받아
ConventionPlugin을 생성할 수 있는 환경을 구성하게 됩니다.


🧩 Convention Plugin 등록 및 사용


플러그인을 만들 때 필요한 플러그인과 디펜던시가 있는데
이전에 Version Catalog에서 정의를 했으니

🤩 ConventionPlugin 클래스에서
libs를 가져오는 확장 함수를 선언하면 편합니다.


AndroidLibraryConvention Plugin

🐛 libs 에서 추가할 때 get() 호출을 잊지 마세요!

어떻게 코드로 매핑되는지 쉽게 이해하기 위해
같은 색끼리 표시를 해봤습니다.

👨🏻‍💻 version 셋팅

  • project.extensions.configure<LibraryExtension> 으로 구성

👨🏻‍💻 compiler 셋팅

  • kotlin 2.0 에서 바뀐 문법으로 jvm 설정 시
    project.extensions.configure<KotlinAndroidProject> 로 구성

👨🏻‍💻 flavor 셋팅

  • enum 설정 시 대문자가 디폴트인데 해당 값을 소문자 그대로 사용하기 위해 @Suppress("EnumEntryName") 사용
  • 해당 함수를 flavor에 따라 다른 처리를 위해 콜백
    🐛 컨벤션에서 추가했는데 다른 곳에서 두 번 사용하지 마세요!

🌱 플러그인 등록

  • 위에서 설정한 build-logic > convention > build.gradle.kts 에서
    gradlePlugin { } 에서 plugins { } > register()

register()
id 값에 우리가 다른 곳에서 사용할 plugin id 명시
implementationClass 값에 구현한 클래스 명시

등록한 플러그인 id는 문자열 그대로라서
이를 Version Catalog 에 명시하여 사용할 수 있게 구성합니다.

  • 기존에 사용하는 것이 아닌 CustomConventionPlugin 은 앞에 prefix 회사명으로 구분 했습니다.

🫳🏻 플러그인 사용

(플러그인 사용으로 빌드 스크립트가 매우 깔끔해진 모습 😎)


👀 그럼 어떤 플러그인들을 만들어서 사용중인가요?


지라티켓에 정리한 내용을 그대로 보여드리겠습니다.

플러그인을 만든 기준은 다음과 같았습니다.

  • 모듈에서 자주 사용하는 같은 관심사의 plugin과 dependencies
  • 명확히 분리가 필요한
    • 가령 Sentry 를 제가 도입해서 저는 알지만 다른 개발자는 모듈을 만들 때 어떤 것을 넣고 빼야하는지 sdk에 대한 이해가 필요
    • 레거시를 나중에 지울 때 찾으면서 일일이 지우는 것이 아닌 하나만 지우면 될 수 있는 구조

🔮 build.gradle.kts(:app)






🔨 Troubleshooting


🐛 Unable to load class Plugin

  • 해당 컨벤션 플러그인 클래스에서 패키지를 제거 했습니다.

🐛 Failed to calculate the value of property 'jvmTarget'.Unknown Kotlin JVM target: 21

  • jvm 버전 확인
// 명령어
$ /usr/libexec/java_home -V

// output
Matching Java Virtual Machines (3):
21.0.2 (arm64) "Azul Systems, Inc." - "Zulu 21.32.17" /Users/gimhyeong-geun/Library/Java/JavaVirtualMachines/azul-21.0.2/Contents/Home
17.0.8.1 (arm64) "Azul Systems, Inc." - "Zulu 17.44.53" /Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home
11.0.18 (arm64) "Azul Systems, Inc." - "Zulu 11.62.17" /Library/Java/JavaVirtualMachines/zulu-11.jdk/Contents/Home
  • 17로 변경
vim ~/.zshrc
// .zshrc 안에서
...
export JAVA_HOME_17=$(/usr/libexec/java_home -v 17)
export JAVA_HOME=$JAVA_HOME_17
  • 변경사항 저장
source ~/.zshrc

🐛 ':app:uploadCrashlyticsMappingFileProdRelease' property 'appIdFile'.

  • build.gradle(:root)의 com.google.gms:google-services 클래스 패스를
    최신으로 올려서 해결 했습니다. 4.3.14 → 4.4.2


🐛 java.lang.NoClassDefFoundError: Failed resolution of: Lio/sentry/android/core/performance/AppStartMetrics;

  • 등록된 이슈
  • 마이그레이션 하면서 올렸던 sentry version을 사용했던 버전으로 원복

🐛 Android build fail - Unable to make progress running work. There are items queued for execution but none of them can be started

  • issue tracker에 등록되어 있었음 > agp 와 androidstuio 문제로 보임
  • build.gradle(:root) 에서 문제되는 테스크를 제외시킴


👏🏻 포스트를 마치며


이번 마이그레이션을 하면서 시간이 생각보다 꽤 걸렸지만
추가로 생기는 모듈 생성에 훨씬 효율적이고
스크립트가 훨씬 가독성이 좋아져서 매우 의미있는 작업이였습니다.

예전 멀티모듈을 사용할 때 buildSrc 를 만들고 그 안에서 코틀린 Object 로
버전 관리를 했을 때는 버전이 달라질 때마다 전부 빌드하여 시간이 꽤 걸렸던 기억이 나는데
Version Catalog는 필요한 부분만 빌드되서
buildSrc로 버전관리를 해본 사람이라면 만족하실 수 있을 것 같습니다.

피드백은 언제나 환영이며
이만 포스트를 마치겠습니다.

profile
공부하는 개발자

0개의 댓글

관련 채용 정보