클라이언트 앱 CI/CD 가이드
작성일: 2026-04-06 Last Updated: 2026-04-06 대상: Browser Extension, Tauri Desktop, Flutter Mobile
목차
1. 빌드 파이프라인 개요
GitHub Actions 기반 파이프라인 구조
push/PR → Lint & Test → Build → Sign → Upload Artifact → Deploy/Publish
공통 워크플로우 파일 구조
.github/workflows/
+-- ci-common.yml # 공통 lint, test
+-- ci-browser-ext.yml # Browser Extension 빌드
+-- ci-tauri.yml # Tauri 빌드
+-- ci-flutter.yml # Flutter 빌드
+-- release-browser-ext.yml # Browser Extension 릴리즈
+-- release-tauri.yml # Tauri 릴리즈
+-- release-flutter.yml # Flutter 릴리즈
트리거 조건
| 이벤트 | CI (테스트/빌드) | CD (배포) |
|---|---|---|
push to develop | O | X |
pull_request to main | O | X |
push to main | O | staging |
tag v*.. | O | production |
workflow_dispatch | O | 수동 선택 |
2. 멀티 플랫폼 빌드 매트릭스
Browser Extension
strategy:
matrix:
browser: [chrome, firefox]
# Edge는 Chrome 빌드와 동일 (manifest 분기)
| 항목 | Chrome (MV3) | Firefox (MV3) |
|---|---|---|
| 매니페스트 | manifest.chrome.json | manifest.firefox.json |
| 빌드 출력 | .zip (CWS용) | .zip (AMO용) |
| Runner | ubuntu-latest | ubuntu-latest |
| 서명 | CWS API로 업로드 시 자동 | web-ext sign |
Tauri Desktop
strategy:
matrix:
include:
- platform: ubuntu-22.04
target: x86_64-unknown-linux-gnu
- platform: macos-latest
target: aarch64-apple-darwin
- platform: macos-latest
target: x86_64-apple-darwin
- platform: windows-latest
target: x86_64-pc-windows-msvc
| OS | 빌드 출력 | Runner |
|---|---|---|
| Linux | .deb, .AppImage | ubuntu-22.04 |
| macOS (ARM) | .dmg, .app | macos-latest (M1) |
| macOS (Intel) | .dmg, .app | macos-latest |
| Windows | .msi, .exe (NSIS) | windows-latest |
Flutter Mobile
strategy:
matrix:
include:
- platform: android
runner: ubuntu-latest
- platform: ios
runner: macos-latest
| OS | 빌드 출력 | Runner | 비고 |
|---|---|---|---|
| Android | .aab (Play Store), .apk (직접 배포) | ubuntu-latest | JDK 17, Android SDK |
| iOS | .ipa | macos-latest | Xcode 15+, CocoaPods |
3. 코드 서명 자동화
3.1 플 랫폼별 코드 서명
Browser Extension
| 브라우저 | 서명 방식 | 비고 |
|---|---|---|
| Chrome | CWS 업로드 시 Google이 자동 서명 | API 키만 필요 |
| Firefox | web-ext sign --api-key --api-secret | AMO API 키 |
Tauri Desktop
| OS | 도구 | 인증서/키 |
|---|---|---|
| macOS | codesign + notarytool | Apple Developer ID (P12) |
| Windows | signtool.exe | EV Code Signing Certificate (PFX 또는 HSM) |
| Linux | 서명 선택사항 | GPG 서명 권장 |
# macOS 코드 서명 + 공증 (GitHub Actions)
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
# Windows 코드 서명
env:
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_PFX_BASE64 }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_PFX_PASSWORD }}
Flutter Mobile
| OS | 도구 | 인증서/키 |
|---|---|---|
| Android | Gradle signingConfigs | Keystore (.jks) |
| iOS | Xcode + fastlane match | Apple Distribution Certificate + Provisioning Profile |
# Android 키스토어 (GitHub Actions)
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
3.2 Secrets 관리 체크리스트
| 항목 | 저장 위치 | 비고 |
|---|---|---|
| API 키 (CWS, AMO) | GitHub Secrets | 환경별 분리 |
| Apple 인증서 (P12) | GitHub Secrets (Base64) | 만료 관리 필수 |
| Windows 인증서 (PFX) | GitHub Secrets (Base64) | EV 인증서는 HSM 고려 |
| Android Keystore | GitHub Secrets (Base64) | 분실 시 앱 업데이트 불가 |
| iOS Provisioning | fastlane match (Git 저장소) | 암호화된 별도 repo |
| 환경 변수 (.env) | GitHub Environments | dev/staging/prod 분리 |
4. 스토어 배포 자동화
4.1 Browser Extension
Chrome Web Store
- name: Upload to Chrome Web Store
uses: mnao305/chrome-extension-upload@v5
with:
file-path: dist/chrome.zip
extension-id: ${{ secrets.CWS_EXTENSION_ID }}
client-id: ${{ secrets.CWS_CLIENT_ID }}
client-secret: ${{ secrets.CWS_CLIENT_SECRET }}
refresh-token: ${{ secrets.CWS_REFRESH_TOKEN }}
publish: true # false면 업로드만 (수동 게시)
Firefox AMO
- name: Upload to Firefox AMO
run: |
npx web-ext sign \
--source-dir dist/firefox \
--api-key ${{ secrets.AMO_API_KEY }} \
--api-secret ${{ secrets.AMO_API_SECRET }} \
--channel listed
4.2 Tauri Desktop
| 배포 채널 | 방법 | 비고 |
|---|---|---|
| GitHub Releases | tauri-action으로 자동 생성 | 기본 채널 |
| 자체 업데이트 서버 | S3/R2 + JSON manifest | tauri-plugin-updater 연동 |
| Microsoft Store | MSIX 패키지 + Partner Center API | 선택사항 |
| Mac App Store | Xcode Archive + Transporter | sandbox 요구사항 주의 |
| Linux Repos | PPA, Flatpak, Snap | 커뮤니티 기여 활용 |
# Tauri 릴리즈 (GitHub Actions)
- name: Build and Release
uses: tauri-apps/tauri-action@v0
with:
tagName: v__VERSION__
releaseName: 'v__VERSION__'
releaseBody: 'See CHANGELOG.md for details.'
releaseDraft: true
prerelease: false
4.3 Flutter Mobile
Google Play Store
- name: Deploy to Play Store
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT }}
packageName: com.example.app
releaseFiles: build/app/outputs/bundle/release/app-release.aab
track: internal # internal → alpha → beta → production
status: completed
Apple App Store
- name: Deploy to App Store
run: |
fastlane deliver \
--ipa build/ios/ipa/App.ipa \
--skip_screenshots true \
--skip_metadata true \
--submit_for_review false
4.4 배포 파이프라인 순서
1. internal/alpha 트랙 배포 (QA 팀)
2. beta 트랙 배포 (베타 테스터)
3. 단계적 production 배포 (10% → 25% → 50% → 100%)
4. 문제 발견 시 롤백 또는 핫픽스
5. 버전 관리
5.1 SemVer + 빌드 넘버
{MAJOR}.{MINOR}.{PATCH}+{BUILD_NUMBER}
예: 2.3.1+142
│ │ │ │
│ │ │ └─ CI 빌드 번호 (자동 증가)
│ │ └──── 버그 수정
│ └────── 기능 추가 (하위 호환)
└──────── 브레이킹 체인지
5.2 플랫폼별 버전 표기
| 플랫폼 | 버전 필드 | 빌드 번호 | 예시 |
|---|---|---|---|
| Chrome Extension | manifest.json → version | 4자리 M.m.p.B | 2.3.1.142 |
| Firefox Extension | manifest.json → version | SemVer | 2.3.1 |
| Tauri | tauri.conf.json → version | SemVer | 2.3.1 |
| Android | build.gradle → versionName + versionCode | versionCode 정수 | 2.3.1 / 20301142 |
| iOS | Info.plist → CFBundleShortVersionString + CFBundleVersion | CFBundleVersion | 2.3.1 / 142 |
5.3 버전 단일 소스 (Single Source of Truth)
// version.json (프로젝트 루트)
{
"version": "2.3.1",
"buildNumber": 142
}
CI에서 이 파일을 읽어 각 플랫폼 설정 파일에 주입:
- name: Sync version
run: |
VERSION=$(jq -r .version version.json)
BUILD=$(jq -r .buildNumber version.json)
# 각 플랫폼별 버전 주입 스크립트 실행
./scripts/sync-version.sh "$VERSION" "$BUILD"
6. 환경 분리
6.1 환경 정의
| 환경 | 용도 | API 서버 | 빌드 모드 |
|---|---|---|---|
dev | 로컬 개발 | localhost:8000 | debug |
staging | QA/테스트 | staging-api.example.com | release (디버그 심볼 포함) |
prod | 프로덕션 | api.example.com | release (최적화) |
6.2 환경 변수 관리
config/
+-- env.dev.json
+-- env.staging.json
+-- env.prod.json
// env.prod.json
{
"API_BASE_URL": "https://api.example.com",
"SENTRY_DSN": "https://xxx@sentry.io/123",
"ANALYTICS_ENABLED": true,
"LOG_LEVEL": "error",
"UPDATE_CHECK_URL": "https://releases.example.com/check"
}
6.3 플랫폼별 환경 주입
| 플랫폼 | 방법 |
|---|---|
| Browser Extension | 빌드 시 env.*.json → config.js 생성 |
| Tauri | tauri.conf.json의 build.beforeBuildCommand에서 주입 |
| Flutter | --dart-define-from-file=env.prod.json |
6.4 GitHub Environments 설정
jobs:
deploy:
environment: production # GitHub Environment로 승인 게이트 설정
env:
API_URL: ${{ vars.API_URL }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
7. 릴리즈 브랜치 전략
7.1 Git Flow 기반
main ─────────────────────────────────────────────
│ ▲ ▲
│ │ │
├── release/2.3.0 ──────────┘ │
│ ▲ │
│ │ │
├── develop ───┴── feature/* ──────────────┘
│
└── hotfix/2.3.1 ──────────→ main + develop
7.2 브랜치 규칙
| 브랜치 | 용도 | 보호 규칙 |
|---|---|---|
main | 프로덕션 릴리즈 | PR 필수, 2명 리뷰, CI 통과 |
develop | 개발 통합 | PR 필수, 1명 리뷰, CI 통과 |
release/* | 릴리즈 준비 | develop에서 분기, 버그 수정만 |
feature/* | 기능 개발 | develop에서 분기/병합 |
hotfix/* | 긴급 수정 | main에서 분기, main + develop 병합 |
7.3 릴리즈 프로세스
1. develop에서 release/X.Y.Z 분기
2. 버전 번 호 업데이트 (version.json)
3. QA 테스트 (staging 환경)
4. 버그 수정 → release 브랜치에 커밋
5. main에 병합 + 태그 생성 (vX.Y.Z)
6. CI/CD가 자동으로 빌드 + 서명 + 배포
7. release 브랜치를 develop에 역병합
8. 아티팩트 관리
8.1 빌드 아티팩트 저장
| 저장소 | 용도 | 보존 기간 |
|---|---|---|
| GitHub Actions Artifacts | CI 빌드 결과물 | 90일 |
| GitHub Releases | 릴리즈 바이너리 | 영구 |
| S3/R2 | 업데이트 서버용 바이너리 | 최근 5개 버전 |
| Docker Registry | 서비스 이미지 (해당 시) | 태그별 영구 |
8.2 아티팩트 업로드 (GitHub Actions)
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-${{ matrix.platform }}-${{ github.sha }}
path: |
dist/*.zip
dist/*.dmg
dist/*.msi
dist/*.deb
dist/*.AppImage
build/app/outputs/**/*.aab
build/ios/ipa/*.ipa
retention-days: 90
8.3 아티팩트 체크리스트
| 항목 | 확인 |
|---|---|
| 빌드 결과물에 디버그 심볼이 별도 저장되었는가 | [ ] |
| source map / dSYM / proguard mapping 보존되었는가 | [ ] |
| 빌드 재현 가능한 환경 정보가 기록되었는가 | [ ] |
| 이전 버전 아티팩트가 보존되었는가 (롤백용) | [ ] |
| 아티팩트 해시(SHA256)가 기록되었는가 | [ ] |
부록: CI/CD 전체 체크리스트
파이프라인 설정
| 항목 | 상태 |
|---|---|
| GitHub Actions 워크플로우 파일 생성 | [ ] |
| 환경별 GitHub Environments 설정 | [ ] |
| 필요한 Secrets 모두 등록 | [ ] |
| 브랜치 보호 규칙 설정 | [ ] |
| 빌드 캐시 설정 (node_modules, cargo, gradle) | [ ] |
| 병렬 빌드 매트릭스 구성 | [ ] |
| 빌드 타임아웃 설정 | [ ] |
코드 서명
| 항목 | 상태 |
|---|---|
| Apple Developer ID 인증서 발급/등록 | [ ] |
| Windows 코드 서명 인증서 구매/등록 | [ ] |
| Android Keystore 생성/백업 | [ ] |
| iOS Provisioning Profile 설정 (fastlane match) | [ ] |
| 인증서 만료 알림 설정 | [ ] |
스토어 배포
| 항목 | 상태 |
|---|---|
| Chrome Web Store 개발자 계정 + API 키 | [ ] |
| Firefox AMO 개발자 계정 + API 키 | [ ] |
| Google Play Console 서비스 계정 | [ ] |
| Apple App Store Connect API 키 | [ ] |
| 각 스 토어 앱 등록 (앱 ID, 번들 ID) | [ ] |
| 스토어 메타데이터 (설명, 스크린샷) 준비 | [ ] |
버전 관리
| 항목 | 상태 |
|---|---|
version.json 단일 소스 설정 | [ ] |
| 버전 동기화 스크립트 작성 | [ ] |
| 빌드 넘버 자동 증가 로직 | [ ] |
| CHANGELOG.md 자동 생성 (conventional commits) | [ ] |