앱의 다음 업데이트 출시를 위해 한창 바쁘게 개발을 하던 중에 팀원한테 배포를 자동화해서 시간을 줄여보는 게 어떻겠냐고 제안을 받았다.
지금까지는 개발이 끝나면 iOS, Android 각각 수동으로 빌드를 하고 QA 팀에서 테스트 앱을 다운로드할 수 있도록 firebase에 배포를 했다. 기획이나 디자인이 변경되는 상황이 매우 많기 때문에 개발이 완전히 끝나기 전에도 중간 점검을 위해 배포를 하는 일이 많았다. 이 과정을 매번 수동으로 진행하다 보니까 흐름이 끊기기도 하고 평균적으로 OS마다 20분 정도 소요되었으니 시간도 많이 부족하다고 느껴졌다.
공식 홈페이지 설명에 따르면, Fastlane은 Android 및 iOS 배포를 단순화하기 위한 오픈 소스 플랫폼이다. Fastlane을 사용하면 development 및 release 워크플로우의 모든 측면을 자동화할 수 있다. 이 포스팅에서는 개발용 테스트 앱 배포에 대해서만 다룬다.
Fastlane은 이미 사용 중이었지만, 빌드 파일을 firebase에 직접 업로드를 하느냐 하지 않느냐의 차이였고 마찬가지로 Fastlane이 실행되는 중에는 개발을 할 수 없었기에 단축되는 시간도 체감상 크게 느껴지지 않았다. 이러한 불편함을 해소하기 위해서는 개발과 완전히 분리되어 배포를 자동화할 필요가 있다고 판단했다. 이를 위해 Fastlane과 Github Actions을 조합해서 사용하게 되었다.
Fastlane은 레거시 코드를 다 뒤엎고 단계별 lane을 새롭게 개발했다. 우선, 팀원마다 iOS Provisioning Profile 인증서를 별도로 발급해서 사용하고 있었는데 팀 전체에서 certificates와 profiles를 쉽게 동기화 할 수 있도록 match를 사용하기로 했다. fastfile은 android와 ios 각각 존재하고 사용법에 약간의 차이가 있다. 아래는 ios fastfile 예시 코드이다.
default_platform(:ios)
platform :ios do
desc
lane :firebase do
match(
type: "development",
git_url: "{git 저장소 주소}",
storage_mode: "git",
readonly: true,
)
clear_derived_data
latest_release = firebase_app_distribution_get_latest_release(
app: '{firebase 앱 id}',
firebase_cli_token: "{firebase 인증용 토큰}",
)
increment_build_number({build_number: latest_release[:buildVersion].to_i+1})
build_app(
workspace: "{프로젝트 이름}.xcworkspace",
scheme: "{앱 scheme}",
silent: true,
clean: true,
output_directory: ".",
output_name: "{빌드파일이름}.ipa",
export_team_id: CredentialsManager::AppfileConfig.try_fetch_value(:team_id),
export_options: {
method: 'development',
provisioningProfiles: {
"bundle id": "com.alphabridge.tangopick",
}
}
)
firebase_app_distribution(
app: "1:32734331599:ios:f6d9fa7cf137a301cd6dba",
testers: "ap.alphabridge@gmail.com",
ipa_path: "{빌드파일경로}",
firebase_cli_token: "{firebase 인증용 토큰}",
groups: "{테스터 그룹}",
release_notes: "{출시 노트}",
)
end
end
match는 Github Repository에 공용으로 사용할 인증서를 저장하고, 명령어를 통해 간단하게 인증서 갱신 및 공유가 가능하다. 배포를 할 때는 match를 사용해서 인증이 가능하기 때문에 프로젝트에 인증서 파일을 포함하지 않아도 된다.
clear_derived_data는 예기치 않은 문제의 발생을 최소화하기 위해 사용한다. clean을 하는 과정이 필수는 아니지만 derived data를 지우면 오래된 빌드 아티팩트로 인한 빌드 관련 문제를 해결하는 데 도움이 될 수 있고, 프로젝트에 참여하는 모든 사람이 동일한 환경에서 시작할 수 있다.
build_app은 lane에 정의된 옵션에 따라 앱을 빌드한다. lane 중에서 export_team_id는 fastlane의 모듈인 CredentialsManager를 통해서 프로젝트 내에 Fastfile과 함께 생성된 Appfile에 있는 tead_id 값을 가져오려고 시도한다.
마지막으로 firebase_app_distribution는 빌드가 끝나면 앱을 firebase를 통해서 테스터에게 배포하는 역할을 한다. 테스터 그룹을 지정할 수 있고, 출시 노트를 작성하여 해당 빌드에 대한 간략한 설명이 가능하다.
GitHub Actions는 GitHub에서 제공하는 워크플로우 자동화 및 CI/CD 플랫폼이다. 다양한 작업을 자동화하고 GitHub 리포지토리에서 직접 코드를 빌드 및 배포할 수 있다.
Github Actions는 워크플로우를 실행할 runner가 필요한데, github-hosted runner와 self-hosted runner가 있다. github-hosted runner는 github에서 리소스를 빌려 작업을 수행하기 때문에 유료이다. 팀에 사용하지 않는 PC가 하나 있어서 self-hosted runner로 사용했다.
특정 브랜치에 push 또는 pull request 이벤트가 생기면, fastlane을 실행해 firabase에 테스트 앱을 배포하는 워크플로우를 만들었다. 브랜치는 하나만 지정할 수도 있고, 여러 개 혹은 모든 브랜치를 포함할 수 있다. 변경사항이 생길 때마다 매번 배포하는 것은 비효율적이므로, 이벤트의 발생을 내가 컨트롤할 수 있도록 commit message를 확인하는 조건을 포함했다.
결과적으로, fastlane만 실행했을 때 배포에 소요되는 평균 시간이 OS별로 20분에서 5분으로 단축되었고 프로젝트 내에 모듈의 업데이트나 추가 등 변경사항이 생겼을 때 발생할 수 있는 문제를 방지하기 위해 node_modules와 pod을 clean하는 워크플로우가 추가되었다. 워크플로우가 실행되는데 소요되는 총 시간은 OS별 15분이지만 자동으로 배포가 이루어지고 총 배포 시간이 10분 줄어들었다는 것 만으로도 충분히 많은 성과가 있었다고 생각한다.
node_modules를 clean하는 워크플로우가 OS별로 총 2번 실행되는데, 워크플로우가 실행되는 순서를 제어할 수 있다면 중복되는 워크플로우를 줄일 수 있기 때문에 그만큼 시간을 단축할 수 있을 것 같다.
++