안드로이드 개발자로서, 오픈 소스 라이브러리를 Maven Central에 배포하는 것은 다른 개발자들과 코드를 공유하고 사용성을 넓히는 중요한 과정입니다. 이 글에서는 GitHub 계정을 활용해 Android 라이브러리를 Maven Central에 배포하는 방법을 단계별로 자세히 설명합니다.
예시 Git Repository 주소: https://github.com/kez-lab/Compose-DateTimePicker
먼저 Maven Central에 안드로이드 라이브러리를 배포하려면 몇 가지 사전 준비가 필요합니다. 특히 2024년 상반기에 issues.sonatype.org가 폐기되면서 새로운 사용자는 Maven Central Portal을 통해 라이브러리를 배포해야 합니다.
https://central.sonatype.org/faq/what-happened-to-issues-sonatype-org/
vanniktech Maven Publish Plugin: Maven에는 현재 현재 Central Publishing Portal을 통해 Maven Central에 게시하기 위한 공식 Gradle 플러그인은 없습니다. 관련 링크
또한 저는 김동민님의 안드로이드 라이브러리 Maven Central 에 배포하기를 참고해서 라이브러리 배포를 진행했으며, 동민님께서 사용하신 것 처럼 vanniktech
플러그인을 사용하여 Maven Central에 배포할 예정입니다!
먼저 Maven Central Portal에서 계정을 생성해야합니다. 이 포털은 Maven Central에 라이브러리를 배포하기 위한 계정 관리와 네임스페이스 검증, 그리고 토큰 생성 등을 지원합니다.
example
이라면 io.github.example
로 생성합니다.(Maven Central Portal 회원가입을 Github 소셜로그인으로 이미 진행했다면 github의 namespcae가 자동으로 검증됩니다.)
Unverified Namespace
를 검증하기 위해, 포털에서 제공하는 Verification Key를 GitHub에 동일한 이름의 공개 레포지토리로 생성해야합니다.즉, Verification Key가 kezlab01이라면 kezlab01 이름의 레포지토리를 만들어야 합니다. 그러면 github.com/sample/kezlab01이라는 URL이 생성됩니다.
Verify Namespace
버튼을 클릭해 검증을 완료합니다. 검증 후에는 레포지토리를 삭제해도 됩니다.사진을 보시면 아시겠지만 저는 github NameSpace뿐만 아니라 개인용 도메인도 연결해두었는데요, 개인용 도메인 연결 방법 및 private 라이브러리 배포 방법은 추후 포스팅하겠습니다.
Maven Central에 라이브러리를 배포하기 위해서는 GPG 키가 필요합니다. 이 키는 라이브러리가 정상적으로 서명되었음을 보증하며, 사용자들이 서명을 검증할 수 있도록 공개 키 서버에 업로드됩니다.
먼저, GPG 키를 생성합니다. 다음 명령어를 사용하여 키 생성 프로세스를 시작합니다.
$ gpg --full-generate-key
이 명령어를 실행하면, 이름과 이메일을 입력하라는 메시지가 나타납니다. 또한, 키의 유효 기간을 설정할 수 있습니다.
예시 출력:
gpg (GnuPG) 2.2.19; Copyright (C) 2019 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
GnuPG needs to construct a user ID to identify your key.
Real name: Your Name
Email address: youremail@example.com
You selected this USER-ID:
"Your Name <youremail@example.com>"
Change (N)ame, (E)mail, or (O)kay/(Q)uit? O
키 생성이 완료되면, 아래와 같은 메시지가 표시됩니다:
public and secret key created and signed.
pub rsa3072 2024-06-23 [SC] [expires: 2026-06-23]
ABCD1234EF5678901234567890ABCDEF12345678
uid Your Name <youremail@example.com>
sub rsa3072 2024-06-23 [E] [expires: 2026-06-23]
생성된 GPG 키를 확인하기 위해 다음 명령어를 사용합니다:
$ gpg --list-keys
예시 출력:
/home/youruser/.gnupg/pubring.kbx
---------------------------------
pub rsa3072 2024-06-23 [SC] [expires: 2026-06-23]
ABCD1234EF5678901234567890ABCDEF12345678
uid [ultimate] Your Name <youremail@example.com>
sub rsa3072 2024-06-23 [E] [expires: 2026-06-23]
여기서 pub의 마지막 8자인 12345678이 publick key입니다.
다른 사람들이 위 public key를 사용할 수 있도록 GPG public key를 key server에 업로드합니다.
$ gpg --keyserver keyserver.ubuntu.com --send-keys 12345678
참고로 GPG key를 서버에 업로드하지 않는다면 다음과 같이 배포시에 에러가 발생하니 참고해주세요.
간혹 key server의 host가 없다는(gpg: keyserver send failed: No route to host) 등의 어이없는 오류가 발생하기도 하는데요, 그럴 경우 아래의 명령어를 통해 연결 가능한 key server를 찾아 해당 도메인으로 public key 를 전송하시면 됩니다.
$ gpg -connect-agent --dirmngr 'keyserver --hosttable'
vanniktech 플러그인에서는 secretKeyRingFile을 통해서 GPG를 검증하여 서명에 사용하기 때문에 다음처럼 서명 파일을 생성해주셔야합니다.
gpg --keyring secring.gpg --export-secret-keys > ~/.gnupg/secring.gpg
Maven Central에 라이브러리를 배포하려면 build.gradle.kts
파일에서 적절한 설정을 추가해야 합니다. 이 과정은 플러그인 적용, Maven Central에 대한 설정, 그리고 POM 구성으로 나눌 수 있습니다.
Maven Central에 라이브러리를 배포하기 위해 vanniktech Maven Publish
플러그인을 적용합니다. 이 플러그인은 Gradle 프로젝트의 아티팩트를 쉽게 Maven Central에 배포할 수 있도록 돕습니다.
plugins {
id("com.vanniktech.maven.publish") version "0.29.0"
}
플러그인을 적용한 후, Maven Central로 배포를 활성화하려면 대상 저장소를 지정하고 GPG 서명을 활성화해야 합니다. 이는 Maven Central에서 요구하는 필수 사항입니다. 이 설정은 DSL을 통해 추가하거나 Gradle 속성을 설정하여 추가할 수 있습니다.
import com.vanniktech.maven.publish.SonatypeHost
mavenPublishing {
// 기본 Maven Central로 배포
publishToMavenCentral(SonatypeHost.DEFAULT)
// 또는 https://s01.oss.sonatype.org로 배포
publishToMavenCentral(SonatypeHost.S01)
// 또는 https://central.sonatype.com/를 사용하는 Central Portal로 배포
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)
// 모든 배포에 대해 GPG 서명 활성화
signAllPublications()
}
위의 설정에서 SonatypeHost.DEFAULT, SonatypeHost.S01, SonatypeHost.CENTRAL_PORTAL 중 적절한 옵션을 선택하여 배포할 호스트를 지정할 수 있습니다. signAllPublications() 메서드는 모든 배포 아티팩트에 대해 GPG 서명을 적용합니다.
POM(프로젝트 객체 모델) 파일은 프로젝트의 메타데이터를 포함하며, 배포 시 함께 게시됩니다. 이 메타데이터는 라이브러리를 소비할 때 사용되는 프로젝트의 좌표(그룹 ID, 아티팩트 ID, 버전)와 프로젝트의 기본 정보(프로젝트 이름, 설명, URL, 라이선스 등)를 나타냅니다.
mavenPublishing {
// 프로젝트의 그룹 ID, 아티팩트 ID, 버전 설정
coordinates("io.github.kez-lab", "compose-datepicker", "0.0.1")
// POM 정보 설정
pom {
name.set("Compose-DatePicker") // 라이브러리 이름
description.set("Compose DatePicker") // 라이브러리 설명
url.set("https://github.com/KwakEuiJin/Compose-DatePicker") // 프로젝트 URL
inceptionYear.set("2024") // 프로젝트 시작 연도
// 라이선스 정보 설정
licenses {
license {
name.set("The Apache License, Version 2.0")
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
distribution.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
}
}
// 개발자 정보 설정
developers {
developer {
id.set("KwakEuiJin") // 개발자 ID
name.set("KEZ") // 개발자 이름
url.set("https://github.com/KwakEuiJin") // 개발자 URL
}
}
// 소스 코드 관리(SCM) 정보 설정
scm {
url.set("https://github.com/KwakEuiJin/Compose-DatePicker")
connection.set("scm:git:git://github.com/KwakEuiJin/Compose-DatePicker.git")
developerConnection.set("scm:git:ssh://git@github.com:KwakEuiJin/Compose-DatePicker.git")
}
}
}
프로젝트 루트의 gradle.properties 혹은 ~/.gradle/gradle.properties 에 다음 내용을 추가합니다.
해당 정보들은 보안상의 이유로 공개레포지토리에 올라가면 안되기 때문에 전역 gradle.properties 파일에 추가하거나 CI 서버에서 환경 변수를 통해 제공할 수 있습니다.
mavenCentralUsername= Maven Central token 의 username
mavenCentralPassword= Maven Central token 의 password
signing.keyId=12345678 # key 의 id
signing.password=paswword # key 의 패스워드
signing.secretKeyRingFile=/Users/guest/.gnupg/secring.gpg # secring.gpg 의 경로
import com.vanniktech.maven.publish.SonatypeHost
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.jetbrains.kotlin.android)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.vanniktech.maven)
}
group = "io.github.KwaEuiJin"
version = "0.0.1"
android {
. . .
}
dependencies {
. . .
}
mavenPublishing {
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)
signAllPublications()
coordinates("io.github.kez-lab", "compose-datepicker", "0.0.1")
pom {
name = "Compose-DatePicker"
description = "Compose DatePicker"
url = "https://github.com/KwakEuiJin/Compose-DatePicker"
inceptionYear = "2024"
licenses {
license {
name = "The Apache License, Version 2.0"
url = "http://www.apache.org/licenses/LICENSE-2.0.txt"
}
}
developers {
developer {
id = "KwakEuiJin"
name = "KEZ"
url = "https://github.com/KwakEuiJin"
}
}
scm {
url.set("https://github.com/KwakEuiJin/Compose-DatePicker")
connection.set("scm:git:git://github.com/KwakEuiJin/Compose-DatePicker.git")
developerConnection.set("scm:git:ssh://git@github.com/KwakEuiJin/Compose-DatePicker.git")
}
}
}
이제 라이브러리를 Maven Central에 배포할 준비가 되었습니다. 배포는 Gradle 명령어 하나로 쉽게 수행할 수 있습니다. 다음 단계를 따라 진행하세요
./gradlew publishAndReleaseToMavenCentral --no-configuration-cache
해당 명령어를 터미널에 입력 후 Maven Central Portal로 가보면 여러분을 기다리는 빨간 에러가 있을겁니다 ㅋ.ㅋ (없으시다면 라이브러리에서 컴포즈를 안쓰시는 분일겁니다.)
Maven Central로 라이브러리를 배포할 때, publish 태스크가 성공적으로 완료되었음에도 불구하고 "Dependency version information is missing" 오류가 발생할 수 있습니다. 이 문제는 주로 build.gradle.kts 파일의 dependencies 블록에 버전 정보가 누락되어 있을 때 발생합니다.
특히, Compose BOM(Bill of Materials)을 사용하여 의존성을 관리하는 경우, 이러한 오류가 발생할 가능성이 큽니다. 이는 Maven Central이 Google Maven Repository에 있는 BOM 의존성의 버전을 자동으로 찾을 수 없기 때문입니다.
Maven Central은 Google Maven Repository에 있는 compose-bom 패키지의 버전을 자동으로 인식하지 못합니다. 따라서 BOM을 사용하는 경우 명시적으로 버전 정보를 제공해야 합니다.
아래와 같이 libs.toml 파일에서 각 의존성에 대한 명시적인 버전 정보를 설정하고, platform(libs.androidx.compose.bom)를 제거하여 문제를 해결할 수 있습니다.
platform 사용 제거
implementation(platform(libs.androidx.compose.bom))
libs.toml 파일에서 버전 정보 설정
libs.toml 파일에서 다음과 같이 각 의존성의 버전을 명시적으로 설정합니다.
[versions]
compose-android = "1.6.8"
[libraries]
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics", version.ref = "compose-android" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "compose-android" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "compose-android" }
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest", version.ref = "compose-android" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4", version.ref = "compose-android" }
네 지금까지 난관을 이겨내고 배포하신 여러분에게 칭찬의 박수를 보냅니다 👍
여러분도 이젠 어엿한 라이브러리 개발자가 되신 것입니다.
혹시나 제 블로그를 보시고 배포에 성공/실패하셨다면 한마디씩 남겨주신다면 큰 힘/도움이 될 수 있을 것 같습니다. 감사합니다
늘감사합니다 :)