Flutter 튜토리얼 65편: CI/CD 설정 - 자동화된 빌드와 배포 파이프라인 구축

요약#

핵심 요지#

  • 문제 정의: 수동 빌드와 배포는 시간이 많이 걸리고 휴먼 에러가 발생하기 쉽다.
  • 핵심 주장: CI/CD 파이프라인을 구축하면 빌드, 테스트, 배포 과정을 자동화하여 개발 효율성과 안정성을 높일 수 있다.
  • 주요 근거: Flutter 공식 문서에서 소개하는 fastlane, Codemagic, GitHub Actions 등의 도구를 활용한다.
  • 한계: 초기 설정에 시간이 필요하며, 각 플랫폼별 인증서와 프로비저닝 관리가 복잡할 수 있다.

문서가 설명하는 범위#

  • CI/CD의 개념과 Flutter 앱에서의 필요성
  • fastlane을 사용한 로컬 빌드 자동화
  • 주요 CI/CD 서비스 비교 및 설정
  • GitHub Actions를 활용한 실전 파이프라인 구축

읽는 시간: 30분 | 난이도: 고급


참고 자료#


문제 상황#

Flutter 앱을 App Store와 Google Play에 배포하려면 여러 단계를 거쳐야 합니다. 수동으로 진행하면 빌드 명령 실행, 인증서 관리, 스토어 업로드 등 반복적인 작업이 발생합니다.

기존 방식의 한계#

Terminal window
# 수동 배포 과정 - iOS
flutter build ipa --release
# Xcode에서 Archive
# App Store Connect에 업로드
# 메타데이터 수동 입력
# 심사 제출
# 수동 배포 과정 - Android
flutter build appbundle --release
# Google Play Console 접속
# 앱 번들 업로드
# 릴리스 노트 작성
# 출시

문제는 다음과 같습니다.

  • 매번 동일한 명령을 반복해야 한다
  • 휴먼 에러로 잘못된 빌드가 배포될 수 있다
  • 여러 환경(개발, 스테이징, 프로덕션)별 빌드 관리가 어렵다
  • 팀원 간 빌드 환경 차이로 문제가 발생할 수 있다

해결 방법#

챕터 1: CI/CD 개념 이해#

Why#

NOTE

CI/CD1는 코드 변경 시 자동으로 빌드, 테스트, 배포가 진행되는 방식입니다. 개발자는 코드 작성에 집중하고, 반복적인 작업은 자동화 시스템이 처리합니다.

What#

NOTE

CI/CD는 두 가지 개념으로 구성됩니다.

flowchart LR subgraph CI["CI - 지속적 통합"] A[코드 Push] B[자동 빌드] C[자동 테스트] end subgraph CD["CD - 지속적 배포"] D[릴리스 빌드] E[스토어 업로드] F[심사 제출] end A --> B --> C --> D --> E --> F
용어영문설명
CIContinuous Integration코드 변경을 자주 통합하고 자동으로 빌드/테스트
CDContinuous Delivery언제든 배포 가능한 상태 유지
CDContinuous Deployment자동으로 프로덕션까지 배포

How#

TIP

Flutter 앱의 일반적인 CI/CD 파이프라인 구성입니다.

# 파이프라인 단계
stages:
- lint # 코드 스타일 검사
- test # 단위/위젯 테스트
- build # 앱 빌드
- deploy # 스토어 배포
// 각 단계에서 실행되는 명령
// 1. Lint
// flutter analyze
// dart format --set-exit-if-changed .
// 2. Test
// flutter test
// 3. Build
// flutter build ipa --release
// flutter build appbundle --release
// 4. Deploy
// fastlane ios release
// fastlane android release

Watch out#

WARNING

CI/CD를 도입한다고 모든 문제가 해결되는 것은 아닙니다. 테스트 커버리지가 낮으면 CI를 통과해도 버그가 배포될 수 있습니다. 자동화와 함께 테스트 품질도 관리해야 합니다.

결론: CI/CD는 빌드, 테스트, 배포 과정을 자동화하여 개발 효율성과 안정성을 높인다.


챕터 2: fastlane 기초 설정#

Why#

NOTE

fastlane2은 iOS와 Android 앱의 빌드와 배포를 자동화하는 도구입니다. 로컬에서도 사용할 수 있고, CI/CD 서비스와 통합하여 사용할 수도 있습니다.

What#

NOTE

