여러 프로젝트에서 지속적 통합 및 지속적 배포는 필수가 되었어요.
빌드 시간이 길고, QA, PO, 디자이너까지 다양한 인원이 앱의 최신 버전을 빠르게 확인해야 하는 환경에서는 특히나 자동화가 중요하다고 생각해요.
이번 아티클은 Android를 기준으로 GitHub Actions CI/CD의 개념부터 실전 적용, 고급 설정, 배포, 알림까지 모든 내용을 다룰 예정 입니다.
-> CI/CD를 도입하게 된다면?
빌드, 배포를 자동화 → 개발자는 코드에만 집중
QA, PO에게 자동으로 APK 전달
오류가 PR 단계에서 빠르게 검출
| 구성 요소 | 설명 |
|---|---|
| Workflow | .github/workflows/*.yml에 정의되는 CI/CD의 단위 |
| Event | Workflow를 실행시키는 조건 (push, pull_request 등) |
| Job | Workflow 안에서 실행되는 작업 그룹 (병렬, 순차 가능) |
| Step | Job 내의 실제 작업 단위 (shell, action) |
| Runner | 작업이 실행되는 환경 (ubuntu-latest, macos-latest) |
| Secret | 환경 변수 및 보안 정보 관리 (keystore, API Key 등) |
on:
push:
branches: [ main ]
pull_request:
branches: [ develop ]
Event 발생 → Workflow 실행 → Job 실행 → Step 순차 실행 → Runner에서 실제 동작
CI/CD를 적용하기 위해 Android 프로젝트에서 반드시 준비해야 하는 것들을 살펴보겠습니다.
안드로이드에서 Release 빌드를 Play Store에 업로드할 때 반드시 Signing이 필요합니다.
필수 환경 변수 목록:
ANDROID_KEYSTORE : base64로 인코딩된 keystore 파일
KEY_ALIAS
KEY_PASSWORD
STORE_PASSWORD
💡 GitHub Actions에서는 위 정보를 모두 Settings > Secrets > Actions 에 등록해야 합니다.
다만 APK로 CD를 설정한다면 굳이 해당 환경변수를 설정할 필요는 없고,build gradle의 release 환경에
signingConfig = signingConfigs.getByName("debug")
요것만 추가해주면 됩니다.
local.properties 파일은 git에 올라가지 않는 개발용 환경 변수 파일입니다.
CI 서버에서도 baseUrl, apiKey 등을 제공해야 하므로 Workflow에서 동적으로 생성합니다.
- name: Generate local.properties
run: |
echo "baseUrl=$BASE_URL" >> local.properties
1번에서 만약 AAB로 하실려면 gradle설정에 아래 항목을 추가해야됩니다.
signingConfigs {
release {
storeFile file(System.getenv("ANDROID_KEYSTORE"))
storePassword System.getenv("STORE_PASSWORD")
keyAlias System.getenv("KEY_ALIAS")
keyPassword System.getenv("KEY_PASSWORD")
}
}
이렇게 설정하면 CI/CD에서도 Signing이 자동으로 처리됩니다.
Firebase App Distribution을 사용한다면, google-services.json도 CI에서 decode 후 제공해야 합니다.
base64로 인코딩된 후 FIREBASE_SECRET이라는 이름으로 Secrets에 저장
CI에서는 다음과 같이 복원하면 됨!
- name: Decode google-services.json
env:
FIREBASE_SECRET: ${{ secrets.FIREBASE_SECRET }}
run: echo $FIREBASE_SECRET | base64 --decode > app/google-services.json
아래 예제 CI는 PR(Pull Request)이 생성될 때마다 develop 브랜치에서 자동으로 빌드, Lint를 수행하는 워크플로우입니다.
name: Clody CI
on:
pull_request:
branches: [ develop ]
paths:
- 'app/**'
- 'build.gradle'
- '**/*.kt'
defaults:
run:
working-directory: ./
jobs:
build:
runs-on: ubuntu-latest
- name: Cache Gradle packages
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('gradle.properties', '**/*.gradle*', '**/gradle-wrapper.properties') }}
- name: Cache Android SDK
uses: actions/cache@v4
with:
path: ~/.android
key: ${{ runner.os }}-android
- name: Checkout the code
uses: actions/checkout@v4
- name: Setup JDK
uses: actions/setup-java@v4
with:
distribution: 'corretto'
java-version: '17'
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Grant execute permission for gradlew
run: chmod +x gradlew
만약 firebase를 사용하지 않는다면 굳이 필요 없다.
- name: Decode google-services.json
env:
FIREBASE_SECRET: ${{ secrets.FIREBASE_SECRET }}
run: echo $FIREBASE_SECRET | base64 --decode > app/google-services.json
- name: Generate local.properties
env:
BASE_URL: ${{ secrets.BASE_URL }}
...
run: |
echo "baseUrl=$BASE_URL" >> local.properties
...
- name: Run Lint and Build
run: ./gradlew --no-daemon --configuration-cache ktlintCheck assembleDebug
아래 예제는 Staging 브랜치로 PR이 생성되거나 병합되었을 때 자동으로 Release APK를 생성하고, Firebase App Distribution을 통해 배포하며, Discord에 배포 완료 알림을 전송하는 워크플로우입니다.
CI와 겹치는 부분은 설명을 생략하겠습니다.
D 서버는 사람이 Firebase Console에 직접 로그인할 수 없습니다.
그래서 CD가 대신 인증을 받아야 하는데, 이때 사용되는 것이 바로 Service Account Credential입니다.
내부에서 어떻게 동작할까요?
1. Firebase Console에서 Service Account JSON 발급
2. 해당 JSON을 base64로 인코딩하여 GitHub Secrets에 저장
3. CI/CD 환경에서 base64 디코딩으로 다시 JSON 복원
4. 환경변수 GOOGLE_APPLICATION_CREDENTIALS로 등록
5. Firebase CLI가 이 Credential을 읽어 Firebase API 인증 수행
6. 인증이 완료된 후, 배포 API를 통해 AppDistribution에 APK 업로드
요 과정을 통해 CI/CD 환경에서도 안전하게 인증된 Firebase 배포가 가능합니다!
name: Clody CD
on:
pull_request:
branches: [ staging ]
jobs:
cd:
name: Continuous Deployment
runs-on: ubuntu-latest
- name: Checkout code
uses: actions/checkout@v4
CI 서버에 현재 브랜치의 소스코드 다운로드
- name: Cache Gradle dependencies
uses: actions/cache@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'corretto'
cache: gradle
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Change gradlew permissions
run: chmod +x gradlew
- name: Install Firebase CLI
run: curl -sL https://firebase.tools | bash
- name: Decode google-services.json
env:
FIREBASE_SECRET: ${{ secrets.FIREBASE_SECRET }}
run: echo $FIREBASE_SECRET | base64 --decode > app/google-services.json
- name: Generate local.properties
- name: Build Release APK
run: ./gradlew assembleRelease --stacktrace
- name: Set up Firebase Service Account Credentials
env:
GOOGLE_APPLICATION_CREDENTIALS_JSON: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS_JSON }}
- name: Upload APK to Firebase App Distribution
run: firebase appdistribution:distribute app/build/outputs/apk/release/app-release.apk ...
- name: Notify Discord
run: |
curl -H "Content-Type: application/json" -X POST ...
- name: Upload Debug APK
uses: actions/upload-artifact@v4
with:
name: debug-apk
path: app/build/outputs/apk/debug/app-debug.apk
| 옵션 | 설명 |
|---|---|
on | Workflow Trigger 지정 |
concurrency | 동일한 Workflow 중복 실행 방지 |
matrix | 여러 환경(버전) 병렬 빌드 |
needs | Job 간 실행 순서 지정 |
timeout-minutes | Workflow 실행 제한 시간 |
continue-on-error | 오류 발생 시 다음 Step 진행 여부 |
defaults | Workflow 전역 기본 설정 |
outputs | Job 결과 값을 다른 Job에 전달 |
env | 환경 변수 설정 |
main → 실제 서비스 출시 버전
staging → QA, Tester 배포용
develop → 개발 및 기능 통합
feature/* → 기능 단위 브랜치
저 같은 경우는 feat/* -> develop -> staging -> release를 따르고 있습니다.
CI/CD는 안드로이드 개발에 있어서 필수가 되었습니다.
대부분의 회사에서 사용중이죠.
여러분들도 프로젝트에 꼭 CI/CD를 적용해보세요.
초기 세팅이 번거로울 뿐, 유지보수와 팀 생산성이 눈에 띄게 향상된답니다.
https://docs.github.com/ko/actions
https://github.com/Triple-T/gradle-play-publisher