ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Android] 버전 카탈로그로 Gradle 버전 관리하기
    Android 2024. 4. 24. 00:42
    반응형

     

    멀티 모듈에서의 버전 컨트롤의 중요성

    이건 플러터를 할 때도 있었던 문제인데, 멀티 모듈을 사용하게 되면 꼭 한 번 겪는 문제가 있습니다. 각 모듈 별로 여러 가지 라이브러리를 사용할테고, 이 때 같은 라이브러리를 각 모듈이 사용할 때 다른 버전을 쓰게 된다면 반드시 충돌이 난다는 것이었습니다. 당연하게도 같은 라이브러리라도 다른 버전을 사용한다면 해당 버전에서 사용하는 메서드 형태나 속성 등이 달라지게 될테니, 무엇을 사용해야하는 지 모를테지요. 그렇기 때문에 일일이 버전을 통일화 시켜줘야 하는 문제가 있었습니다.

     

    놀랍지 않게도 안드로이드 또한 마찬가지입니다. 많이들 사용하는 Clean Architecture를 위한 모듈 (레이어) 분리를 한다고 한다면, 같은 현상이 발생할 수 있습니다. 버전이 0.1만 달라져도 빌드가 안 되고 빨간 에러가 수두룩 빽빽하게 나오겠죠.

    dependencies {
        implementation 'androidx.core:core-ktx:1.7.0'
        implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
        implementation 'androidx.activity:activity-compose:1.3.1'
        implementation "androidx.compose.ui:ui:$compose_version"
        implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
        implementation 'androidx.compose.material3:material3:1.0.0-alpha02'
        testImplementation 'junit:junit:4.13.2'
        androidTestImplementation 'androidx.test.ext:junit:1.1.5'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
        androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
        debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
        debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
    }

     

    하나의 모듈에서만 관리된다면 그나마 상관은 없을텐데, 모듈이 과장해서 한 10개 정도 있다면? 동기화하는 것도 일일 것입니다. 심지어 저렇게 dependencies에 버전을 전부 선언해버리면 찾기도 힘들겠죠.

     

    이런 현상을 막기 위한 방법이 없는 것은 아닙니다. project 레벨의 gradle에서 버전을 선언하는 방법이 있습니다.

    buildscript {
        ext {
            compose_version = '1.1.1'
        }
    }// Top-level build file where you can add configuration options common to all sub-projects/modules.
    plugins {
        id 'com.android.application' version '7.3.0' apply false
        id 'com.android.library' version '7.3.0' apply false
        id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
    }

     

    build.gradle (project)buildscript 안의 ext 에서 버전을 선언하게 된다면, 각 모듈에서 해당 버전 변수명으로 접근이 가능해지고 그렇게 된다면 좀 더 관리가 수월해지게 되겠죠. 위에서 저렇게 선언해주면 다른 모듈에서도 아래와 같이 사용할 수 있게 된답니다.

    dependencies {
    	// 이거보다는
        implementation 'androidx.core:core-ktx:1.7.0'
        implementation 'androidx.appcompat:appcompat:1.6.1'
        implementation 'com.google.android.material:material:1.11.0'
        testImplementation 'junit:junit:4.13.2'
        androidTestImplementation 'androidx.test.ext:junit:1.1.5'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
    
    	// 이게 좀 더 낫잖아요? 관리도 편해지구 다른 모듈끼리고 서로 공유할 수 있고 말이죠.
        androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
        debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
        debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
    }

     

     

     

    반응형

     

     

    버전 카탈로그 (Version Catalog) 란?

    위의 방식을 좀 더 깔끔하게 사용하는 방법이 있습니다. 사실 어떻게 보면 그렇게 차이가 있지는 않은데, 일단 위에서 선언하는 아티펙트 아이디와 그룹 아이디조차 한꺼번에 묶어서 처리해버릴 수 있습니다. 좀 더 깔끔하게 말이죠. 

    그전에 참고로, 안드로이드의 dependencies에 선언하는 라이브러리의 형태를 보면 아래와 같은 형식입니다.

    androidx.appcompat:appcompat:1.6.1
    com.google.android.material:material:1.11.0

    이것을 공통적으로 아래와 같이 표현합니다.
    {GroupId}:{ArtifactId}:{Version}

    - GroupId: 앱의 패키지명을 빌려서 적습니다. 보통 com.회사명.앱이름의 형식을 많이 사용합니다.
    - ArtifactId: 프로젝트의 이름을 빌려서 씁니다. 만약 betterafter라는 회사에서 keykat이라는 프로젝트명을 사용한다고 하면 com.betterafter.keykat3:keykat:1.0.1 과 같이 쓸 수 있습니다. 아래에서 좀 더 자세하게 알 수 있어요!

    https://johngrib.github.io/wiki/groupId-artifactId/

     

    어쨌든 라이브러리 네임과 버전을 같이 좀 더 수월하게 관리할 수 있도록 하는 것이 버전 카탈로그 (version catalog) 라는 것이라고 합니다. 좀 더 확장 가능하고 유지 보수가 쉽게 만들 수 있도록 도와주는 방식이라고 합니다. 아래에서 좀 더 자세하게 알 수 있습니다!

     

     

    버전 카탈로그로 빌드 이전  |  Android Studio  |  Android Developers

    Gradle 구성 파일을 Gradle 버전 카탈로그로 이전합니다.

    developer.android.com

     

     

     

     

     

    버전 카탈로그 만들어보기

    위의 내용을 빠르게 만들어봅시다. 일단 순서대로 차근차근 따라해보세요.

     

     

     

     

     

    1. 버전 카탈로그 파일 만들기

    libs.versions.toml 파일을 만들어야 합니다. 파일은 저게 네이밍 컨벤션이고, 그냥 저 이름으로 하시면 됩니다. 위치는 루트 프로젝트의 gradle에 넣으면 됩니다. gradle.wrapper.jar 파일이 있는 곳 말이에요!

    wrapper 폴더랑 같은 레벨의, 그리고 gradle과 같은 위치 입니다.

     

     

     

     

    2. 기본적인 틀 갖추기

    libs.versions.toml을 작성할 때의 기본 틀은 아래와 같습니다. 즉 아래의 3개는 일단 써놓고 시작하면 됩니다. 그리고 예시는 바로 그 다음과 같습니다.

    [versions]
    # 버전을 모아놓는 곳입니다. 요기 아래부터 버전을 쭉 써주시면 됩니다.
    
    [libraries]
    # 라이브러리 이름들을 모아놓는 곳입니다. 버전은 위에서 끌어오면 됩니다.
    
    [plugins]
    # 라이브러리 외에 플러그인을 모아놓는 곳입니다.
    [versions]
    # data layer
    retrofit2 = "2.9.0"
    okhttp = "4.10.0"
    ktx = "1.7.0"
    appcompat = "1.6.1"
    
    # plugin
    androidGradlePlugin = "7.3.0"
    kotlin = "1.7.10"
    
    
    [libraries]
    # {groupId}:{artifactId}:{version}
    
    retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit2" }
    gson = { group = "com.squareup.retrofit2", name = "converter-moshi", version.ref = "retrofit2" }
    rxjava = { group = "com.squareup.retrofit2", name = "adapter-rxjava2", version.ref = "retrofit2" }
    okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
    logging-interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
    
    core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "ktx" }
    appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
    
    
    [plugins]
    android-application = { id = "com.android.application", version.ref = "androidGradlePlugin"}
    android-library = { id = "com.android.library", version.ref = "androidGradlePlugin"}
    kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin"}

     

    어떤 느낌인지 감이 오시나요? [libraries] 와 [plugins] 에 선언한 친구들은 위의 [versions] 에서 선언한 버전의 이름을 가져다 쓰고 있고, 이것은 결국 각각 dependencies와 plugin에서 사용하게 될 것입니다. 아래와 같이 말이죠. 참고로 아래의 첫번째는 모듈 단위의 build.gradle, 두번째는 프로젝트 단위 build.gradle 입니다.

     

     

     

     

    3. 실제로 사용해보기

    plugins {
        alias(libs.plugins.android.application)
        alias(libs.plugins.kotlin.android)
    }
    
    android {
        ...
    }
    
    dependencies {
        implementation(libs.core.ktx)
        implementation(libs.appcompat)
    	...
    }
    plugins {
        alias(libs.plugins.android.application) apply false
        alias(libs.plugins.kotlin.android) apply false
        alias(libs.plugins.android.library) apply false
    }

     

    재밌는 점은 libs.versions.toml의 [libraries] 와 [plugins] 에서 aaa-bbb-ccc로 선언한 방식을 각 build.gradle에서 aaa.bbb.ccc로 변환해서 쓰고 있네요. 요것보다 더 자세하게 알고 싶으면 위의 공식 문서 링크에서 더 많은 정보를 확인할 수 있습니다.

     

     

     

     

     

    끝..

    그래서.. 사실 이게 답니다. 아니 사실 좀 더 깊게 들어가면 더 많은데, 가령 네이밍이라던가, 버전의 확장이라던가.. 그래도 일단 간단하게 시작하고 빌드에 성공을 해야 다음 것에 흥미가 갈테고, 자연스럽게 공식 문서를 따라 들어갈테니까 최대한 간단하게 설명해 봤습니다. 도움이 되셨으면 좋겠네요 ㅎㅎ

    반응형
Designed and Written by keykat.