iOS 앱을 개발하다 보면, 번거로운 작업이 있는데요, 바로 배포 과정입니다.
앱을 배포하기 위해서는
- 배포를 위해 인증서나 프로비저닝을 설정
- Xcode에서 Archive
- 수동으로 업로드
와 같은 과정을 거쳐야 합니다.
사실 하나하나 보면 어려운 작업은 아니지만, 생각보다 번거로운 작업입니다.
대 자동화의 시대에서 번거로움을 줄이고 효율적으로 작업을 해봅시다.
뿐만 아니라 팀 프로젝트에서는 인증서 공유나 환경 설정 문제가 빈번하게 발생합니다.
- 인증서 공유 문제
- 환경 설정 차이
- 빌드 및 배포 실패
특히 애플 디벨로퍼 아카데미에서 팀 프로젝트를 진행할 때 팀 동료한테 가장 많이 들었던 이야기 중 하나인 "제 컴퓨터에서는 잘 됩니다!"와 같은 문제를 해결하기 위함도 있습니다.
이러한 이유로 Fastlane과 match를 활용하여 배포 자동화를 구축했고, 이에 대해 이야기 해보려고 합니다.
Fastlane?
fastlane - App automation done right
The easiest way to build and release mobile apps. fastlane handles tedious tasks so you don’t have to. Developer hours saved 10,558,200
fastlane.tools
이 번거로운 배포 과정을 어떻게 자동화할 수 있을까요? 바로 Fastlane입니다.
Fastlane은 iOS, AOS 앱의 빌드, 테스트, 배포 과정을 자동화해주는 오픈소스 도구입니다. 간단히 말하면, 명령어 한 줄로 앱 빌드부터 배포까지 처리할 수 있는 자동화 도구입니다. (역시 딸깍)
예를 들어, 아래와 같은 Fastlane 명령어를 입력하면
fastlane ios beta
- 인증서 및 프로비저닝 설정
- 앱 빌드
- 테플 업로드
와 같은 작업이 한 번에 수행됩니다.
누구나 동일한 방식으로 배포할 수 있고, 배포 과정에서의 실수를 줄일 수 있고, CI/CD와도 쉽게 연결할 수 있습니다.
Fastlane은 단순한 배포 도구가 아니라, 배포 과정을 코드로 관리하는 자동화 도구라고 볼 수 있습니다!
Fastlane이 필요한 이유
기존 iOS 배포 과정은 아래와 같습니다.
- 인증서 및 프로비저닝 설정
- Xcode에서 Archive
- 수동 업로드
- 테플 등록
이 과정을 매번 반복하다보면 생각보다 시간 소모가 크고, 사람마다 방식이 달라지고, 실수가 발생할 수 있습니다.
특히 팀 프로젝트에서는 인증서 공유 문제, 환경 설정 문제도 발생합니다.
이러한 문제나, 번거로움을 해결하기 위해 Fastlane이 필요합니다. 특히, 인증서 문제는 match를 통해 해결할 수 있습니다.
인증서 관리 문제와 match
iOS 개발에서 가장 까다로운 부분 중 하나는 인증서 관리입니다.
- 인증서 만료
- 팀원 간 공유 문제
- Keychain 꼬임
저도 실제로 팀 프로젝트를 진행하면서 가장 많이 겪었던 문제이기도 했습니다.
특히 인증서가 꼬이거나, 누군가는 되고 누구는 안 되는 상황이 반복되면서 개발보다 환경 문제를 해결하는 데 더 많은 시간을 쓰게 되는 경우도 있었습니다.
(여담이지만, 오죽하면 애플 디벨로퍼 아카데미에서 멘토분들이 따로 인증서 관리 세션을 준비하실 정도였습니다.. 하하..)
Fastlane에서는 이를 해결하기 위해 match라는 기능을 제공합니다.
match?
match - fastlane docs
type Define the profile type, can be appstore, adhoc, development, enterprise, developer_id, mac_installer_distribution, developer_id_installer development
docs.fastlane.tools
match는 인증서와 프로비저닝 프로파일을 Git 저장소로 관리하고 자동으로 설치해주는 기능입니다.
즉, 인증서는 Git 저장소에 저장되어있고, 필요할 때 자동으로 다운로드하여 Keychain에 설치합니다.
더 이상 인증서를 수동으로 관리할 필요가 없어집니다.
구축 과정
1. Fastlane 설치
먼저 Fastlane을 설치해야 합니다. 저는 Homebrew를 통해 설치했습니다.
brew install fastlane
설치가 끝나면 아래 명령어로 정상 설치 여부를 확인할 수 있습니다.
fastlane --version
2. Fastlane 초기화
설치가 완료되면 프로젝트 루트에서 Fastlane을 초기화합니다.
fastlane init
이 과정을 통해 기본적으로 fastlane 디렉토리와 함께 Fastfile, Appfile 등의 설정 파일이 생성됩니다.
- Fastfile : 실제 배포 명령(lane)을 정의하는 파일
- Appfile: 앱 식별자나 Apple 계정 관련 정보를 정의하는 파일
3. App Store Connect API Key 방식으로 인증 전환
기존에는 Apple ID 기반 로그인 방식도 사용할 수 있지만, 팀 프로젝트에서는 로그인 세션이나 2차 인증 이슈 때문에 관리가 번거로울 수 있습니다.
그래서 Flyleaf에서는 App Store Connect API Key 방식으로 인증을 구성했습니다.
필요한 값은 아래와 같습니다.
APP_STORE_CONNECT_KEY_ID
APP_STORE_CONNECT_ISSUER_ID
APP_STORE_CONNECT_KEY_FILEPATH
예를 들어 .env에는 아래와 같이 설정했습니다.
APP_STORE_CONNECT_KEY_ID=...
APP_STORE_CONNECT_ISSUER_ID=...
APP_STORE_CONNECT_KEY_FILEPATH=./fastlane/AuthKey_XXXXXX.p8
이 방식의 장점은 Apple ID 세션에 의존하지 않고, 보다 안정적으로 자동화 파이프라인을 구성할 수 있다는 점입니다.
4. 인증서 관리 방식으로 match 도입
다음으로 가장 중요한 인증서 문제를 해결하기 위해 match를 도입했습니다.
앞서 이야기했듯이 iOS 배포에서는 인증서와 프로비저닝 프로파일 관리가 항상 문제였습니다. 그래서 인증서를 개인 로컬 환경이 아니라, 공용 저장소 기반으로 관리하도록 구조를 바꾸었습니다.
먼저 match용 레포를 준비하고, Matchfile을 구성했습니다.
예를 들면 아래와 같습니다.
git_url(match용 레포 URL)
git_branch("main")
storage_mode("git")
type("appstore")
app_identifier([
"com.yeo.flyleaf.dev",
"com.yeo.flyleaf"
])
username("...")
readonly(false)
이후 명령어를 통해 필요한 인증서를 생성하고 저장소에 업로드했습니다.
fastlane match development
fastlane match appstore
이 과정을 거치면 인증서와 프로비저닝 프로파일이 암호화되어 저장소에 저장되고, 필요할 때마다 match가 자동으로 내려받아 Keychain에 설치하게 됩니다.
즉, 팀원 입장에서는 인증서를 직접 복사하거나 수동으로 설치할 필요가 없어집니다.
5. dev / prod 배포 흐름 분리
Flyleaf는 개발용 앱과 실제 배포용 앱이 분리되어 있기 때문에 Fastlane도 이에 맞춰 beta / release 두 가지 흐름으로 분리했습니다.
- FlyleafDev -> 테플 배포
- Flyleaf -> 앱스토어 배포
이 구분이 중요한 이유는, 개발용 빌드와 실제 릴리즈 빌드는 아이콘, 번들, ID, Firebase 설정, 서명 방식 등이 다를 수 있기 때문입니다.
따라서 단일 lane으로 처리하기보다, 배포 목적에 따라 명확하게 lane을 나누는 것이 안정적이라고 판단하여 두 가지 흐름으로 분리했습니다
6. Fastfile에 lane 구성
이제 실제 배포 명령을 Fastfile에 정의했습니다. Flyleaf에서는 베타와 릴리즈 두 개의 lane을 구성했습니다.
예를 들면 아래와 같습니다.
platform :ios do
def asc_api_key
app_store_connect_api_key(
key_id: ENV["APP_STORE_CONNECT_KEY_ID"],
issuer_id: ENV["APP_STORE_CONNECT_ISSUER_ID"],
key_filepath: ENV["APP_STORE_CONNECT_KEY_FILEPATH"],
duration: 1200,
in_house: false
)
end
desc "FlyleafDev를 빌드하고 TestFlight에 업로드합니다"
lane :beta do
api_key = asc_api_key
match(
type: "appstore",
app_identifier: ["com.yeo.flyleaf.dev"],
api_key: api_key,
readonly: true
)
build_app(
workspace: "Flyleaf.xcworkspace",
scheme: "FlyleafDev",
configuration: "Release",
clean: true,
export_method: "app-store"
)
upload_to_testflight(
api_key: api_key,
skip_waiting_for_build_processing: true
)
end
desc "Flyleaf를 App Store 배포용으로 빌드합니다"
lane :release do
api_key = asc_api_key
match(
type: "appstore",
app_identifier: ["com.yeo.flyleaf"],
api_key: api_key,
readonly: true
)
build_app(
workspace: "Flyleaf.xcworkspace",
scheme: "Flyleaf",
configuration: "Release",
clean: true,
export_method: "app-store"
)
upload_to_app_store(
api_key: api_key,
submit_for_review: false,
automatic_release: false,
skip_metadata: true,
skip_screenshots: true,
run_precheck_before_submit: false
)
end
end
이 설정을 통해 배포 과정이 명확히 분리 되었습니다.
- beta -> 개발용 스킴 빌드 + 테플 업로드
- release -> 프로덕션 스킴 빌드 + 앱스토어 업로드
7. 민감한 값은 .env로 분리
Fastlane을 구성하다 보면 API Key, Git URL, Team ID 같은 값들이 설정 파일 안에 직접 들어가기 쉽습니다.
하지만 이런 값들을 그대로 코드에 넣으면 보안상 좋지 않습니다.
그래서 민감한 설정을 .env로 분리했습니다.
예를 들어,
APP_STORE_CONNECT_KEY_ID=...
APP_STORE_CONNECT_ISSUER_ID=...
APP_STORE_CONNECT_KEY_FILEPATH=./fastlane/AuthKey_XXXXXX.p8
MATCH_GIT_URL=...
FASTLANE_USER=...
APP_IDENTIFIER_DEV=com.yeo.flyleaf.dev
APP_IDENTIFIER_PROD=com.yeo.flyleaf
APP_STORE_TEAM_ID=...
이렇게 구성하면 Fastlane 설정 파일은 유지하고, 민감한 값만 따로 관리할 수 있습니다.
최종 배포 흐름
최종적으로 Flyleaf의 배포 흐름은 아래처럼 정의되었습니다.
fastlane ios beta
-> match 인증서 설치
-> FlyleafDev 빌드
-> 테플 업로드
fastlane ios release
-> match 인증서 설치
-> Flyleaf 빌드
-> App Store Connect 업로드
'Flyleaf - 독서를 여행처럼 > 개발일지' 카테고리의 다른 글
| [개발일지] 07. Tuist Scaffold로 모듈 생성 자동화하기 (0) | 2026.03.24 |
|---|---|
| [개발일지] 06. 단위 테스팅을 해봅시다! (feat. AI) (0) | 2026.03.20 |
| [개발일지] 05. 홈 독서 지도를 어떻게 구현했을까요? (MapKit 커스텀 + 항공 경로 시각화) (0) | 2026.03.16 |
| [개발일지] 04. Composition Root (feat. 어디까지 몰라야하는가?) (0) | 2026.03.11 |
| [개발일지] 03. Flyleaf의 Micro-Features Architecture 구조 (0) | 2026.03.10 |