Fastlane 배포 자동화 적용 과정

sooyeon
roubit.me
Published in
12 min readMar 9, 2023

안녕하세요.
항상 티스토리 글만 쓰다가 미디움으로 쓰려고 하니, 여간 불편한 게 아니네요..
미디움 분발하라.. 🔥

저희 개발팀 내에서는 좋은 코드를 만드는 것도 좋지만, 반복적인 작업들을 자동화하기 위해 노력하고 있습니다 ㅎㅎ

매주에 하나 아티클 발행을 하려고 하니 열심히 지켜봐주세요 🙌

Fastlane 적용 동기

배포할 때 android는 생각보다 간단하지만
iOS는 빌드 버전을 올린 후 아카이브를 하고, 앱 스토어 커넥트에 업로드 하고, 심사 요청을 해야하는 귀찮고 번거로운 작업들이 발생한다.
좋은 코드를 만드는 것도 좋지만, 회사 내에서는 이러한 반복적인 코드들을 자동화하는 것도 중요하다.

우리 개발팀의 이번 상반기 목표는 fastlane 자동화 시키는 일이었다.
적용해보니 간단한 일이었지만, 그 일련의 과정을 공유해보려고 한다.

iOS 먼저 적용 후 Android 적용해보도록 하자.

  1. Fastlane 설치
# ruby가 이미 설치되어 있다면 생략
$ brew install ruby

# Fastlane 설치
$ sudo gem install fastlane

2. iOS 설정

# /ios
$ fastlane init
iOS fastlane init시 나오는 화면 (4가지 중 1개 선택)

저는 일단 테스트 플라이트에 올릴 때 사용하기 위해, 2번을 선택했다.
+ 추가) 나중에 또 추가할 수 있으니 신중하게 선택 안해도 된다.

apple ID를 입력하라고 나오는데
애플 아이디 입력 후 숫자 6자리를 입력했더니 이런 오류가 나서, 다시 n을 누르니 이중 로그인 절차를 한 번 더 거치고, 완료가 됐다

로그인 성공 시 Appfile과 Fastfile이 생성된다
  • AppFile
app_identifier("your.app.identifier") # The bundle identifier of your app
apple_id("your-apple-id") # Your Apple email address

Appfile에는 app bundle id와 apple id가 포함되어 있는 걸 확인할 수 있다.

우리 팀은 애플아이디를 공유하고 있지 않고, 각자의 계정을 사용하고 있다.
각자가 배포할 때마다 apple id를 바꿔줘야하면 불편함이 생기므로 이것을 .env 를 이용해 환경변수로 관리한다.

5. .env

# /ios 루트 안에 해당 파일을 만든다
$ vim ./fastlane/.env
APP_IDENTIFIER="your.app.identifier"
APPLE_ID="my-apple-id@email.com"
...

이렇게 넣어준 뒤 Appfile을 수정하자

app_identifier(ENV["APP_IDENTIFIER"]) # The bundle identifier of your app
apple_id(ENV["APPLE_ID"]) # Your Apple Developer Portal username
  • Fastfile

실제 커맨드 명령들을 만드는 파일이다.

# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane
default_platform(:ios)
platform :ios do
desc "Push a new beta build to TestFlight"
lane :beta do
# ✅ 자동으로 빌드 넘버를 증가
increment_build_number(xcodeproj: "roubit.xcodeproj")
# ✅ 빌드할 워크스페이스와 빌드 스키마 지정
build_app(workspace: "roubit.xcworkspace", scheme: "roubit")
# ✅ TestFlight 업로드
upload_to_testflight(
# ✅ 업로드 후에 App Store Connect 에 올라가기 전까지 시간이 걸리는데 이걸 기다리고 싶지 않다면 true 로 설정.
skip_waiting_for_build_processing: true
)
slack(
message: "Testflight 배포 성공",
channel: "#r_frontend_deploy",
slack_url: ENV["SLACK_WEBHOOK_URL"],
)
end
error do |lane, exception, options|
slack(
message: "에러 발생 : #{exception}",
success: false,
slack_url: ENV["SLACK_WEBHOOK_URL"],
)
end

end

3. SLACK_WEBHOOK_URL 설정

Slack에서 원하는 채널을 선택해서 Webhook을 설정하고, 해당 url을 통해서 요청을 보내면 해당 메세지가 전달되는 방식이다.

https://my.slack.com/services/new/incoming-webhook/ 에 들어가서 설정해보자

슬랙 채널을 하나 생성하고 추가를 하면 이렇게 웹후크 URL이 생성되는데 이를 복사하고, 설정 저장을 누르면 끝!

4. TestFlight 배포 설정

원래 실행 명령어는 fastlane ios beta이다. 하지만 우리가 플랫폼을 iOS로 미리 설정해두었기 때문에 이 명령어가 실행 가능하다.

fastlane beta

TIP. 명령어를 실행하기 전에 ios에서는 앱 암호를 발급받아 환경변수로 적용을 해줘야하고, android에서는 서비스 어카운트라는 것을 만들어 접근권한을 부여해주어야 한다.

error. 앱 암호 생성 에러

FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD variable

https://support.apple.com/ko-kr/HT204397 해당 링크를 들어가 암호를 설정하자.

