RSS

Expo 앱 스토어 배포 준비 체크리스트: 순서가 중요한 이유

새 Expo 앱 하나를 두 스토어에 올릴 준비를 했습니다. 필요한 항목 자체는 검색하면 다 나옵니다. 그런데 정작 저를 막은 건 항목이 아니라 순서였습니다. 앞 단계가 끝나야 뒤 단계의 값이 생기는 구간이 몇 군데 있고, 그걸 모르고 덤비면 "왜 이 필드가 비어 있지"에서 멈춥니다.

첫 푸시부터 빨간 X가 떴습니다

저장소를 만들고 첫 커밋을 푸시했더니 워크플로 하나가 바로 실패했습니다. 타입체크는 통과했는데 OTA 발행이 죽었습니다. 로그를 보니 이랬습니다.

An Expo user account is required to proceed.
Either log in with eas login or set the EXPO_TOKEN environment variable

당연한 실패였습니다. 그 시점에 저는 EAS 프로젝트를 만들지도 않았고 토큰도 넣지 않았으니까요.

이런 상황이 생긴 이유는 제가 이 앱을 빈 템플릿이 아니라 이미 배포까지 끝난 형제 앱을 복사해서 시작했기 때문입니다. 코드와 워크플로는 완성된 상태로 따라왔는데, 계정 연결은 따라오지 않았습니다. 워크플로는 "준비가 다 끝났다"고 가정하고 첫 푸시부터 돌았습니다.

검증된 설정을 복사해 오는 건 좋은 선택이었지만, 그 대가로 "아직 준비 안 된 파이프라인이 즉시 실행된다"는 부작용이 붙습니다.

계정 확인이 0단계입니다

가장 먼저 할 일은 로그인이 아니라 누구로 로그인돼 있는지 확인하는 것입니다.

npx eas-cli whoami

저는 여기서 한 번 걸렸습니다. 저장소가 전부 같은 계정 아래 있으니 Expo 계정도 같겠거니 하고 그대로 프로젝트 생성 명령을 돌렸는데, "그 계정에 프로젝트를 만들 권한이 없다"는 오류가 났습니다. 확인해보니 기존 앱의 EAS 프로젝트를 조회하는 것조차 안 됐습니다. 저장소 계정과 Expo 계정이 서로 다른 로그인이었던 겁니다.

설정 파일에 적힌 소유자 이름이 저장소 조직 이름과 같아 보였던 게 착각의 원인이었습니다. 이름이 같다고 계정이 같은 건 아닙니다.

EAS 프로젝트가 다른 모든 것의 앞

npx eas-cli init              # projectId 발급
npx eas-cli update:configure  # updates.url 설정

둘 다 설정 파일에 값을 자동으로 써넣습니다. 이 값들은 앱마다 고유하기 때문에 형제 앱 값을 복사하면 안 됩니다. 복사해도 당장은 에러가 안 나고, 나중에 엉뚱한 앱으로 업데이트가 발행되는 방식으로 터질 겁니다.

토큰은 계정 단위, 등록은 저장소 단위

CI용 토큰은 프로젝트가 아니라 계정을 인증합니다. 그래서 값 하나로 여러 앱을 커버할 수 있습니다. 하지만 GitHub 시크릿은 저장소마다 따로 등록해야 하므로, 저장소가 늘면 같은 값을 그 수만큼 넣어야 합니다.

여기서 예전에 저지른 실수가 드러났습니다. 몇 달 전 형제 앱 저장소에 토큰을 등록해뒀는데, 그 값을 다시 꺼낼 방법이 없었습니다. GitHub 시크릿은 쓰기 전용이라 등록한 뒤에는 조회가 안 됩니다. 등록만 하고 어디에도 남겨두지 않았으니, 같은 값이 다시 필요해진 지금 방법이 없었던 겁니다.

결국 새로 발급해서 두 저장소에 같은 값으로 넣고, 이번에는 볼트에도 보관했습니다. 시크릿을 "넣고 끝"으로 취급하면 다음에 반드시 이 자리로 돌아옵니다.

