RN 앱 두 번째 배포: 재사용되는 인증서, 새로 해야 하는 등록
몇 주 전에 만든 고객 포털 앱을 반쯤 복사해서 새 RN 앱을 하나 더 만들었습니다. 성경 속 여정을 지도에 그리는 화면 하나를 웹에서 옮겨온 것뿐인데, 옮기고 나니 "인증서까지 그대로 가져다 쓸 수 있나"가 궁금해졌습니다. 결론부터 적으면 절반만 맞았습니다.
스캐폴드부터 새로 하려다 멈췄습니다
처음엔 create-expo-app으로 빈 프로젝트를 새로 만들 생각이었습니다. 폴더까지 파 놓은 상태에서 "어차피 기존 앱이 이미 스토어에 낼 수 있는 상태인데, 그거 그대로 복사해오면 안 되냐"는 말을 듣고 멈췄습니다. 맞는 말이었습니다. 빈 템플릿에서 시작하면 eas.json 빌드 프로필, GitHub Actions 워크플로 다섯 개, .gitignore의 서명 파일 제외 규칙을 전부 손으로 다시 적어야 합니다. 이미 한 번 실전에서 검증된 설정을 버리고 처음부터 다시 쓸 이유가 없었습니다.
그래서 기존 앱 저장소를 rsync로 통째로 복사했습니다. node_modules, .git, 빌드로 생성되는 네이티브 폴더, 서명 키가 든 폴더만 빼고요. 복사한 다음엔 기존 앱의 로그인 화면·프로젝트 관리 화면·API 클라이언트 코드를 걷어내고, 지도 화면과 데이터 파일을 채워 넣었습니다. 새 저장소의 첫 커밋이 파일 32개, 1만 줄 남짓이었는데, 그중 실제로 새로 쓴 코드는 지도 화면 하나와 데이터 파일 몇 개뿐이고 나머지는 전부 기존 앱에서 온 배포 뼈대였습니다.
그대로 가져온 것
- GitHub Actions 워크플로(타입체크, EAS 빌드, OTA 업데이트):
secrets: inherit패턴이라 이름만 맞으면 그대로 동작합니다 eas.json의 빌드 프로필 구조(개발/미리보기/정식 세 갈래).gitignore의 서명 파일·비밀 키 제외 규칙package.json의 핵심 의존성 버전(Expo SDK, React Native 버전)- Apple Developer 팀 계정
Apple Developer Program은 연 $99를 내는 게 앱 단위가 아니라 계정(팀) 단위입니다. 같은 팀 아래 앱을 몇 개 등록하든 추가 비용이 없습니다. 그래서 "새 앱이니까 개발자 계정도 새로 파야 하나" 고민했는데, 그럴 필요가 없었습니다. 배포 인증서도 팀 단위로 발급되기 때문에 마찬가지로 재사용됩니다.
그런데 앱마다 새로 해야 하는 것도 있었습니다
인증서와 계정은 팀 단위인데, 그 아래 앱 하나하나는 각자 등록해야 하는 항목이 따로 있습니다.
- EAS 프로젝트 ID: 앱마다 고유합니다.
eas-cli init을 새로 돌려야 발급됩니다. - App Store Connect 앱 레코드: 번들 ID를 등록하고 앱을 하나 새로 만들어야 앱 고유 ID를 알 수 있습니다. 이게 없으면 자동 제출 설정 자체를 쓸 수 없습니다.
- Google Play Console 앱: 마찬가지로 새로 등록해야 합니다. 기존 앱 배포에 쓰던 서비스 계정 키가 있긴 한데, 그 키가 새 앱에 대한 권한을 자동으로 갖는 게 아닙니다. Play Console에서 그 서비스 계정에 새 앱의 배포 권한을 따로 부여해야 알 수 있습니다.
- 프로비저닝 프로파일: 인증서는 팀 단위지만 프로비저닝은 번들 ID 단위라 앱마다 새로 발급됩니다. 다행히 이건 EAS가 빌드할 때 알아서 만들어줍니다.
- CI 시크릿: 저장소가 다르면 시크릿도 다시 등록해야 합니다. 기존 저장소에 넣어둔 토큰이 새 저장소로 자동으로 넘어오지 않습니다.
Android 지도에서 한 번 걸릴 뻔한 부분
처음엔 react-native-maps를 썼습니다. 그런데 타일 이미지 자체는 CARTO에서 무료로 받아 오버레이로 얹는데도, 바탕 지도 컴포넌트는 기본값이 Google Maps라서 Android에서는 Google Maps API 키가 없으면 지도가 아예 안 뜬다는 걸 알게 됐습니다. iOS는 Apple Maps가 기본이라 이 키가 필요 없습니다. 같은 코드인데 플랫폼에 따라 등록해야 하는 게 다른 셈이었습니다.
결국 그 키를 받지 않기로 했습니다
Google Maps API 키를 그냥 발급받고 넘어가려다가, 이 앱을 처음 만들 때 정해둔 원칙이 걸렸습니다. 서버도, API 키도 없는 앱으로 시작한 프로젝트인데, Android 지도 하나 때문에 그 원칙이 깨지는 셈이었습니다. Google Maps Platform은 무료 한도 안에 있어도 결제 카드 등록이 전제라, 예전에 구글 지도를 포기하고 Leaflet과 OpenStreetMap으로 갔을 때와 똑같은 문제가 다시 나온 것이기도 했습니다.
그래서 react-native-maps 대신 MapLibre(오픈소스 지도 렌더러)로 바꿨습니다. CARTO 타일을 raster 소스로 직접 얹는 구조라 iOS·Android 둘 다 키가 필요 없습니다. 지도 데이터 출처는 그대로 OpenStreetMap입니다. 마커·선 그리는 로직은 다시 짰지만 화면에 보이는 결과물은 거의 똑같습니다. 다만 육로/바닷길을 점선으로 구분하는 부분에서 한 번 더 걸렸습니다. 선의 점선 스타일을 구간(feature)별로 다르게 주는 게 이 렌더러에서는 안 먹혀서, 여정 하나를 육로용 소스와 바닷길용 소스 둘로 나눠 그리는 쪽으로 구조를 바꿔야 했습니다.
계정을 착각했습니다
지도를 다 고치고 EAS 프로젝트를 새로 만들려는데 이번엔 다른 데서 걸렸습니다. GitHub 저장소는 전부 같은 계정 아래 있어서, 설정 파일의 소유자 값도 당연히 같은 계정일 거라고 생각했습니다. 로그인한 채로 프로젝트 생성 명령을 돌렸더니 "그 계정에 프로젝트를 만들 권한이 없다"는 에러가 났습니다.
확인해보니 기존 앱의 EAS 프로젝트를 조회하는 것조차 권한 오류가 났습니다. GitHub 계정과 Expo 계정이 서로 다른 로그인이었던 겁니다. 로그아웃하고 실제 소유 계정으로 다시 로그인하고 나서야 프로젝트가 만들어졌습니다. 조직 이름이 같다고 계정도 같은 게 아니라는 걸, 이번에도 직접 걸려보고서야 알았습니다.
지금 상태
EAS 프로젝트 생성과 OTA 설정까지는 끝났습니다. 재사용 가능한 층(팀 계정·인증서·워크플로 구조)과 앱마다 새로 해야 하는 층(프로젝트 ID·스토어 앱 레코드·프로비저닝·CI 시크릿)이 갈린다는 처음 정리는 맞았는데, 그 사이에 계정 자체가 착각이었던 경우까지 하나 더 있었던 셈입니다. 아직 남은 건 App Store Connect 앱 등록, Google Play Console 앱 등록과 서비스 계정 권한 확인, 그리고 CI용 토큰 등록입니다. 전부 로그인이 필요한 대화형 작업이라 하나씩 진행 중입니다.
함께 읽기
- App Store Connect API 키: Team Key로 할 수 있는 것과 없는 것새 앱을 App Store Connect에 등록하면서, 화면을 계속 왔다 갔다 하기 귀찮아서 API로 자동화할 수 있는 부분은 자동화해보려고 했습니다. 결론부터 적으면 절반만 성공했습니다. 조회·수정은 되는데, 앱 생성 자체는 API로 안 됩니다.
- 같은 Expo 앱인데 배포 방식은 달랐습니다: iOS와 Android 심사 제출 비교기앞선 글에서는 이미 TestFlight에 올라간 iOS 빌드를 AI가 App Store 심사까지 제출한 과정을 다뤘습니다.
- 모바일 앱 스토어 배포 자동화를 설계할 때 볼 것들모바일 앱 배포는 빌드 파일을 만드는 것에서 끝나지 않습니다. 인증서, 서명 키, 스토어 메타데이터, 심사 정보, 버전 관리, 제출 자동화까지 함께 설계해야 반복 가능한 배포가 됩니다.
- Expo 앱은 만들었는데 출시가 안 됩니다: `eas build`보다 먼저 준비할 것들Expo를 사용하면 네이티브 빌드 과정이 크게 단순해집니다. 하지만 앱스토어 출시까지 명령어 한두 줄로 끝나는 것은 아닙니다. 실제 자동 배포를 만들려면 앱 등록, 서명 자격 증명, 스토어 API 권한, CI/CD 승인 절차가 먼저 준비되어야 합니다.
- 코드는 그대로인데 앱 심사는 제출됐습니다: AI에게 Expo 배포를 맡겨본 기록코드를 한 줄도 수정하지 않았는데 TestFlight에 올라가 있던 앱이 App Review 제출 상태로 바뀌었습니다. 얼핏 보면 AI가 앱을 새로 빌드해서 배포한 것처럼 보이지만, 실제로는 이미 준비된 빌드와 스토어 정보를 확인한 뒤 App Store Connect의 심사 절차를 진행한 것입니다.