fastlane은 Ruby 기반 도구로, lane이라는 단위로 작업을 정의합니다.

flowchart TD subgraph fastlane F[Fastfile] L1[beta lane] L2[release lane] A1[build_app] A2[upload_to_testflight] A3[upload_to_play_store] end F --> L1 F --> L2 L1 --> A1 --> A2 L2 --> A1 --> A3
파일역할
Fastfilelane 정의 파일
Appfile앱 ID, 팀 ID 등 설정
Matchfile코드 서명 인증서 관리 설정

How#

TIP

fastlane 설치 및 초기 설정 방법입니다.

Terminal window
# macOS에서 fastlane 설치
brew install fastlane
# 또는 gem으로 설치
gem install fastlane
# iOS 프로젝트 초기화
cd ios
fastlane init
# Android 프로젝트 초기화
cd android
fastlane init

iOS용 기본 Fastfile 예시입니다.

# ios/fastlane/Fastfile
default_platform(:ios)
platform :ios do
desc "Push a new beta build to TestFlight"
lane :beta do
# 버전 증가
increment_build_number(
xcodeproj: "Runner.xcodeproj"
)
# 앱 빌드
build_app(
workspace: "Runner.xcworkspace",
scheme: "Runner",
export_method: "app-store"
)
# TestFlight 업로드
upload_to_testflight(
skip_waiting_for_build_processing: true
)
end
desc "Push a new release build to App Store"
lane :release do
build_app(
workspace: "Runner.xcworkspace",
scheme: "Runner",
export_method: "app-store"
)
upload_to_app_store(
skip_metadata: true,
skip_screenshots: true
)
end
end

Android용 기본 Fastfile 예시입니다.

# android/fastlane/Fastfile
default_platform(:android)
platform :android do
desc "Deploy a new beta build to Play Store Internal Track"
lane :beta do
# Flutter 빌드
sh("flutter build appbundle --release")
# Play Store 업로드
upload_to_play_store(
track: "internal",
aab: "../build/app/outputs/bundle/release/app-release.aab"
)
end
desc "Deploy a new release build to Play Store"
lane :release do
sh("flutter build appbundle --release")
upload_to_play_store(
track: "production",
aab: "../build/app/outputs/bundle/release/app-release.aab"
)
end
end

Watch out#

WARNING

fastlane은 Ruby 환경이 필요합니다. macOS에는 Ruby가 기본 설치되어 있지만, 버전 문제가 발생할 수 있습니다.

Terminal window
# Ruby 버전 관리자 설치 권장
brew install rbenv
rbenv install 3.2.0
rbenv global 3.2.0
# 환경 변수 설정
echo 'eval "$(rbenv init -)"' >> ~/.zshrc

결론: fastlane을 사용하면 복잡한 빌드와 배포 과정을 간단한 명령으로 자동화할 수 있다.


챕터 3: iOS 코드 서명 자동화#

Why#

NOTE

iOS 앱 배포의 가장 어려운 부분은 코드 서명3입니다. 인증서와 프로비저닝 프로파일을 팀원 간에 공유하고 관리하는 것이 복잡합니다.

What#

NOTE

fastlane match는 인증서와 프로비저닝 프로파일을 Git 저장소에서 중앙 관리합니다.

flowchart LR subgraph Team["개발팀"] D1[개발자 1] D2[개발자 2] CI[CI 서버] end subgraph Storage["저장소"] G[Git Repo] C[인증서] P[프로파일] end D1 -->|"match"| G D2 -->|"match"| G CI -->|"match"| G G --> C G --> P
타입용도파일
development개발용Development 인증서 + 프로파일
adhoc테스트 배포용Distribution 인증서 + Ad Hoc 프로파일
appstore스토어 배포용Distribution 인증서 + App Store 프로파일

How#

TIP

fastlane match 설정 방법입니다.

Terminal window
# 비공개 Git 저장소 생성 후 match 초기화
fastlane match init
# 인증서 생성 및 저장소에 업로드
fastlane match development
fastlane match adhoc
fastlane match appstore

Matchfile 설정입니다.

# ios/fastlane/Matchfile
git_url("[email protected]:your-org/certificates.git")
storage_mode("git")
type("appstore") # 기본 타입
app_identifier(["com.example.app"])
username("[email protected]")
# CI 환경에서는 readonly 모드 사용
readonly(is_ci)

