pile·
모바일·스캐터랩스캐터랩 (이루다)·

GitHub Actions로 앱 자동 배포하기

React Native 메신저 앱의 Android·iOS 빌드와 스토어 업로드를 fastlane 스크립트로 통일하고, GitHub Actions에서 재현 가능하게 실행한 과정을 설명한다. 특히 플랫폼별 서명 인증서와 API key 같은 파일형 비밀정보를 self-hosted macOS runner에 안전하게 복원하는 방법을 구체적인 명령과 함께 보여준다.

핵심 포인트
  • fastlane lane에 Gradle 빌드, Play Store 업로드, iOS 서명, TestFlight 업로드를 정의해 로컬 버튼 작업을 명령 한 줄로 바꿨다.
  • 자주 갱신되는 iOS Provisioning Profile은 fastlane match와 private GitHub 저장소로 동기화한다.
  • iOS 빌드는 macOS가 필요하고 hosted runner 비용이 Linux보다 10배 높아 사내 self-hosted macOS runner를 사용했다.
  • 큰 인증 파일은 암호화한 압축 파일로 보관하고 GitHub Secret의 키로 복호화하며, 작은 파일은 base64 문자열로 Secret에 저장했다.
  • OS별 `sed` 차이와 keychain 생성·삭제까지 워크플로에 포함해 개발자 로컬 환경에 의존하지 않는 배포를 만들었다.
상세 정리
  • 목표: MVP를 빠르게 배포하고 자주 개선하려면 Android Studio와 Xcode에서 수동으로 빌드하고 스토어에 올리는 반복 작업을 없애야 했다.
  • Android lane: `gradle(task: "clean bundleRelease")`로 AAB를 만들고 `upload_to_play_store`로 internal track에 draft 상태로 올린다.
  • iOS lane: 임시 keychain을 만들고 match로 인증서를 가져온 뒤 `build_app`과 App Store Connect API key를 이용해 TestFlight에 업로드한다.
  • 인증 종류: iOS는 서명 인증서, Provisioning Profile, App Store Connect API key가 필요하고 Android는 upload key와 Play Console service account key가 필요하다.
  • 갱신 문제: 앱 capability나 등록 기기가 바뀌면 Provisioning Profile을 다시 발급해야 해 고정 Secret만으로 관리하기 어렵다.
  • match 동작: App Identifier에 맞는 프로필을 private 저장소에서 찾고 App Store Connect 상태와 대조한다. 유효하지 않으면 새로 발급해 암호화한 뒤 저장소에 반영한다.
  • CI 안전장치: runner에서는 match를 readonly로 실행해 유효한 인증서가 없을 때 임의 발급하지 않고 배포를 중단하게 했다.
  • runner 선택: GitHub-hosted macOS는 Linux보다 비용이 10배 높아 이미 조직에 있던 self-hosted macOS runner를 재사용했다.
  • 환경 재현: 개발자 컴퓨터에 있던 인증 정보와 환경 변수를 모두 GitHub Secrets와 스크립트에서 준비해 어디서든 같은 lane을 실행한다.
  • 큰 파일 처리: Android keystore와 service account JSON을 tar.gz로 묶어 AES-256-CBC로 암호화하고, CI에서 Secret key로 복호화한 뒤 앱 디렉터리에 푼다.
  • 비밀번호 주입: gradle.properties의 placeholder를 GitHub Secret 값으로 치환한다. macOS와 Linux의 `sed -i` 문법 차이를 OSTYPE 분기로 처리했다.
  • 작은 파일 처리: App Store Connect의 `.p8`처럼 작은 파일은 base64로 인코딩해 Secret에 넣고 runner에서 원래 확장자로 디코딩한다.
  • 실행 흐름: 인증 파일 복원과 환경 변수 설정이 끝난 뒤 같은 fastlane lane을 호출하는 GitHub Actions workflow를 작성한다.
  • 확장 기반: 이 자동화는 이후 React Native의 binary·CodePush 배포와 여러 configuration, 내부 테스트 트랙을 연결하는 토대가 됐다.
왜 읽나모바일 CI/CD에서 코드보다 까다로운 서명·인증 파일과 macOS 실행 환경을 다루면서 Android와 iOS 배포를 재현 가능하게 만드는 실전 패턴을 얻을 수 있다.
스캐터랩
스캐터랩 (이루다) 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 모바일·토스 SLASH토스 SLASH·

    토스의 디바이스 팜 만들기

    토스가 각 팀이 각자 운영하던 작은 디바이스 팜을 하나의 중앙 플랫폼 '네뷸라(Nebula)'로 통합한 과정을 다룬다. Appium 대신 자체 드라이버를 개발해 클릭 13.5배, 텍스트 입력 10배의 성능을 달성했고, Android와 iOS 미러링도 직접 내재화해 실기기 원격 조작과 화면 실시간 공유를 동시에 가능하게 만들었다. 현재 100대를 넘어 수백 대를 향해 24시간 가동 중이다.

    #android#ios#appium+2
  2. 모바일·LY CorporationLY Corporation·

    AI 에이전트를 위한 Android CLI: 대규모 모바일 개발 환경에 적용하기

    LINE Android 팀이 수백 개 Gradle 모듈 규모의 대형 저장소에서 Google의 Android CLI를 AI 에이전트(Claude Code, Codex, Gemini 등)와 함께 운용하며 겪은 토큰 낭비·오작동 문제를 해결한 과정을 다룬다. 얇은 래퍼와 스킬 레이어를 추가해 CLI 출력을 에이전트 친화적으로 정규화하고, Android Studio 연동 기능을 텍스트 검색보다 정확한 시맨틱 심볼 탐색에 활용하는 방법을 소개한다.

    #android#claude-code#mcp+2
  3. 모바일·토스 SLASH토스 SLASH·

    디자이너가 시안 대신 앱을 만든 이유

    토스 UX 디자이너가 AI를 협력자로 삼아 피그마 시안 대신 실제 SwiftUI 코드로 iOS 앱을 직접 구현한 경험을 다룬다. "데드엔드" 화면(송금·결제 완료 등)을 다음 경험의 시작점으로 전환하는 underlay 컴포넌트를 설계하면서 Metal 셰이더 구현까지 디자이너가 주도했고, 완성된 코드를 개발팀에 전달해 협업 방식 자체를 바꿨다.

    #design-system#ai-coding#ios+2