Expo 앱은 만들었는데 출시가 안 됩니다: `eas build`보다 먼저 준비할 것들

Expo를 사용하면 네이티브 빌드 과정이 크게 단순해집니다. 하지만 앱스토어 출시까지 명령어 한두 줄로 끝나는 것은 아닙니다. 실제 자동 배포를 만들려면 앱 등록, 서명 자격 증명, 스토어 API 권한, CI/CD 승인 절차가 먼저 준비되어야 합니다.

이 글은 새로운 Expo 프로젝트를 시작한다는 가정 아래, iOS와 Android 앱을 빌드하고 스토어 심사까지 연결할 때 꼭 알아야 할 항목을 정리한 실전 요약입니다. 예시는 Expo SDK 56과 EAS를 기준으로 합니다.

먼저 구분해야 할 네 단계

모바일 배포 자동화는 다음 네 단계로 나누어 생각하면 이해하기 쉽습니다.

빌드: iOS의 IPA, Android의 AAB 같은 설치·제출 파일을 만듭니다.

업로드: 빌드 결과물을 App Store Connect 또는 Google Play Console에 올립니다.

심사 제출: 스토어 정보와 심사 답변을 확인하고 검토를 요청합니다.

출시: 승인된 버전을 즉시 또는 단계적으로 사용자에게 공개합니다.

eas build는 첫 번째 단계를, eas submit은 주로 두 번째 단계를 담당합니다. 특히 iOS에서 TestFlight에 업로드되었다고 해서 App Store 심사가 자동으로 시작되는 것은 아닙니다. Android도 트랙 업로드와 프로덕션 공개를 구분해야 합니다.

시작 전에 필요한 계정과 등록

공통

Expo 계정과 EAS 프로젝트

소스 저장소와 CI 실행 환경

앱마다 고유한 iOS Bundle ID와 Android Package Name

버전 번호와 빌드 번호를 올리는 규칙

iOS

Apple Developer Program 가입

Apple Developer의 App ID 등록

App Store Connect의 앱 레코드 생성

배포 인증서와 프로비저닝 프로파일

자동 업로드용 App Store Connect API Key

App Store Connect API Key는 보통 Issuer ID, Key ID, .p8 개인 키 파일로 구성됩니다. 이 키는 앱 서명용 인증서와 역할이 다릅니다. EAS가 자격 증명을 관리하도록 맡기더라도 두 종류의 권한을 구분해서 보관해야 합니다.

Android

Google Play Console 개발자 계정

Play Console의 앱 등록

Play App Signing 설정

앱 업로드용 keystore

자동 업로드용 Google Play 서비스 계정 JSON 키

Android에서도 google-services.json과 Play Console 업로드용 서비스 계정 JSON은 전혀 다른 파일입니다. 전자는 Firebase 연동 설정이고, 후자는 Google Play Developer API를 호출하기 위한 비밀 자격 증명입니다.

CI에 보관할 값과 보관하면 안 되는 값

다음 값은 저장소에 커밋하지 않고 EAS Secret 또는 CI의 암호화된 Secret에 보관해야 합니다.

EXPO_TOKEN

App Store Connect API의 Issuer ID와 Key ID

App Store Connect API .p8 키

Android keystore와 비밀번호

Google Play 서비스 계정 JSON

반면 앱 식별자, 제출 대상 트랙, 빌드 프로필 이름처럼 공개되어도 문제가 없는 설정은 일반 변수로 관리할 수 있습니다.

권한은 가능한 한 작게 부여하고, 운영 배포 환경에는 승인 규칙을 두는 편이 안전합니다. 로그에 비밀 값이 출력되지 않는지도 반드시 확인해야 합니다.

Expo SDK 56 프로젝트의 기본 구성

SDK 56은 React Native 0.85, React 19.2.3을 사용하며 Node.js 20.19 이상이 필요합니다. 새 프로젝트에서는 먼저 버전을 고정하고 상태를 확인합니다.

npx expo-doctor
npx expo install --check
npx eas-cli@latest login
npx eas-cli@latest build:configure

앱 식별자는 환경이 달라도 임의로 바뀌지 않도록 명시합니다.

// app.config.js
export default {
expo: {
name: "Example App",
slug: "example-app",
version: "1.0.0",
ios: {
bundleIdentifier: "com.example.app",
},
android: {
package: "com.example.app",
},
},
};

EAS 프로필은 개발, 사내 검증, 스토어 배포를 분리하는 방식이 관리하기 좋습니다.

