3. Fastlane으로 Testflight 배포 자동화

문인범·2025년 6월 17일
0

RunMile

목록 보기
4/5

앱을 완성 시켜 배포를 완료했습니다! 👏👏👏

Run Mile 앱스토어 링크

하지만 이번에 앱 출시를 하면서 앱 출시를 위해 해야하는 다양한 과정들이 생각보다 귀찮고 많은 시간이 소요되더군요.
이런 생각을 한 사람들이 저 뿐만이 아니라 많은 사람들이 있었기 때문에 CI/CD 라는게 나왔을 것이라 생각합니다.

그래서 이번에는 CI/CD 툴 중 하나인 Fastlane을 사용해 Testflight 업로드 까지 자동화를 해보았습니다.

1. 설치

fastlane 도큐먼트

Fastlane에서는 다양한 경로를 통해 설치를 지원하고 있습니다.

그 중 Bundler 를 이용한 방법을 추천하고 있네요.

하지만 전 Homebrew를 사용하는 방법이 더 쉬워보여 brew로 설치했습니다.

brew install fastlane

2. 초기 설정

그 후 프로젝트 경로로 이동한 다음 커맨드를 실행합니다.

fastlane init

그러면 아래와 같이 선택 창이 나오는데 저는 테스트플라이트에 배포하는 것이 목적이기 때문에 2번을 선택했습니다.
나중에 직접 다 수정이 가능하니 아무거나 선택하셔도 상관 없습니다!

apple id와, 비밀번호, 2차 인증, 팀 선택 진행

그 후 apple id와, 비밀번호, 2차 인증, 팀 선택 까지 완료하고 나면

  • Gemfile
  • Gemfile.lock
  • Appfile
  • Fastfile

총 4개의 파일이 생성되는데 이 중 Fastfile에서 자동화 작업을 정의하므로 Fastfile을 수정하면 됩니다!

3. Workflow 생성

Fastlane은 Ruby로 만들어져 있어 Ruby 문법을 알고 있으면 좋지만 사실 대체적으로 사용되는 메소드들은 도큐먼트에 있으므로 루비를 몰라도 만들 수 있습니다.

그리고 GPT도 잘 알려주기 때문에 ㅎㅎ;;;

Available Actions - fastlane docs(사용 가능한 명령어들)

Fastfile을 열게되면 아래와 같이 기본적으로 세팅되어 있습니다.

default_platform(:ios)

platform :ios do
  desc "Push a new beta build to TestFlight"
  lane :beta do
    increment_build_number(xcodeproj: "프로젝트 명.xcodeproj")
    build_app(scheme: "Bomudadima")
    upload_to_testflight
  end
end

beta 라는 lane이 하나의 작업 라인이라고 보시면 됩니다.

저는 해당 작업 라인에 테스트 플라이트를 올리기 위한 전처리 작업을 진행한 후 테스트 플라이트에 업로드를 진행하도록 구성했습니다.

제가 구성한 작업 구조는 다음과 같습니다.

  • 빌드 넘버 설정(yyyymmdd+number)
    • 현재 날짜 가져오기
    • 테스트플라이트에 올라와있는 빌드 넘버 가져오기
    • 기존 빌드 넘버 파싱 후 날짜 비교 진행
    • 날짜가 같을 경우 뒷 number + 1 → 빌드 넘버 완성
    • 날짜가 다를 경우 현재 날짜 + number → 빌드 넘버 완성
  • 프로젝트 파일에 빌드 넘버 반영(increment_build_number)
  • 시스템 noti(준비 완료)
  • 앱 build 진행(build_app)
  • 테스트플라이트 업로드(upload_to_testflight)
  • 시스템 noti(업로드 완료)

소스코드는 아래와 같습니다.

default_platform(:ios)

platform :ios do
  desc "Push a new beta build to TestFlight"
  lane :beta do  
    today = Time.now.strftime("%Y%m%d")
    current_build_number = "#{latest_testflight_build_number}"

    if current_build_number.length == 9
      current_date = current_build_number[0,8]
      current_count = current_build_number[8].to_i
    else
      # 빌드 넘버가 없거나 형식이 다르면 초기화
      current_date = ""
      current_count = 0
    end

    if current_date == today
      next_count = current_count + 1
    else
      next_count = 1
    end

    new_build_number = "#{today}#{next_count}"
    
    increment_build_number(
      xcodeproj: "Run Mile.xcodeproj",
      build_number: new_build_number
    )
    
    notification(
      subtitle: "Build Number: #{new_build_number}",
      message: "테스트 플라이트 배포 준비"
    )
    
    build_app(
      scheme: "Run Mile",
      export_options: {
        provisioningProfiles: {
          "com.mooni.Run-Mile" => "com.mooni.Run-Mile AppStore"
				}
			}
    )
    
    upload_to_testflight(
      api_key_path: "connect api key 파일 경로"
    )
    
    notification(
      subtitle: "Testflight 배포 성공!",
      message: "Build Number : #{new_build_number}, 테스트 플라이트 배포에 성공했습니다!"
    )
  end
  
  # 에러처리 => 시스템 노티 발행
  error do |lane, exception, options|
    notification(
    subtitle: "Testflight 배포 중 오류가 발생했습니다.",
    message: "#{exception}"
    )
  end
