| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
- 멀티모듈
- ci/cd
- 트러블슈팅
- 분산시스템
- SpringCloud
- 마이크로서비스아키텍처
- 백엔드
- 백엔드면접준비
- 도커
- 공통모듈
- MSA
- 컨테이너
- 마이그레이션
- GitHub Packages
- Flyway
- gradle
- PostgreSQL
- 마이크로서비스
- 아키텍처
- docker
- CS
- dockercompose
- github actions
- java
- 인프라
- 자바
- springboot
- Database
- GCP
- Java 8
- Today
- Total
NYO_O
GitHub Packages를 활용한 공통 모듈 배포 파이프라인 구축 본문
지난 시간, 공통 모듈에 어떤 내용을 담아야 하는지, 그리고 포함해서는 안 되는 것들은 무엇인지 그 설계 기준을 살펴보았습니다. 비즈니스 로직을 배제하고, 서비스들이 함께 공유하는 기술적인 규약과 도구 상자를 만드는 것이 핵심이었습니다.
2026.05.27 - [Tech/MSA] - 공통 모듈, 무엇을 담고 무엇을 빼야할까?
공통 모듈, 무엇을 담고 무엇을 빼야할까?
지난 시간, 우리는 MSA 환경에서 왜 공통 모듈(Common Library)이 필요한 이유에 대해 살펴보았습니다. 서비스가 분산될수록 파편화되는 코드를 관리하기 위해 공통 모듈은 필수적인 선택지입니다.
ddangnyo.tistory.com
모듈 설계가 어느 정도 갖춰지고 나면 자연스럽게 다음 고민이 생깁니다.
"잘 만든 공통 모듈을, 어떻게 각 서비스에서 가져다 쓸 수 있을까?"
오늘은 이 질문에 답하기 위해 GitHub Packages를 Maven 저장소로 활용하여 공통 모듈을 패키지로 배포하고, GitHub Actions로 자동화 파이프라인을 구성하는 과정을 정리해 보겠습니다.
GitHub Packages란?
GitHub Packages는 GitHub에서 제공하는 패키지 호스팅 서비스입니다. Maven, npm, Docker 등 다양한 패키지 형식을 지원하는데, 우리가 만든 공통 모듈(JAR 파일)을 Maven 저장소 형태로 올려두고 관리하는 용도로 활용할 수 있습니다.
별도의 사설 Maven 저장소(Nexus, Artifactory 등)를 직접 구축하고 운영하는 방법도 있지만, GitHub 저장소와 연동된 패키지 레지스트리를 무료로 사용할 수 있다는 점에서 소규모 팀이나 프로젝트 초기 단계에서 GitHub Packages가 꽤 합리적인 선택지가 될 수 있습니다.
GitHub Packages를 선택했을 때의 장점
- 별도의 인프라 비용 없이 사설 Maven 저장소처럼 활용할 수 있습니다.
- GitHub Actions와 자연스럽게 연동됩니다.
- 저장소 접근 권한을 GitHub의 기존 권한 체계로 함께 관리할 수 있습니다.
전체 구조 이해
설정을 하나씩 살펴보기 전에 전체 흐름을 먼저 그려보면 이해가 쉽습니다.
[공통 모듈 저장소]
└─ 코드 변경 및 Push/Tag
│
▼
[GitHub Actions - CI/CD]
└─ 빌드 → 테스트 → GitHub Packages에 배포(publish)
│
▼
[GitHub Packages - Maven Registry]
└─ common-module-x.x.x.jar 저장
│
▼
[각 도메인 서비스]
└─ build.gradle에서 의존성 선언 후 사용
공통 모듈은 독립된 GitHub 저장소로 관리하고, 변경이 생기면 Actions가 자동으로 패키지를 빌드·배포합니다. 각 도메인 서비스는 이 패키지를 의존성으로 선언해서 가져다 씁니다.
1단계: 공통 모듈 프로젝트 설정 (Gradle 기준)
공통 모듈 프로젝트의 build.gradle에 GitHub Packages 배포를 위한 설정을 추가해야 합니다.
YOUR_GITHUB_USERNAME과 YOUR_REPO_NAME은 실제 GitHub 계정명과 저장소 이름으로 교체하면 됩니다. 인증 정보는 환경 변수로 주입받도록 구성해서 코드에 직접 노출되지 않도록 하는 것이 좋습니다.
bootJar vs jar — 한 번쯤 꼭 마주치게 되는 설정
Spring Boot 기반으로 공통 모듈을 만들다 보면 JAR 파일이 제대로 생성되지 않는 문제를 한 번쯤 겪게 됩니다. 원인은 Spring Boot의 기본 동작 방식에 있습니다.
spring-boot-starter가 classpath에 존재하면 Gradle은 기본적으로 bootJar 태스크를 실행해서 실행 가능한 Fat JAR(Executable JAR) 을 만들어냅니다. 이 Fat JAR은 main 메서드를 진입점으로 하는 실행용 파일이기 때문에, 다른 서비스에서 라이브러리처럼 import해서 쓰는 것이 안 됩니다.
공통 모듈은 직접 실행되는 애플리케이션이 아니라 다른 서비스들이 의존성으로 가져가는 라이브러리이므로, 아래 두 줄을 추가해서 일반 라이브러리 JAR이 생성되도록 설정해줘야 합니다.
// build.gradle
bootJar { enabled = false } // 실행 가능한 Fat JAR 생성 비활성화
jar { enabled = true } // 일반 라이브러리 JAR 생성 활성화
이 설정 없이 배포하면 도메인 서비스에서 의존성을 가져가더라도 클래스를 찾을 수 없다는 오류가 발생하는데, 처음에 이 원인을 모르면 꽤 당황스러울 수 있습니다.
최종적으로 build.gradle 전체 구성은 다음과 같습니다.
plugins {
id 'java-library'
id 'maven-publish'
id 'org.springframework.boot' version '3.x.x'
id 'io.spring.dependency-management' version '1.x.x'
}
group = 'com.example'
version = '1.0.0'
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
// 공통 모듈은 실행 애플리케이션이 아니라 라이브러리이므로
// bootJar를 비활성화하고 일반 jar를 활성화한다
bootJar { enabled = false }
jar { enabled = true }
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
}
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
groupId = 'com.example'
artifactId = 'common-module'
version = project.version
}
}
repositories {
maven {
name = 'GitHubPackages'
url = uri('https://maven.pkg.github.com/YOUR_GITHUB_USERNAME/YOUR_REPO_NAME')
credentials {
username = System.getenv('GITHUB_ACTOR')
password = System.getenv('GITHUB_TOKEN')
}
}
}
}
2단계: GitHub Actions 워크플로우 작성
공통 모듈 저장소의 .github/workflows/ 디렉토리 아래에 워크플로우 파일을 생성합니다.
# .github/workflows/publish.yml
name: Publish to GitHub Packages
on:
push:
tags:
- 'v*.*.*' # v1.0.0 형태의 태그가 Push될 때 실행
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # GitHub Packages에 쓰기 권한 부여
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build and Test
run: ./gradlew clean build
- name: Publish to GitHub Packages
run: ./gradlew publish
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
몇 가지 포인트를 짚어보겠습니다.
- 트리거 조건: tags: - 'v*.*.*' 설정 덕분에 버전 태그가 Push될 때만 배포가 실행됩니다. main 브랜치 Push마다 자동 배포하도록 구성할 수도 있는데, 태그 기반으로 관리하면 "언제, 어떤 버전이 배포됐는지"를 이력으로 남길 수 있어서 개인적으로는 태그 방식을 더 선호합니다.
- 권한 설정: packages: write 권한이 빠지면 GitHub Packages에 배포가 되지 않습니다.
- GITHUB_TOKEN: 별도로 발급할 필요 없이 Actions에서 자동으로 제공하는 토큰입니다. secrets.GITHUB_TOKEN으로 바로 사용할 수 있습니다.
3단계: 수동 배포부터 자동화까지
실무에서의 전체 배포 순서
공통 모듈을 수정하고 실제로 반영하기까지의 흐름을 정리하면 대략 이렇게 됩니다.
1. 공통 모듈 코드 수정
2. build.gradle의 version 번호 변경 (예: 1.0.0 → 1.0.1)
3. 공통 모듈 배포 (수동 또는 Actions 자동화)
4. 도메인 서비스의 build.gradle에서 의존성 버전 수정 후 로컬 테스트
5. 이상 없으면 도메인 서비스 확정 배포
2번과 3번 사이에 코드 리뷰와 main 브랜치 병합 과정을 끼워 넣으면 더 안전하게 운영할 수 있습니다.
명령어로 직접 수동 배포하기
Actions가 아직 구성되어 있지 않거나, 긴급 픽스처럼 빠르게 배포해야 하는 상황이라면 로컬에서 직접 명령어로 배포하는 것도 방법입니다.
수동 배포 시에는 GITHUB_ACTOR와 GITHUB_TOKEN 환경 변수를 직접 주입해서 실행하면 됩니다.
# macOS / Linux
GITHUB_ACTOR=YOUR_GITHUB_USERNAME \
GITHUB_TOKEN=YOUR_PERSONAL_ACCESS_TOKEN \
./gradlew clean build publish
# Windows (PowerShell)
$env:GITHUB_ACTOR="YOUR_GITHUB_USERNAME"
$env:GITHUB_TOKEN="YOUR_PERSONAL_ACCESS_TOKEN"
./gradlew clean build publish
여기서 사용하는 Personal Access Token은 write:packages 권한으로 발급해야 합니다. 배포 후 GitHub 저장소의 Packages 탭에서 업로드가 정상적으로 됐는지 확인해보는 것이 좋습니다.
참고: 매번 환경 변수를 직접 입력하는 게 번거롭다면, ~/.gradle/gradle.properties에 아래처럼 등록해 두고 build.gradle의 credentials에서 findProperty로 읽어오는 방식도 활용할 수 있습니다.
gpr.user=YOUR_GITHUB_USERNAME gpr.key=YOUR_PERSONAL_ACCESS_TOKEN
GitHub Actions를 통한 자동 배포
코드 수정과 버전 변경이 완료되어 main에 병합된 이후에는 태그를 Push하는 것만으로 Actions가 알아서 빌드, 테스트, 배포를 처리해 줍니다.
# 코드 수정 및 버전 변경 후 커밋
git add .
git commit -m "chore: bump version to 1.0.1"
git push origin main
# 태그 생성 및 Push → GitHub Actions 트리거
git tag v1.0.1
git push origin v1.0.1
Actions 탭에서 실행 상태를 확인할 수 있고, 성공하면 저장소의 Packages 탭에서 배포된 패키지를 확인할 수 있습니다.
4단계: 도메인 서비스에서 패키지 사용 (의존성 주입)
공통 모듈이 GitHub Packages에 올라갔다면, 이제 각 도메인 서비스의 build.gradle에 저장소와 의존성을 선언해 줍니다.
repositories {
mavenCentral()
maven {
name = 'GitHubPackages'
url = uri('https://maven.pkg.github.com/YOUR_GITHUB_USERNAME/YOUR_REPO_NAME')
credentials {
username = System.getenv('GITHUB_ACTOR') ?: project.findProperty('gpr.user')
password = System.getenv('GITHUB_TOKEN') ?: project.findProperty('gpr.key')
}
}
}
dependencies {
implementation 'com.example:common-module:1.0.0'
// 버전 업그레이드 시 버전 번호만 변경하면 됩니다
}
한 가지 주의할 점은, GitHub Packages는 프라이빗 패키지를 다운로드할 때도 인증을 요구한다는 것입니다. 로컬 개발 환경에서는 ~/.gradle/gradle.properties에 인증 정보를 등록해 두는 방식을 사용합니다.
# ~/.gradle/gradle.properties (로컬 환경 전용 - 절대 커밋하지 않는다)
gpr.user=YOUR_GITHUB_USERNAME
gpr.key=YOUR_GITHUB_PERSONAL_ACCESS_TOKEN
Personal Access Token(PAT)은 GitHub → Settings → Developer settings → Personal access tokens에서 read:packages 권한으로 발급하면 됩니다.
보안 관련: gradle.properties에 직접 토큰을 작성하는 경우, 이 파일이 .gitignore에 포함되어 있는지 꼭 확인하세요. 실수로 커밋되어 GitHub에 올라가면 토큰이 노출됩니다.
5단계: 도메인 서비스 CI/CD에서 인증 처리
도메인 서비스도 GitHub Actions로 빌드한다면, 워크플로우에서 GitHub Packages 인증을 함께 처리해 줘야 합니다.
# 도메인 서비스의 .github/workflows/build.yml
- name: Build
run: ./gradlew clean build
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_ACTOR와 GITHUB_TOKEN을 환경 변수로 주입하면, 빌드 시 GitHub Packages에서 공통 모듈을 자동으로 받아옵니다.
단, 공통 모듈 저장소와 도메인 서비스 저장소가 동일한 GitHub 조직(Organization) 또는 동일한 계정 아래에 있어야 기본 GITHUB_TOKEN으로 접근이 됩니다. 서로 다른 계정에 걸쳐 있다면 별도의 PAT을 발급해서 Repository Secret으로 등록해줘야 합니다.
버전 관리 전략
공통 모듈은 여러 서비스가 동시에 사용하기 때문에 버전 관리를 어느 정도 체계적으로 가져가는 것이 좋습니다. Semantic Versioning(SemVer) 을 기준으로 아래 기준을 참고해서 적용해보시면 됩니다.
버전 유형 변경 예시 설명
| MAJOR (x.0.0) | 기존 API 시그니처 변경, 삭제 | 하위 호환성이 깨지는 변경 |
| MINOR (1.x.0) | 새로운 유틸리티 클래스 추가 | 하위 호환성을 유지하는 기능 추가 |
| PATCH (1.0.x) | 버그 수정, 오타 수정 | 기존 동작의 버그 수정 |
특히 MAJOR 버전이 올라가는 변경은 가능하면 사전에 충분히 공유하고 마이그레이션 기간을 주는 것이 좋습니다. 공통 모듈은 모든 서비스가 의존하고 있기 때문에, 갑작스러운 Breaking Change는 팀 전체에 영향을 줄 수 있습니다.
전체 파이프라인 정리
지금까지 구성한 파이프라인을 한눈에 정리하면 다음과 같습니다.
1. 공통 모듈 변경 사항 개발 및 PR 생성
2. 코드 리뷰 후 main 브랜치에 병합
3. 버전 태그 Push (git tag v1.x.x && git push origin v1.x.x)
4. GitHub Actions 자동 실행 → 빌드 → 테스트 → GitHub Packages 배포
5. 도메인 서비스에서 build.gradle의 버전 번호 업데이트
6. 도메인 서비스 재배포
마무리
오늘은 공통 모듈을 GitHub Packages에 배포하고, GitHub Actions로 자동화 파이프라인을 구성하는 방법을 살펴보았습니다. 별도의 저장소 서버를 구축하지 않아도 GitHub 생태계 안에서 패키지 배포와 버전 관리를 비교적 쉽게 처리할 수 있다는 게 꽤 매력적이었습니다.
다음 시간에는 이렇게 배포된 공통 모듈을 각 도메인 서비스에서 실제로 의존성을 주입하고 연동하는 구체적인 가이드를 다루어 보겠습니다. 단순히 implementation으로 선언하는 것을 넘어, 자동 설정(Auto-configuration)과 조건부 빈 등록 등 Spring Boot의 메커니즘을 활용해 연동하는 방법을 소개할 예정입니다.
'Architecture > MSA' 카테고리의 다른 글
| 공통 모듈의 역할별 분리 전략 (0) | 2026.05.27 |
|---|---|
| 공통 모듈 의존성 주입 및 연동 가이드 (0) | 2026.05.27 |
| 공통 모듈, 무엇을 담고 무엇을 빼야할까? (1) | 2026.05.27 |
| MSA 환경에서 공통 모듈이 필요한 이유 (0) | 2026.05.27 |
| 시스템의 결합도를 낮추는 비동기 통신: 이벤트 드리븐(Event-Driven) 아키텍처 (0) | 2026.05.27 |