effidevFlutter · Cloudflare 엣지 · 클라우드 비용 최적화
한국어

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

CI/CD 배포 파이프라인과 App Store, Google Play 아이콘이 그려진 일러스트레이션

Flutter로 앱을 개발하는 과정은 즐겁지만, 완성된 앱을 App Store와 Google Play Store에 배포하는 과정은 종종 개발자들에게 큰 스트레스로 다가옵니다. 인증서 관리, 프로비저닝 프로파일 매칭, 빌드 버전 범프(bump), 그리고 마켓 업로드까지 수작업으로 진행해야 할 일들이 너무 많기 때문입니다.

이 가이드에서는 FastlaneGitHub Actions를 결합하여 Flutter 앱의 iOS 및 Android 빌드, 코드사인, 그리고 스토어 배포 과정을 완전히 자동화하는 구축 방법을 다룹니다.

왜 CI/CD 자동화가 필요할까요?

수동 배포 과정에서 다음과 같은 문제점들이 자주 발생합니다.

GitHub Actions와 Fastlane을 사용하면, 코드를 main 또는 release 브랜치에 푸시하는 것만으로 이 모든 과정을 클라우드에서 안전하게 처리할 수 있습니다.


1. Fastlane 설정 (로컬)

먼저 Flutter 프로젝트의 androidios 디렉토리 각각에 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에 다음과 같은 값들을 미리 저장해야 합니다.

전체 워크플로우 스크립트

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

핵심 고려사항

  1. Mac 환경 선택: iOS 빌드를 위해서는 반드시 macos-latest 러너(Runner)를 사용해야 합니다. (Linux 러너보다 과금률이 높다는 점에 유의하세요.)
  2. Flutter 빌드 최적화: iOS의 경우 flutter build ipa 명령어가 아카이빙까지 모두 처리하게 할 수 있습니다. 이 경우 Fastlane은 업로드 역할만 담당하게 구성하는 것이 속도 면에서 유리합니다.
  3. 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를 도입하여 개발에만 집중할 수 있는 환경을 만들어보세요.