앱 암호를 만들고 만들어진 암호를 입력해주면 된다.
이것은 매번 fastlane을 사용할 때 자동적으로 앱 암호로 로그인하게 하기 위함이다!

Could not store password in keychain

앱 암호는 ‘xxxx-xxxx-xxxx-xxxx’ 이렇게 만들어진 암호를 붙여넣으면 된다!
이 암호로 처음에 붙이면 에러가 나는데, 두번째 성공하면 upload가 된다. 이유는 모르겠음..!!

추가적으로 릴리즈를 위한 앱의 자동 배포까지 하고 싶다면
참고한 자료 https://gyuios.tistory.com/241 에서 확인해보자

Android 배포

$ cd my-project/android
$ fastlane init

작업하기 전 체크 사항

  • 구글 플레이스토어 어드민 계정 필요
  • Google Play Console에 앱을 등록한다
  • Google Credential 을 발급받습니다

https://docs.fastlane.tools/actions/supply/ 에 들어가서 json 파일까지 발급 받아서 Appfile에 넣고 온다.

JSON Secret 설정 및 권한 부여

json 파일을 등록하고
아래 명령어를 통해 다운받은 private key로 구글플레이스토어와 연결이 잘되는지 확인해보자.

$ fastlane run validate_play_store_json_key json_key:/path/to/your/downloaded/file.json
Google Play Store에 연결 성공

git에 올라가면 안되는 파일 .env나 json 파일은 .gitignore에 저장한다!

우리 팀은 테스트를 하기 위해 apk 파일을 Firebase App Distribution에 올리기 때문에 Firebase App Distribution까지 자동화까지 하기로 한다.

fastlane과 Firebase App Distribution 연결

# firebase_app_distribution을 설치한다.
fastlane add_plugin firebase_app_distribution

firebase 공식 문서를 보면 action으로 firebase_app_distribution_login이 있다는 데 해당 action이 없는 것을 확인했다.

궁금해서 해당 fastlane github issue 올렸더니..

버전 0.5.0에서 부터는 firebase_app_distribution_login이 deprecated가 되고, firebase CLI를 설치 해야한다고 한다.

# Firebase CLI를 전역적으로 설치한다.
$ npm install -g firebase-tools

# 설치 후 Firebase 계정에 로그인한다.
$ firebase login

위 단계를 완료하면 Firebase CLI를 사용하여 Firbase App Distribution과 같은 다른 Firebase 서비스를 사용할 수 있다.

  • Fastfile
  ##########################################
########## Firebase Distribution #########
##########################################

buildDir = "./build"

lane :firebase do
firebase_app_distribution(
app: ENV["FIREBASE_APP_ID"],
# 그룹명은 firebase에 있는 group alias를 사용해야한다.
groups: "tester_groups",
debug: true
)
slack(
message: "App Distribution 업로드 성공",
# 해당 slack 채널명 사용
channel: "#r_frontend_deploy",
# iOS에서도 사용했던 웹 훅을 연결해준다. android도 .env 파일을 만들어줄 것
slack_url: ENV["SLACK_WEBHOOK_URL"],
)
end

error do |lane, exception, options|
slack(
message: "에러 발생 : #{exception}",
success: false,
slack_url: ENV["SLACK_WEBHOOK_URL"],
)
end

+) 추가적으로 App Store 배포까지 해보자

desc "Deploy a new version to the Google Play"
lane :playStore do
releaseFilePath = File.join(Dir.pwd, "my-release-key.keystore")
gradle(task: "clean")
gradle(
task: 'bundle',
build_type: 'Release',
properties: {
"android.injected.signing.store.file" => releaseFilePath,
"android.injected.signing.store.password" => ENV["STORE_PASSWORD"],
"android.injected.signing.key.alias" => ENV["KEY_ALIAS"],
"android.injected.signing.key.password" => ENV["KEY_PASSWORD"]
}
)
upload_to_play_store(
track: 'production',
release_status: 'draft',
)
slack(
message: "Google Play Store 업로드 성공",
channel: "#r_frontend_deploy",
slack_url: ENV["SLACK_WEBHOOK_URL"],
)
end

releaseFilePath도 ./env 파일 내에 관리해도 된다.
현재는 /fastlane 파일 안에 my-release-key.keystore를 저장해줬는데 두 번 저장되는 꼴이 되기 때문에 경로를 잘 설정하면 될 것 같다!

후기

그동안 하던 스프린트가 마치고, 시간이 남아 자동화 배포까지 적용 해보았다.
처음에는 낯설었던 fastlane이 에러 코드만 보고도 어떤 부분이 문제인지 알 수 있을 정도로 익숙해졌다.

fastlane 자동화 시스템을 갖추는 데까지는 1~2일 소요되지만, 정말 업무 생산성을 높일 수 있는 시스템인 것 같다.
action으로 버전과 빌드 번호를 증가시키고 슬랙에도 푸시할 수 있으니 얼마나 편리한가 …!

이런 자동화 시스템을 잘 활용하여 업무 생산성을 높이기 위해 노력해야겠다.

프론트 CI/CD를 구축한 나, 제법 멋지다.
오늘 하루도 성장했다.

--

--