Fastfile에서 match 사용합니다.

# ios/fastlane/Fastfile
platform :ios do
lane :beta do
# match로 인증서 동기화
match(type: "appstore")
build_app(
workspace: "Runner.xcworkspace",
scheme: "Runner",
export_method: "app-store",
export_options: {
provisioningProfiles: {
"com.example.app" => "match AppStore com.example.app"
}
}
)
upload_to_testflight
end
end

Watch out#

WARNING

match 저장소에는 민감한 정보가 포함됩니다. 반드시 비공개 저장소를 사용하고, 암호화 비밀번호를 안전하게 관리하세요.

Terminal window
# 환경 변수로 비밀번호 관리
export MATCH_PASSWORD="your-secure-password"
# CI에서는 시크릿으로 설정
# GitHub Actions: secrets.MATCH_PASSWORD
# Codemagic: Environment variables

결론: fastlane match를 사용하면 팀 전체가 동일한 인증서를 공유하여 코드 서명 문제를 해결한다.


챕터 4: CI/CD 서비스 비교#

Why#

NOTE

클라우드 기반 CI/CD 서비스4를 사용하면 빌드 서버를 직접 관리할 필요가 없습니다. 각 서비스마다 특징이 다르므로 프로젝트에 맞는 서비스를 선택해야 합니다.

What#

NOTE

Flutter 공식 문서에서 소개하는 주요 CI/CD 서비스입니다.

flowchart TD subgraph Apple["Apple 생태계"] XC[Xcode Cloud] end subgraph GitHub["GitHub 생태계"] GA[GitHub Actions] end subgraph Specialized["모바일 특화"] CM[Codemagic] BS[Bitrise] AC[Appcircle] end
서비스특징무료 플랜Flutter 지원
CodemagicFlutter 특화, 쉬운 설정월 500분네이티브
Bitrise모바일 특화, 풍부한 플러그인월 300분네이티브
GitHub ActionsGitHub 통합, 유연한 설정월 2000분YAML 설정
Xcode CloudApple 네이티브, Xcode 통합월 25시간Swift/Flutter
Appcircle모바일 특화, 엔터프라이즈 기능월 300분네이티브

How#

TIP

각 서비스의 선택 기준입니다.

// 서비스 선택 가이드
if (team.usesGitHub && team.wantsFlexibility) {
return 'GitHub Actions';
} else if (team.prefersSimplicity && team.isFlutterOnly) {
return 'Codemagic';
} else if (team.needsEnterprise && team.hasComplexWorkflow) {
return 'Bitrise';
} else if (team.isAppleCentric && team.mainlyIOS) {
return 'Xcode Cloud';
}

각 서비스 시작 방법입니다.

Terminal window
# Codemagic
# 1. codemagic.io 접속
# 2. GitHub/GitLab 연동
# 3. Flutter 프로젝트 선택
# 4. 자동 설정 완료
# GitHub Actions
# 1. .github/workflows/flutter.yml 생성
# 2. 워크플로우 YAML 작성
# 3. Secrets 설정
# 4. Push하면 자동 실행

Watch out#

WARNING

무료 플랜의 빌드 시간 제한을 확인하세요. Flutter 앱 빌드는 시간이 오래 걸리므로 대규모 프로젝트에서는 유료 플랜이 필요할 수 있습니다.

# 빌드 시간 참고
iOS 빌드: 10-20분
Android 빌드: 5-15분
테스트: 5-10분
전체 파이프라인: 20-45분

결론: 프로젝트 규모, 팀 환경, 예산을 고려하여 적합한 CI/CD 서비스를 선택한다.


챕터 5: GitHub Actions 기본 설정#

Why#

NOTE

GitHub Actions5는 GitHub에 내장된 CI/CD 서비스입니다. 추가 서비스 가입 없이 GitHub 저장소에서 바로 사용할 수 있습니다.

What#

NOTE

GitHub Actions는 워크플로우 파일로 CI/CD를 정의합니다.

flowchart TD subgraph Workflow["Workflow"] T[Trigger] J1[Job 1] J2[Job 2] end subgraph Job["Job"] S1[Step 1] S2[Step 2] S3[Step 3] end T -->|"on: push"| J1 T -->|"on: push"| J2 J1 --> S1 --> S2 --> S3
개념설명예시
Workflow자동화 프로세스 전체flutter.yml
Job동일 러너에서 실행되는 단위build-android, build-ios
StepJob 내의 개별 작업flutter test, flutter build
Action재사용 가능한 작업 단위actions/checkout@v4

