포스트

[RN/Expo] QA 효율을 높이는 EAS 배포 워크플로우 구축기

[RN/Expo] QA 효율을 높이는 EAS 배포 워크플로우 구축기

이 글에서는 Expo 기반 앱의 QA 배포 효율을 높이기 위한 Internal Preview 워크플로우 구축 과정을 소개합니다. 특히 Expo Fingerprint로 빌드 여부를 자동 판단하고, OTA 업데이트를 효율적으로 적용하는 방법을 다룹니다.

🤔 번거로운 QA 빌드, 개선할 수 없을까?

Expo로 개발하다 보면 QA 빌드 배포가 비효율적이라는 점을 자주 느낍니다.

네이티브 코드가 변경되면 새 빌드를 만들어야 하고, QA 확인 후 다시 개발용 빌드로 돌아오려면 다음 절차를 반복해야 합니다.

  • QA 버전(TestFlight) 설치 → 테스트 → 삭제
  • 개발 빌드 재설치
  • 릴리즈 버전 재확인

결국 빌드 → 설치 → 삭제의 반복이 발생합니다.

이 비효율을 해결하기 위해 “QA 빌드를 더 빠르고 유연하게 배포할 수 없을까?”라는 문제의식에서 출발했습니다.

1️⃣ 첫 시도: 멀티 앱 + TestFlight 배포

처음에는 QA용 앱을 별도 번들 ID로 만들어 배포하는 방식을 검토했습니다.

그러나 TestFlight와 Google Play Internal Test의 제약으로 인해 다음과 같은 한계가 있었습니다.

Apple TestFlight의 한계

  • 하나의 App ID에 하나의 번들 ID만 등록 가능
  • QA/Prod 버전을 동시에 설치 불가
  • 업데이트 채널을 구분하려면 자체적으로 빌드 버전 관리 필요
  • Apple Service key 별도 발급 및 서버 추가 작업

Google Play Internal Test의 한계

  • 최초 빌드 수동 업로드 필요
  • 약관 및 심사 절차 진행 이전까지는 EAS Submit 어려움
  • 이로 인해 자동화 워크플로우 구성 어려움

결국 멀티 앱 방식은 설정 및 유지보수 비용이 과도했습니다.

이에 따라 Expo에서 제시하는 Internal Preview 빌드를 이용하고 OTA 업데이트를 적극적으로 활용하는 방향을 전환했습니다.

🆕 새로운 접근: Internal Preview + OTA

최종적으로 채택한 전략은 다음과 같습니다.

  1. EAS Internal Preview 빌드로 QA를 수행
  2. OTA(EAS Update)로 빠르게 변경사항을 반영

이 방식의 주요 이점

1. 명확한 채널 분리

  • previewproduction 브랜치로 QA/배포 버전 명확히 구분
  • QA 전용 환경에서 안정적 테스트 가능

2. 빌드 시간 단축

  • JS 코드 변경은 OTA로 즉시 반영
  • Fingerprint를 통해 빌드 필요 여부 자동 판단

3. 양 플랫폼 통합 관리

  • iOS와 Android를 동일한 EAS 워크플로우로 운영

잠깐, Expo Fingerprint란?

Fingerprint는 EAS에서 빌드 시점의 네이티브 환경을 식별하는 해시값입니다.

네이티브 코드가 변경되면 새로운 해시값이 생성되고, 그렇지 않으면 그대로 유지됩니다.

변경 사항Fingerprint 변화배포 방식
JS 코드만 변경❌ 없음EAS Update
네이티브 코드 변경✅ 있음EAS Build

🚀 Internal Preview 워크플로우 구축

전체 흐름

1
2
3
Workflow trigger → Fingerprint 체크
→ Fingerprint 변경 시 → EAS Build
→ Fingerprint 유지 시 → EAS Update

주요 단계

아래는 develop 브랜치에 코드가 푸시되었을 때 자동으로 실행되는 EAS Workflow의 실제 실행 흐름 그래프입니다.

EAS Workflow 실행 그래프Workflow graph

각 단계는 다음과 같은 의미를 가집니다.

  1. Fingerprint – 네이티브 환경 해시값 계산
  2. Check for existing build – 동일 Fingerprint 빌드 존재 여부 확인
  3. Build Android/iOS – Fingerprint 변경 시 새 빌드 생성
  4. Publish Update – Fingerprint 동일 시 OTA 업데이트
  5. Send Notification – 결과를 팀 채널에 알림

EAS Workflow 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
name: Deploy to preview
on:
  push:
    branches: ["develop"]

jobs:
  fingerprint:
    name: Fingerprint
    type: fingerprint
    environment: preview

  get_android_build:
    name: Check Android build
    needs: [fingerprint]
    type: get-build
    params:
      fingerprint_hash: ${{ needs.fingerprint.outputs.android_fingerprint_hash }}
      profile: preview

  get_ios_build:
    name: Check iOS build
    needs: [fingerprint]
    type: get-build
    params:
      fingerprint_hash: ${{ needs.fingerprint.outputs.ios_fingerprint_hash }}
      profile: preview

  build_android:
    if: ${{ !needs.get_android_build.outputs.build_id }}
    type: build
    params:
      platform: android
      profile: preview

  build_ios:
    if: ${{ !needs.get_ios_build.outputs.build_id }}
    type: build
    params:
      platform: ios
      profile: preview

  publish_android_update:
    if: ${{ needs.get_android_build.outputs.build_id }}
    type: update
    params:
      branch: preview
      platform: android

  publish_ios_update:
    if: ${{ needs.get_ios_build.outputs.build_id }}
    type: update
    params:
      branch: preview
      platform: ios

최종적으로 이렇게 워크플로우를 구성하여 develop 브랜치에 푸시만 해도 자동으로 배포 진행, Fingerprint를 기준으로 OTA 또는 빌드가 선택적으로 실행됩니다.

💡 Fingerprint 결과 예시

아래는 동일한 Fingerprint(e4313ef)에서 한 번의 빌드 후 OTA 업데이트를 4회 수행한 사례입니다.

EAS FingerprintFingerprint status

최초의 빌드를 제외하고는 네이티브 환경이 바뀌지 않았기 때문에, 핑거프린트가 일치한 경우 새 빌드 없이 4회의 업데이트가 적용 되었습니다.

즉, QA 빌드/업데이트 사이클을 단 한 번의 빌드로 유지하여 빠르게 QA를 처리할 수 있습니다.

🏁 마무리

OTA와 Fingerprint를 함께 활용하면, “빌드를 해야 하는 순간”과 “즉시 반영 가능한 변경”을 명확히 구분할 수 있습니다.

이 구조를 도입하면 QA 주기가 짧아지고, 빌드 횟수는 줄어들며, 배포 속도는 훨씬 빨라집니다.

결국 중요한 건 빌드 자동화가 아니라, 빠르게 실험하고 피드백을 반영할 수 있는 배포 루프를 만드는 것입니다.

이 글이 더 효율적인 QA 워크플로우를 고민하는 분들께 구체적인 참고가 되길 바랍니다.

🔗 레퍼런스

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.