[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
최종적으로 채택한 전략은 다음과 같습니다.
- EAS Internal Preview 빌드로 QA를 수행
- OTA(EAS Update)로 빠르게 변경사항을 반영
이 방식의 주요 이점
1. 명확한 채널 분리
preview와production브랜치로 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의 실제 실행 흐름 그래프입니다.
각 단계는 다음과 같은 의미를 가집니다.
- Fingerprint – 네이티브 환경 해시값 계산
- Check for existing build – 동일 Fingerprint 빌드 존재 여부 확인
- Build Android/iOS – Fingerprint 변경 시 새 빌드 생성
- Publish Update – Fingerprint 동일 시 OTA 업데이트
- 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회 수행한 사례입니다.
최초의 빌드를 제외하고는 네이티브 환경이 바뀌지 않았기 때문에, 핑거프린트가 일치한 경우 새 빌드 없이 4회의 업데이트가 적용 되었습니다.
즉, QA 빌드/업데이트 사이클을 단 한 번의 빌드로 유지하여 빠르게 QA를 처리할 수 있습니다.
🏁 마무리
OTA와 Fingerprint를 함께 활용하면, “빌드를 해야 하는 순간”과 “즉시 반영 가능한 변경”을 명확히 구분할 수 있습니다.
이 구조를 도입하면 QA 주기가 짧아지고, 빌드 횟수는 줄어들며, 배포 속도는 훨씬 빨라집니다.
결국 중요한 건 빌드 자동화가 아니라, 빠르게 실험하고 피드백을 반영할 수 있는 배포 루프를 만드는 것입니다.
이 글이 더 효율적인 QA 워크플로우를 고민하는 분들께 구체적인 참고가 되길 바랍니다.