여기서 순서가 갈립니다

두 스토어의 준비 순서가 다릅니다. 이게 이 글의 핵심입니다.

Apple 쪽

  1. Bundle ID 등록 (API로 가능)
  2. App Store Connect에서 앱 레코드 생성 (웹 콘솔에서만 가능)
  3. 앱을 만들고 나서야 숫자 형태의 Apple ID가 생깁니다
  4. 그 값을 빌드 설정의 제출 항목에 기록

3번이 함정입니다. 설정 파일에 앱 ID를 적는 칸이 있으니 미리 채우려 했는데, 그 값은 앱을 만들기 전에는 존재하지 않습니다. 순서를 뒤집을 수 없습니다.

Google 쪽

  1. Play Console에서 앱 생성 (역시 웹 콘솔에서만)
  2. 서비스 계정 권한 확인

저는 여기서도 순서를 잘못 잡을 뻔했습니다. "서비스 계정에 권한부터 주고 앱을 만들자"고 생각했는데, 권한이 앱별로 부여되는 방식이라면 아직 존재하지 않는 앱에는 권한을 줄 수 없습니다. 앱을 먼저 만들어야 권한 화면에 그 앱이 나타납니다.

다행히 확인해보니 기존 서비스 계정이 계정 전체 권한을 갖고 있어서, 새 앱을 만들자마자 추가 설정 없이 접근됐습니다. 앱별 권한이었다면 순서를 지켜야 했을 겁니다.

인증 파일은 저장소가 아니라 EAS에

형제 앱은 제출 설정에 서비스 계정 키의 로컬 경로를 적어두고 있었습니다. 그런데 그 저장소의 배포 문서가 스스로 이 방식을 함정으로 지목하고 있었습니다.

이유는 단순합니다. 키 파일이 든 폴더는 당연히 버전 관리에서 제외돼 있고, 그러면 CI 러너에는 그 파일이 없습니다. 로컬에서 제출하면 되고 CI에서 하면 깨지는, 발견이 늦는 종류의 문제입니다.

그래서 이번 앱은 경로를 아예 적지 않고 EAS Credentials에 키를 올리는 쪽으로 했습니다. 인증 소스를 한 곳으로 통일해두면 로컬과 CI가 같은 것을 봅니다.

이 키가 하는 일

먼저 이게 무엇인지 정리하면 이렇습니다. 서비스 계정은 사람이 아닌 로봇 계정입니다. Play Console의 사용자 목록에 사람 계정과 나란히 등록되고, 사람 대신 스토어에 바이너리를 올립니다.

빌드가 끝나면 누군가는 그 결과물을 Play Console에 업로드해야 합니다. 사람이 웹에 로그인해서 파일을 끌어다 놓는 대신, 빌드 서비스가 이 로봇 계정으로 로그인해서 대신 올려줍니다. 키를 올려두는 작업은 결국 "내 대신 스토어에 올릴 권한을 이 로봇에게 준다"는 뜻입니다.

메뉴가 네 단계입니다

명령 자체는 한 줄인데, 그 뒤로 물어보는 게 계속 나옵니다.

npx eas-cli credentials -p android
  1. 어느 빌드 프로파일이냐 (development / preview / production)
  2. Keystore냐 Google Service Account냐
  3. Play Store 제출용이냐 푸시 알림용이냐
  4. 새로 설정할 거냐 이미 올린 것을 고를 거냐
  5. 파일 경로 입력

각 단계가 왜 있는지 알고 나면 덜 헷갈립니다.

1번에서 프로파일을 묻는 이유는 프로파일마다 앱 식별자가 다를 수 있기 때문입니다. 제 설정은 개발용 변형에 접미사를 붙이도록 돼 있어서, 여기서 development를 고르면 스토어에 존재하지도 않는 패키지를 대상으로 잡습니다. 스토어 제출용이니 production이 맞습니다. 고르고 나면 화면 위에 대상 식별자가 찍히므로, 그게 스토어에 등록한 패키지명과 같은지 눈으로 확인하고 넘어가면 됩니다.

