CI/CD — GitHub Actions + Fastlane으로 자동 배포

매번 수동으로 빌드하고 스토어에 올리는 것은 비효율적입니다. CI/CD를 구축하면 코드를 푸시하기만 해도 자동으로 테스트, 빌드, 배포가 진행됩니다.


CI/CD 파이프라인 흐름

PLAINTEXT
Push/PR → 린트 & 테스트 → 빌드 → 배포
                                    ├─ Google Play (Android)
                                    ├─ App Store (iOS)
                                    └─ Firebase App Distribution (테스트)

GitHub Actions — Flutter CI

YAML
# .github/workflows/ci.yml
name: Flutter CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.0'
          channel: 'stable'
          cache: true

      - name: 의존성 설치
        run: flutter pub get

      - name: 코드 분석
        run: flutter analyze

      - name: 포맷 확인
        run: dart format --set-exit-if-changed .

      - name: 테스트 실행
        run: flutter test --coverage

      - name: 커버리지 업로드
        uses: codecov/codecov-action@v4
        with:
          file: coverage/lcov.info

  build-android:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4

      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.0'
          cache: true

      - uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '17'

      - name: 의존성 설치
        run: flutter pub get

      - name: APK 빌드
        run: flutter build apk --release

      - name: AAB 빌드
        run: flutter build appbundle --release

      - name: 아티팩트 업로드
        uses: actions/upload-artifact@v4
        with:
          name: android-release
          path: |
            build/app/outputs/flutter-apk/app-release.apk
            build/app/outputs/bundle/release/app-release.aab

  build-ios:
    needs: test
    runs-on: macos-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4

      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.0'
          cache: true

      - name: 의존성 설치
        run: flutter pub get

      - name: iOS 빌드
        run: flutter build ios --release --no-codesign

Fastlane — 스토어 배포 자동화

Android Fastlane 설정

BASH
cd android
fastlane init
RUBY
# android/fastlane/Fastfile
default_platform(:android)

platform :android do
  desc "Google Play 내부 테스트 트랙에 배포"
  lane :deploy_internal do
    upload_to_play_store(
      track: 'internal',
      aab: '../build/app/outputs/bundle/release/app-release.aab',
      json_key: 'fastlane/play-store-key.json',
    )
  end

  desc "Google Play 프로덕션 배포"
  lane :deploy_production do
    upload_to_play_store(
      track: 'production',
      aab: '../build/app/outputs/bundle/release/app-release.aab',
      json_key: 'fastlane/play-store-key.json',
    )
  end
end

iOS Fastlane 설정

RUBY
# ios/fastlane/Fastfile
default_platform(:ios)

platform :ios do
  desc "TestFlight에 배포"
  lane :deploy_testflight do
    build_app(
      workspace: "Runner.xcworkspace",
      scheme: "Runner",
      export_method: "app-store",
    )

    upload_to_testflight(
      skip_waiting_for_build_processing: true,
    )
  end

  desc "App Store에 배포"
  lane :deploy_appstore do
    build_app(
      workspace: "Runner.xcworkspace",
      scheme: "Runner",
    )

    upload_to_app_store(
      submit_for_review: true,
      automatic_release: true,
    )
  end
end

GitHub Actions + Fastlane 통합

YAML
# .github/workflows/deploy.yml
name: Deploy to Stores

on:
  push:
    tags:
      - 'v*'  # v1.0.0 같은 태그 푸시 시 실행

jobs:
  deploy-android:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.0'
          cache: true

      - uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '17'

      - name: 키스토어 디코딩
        run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > android/app/keystore.jks

      - name: key.properties 생성
        run: |
          echo "storePassword=${{ secrets.KEYSTORE_PASSWORD }}" > android/key.properties
          echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/key.properties
          echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/key.properties
          echo "storeFile=keystore.jks" >> android/key.properties

      - name: 빌드
        run: |
          flutter pub get
          flutter build appbundle --release

      - name: Google Play 배포
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.2'
      - run: |
          cd android
          bundle install
          bundle exec fastlane deploy_internal
        env:
          PLAY_STORE_JSON_KEY: ${{ secrets.PLAY_STORE_JSON_KEY }}

Firebase App Distribution (테스트 배포)

YAML
# 개발 브랜치 푸시 시 테스터에게 배포
- name: Firebase App Distribution
  uses: wzieba/Firebase-Distribution-Github-Action@v1
  with:
    appId: ${{ secrets.FIREBASE_APP_ID }}
    serviceCredentialsFileContent: ${{ secrets.FIREBASE_CREDENTIALS }}
    groups: testers
    file: build/app/outputs/flutter-apk/app-release.apk

버전 자동 증가

YAML
# pubspec.yaml
version: 1.0.0+1  # 버전이름+빌드번호

# GitHub Actions에서 빌드번호 자동 증가
- name: 빌드
  run: flutter build apk --build-number=${{ github.run_number }}

시크릿 관리

GitHub Settings → Secrets and variables → Actions에 등록:

시크릿용도
KEYSTORE_BASE64Android 서명 키 (base64)
KEYSTORE_PASSWORD키스토어 비밀번호
KEY_PASSWORD키 비밀번호
KEY_ALIAS키 별칭
PLAY_STORE_JSON_KEYGoogle Play API 키
FIREBASE_APP_IDFirebase 앱 ID

정리

  • GitHub Actions로 푸시/PR 시 자동으로 린트, 테스트, 빌드를 실행합니다
  • Fastlane으로 Google Play, App Store 배포를 자동화합니다
  • 서명 키와 API 키는 GitHub Secrets에 안전하게 저장하세요
  • 태그 기반 배포(v*)로 릴리스 워크플로를 관리할 수 있습니다
  • Firebase App Distribution으로 테스터에게 빠르게 배포할 수 있습니다
댓글 로딩 중...