Fastlane + GitHub Actions로 Flutter iOS/Android 빌드, 코드사인, App Store 배포 자동화

Flutter로 앱을 개발하는 과정은 즐겁지만, 완성된 앱을 App Store와 Google Play Store에 배포하는 과정은 종종 개발자들에게 큰 스트레스로 다가옵니다. 인증서 관리, 프로비저닝 프로파일 매칭, 빌드 버전 범프(bump), 그리고 마켓 업로드까지 수작업으로 진행해야 할 일들이 너무 많기 때문입니다.
이 가이드에서는 Fastlane과 GitHub Actions를 결합하여 Flutter 앱의 iOS 및 Android 빌드, 코드사인, 그리고 스토어 배포 과정을 완전히 자동화하는 구축 방법을 다룹니다.
왜 CI/CD 자동화가 필요할까요?
수동 배포 과정에서 다음과 같은 문제점들이 자주 발생합니다.
- Human Error: 인증서를 잘못 매칭하거나 빌드 버전을 올리는 것을 잊어버리는 실수.
- 시간 낭비: 로컬 머신에서 아카이브(Archive)하고 스토어에 업로드되는 동안 기다려야 하는 시간.
- 의존성: 특정 개발자의 로컬 환경(특히 Mac)과 계정에 의존적인 배포 프로세스.
GitHub Actions와 Fastlane을 사용하면, 코드를 main 또는 release 브랜치에 푸시하는 것만으로 이 모든 과정을 클라우드에서 안전하게 처리할 수 있습니다.
1. Fastlane 설정 (로컬)
먼저 Flutter 프로젝트의 android와 ios 디렉토리 각각에 Fastlane을 초기화해야 합니다.
Android Fastlane 초기화
cd android
fastlane init
초기화 과정에서 패키지 이름과 JSON 비밀 키 경로를 묻습니다. Google Play Console에서 발급받은 서비스 계정(Service Account) JSON 키가 필요합니다.
android/fastlane/Fastfile 예시:
default_platform(:android)
platform :android do
desc "Submit a new Beta Build to Crashlytics"
lane :beta do
gradle(task: "clean bundleRelease")
upload_to_play_store(track: 'beta')
end
end
iOS Fastlane 초기화
iOS 배포는 인증서와 프로비저닝 프로파일 관리가 핵심입니다. Fastlane의 match를 사용하면 이 과정을 Git 리포지토리를 통해 팀원들과 안전하게 공유하고 CI/CD에서 쉽게 불러올 수 있습니다.
cd ios
fastlane init
fastlane match init
ios/fastlane/Fastfile 예시:
default_platform(:ios)
platform :ios do
desc "Push a new beta build to TestFlight"
lane :beta do
setup_ci
match(type: "appstore", readonly: true)
# Flutter 빌드는 GitHub Actions에서 수행하므로, Fastlane에서는 이미 빌드된 앱을 처리합니다.
build_app(workspace: "Runner.xcworkspace", scheme: "Runner")
upload_to_testflight
end
end
[!IMPORTANT] iOS 코드사이닝을 CI 환경에서 실행하려면 Fastlane Match 사용을 강력히 권장합니다. 로컬에서 인증서를 내보내어 CI에 올리는 것보다 훨씬 안전하고 관리가 편합니다.
2. GitHub Actions 워크플로우 구성
이제 리포지토리 최상단에 .github/workflows/deploy.yml 파일을 생성합니다.
워크플로우 기본 구조 및 환경 변수
GitHub Secrets에 다음과 같은 값들을 미리 저장해야 합니다.
MATCH_PASSWORD: Fastlane Match 복호화 비밀번호MATCH_GIT_BASIC_AUTHORIZATION: Match 저장소 접근을 위한 PAT(Personal Access Token)APP_STORE_CONNECT_API_KEY_KEY_ID: App Store Connect API Key IDAPP_STORE_CONNECT_API_KEY_ISSUER_ID: App Store Connect API Issuer IDAPP_STORE_CONNECT_API_KEY_KEY: App Store Connect API Key (.p8 내용)PLAY_STORE_CONFIG_JSON: Google Play 서비스 계정 JSON 내용
전체 워크플로우 스크립트
name: Deploy to App Store & Google Play
on:
push:
tags:
- 'v*' # v1.0.0 같은 태그 푸시 시 실행
jobs:
build-and-deploy:
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: '17'
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: 'stable'
- name: Install dependencies
run: flutter pub get
# --- iOS 배포 파이프라인 ---
- name: Setup Ruby for Fastlane
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
working-directory: ios
- name: Decode App Store Connect API Key
run: |
mkdir -p ~/.appstoreconnect/private_keys/
echo "${{ secrets.APP_STORE_CONNECT_API_KEY_KEY }}" > ~/.appstoreconnect/private_keys/AuthKey_${{ secrets.APP_STORE_CONNECT_API_KEY_KEY_ID }}.p8
- name: Build iOS App
run: flutter build ipa --release --export-options-plist=ios/ExportOptions.plist
- name: Deploy iOS to TestFlight
working-directory: ios
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}
run: bundle exec fastlane beta
# --- Android 배포 파이프라인 ---
- name: Setup Ruby for Fastlane (Android)
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
working-directory: android
- name: Decode Play Store JSON
run: echo "${{ secrets.PLAY_STORE_CONFIG_JSON }}" > android/play_store_config.json
- name: Build Android App
run: flutter build appbundle --release
- name: Deploy Android to Play Store
working-directory: android
run: bundle exec fastlane beta
핵심 고려사항
- Mac 환경 선택: iOS 빌드를 위해서는 반드시
macos-latest러너(Runner)를 사용해야 합니다. (Linux 러너보다 과금률이 높다는 점에 유의하세요.) - Flutter 빌드 최적화: iOS의 경우
flutter build ipa명령어가 아카이빙까지 모두 처리하게 할 수 있습니다. 이 경우 Fastlane은 업로드 역할만 담당하게 구성하는 것이 속도 면에서 유리합니다. - API Key 인증: 기존의 애플 ID/비밀번호 방식은 이중 인증(2FA) 때문에 CI 환경에서 사용하기 매우 까다롭습니다. 반드시 App Store Connect API Key를 발급받아 사용하세요.
트러블슈팅 가이드
| 증상 | 원인 및 해결책 |
|---|---|
Missing private key for ... (iOS) |
Fastlane Match 리포지토리에 접근하지 못했거나, MATCH_PASSWORD가 틀렸습니다. Secret 값을 확인하세요. |
Google Api Error: Invalid request - Invalid package name (Android) |
Google Play Console에 아직 앱이 한 번도 등록되지 않았을 때 발생합니다. 첫 배포는 반드시 수동으로 AAB를 올려야 합니다. |
Code signing is required for product type 'Application' (iOS) |
Xcode 프로젝트 파일(.pbxproj)의 서명 설정이 ’Automatic’으로 되어있으면서 프로비저닝 프로파일이 주입되지 않은 경우입니다. Match를 사용하도록 프로젝트 설정을 변경하세요. |
마무리
CI/CD 파이프라인 구축은 초기 설정에 반나절 정도의 시간이 소요될 수 있지만, 한 번 구축해두면 이후 수십, 수백 번의 배포 과정에서 엄청난 시간과 스트레스를 절약해 줍니다. 특히 팀 단위의 개발에서는 ’누가 배포할 것인가’에 대한 고민을 없애주어 개발 문화를 긍정적으로 변화시킵니다.
지금 바로 프로젝트에 Fastlane과 GitHub Actions를 도입하여 개발에만 집중할 수 있는 환경을 만들어보세요.