3번이 이 메뉴에서 제일 헷갈리는 자리입니다. 똑같이 생긴 서비스 계정 키를 두 가지 용도로 쓰기 때문입니다. 하나는 스토어 제출용이고, 다른 하나는 푸시 알림 발송용입니다. 이름이 거의 같아서 잘못 고르기 쉬운데, 지금 하려는 건 제출이니 Play Store Submissions 쪽입니다.

4번에서는 "이미 올린 것 고르기"를 먼저 확인해보는 편이 낫습니다. 같은 조직에서 앱을 여러 개 굴리면 같은 서비스 계정을 공유하는 경우가 많은데, 그때 키를 앱마다 새로 올리면 EAS 쪽에 같은 키가 여러 벌 쌓입니다. 나중에 키를 교체할 때 어느 게 어느 앱에 붙었는지 추적해야 하는 상황이 생깁니다. 목록이 비어 있으면 그때 새로 올리면 됩니다.

올리고 나면 로컬 파일은 안 씁니다

여기가 헷갈렸던 부분인데, 업로드 이후로는 그 파일 경로가 아무 의미가 없습니다. 키는 EAS 서버에 저장되고, 제출 시점에 로컬에서 돌리든 CI에서 돌리든 서버에 있는 같은 키를 씁니다.

그래서 설정 파일에 경로를 적지 않는 게 오히려 정상입니다. 경로를 적어두면 "로컬 파일"과 "서버에 올라간 키" 두 개의 진실이 생기고, 둘이 어긋나는 순간 환경에 따라 다르게 동작합니다.

서명 자격증명은 미리 준비하지 않아도 됩니다

준비 목록을 만들 때 iOS 배포 인증서, 프로비저닝 프로파일, Android 키스토어를 넣고 걱정했는데, 이 셋은 첫 빌드 때 EAS가 알아서 만듭니다.

이 셋의 성격도 서로 다릅니다. 배포 인증서는 팀 단위라 형제 앱과 그대로 공유되고, 프로비저닝 프로파일은 번들 ID 단위라 앱마다 새로 발급됩니다. 키스토어는 앱마다 새로 생성되고 한 번 정해지면 바꿀 수 없습니다.

준비 단계에서 손댈 게 없는 항목이었습니다. 미리 걱정한 시간이 아까웠습니다.

순서대로 적으면

순서 하는 일 어디서 앞 단계 의존
0 로그인 계정 확인 CLI
1 EAS 프로젝트 생성, OTA 설정 CLI 계정
2 CI 토큰 발급, 저장소마다 등록 웹 + CLI
3 Bundle ID 등록 API 또는 웹
4 App Store Connect 앱 생성 웹만 3
5 앱 ID를 제출 설정에 기록 파일 4
6 Play Console 앱 생성 웹만
7 서비스 계정 권한 확인 6
8 스토어 인증 키를 EAS에 업로드 CLI
9 첫 빌드 (서명 자격증명 자동 생성) CLI 또는 CI 1~8

4, 5, 6, 7번의 의존 관계만 알고 있으면 막힐 데가 없습니다. 저는 그걸 모르고 5번을 먼저 채우려다 한 번, 7번을 먼저 하려다 한 번 멈췄습니다.

남은 것

여기까지가 준비의 끝입니다. 아직 빌드는 한 번도 돌리지 않았습니다.

돌아보면 이 준비 과정에서 실제로 코드를 건드린 시간은 얼마 안 됩니다. 대부분은 계정을 확인하고, 콘솔에서 무언가를 만들고, 그 결과로 생긴 식별자를 설정 파일에 옮겨 적는 일이었습니다. 그리고 그 일들 사이에 순서가 걸려 있어서, 몰랐을 때는 중간에서 두 번 멈췄습니다.

실제로 바이너리를 만들어 스토어에 올리는 건 다음 단계입니다. 거기서 또 무엇에 걸릴지는 해봐야 알 것 같습니다.