How#

TIP

기본 Flutter CI 워크플로우입니다.

.github/workflows/flutter-ci.yml
name: Flutter CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
analyze-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: "3.27.0"
channel: "stable"
cache: true
- name: Get dependencies
run: flutter pub get
- name: Analyze
run: flutter analyze
- name: Format check
run: dart format --set-exit-if-changed .
- name: Run tests
run: flutter test --coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
file: coverage/lcov.info

Android 빌드 Job입니다.

build-android:
needs: analyze-and-test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: "zulu"
java-version: "17"
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: "3.27.0"
channel: "stable"
cache: true
- name: Get dependencies
run: flutter pub get
- name: Build APK
run: flutter build apk --release
- name: Build App Bundle
run: flutter build appbundle --release
- name: Upload APK
uses: actions/upload-artifact@v4
with:
name: android-apk
path: build/app/outputs/flutter-apk/app-release.apk
- name: Upload AAB
uses: actions/upload-artifact@v4
with:
name: android-aab
path: build/app/outputs/bundle/release/app-release.aab

Watch out#

WARNING

GitHub Actions의 무료 플랜은 Linux 러너만 무료입니다. iOS 빌드에 필요한 macOS 러너는 분당 비용이 10배 더 높습니다.

# 러너별 비용 (분당)
runs-on: ubuntu-latest # 무료 2000분 포함
runs-on: macos-latest # 무료 200분 포함 (10배 비용)
runs-on: windows-latest # 무료 1000분 포함 (2배 비용)

결론: GitHub Actions를 사용하면 별도 서비스 없이 GitHub 내에서 CI/CD를 구축할 수 있다.


챕터 6: iOS 빌드 자동화#

Why#

NOTE

iOS 빌드는 macOS에서만 가능하고, 코드 서명이 필요합니다. CI 환경에서 이를 자동화하려면 인증서와 프로파일을 안전하게 관리해야 합니다.

What#

NOTE

iOS 빌드 파이프라인의 구조입니다.

flowchart LR subgraph CI["CI 환경"] C[Checkout] M[match] B[Build] U[Upload] end subgraph Secrets["Secrets"] S1[MATCH_PASSWORD] S2[APP_STORE_CONNECT_API_KEY] S3[DEPLOY_KEY] end Secrets --> M C --> M --> B --> U
시크릿용도
MATCH_PASSWORD인증서 저장소 암호화 키
MATCH_GIT_BASIC_AUTHORIZATION인증서 저장소 접근 토큰
APP_STORE_CONNECT_API_KEYApp Store Connect API 인증

How#

TIP

GitHub Actions에서 iOS 빌드 및 배포 설정입니다.

.github/workflows/ios-release.yml
name: iOS Release
on:
push:
tags:
- "v*"
jobs:
build-ios:
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: "3.27.0"
channel: "stable"
cache: true
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: "3.2"
bundler-cache: true
working-directory: ios
- name: Install fastlane
run: |
cd ios
bundle install
- name: Setup SSH for match
uses: webfactory/[email protected]
with:
ssh-private-key: ${{ secrets.MATCH_DEPLOY_KEY }}
- name: Get dependencies
run: flutter pub get
- name: Build and deploy
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.ASC_KEY_ID }}
APP_STORE_CONNECT_API_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
APP_STORE_CONNECT_API_KEY_CONTENT: ${{ secrets.ASC_KEY_CONTENT }}
run: |
cd ios
bundle exec fastlane beta

App Store Connect API 키를 사용하는 Fastfile입니다.

# ios/fastlane/Fastfile
default_platform(:ios)
platform :ios do
before_all do
# App Store Connect API 키 설정
app_store_connect_api_key(
key_id: ENV["APP_STORE_CONNECT_API_KEY_ID"],
issuer_id: ENV["APP_STORE_CONNECT_API_ISSUER_ID"],
key_content: ENV["APP_STORE_CONNECT_API_KEY_CONTENT"],
is_key_content_base64: true
)
end
lane :beta do
# 인증서 동기화 (CI에서는 readonly)
match(
type: "appstore",
readonly: true
)
# Flutter 빌드
sh("flutter build ipa --release")
# TestFlight 업로드
upload_to_testflight(
ipa: "../build/ios/ipa/Runner.ipa",
skip_waiting_for_build_processing: true
)
end
end