end

여기서 중간에 upload_to_testflight에 파라미터로 있는 api_key_path의 경우 직접 발급이 필요합니다.

3-1. App Store Connect API Key 발급

App Store Connect로 이동합니다.

그 후 키 생성을 통해 API키를 생성하면 p8파일을 다운로드 받을 수 있습니다.

한 번 다운로드 후에는 다시 다운로드가 제한되므로 확실하게 보관해 주시기 바랍니다.

완성되면 키 ID, Issuer ID도 새로 생기는데 해당 정보도 필요하므로 기억해놓습니다!

이 다음에 Fastfile이 담겨있는 fastlane 폴더에 json 파일 하나 만들어 아래와 같이 채워주세요.

파일이름은 아무렇게 하셔도 상관없습니다.

{
  "key_id": "키 ID",
  "issuer_id": "Issuer ID",
  "key": "-----BEGIN PRIVATE KEY-----\n여기 키 정보\n-----END PRIVATE KEY-----",
  "duration": 1200,
  "in_house": false
}

차례대로

  • key_id : 위에서 본 키 ID
  • issuer_id : 위에서 본 Issuer ID
  • key : 다운로드 받은 .p8 파일 내용 복붙
    • 주의 사항
    • Begin private 머시기를 포함한 전체 내용을 넣어야함
    • 위의 예시처럼 개행문자(\n)를 사이에 꼭 넣어야함
    • 이거 안지켜서 삼십분동안 헤맸음 ㅜㅜ

그 후 해당 파일 경로를 api_key_path에 채워줍니다!

upload_to_testflight(
  api_key_path: "fastlane/(키파일이름).json"
)

4. 실행 🚀

다음 터미널을 통해 실행시키고자 하는 lane 명을 실행시켜주면 완료됩니다.
저는 기존 이름 beta 그대로 사용했습니다!

fastlane beta

대략 4~5분의 시간을 거쳐….

성공했다는 메시지와 함께 업로드가 완료됩니다!

스크린샷 2025-06-17 오후 7.07.32.png

테스트 플라이트에 업로드 까지 확인이 되었습니다.

이렇게 Fastlane을 사용해 테스트플라이트 배포를 자동화 해보았습니다.
여기서 조금만 더 응용하면 앱스토어 배포까지 자동화 할 수 있다고 하네요!
곧 앱스토어 배포도 도전 해 본 후 후기글을 올려보도록 하겠습니다 ㅎㅎ

5. 트러블 슈팅

라고 넘어가기엔 성공하기 전까지 수많은 오류들이 제 앞을 가로막았었기 때문에 이 과정 또한 공유해보고자 합니다.

exportArchive Provisioning profile "(프로비저닝 프로파일 네임)" doesn't include signing certificate "(인증서 이름)”

인증서의 문제가 있을 때 나오는 에러입니다.

저는 개인 프로젝트이기 때문에 singing 관리를 자동으로 해놓았습니다.(Xcode의 Automatically manage signing)
이 과정에서 사용되는 Profile에 Apple Distribution 인증서가 포함되어 있지 않을 경우가 있습니다.
수동으로 변경한 후

여기서 Provisioning Profile의 i 버튼을 누르면 인증서 목록이 나옵니다.

이렇게 Certificates에 배포용 인증서(Apple Distribution)이 포함되어 있는지 확인하면 됩니다.

만약 안되어있을 경우 새로운 배포용 인증서를 발급하여 Provisioning Profile 생성 후 수동으로 적용해 주시면 됩니다.

위와 같이 Provisioning Profile을 등록해도 똑같이 에러가 날 경우 fastfile의 build_app에 다음과 같이 파라미터를 추가합니다.

build_app(
  scheme: "Run Mile",
  export_options: {
		provisioningProfiles: {
			"번들 ID" => "Provisoning Profile 이름"
			# 예시 
			# "com.mooni.Run-Mile" => "com.mooni.Run-Mile AppStore"
		}
	}
)

exportArchive "앱 이름" requires a provisioning profile with the HealthKit feature.

HealthKit을 사용하는 프로젝트일 경우 Provisioning Profile에 HealthKit Capabilities가 추가되어 있는지, 또한 프로젝트의 entitlements에도 관련 정보가 포함되어 있는지 확인해야 합니다.

저의 경우 위와 같이 다 포함되어 있는데도 계속 오류가 나 Profile을 새로 다운로드 받아 적용하니 오류가 해결되었습니다;;

**invalid curve name**

App Store Connect API Key 입력에 문제가 있는 경우입니다.

{
  "key_id": "키 ID",
  "issuer_id": "Issuer ID",
  "key": "-----BEGIN PRIVATE KEY-----\n여기 키 정보\n-----END PRIVATE KEY-----",
  "duration": 1200,
  "in_house": false
}

여기서 key값을 입력할 때

-----BEGIN PRIVATE KEY----- 다음 개행 문자(\n)

-----END PRIVATE KEY----- 전 개행 문자(\n)

를 반드시 포함시켜 주어야 합니다.

또한 p8 파일을 열면 키 값이 여러줄로 되어 있는데 해당 값을 그대로 복붙하지 말고 한 줄로 이어서 넣어주어야 합니다.

profile
월클 개발자를 향한 도전일지

0개의 댓글