{
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal"
},
"production": {
"autoIncrement": true
}
},
"submit": {
"production": {
"ios": {
"ascAppId": "APP_STORE_CONNECT_APP_ID"
},
"android": {
"track": "internal"
}
}
}
}

예시의 값은 그대로 사용하는 비밀 키가 아니라 프로젝트별로 교체해야 하는 자리표시자입니다.

실제 배포 흐름

iOS

npx eas-cli@latest build --platform ios --profile production
npx eas-cli@latest submit --platform ios --profile production

제출이 성공하면 빌드가 App Store Connect와 TestFlight에 나타납니다. 이후 수출 규정, 개인정보 처리, 심사 연락처, 스크린샷, 설명, 심사 메모 등을 확인하고 App Review에 제출합니다. 이 마지막 단계는 자동화할 수 있지만, 제품 정보와 법적 답변까지 자동으로 결정해서는 안 됩니다.

Android

npx eas-cli@latest build --platform android --profile production
npx eas-cli@latest submit --platform android --profile production

처음에는 내부 테스트 트랙으로 업로드하고 실제 기기에서 설치·업데이트·로그인·결제를 확인하는 것을 권장합니다. 같은 versionCode의 AAB는 다시 올릴 수 없으므로 모든 스토어 빌드에서 값을 증가시켜야 합니다.

Google Play의 신규 개인 개발자 계정에는 프로덕션 액세스 전에 별도의 테스트 요건이 적용될 수 있습니다. 현재 공식 안내에는 대상 계정이 최소 12명의 테스터가 14일 연속 참여한 비공개 테스트를 거친 뒤 프로덕션 액세스를 신청하도록 설명되어 있습니다. 계정 유형과 정책 변경 여부는 실제 제출 전에 Play Console에서 다시 확인해야 합니다.

CI/CD에서 자동화할 부분

안전한 기본선은 다음과 같습니다.

Pull Request: 타입 검사, 린트, 테스트

기본 브랜치 병합: preview 빌드 또는 내부 배포

버전 태그: production 빌드

승인 후: TestFlight와 Google Play 내부 트랙 업로드

담당자 확인 후: 스토어 심사 제출과 공개

EAS Workflows 또는 GitHub Actions 모두 사용할 수 있습니다. 중요한 것은 도구보다 환경 분리와 승인 경계입니다. 프로덕션 단계에는 보호 환경을 적용하고, 같은 커밋에서 검증된 결과물만 제출해야 합니다.

자주 놓치는 부분

TestFlight 업로드와 App Store 심사 제출을 같은 작업으로 오해합니다.

google-services.json을 Google Play API 키로 착각합니다.

iOS 서명 인증서와 App Store Connect API Key의 용도를 혼동합니다.

앱의 버전, iOS 빌드 번호, Android versionCode를 같은 값으로만 관리합니다.

스토어 메타데이터와 개인정보 관련 답변을 준비하지 않은 채 제출 자동화부터 만듭니다.

비밀 키를 저장소, 빌드 로그, 캐시 또는 산출물에 포함합니다.

Android 내부 테스트를 생략하고 곧바로 프로덕션 공개를 시도합니다.

DUOLABS의 권장 기준

저희는 모바일 배포를 “빌드 명령 자동화”가 아니라 “릴리스 판단을 재현 가능한 절차로 만드는 일”로 봅니다. 따라서 새 프로젝트에서는 다음 기준부터 적용하는 편이 좋습니다.

개발·검증·운영 환경과 앱 식별자를 명확히 구분합니다.

키의 소유자, 용도, 만료일, 교체 절차를 기록합니다.

iOS와 Android 모두 내부 배포를 통과한 빌드만 심사에 제출합니다.

운영 제출에는 승인 단계를 두고 감사 가능한 로그를 남깁니다.

Firebase와 푸시 알림은 별도 확장 단계로 두되, 필요한 APNs 키와 Firebase 설정의 책임 범위를 미리 문서화합니다.

이 구조를 먼저 마련하면 새 앱에서도 담당자의 기억에 의존하지 않고 동일한 품질로 배포할 수 있습니다.

공식 문서

Expo SDK 56 문서

EAS Build 설정

Expo 앱 서명 자격 증명

EAS Submit: iOS

EAS Submit: Android

EAS 환경 변수

Apple App ID 등록

App Store Connect API

Google Play App Signing

Google Play Developer API 시작하기