Watch out#

WARNING

App Store Connect API 키는 Base64로 인코딩하여 저장해야 합니다.

Terminal window
# API 키 파일을 Base64로 인코딩
base64 -i AuthKey_XXXXXXXXXX.p8 | pbcopy
# GitHub Secrets에 붙여넣기
# ASC_KEY_CONTENT: (Base64 인코딩된 값)

결론: fastlane match와 App Store Connect API를 조합하면 iOS 빌드와 배포를 완전히 자동화할 수 있다.


챕터 7: Android 빌드 자동화#

Why#

NOTE

Android 앱 배포에는 서명 키6가 필요합니다. CI 환경에서 서명 키를 안전하게 관리하고 자동으로 서명된 앱을 빌드해야 합니다.

What#

NOTE

Android 빌드 파이프라인의 구조입니다.

flowchart LR subgraph CI["CI 환경"] C[Checkout] K[Keystore 복원] B[Build AAB] S[Sign] U[Upload] end subgraph Secrets["Secrets"] S1[KEYSTORE_BASE64] S2[KEY_ALIAS] S3[KEY_PASSWORD] S4[PLAY_STORE_JSON] end Secrets --> K C --> K --> B --> S --> U
시크릿용도
KEYSTORE_BASE64서명 키스토어 (Base64 인코딩)
KEY_ALIAS키 별칭
KEY_PASSWORD키 비밀번호
STORE_PASSWORD키스토어 비밀번호
PLAY_STORE_CREDENTIALSGoogle Play API 인증 JSON

How#

TIP

GitHub Actions에서 Android 배포 설정입니다.

.github/workflows/android-release.yml
name: Android Release
on:
push:
tags:
- "v*"
jobs:
build-android:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: "zulu"
java-version: "17"
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: "3.27.0"
channel: "stable"
cache: true
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: "3.2"
bundler-cache: true
working-directory: android
- name: Decode keystore
run: |
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > android/app/upload-keystore.jks
- name: Create key.properties
run: |
cat > android/key.properties << EOF
storePassword=${{ secrets.STORE_PASSWORD }}
keyPassword=${{ secrets.KEY_PASSWORD }}
keyAlias=${{ secrets.KEY_ALIAS }}
storeFile=upload-keystore.jks
EOF
- name: Get dependencies
run: flutter pub get
- name: Build App Bundle
run: flutter build appbundle --release
- name: Setup Play Store credentials
run: |
echo '${{ secrets.PLAY_STORE_CREDENTIALS }}' > android/play-store-credentials.json
- name: Deploy to Play Store
run: |
cd android
bundle exec fastlane beta

Android용 Fastfile입니다.

# android/fastlane/Fastfile
default_platform(:android)
platform :android do
lane :beta do
upload_to_play_store(
track: "internal",
aab: "../build/app/outputs/bundle/release/app-release.aab",
json_key: "play-store-credentials.json",
skip_upload_metadata: true,
skip_upload_images: true,
skip_upload_screenshots: true
)
end
lane :release do
upload_to_play_store(
track: "production",
aab: "../build/app/outputs/bundle/release/app-release.aab",
json_key: "play-store-credentials.json"
)
end
end

build.gradle에서 서명 설정입니다.

android/app/build.gradle
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}

Watch out#

WARNING

키스토어 파일을 절대 Git에 커밋하지 마세요. .gitignore에 반드시 추가하세요.

android/.gitignore
key.properties
*.jks
*.keystore
play-store-credentials.json

결론: 서명 키를 시크릿으로 관리하고 fastlane으로 Play Store 업로드를 자동화할 수 있다.


챕터 8: 버전 관리와 릴리스 전략#

Why#

NOTE

앱 버전을 수동으로 관리하면 빌드 번호 충돌이나 버전 누락이 발생할 수 있습니다. CI/CD와 연동하여 버전을 자동으로 관리하면 이런 문제를 방지할 수 있습니다.

What#

NOTE

시맨틱 버저닝7을 따르는 버전 관리 전략입니다.

