junsobi

Menu

Close

오너그라운드 점주앱
Expo + EAS Update 기반 RN 점주앱의 CI/CD·OTA 배포 파이프라인 및 안정성 인프라 담당.

Thu Mar 12 2026

React Native
Expo
TypeScript
EAS Update
Codemagic
GitHub Actions
Firebase
Slack
Carousel Image 1
Carousel Image 2
Carousel Image 3
Carousel Image 4
Carousel Image 5
Carousel Image 6
Carousel Image 7

오너그라운드는 프랜차이즈 매장주용 통합 운영 앱입니다. 매출 대시보드, 시간대별 매출 분석, 실시간 결제 내역, 공지, 스탬프·쿠폰 리워드까지 매장 운영에 필요한 것들을 한 앱에 묶어줍니다.

기능 개발은 다른 개발자가 담당하고, 저는 CI/CD·OTA 배포·런타임 안정성 파트를 맡아 배포 인프라와 크래시 방어 영역을 정리하고 있습니다.

맡은 부분

환경 설정 단일 소스화

dev · preview · prod 3개 variant에 번들 ID, API 도메인, Firebase 설정, 딥링크 스킴이 흩어져 있었습니다. 이걸 app.config.ts 한 파일 안에서 환경변수로 전환되는 단일 소스 구조로 정리했습니다. 빌드 설정 실수로 prod 번들에 dev API가 박히는 사고를 원천 차단하기 위한 작업이었습니다.

CI 도구 재배치

처음엔 GitHub Actions 단일 의존 구조였는데, EAS 명령 호환성과 실행 시간 측면에서 한계가 있었습니다. 워크로드를 쪼개서 dev CI와 Slack 알림은 Codemagic, 플랫폼별 빌드·OTA·TestFlight 타겟팅은 EAS Workflow, 범용 Node 스크립트는 GitHub Actions에 배치했습니다. 각 도구가 제일 잘하는 일에만 쓰도록 나눈 구조입니다.

EAS Update 기반 OTA

runtimeVersion 을 앱 버전 기반으로 묶고 에러 복구 모드로 자동 복구를 켰습니다. OTA 업데이트 스플래시와 진행률 바를 붙여서 런타임 중 번들 교체로 UX가 깨지는 걸 막았고, dev 푸시는 자동 OTA, 프로덕션은 수동 트리거 + 버전 드롭다운으로 운영합니다.

스토어 자동 제출

App Store Connect와 Google Play Console 자동 제출 파이프라인을 붙였습니다. preview 빌드는 TestFlight로 자동 업로드되고, iOS는 auto-submit, Android는 draft로 떨어뜨려 QA 검수 후 수동 승인하는 안전장치를 뒀습니다. 실수의 여파가 큰 동작이라 한 단계 더 거르는 구조가 필요했습니다.

Firebase App Distribution

Android preview 빌드를 Firebase 테스터 그룹에 자동 배포하는 워크플로를 별도로 뒀습니다. EAS로 빌드된 아티팩트를 받아 Firebase로 업로드하는 스크립트를 짜서 붙였습니다.

런타임 크래시 방어

useSuspenseQuery 쓰는 전 경로에 쿼리 에러 바운더리를 배치해 페칭 실패 때 앱이 뚫리지 않게 했습니다. Axios 리프레시 토큰 null 체크와 타임아웃, WebView 메시지 파싱 예외, AsyncStorage 접근 실패, 푸시 알림 비동기 예외 처리까지 크래시가 나올 법한 진입점을 하나씩 메꿨습니다.

플랫폼 호환성 이슈

Android 13+ 알림 권한 누락, iOS Firebase 빌드 에러, iOS 스와이프-백 제스처로 로그인 페이지가 다시 노출되던 버그를 정리했습니다.

긴급 hotfix 사례

프로덕션 API 엔드포인트 이슈가 터졌을 때, 스토어 심사 없이 OTA로 API URL 오버라이드를 내려 서비스 연속성을 확보한 적이 있습니다. 앞서 세팅해 둔 OTA 파이프라인이 없었다면 불가능했을 대응이었습니다.

스택

React Native, Expo, TypeScript, Expo Router, EAS Update, Codemagic, EAS Workflow, GitHub Actions, Firebase Analytics, Firebase App Distribution, Slack.