flowchart TD subgraph Version["버전 구조"] M[Major.Minor.Patch+Build] E["1.2.3+45"] end subgraph Triggers["버전 증가 트리거"] T1["호환성 깨짐 → Major"] T2["기능 추가 → Minor"] T3["버그 수정 → Patch"] T4["모든 빌드 → Build"] end M --> E T1 --> M T2 --> M T3 --> M T4 --> M
구성요소설명증가 시점
Major호환성 깨지는 변경대규모 리뉴얼
Minor기능 추가새 기능 릴리스
Patch버그 수정핫픽스
Build빌드 번호모든 CI 빌드

How#

TIP

Git 태그 기반 자동 버전 관리입니다.

.github/workflows/release.yml
name: Release
on:
push:
tags:
- "v*.*.*"
jobs:
extract-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
build_number: ${{ steps.version.outputs.build_number }}
steps:
- name: Extract version from tag
id: version
run: |
# v1.2.3 형식에서 버전 추출
VERSION=${GITHUB_REF#refs/tags/v}
echo "version=$VERSION" >> $GITHUB_OUTPUT
# 빌드 번호는 GitHub run number 사용
echo "build_number=${{ github.run_number }}" >> $GITHUB_OUTPUT
build:
needs: extract-version
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Update version in pubspec.yaml
run: |
VERSION="${{ needs.extract-version.outputs.version }}"
BUILD="${{ needs.extract-version.outputs.build_number }}"
sed -i "s/^version:.*/version: $VERSION+$BUILD/" pubspec.yaml
- name: Build
run: flutter build appbundle --release

pubspec.yaml 버전 설정입니다.

pubspec.yaml
name: my_app
description: My Flutter App
# 버전 형식: major.minor.patch+build_number
# CI에서 자동으로 업데이트됨
version: 1.0.0+1
environment:
sdk: ">=3.0.0 <4.0.0"

릴리스 워크플로우 예시입니다.

Terminal window
# 새 버전 릴리스
# 1. 변경사항 커밋
git add .
git commit -m "feat: add new feature"
# 2. 태그 생성
git tag -a v1.2.0 -m "Release version 1.2.0"
# 3. 푸시 (태그 포함)
git push origin main --tags
# 4. CI/CD 자동 실행
# - 테스트
# - 빌드
# - 스토어 배포

Watch out#

WARNING

iOS와 Android의 빌드 번호는 항상 증가해야 합니다. 이전보다 낮은 빌드 번호로 업로드하면 스토어에서 거부됩니다.

# GitHub run_number를 사용하면 항상 증가 보장
build_number: ${{ github.run_number }}
# 또는 날짜 기반
build_number: $(date +%Y%m%d%H%M)

결론: Git 태그와 CI/CD를 연동하면 버전 관리와 릴리스 프로세스를 자동화할 수 있다.


한계#

  • 복잡한 초기 설정: 인증서, API 키, 시크릿 등 초기 설정에 상당한 시간이 필요하다
  • 플랫폼별 차이: iOS와 Android의 빌드/배포 방식이 달라 각각 설정해야 한다
  • 비용: iOS 빌드를 위한 macOS 러너는 비용이 높고, 대규모 프로젝트는 유료 플랜이 필요하다
  • 디버깅 어려움: CI 환경에서 발생하는 문제는 로컬에서 재현하기 어려울 수 있다

Footnotes#

  1. CI/CD(Continuous Integration/Continuous Delivery): 코드 변경을 자동으로 빌드, 테스트, 배포하는 개발 방식이다.

  2. fastlane(패스트레인): iOS와 Android 앱의 빌드와 배포를 자동화하는 오픈소스 도구이다.

  3. Code Signing(코드 서명): 앱의 출처와 무결성을 보장하기 위해 인증서로 서명하는 과정이다.

  4. CI/CD Service(CI/CD 서비스): 클라우드에서 빌드와 배포 파이프라인을 실행하는 서비스이다.

  5. GitHub Actions(깃허브 액션): GitHub에 내장된 CI/CD 및 자동화 플랫폼이다.

  6. Signing Key(서명 키): 앱에 디지털 서명을 하기 위한 암호화 키이다.

  7. Semantic Versioning(시맨틱 버저닝): Major.Minor.Patch 형식의 버전 관리 규칙이다.

공유

이 글이 도움이 되었다면 다른 사람과 공유해주세요!

Flutter 튜토리얼 65편: CI/CD 설정 - 자동화된 빌드와 배포 파이프라인 구축
https://moodturnpost.net/posts/flutter/flutter-cicd-setup/
작성자
Moodturn
게시일
2026-01-08
Moodturn

목차