2026년 Cordova 앱의 CI/CD는 Linux runner에서 Android AAB를 빌드 및 서명하고, macOS runner에서 iOS IPA를 빌드 및 서명하여 동일한 파이프라인에서 양쪽 스토어로 업로드하는 것을 의미합니다. 기존에 Cordova 팀이 사용하던 매니지드 서비스들이 사라지거나 종료됨에 따라, 이제 대부분의 팀은 일반 CI 환경에서 Cordova CLI를 실행합니다. 이 가이드에서는 바로 사용 가능한 GitHub Actions 설정법과 다른 CI에서의 적용 단계, 그리고 Live Update 및 장기적인 대안에 대해 솔직하게 다룹니다.
Cordova 팀에게 일어난 변화
Cordova CI/CD의 흐름을 바꾼 세 가지 사건:
- PhoneGap Build 2020년 10월 1일에 종료되었습니다.
- Microsoft App Center 2025년 3월 31일에 은퇴했습니다. 이와 함께 빌드, 배포 및 호스팅된 CodePush 서비스도 종료되었습니다.
- Ionic Appflow는 2025년 2월에 신규 판매를 중단했습니다. 기존 고객은 2027년 12월 31일까지 서비스를 이용할 수 있습니다.
Cordova 자체는 여전히 작동합니다. Apache 프로젝트는 cordova-android 및 cordova-ios 릴리스를 제공하며, 수많은 내부 및 엔터프라이즈 앱이 이를 기반으로 구동됩니다. 문제는 관리 레이어입니다. 서명 저장소, 스토어 업로드, OTA 업데이트 등이 여기에 해당합니다. 이제는 CI 서비스와 몇 가지 도구를 조합하여 이를 직접 구축해야 합니다.
Cordova 파이프라인의 구조
| 단계 | Android | iOS |
|---|---|---|
| Runner | Linux | Xcode 26 기반 macOS |
| 복구 | cordova prepare (다음으로부터 플랫폼 및 플러그인) package.json) |
동일한 것을 공유하고 |
| 웹 빌드 | 번들러(CLI Vite, webpack, Angular)를 www/ |
동일한 것을 공유하고 |
| Native Build | Gradle를 통한 cordova build android --release |
xcodebuild 를 통한 cordova build ios --release --device |
| 서명 | keystore 업로드 (.jks) |
배포 인증서 (.p12) 및 프로비저닝 프로파일 |
| 아티팩트 | .aab Google Play용 |
.ipa TestFlight 및 App Store용 |
| 업로드 | Google Play Developer API (서비스 계정) | App Store Connect API 키 |
모든 파이프라인에는 두 가지 플랫폼 규칙이 적용됩니다:
- 2026년 4월 28일부터 App Store Connect은 Xcode 26 및 iOS 26 SDK 이상의 버전으로 빌드된 앱만 허용합니다. 사용 중인
cordova-ios버전이 이를 지원하는지 확인하세요. - Google Play는 신규 앱에 AAB 업로드를 요구하며, 매년 최소 타겟 API 레벨을 높입니다. 항상 최신 상태를
cordova-android유지하여 기본 설정이targetSdkVersion따라가도록 하세요.
1단계: 프로젝트 재현성 확보하기
CI는 저장소에 있는 내용만 다시 빌드할 수 있습니다. 워크플로우를 작성하기 전에:
- Commit
package.json및 lockfile을 커밋하세요. Cordova는 플랫폼과 플러그인을cordova의package.json섹션에 저장하므로,cordova prepare복원합니다. - 커밋하지 마세요
platforms/또는plugins/. 생성됩니다. - 모든 머신에서 동일한 버전을 사용하도록 Cordova CLI를 dev dependency로 설치하세요:
bun add -d cordova
- 서명 값을
config.xmland into abuild.json파일로 옮기세요(다음 단계). - Node.js 버전을 다음 파일에 고정하세요:
.nvmrc또는package.jsonengines.
2단계: build.json으로 서명
Cordova는 서명 설정을 다음에서 읽습니다: build.json 파일을 커밋하세요. build.template.json 플레이스홀더가 포함된 파일을 사용하여 실제 값을 build.json CI에서 secrets로 생성하며, 생성된 파일은 절대 커밋하지 마세요:
{
"android": {
"release": {
"keystore": "release.jks",
"storePassword": "${KEYSTORE_STORE_PASSWORD}",
"alias": "upload",
"password": "${KEYSTORE_KEY_PASSWORD}",
"packageType": "bundle"
}
},
"ios": {
"release": {
"codeSignIdentity": "Apple Distribution",
"developmentTeam": "ABCDE12345",
"packageType": "app-store",
"provisioningProfile": "com.example.app AppStore",
"automaticProvisioning": false
}
}
}
이 ${...} Cordova는 플레이스홀더를 확장하지 않습니다. 아래 워크플로우는 다음을 사용하여 실제 값을 작성합니다: envsubst.
아직 Android keystore가 없다면, 다음 명령어로 생성하세요: keytool 또는 브라우저 기반의 Android 키스토어 생성기를 사용하세요. iOS의 경우, iOS 인증서 생성기 를 통해 인증서 서명 요청(CSR)을 생성하고 .p12 Mac 없이도 가능합니다.
3단계: GitHub Actions 워크플로우
이 워크플로우는 버전 태그를 기준으로 양쪽 플랫폼을 병렬로 빌드하고 내부 테스트 트랙에 업로드합니다.
# .github/workflows/cordova-release.yml
name: Cordova release
on:
push:
tags: ['v*']
jobs:
android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
- run: bun install --frozen-lockfile
- run: bun run build # outputs to www/
- run: bunx cordova prepare android
- name: Signing files
env:
KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }}
KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }}
run: |
echo "$KEYSTORE_BASE64" | base64 --decode > release.jks
envsubst < build.template.json > build.json
- run: bunx cordova build android --release --buildConfig=build.json
- uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
packageName: com.example.app
releaseFiles: platforms/android/app/build/outputs/bundle/release/app-release.aab
track: internal
status: completed
ios:
runs-on: macos-26
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- run: bun run build
- run: bunx cordova prepare ios
- name: Install certificate and profile
env:
P12_BASE64: ${{ secrets.IOS_P12_BASE64 }}
P12_PASSWORD: ${{ secrets.IOS_P12_PASSWORD }}
PROFILE_BASE64: ${{ secrets.IOS_PROFILE_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
echo "$P12_BASE64" | base64 --decode > dist.p12
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security set-keychain-settings -lut 21600 build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security import dist.p12 -k build.keychain -P "$P12_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" build.keychain
security list-keychains -d user -s build.keychain login.keychain
for dir in "$HOME/Library/MobileDevice/Provisioning Profiles" \
"$HOME/Library/Developer/Xcode/UserData/Provisioning Profiles"; do
mkdir -p "$dir"
echo "$PROFILE_BASE64" | base64 --decode > "$dir/app.mobileprovision"
done
cp build.template.json build.json # iOS values hold no secrets
- run: bunx cordova build ios --release --device --buildConfig=build.json
- name: Upload to TestFlight
env:
API_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
API_ISSUER: ${{ secrets.APPLE_ISSUER_ID }}
API_KEY_BASE64: ${{ secrets.APPLE_KEY_CONTENT }}
run: |
mkdir -p ~/.appstoreconnect/private_keys
echo "$API_KEY_BASE64" | base64 --decode > ~/.appstoreconnect/private_keys/AuthKey_${API_KEY_ID}.p8
IPA=$(find platforms/ios/build -name '*.ipa' | head -n 1)
xcrun altool --upload-app -f "$IPA" -t ios --apiKey "$API_KEY_ID" --apiIssuer "$API_ISSUER"
- name: Clean up keychain
if: always()
run: security delete-keychain build.keychain || true
특정 라인이 포함된 이유:
- 두 개의 프로비저닝 프로필 폴더. Xcode 16 이상 버전은 다음에서 프로필을 읽습니다:
~/Library/Developer/Xcode/UserData/Provisioning Profiles. 이전 Xcode 버전은 다음을 사용했습니다:~/Library/MobileDevice/Provisioning Profiles. 두 곳 모두 작성하면 “No profiles for ‘com.example.app’ were found” 오류를 방지할 수 있습니다. set-key-partition-listmacOS의 키체인 접근 프롬프트를 방지하여 CI 작업이 중단되는 것을 막아줍니다.- JDK 버전. 사용 중인
cordova-android릴리스 문서를 참고하세요. 최신 릴리스는 JDK 17로 빌드됩니다. - 빌드 번호. 버전 올리기
android-versionCode및ios-CFBundleVersion에서config.xml빌드 전 CI 실행 번호를 사용하거나, 그렇지 않으면 스토어에서 중복된 파일로 간주하여 upload를 거부할 수 있습니다.
다음 항목을 교체할 수 있습니다: altool upload를 fastlane의 upload_to_testflight 및 Play 액션을 fastlane supply 팀에서 이미 fastlane을 사용 중이라면 말이죠.
4단계: 다른 CI 서비스에서의 동일한 파이프라인
CI 간에 명령어는 바뀌지 않습니다. Runner 선택과 secret 구문만 달라집니다.
| CI | Android 러너 | iOS runner | 참고 사항 |
|---|---|---|---|
| GitHub 작업 | ubuntu-latest |
macos-26 / macos-latest |
프라이빗 레포지토리에서는 macOS 분당 비용이 Linux보다 높습니다 |
| GitLab CI | 모든 Linux runner | 호스팅된 macOS runner (beta, Premium, Ultimate) 또는 self-hosted | 다음과 같은 이미지 macos-26-xcode-26 |
| Bitbucket Pipelines | Linux Docker 이미지 | self-hosted Mac 전용 | 보기 Bitbucket iOS 가이드 |
| Gitea / Forgejo | Linux runner | 호스트 모드 self-hosted Mac | 보기 Gitea iOS 가이드 |
| Bitrise | Linux 스택 | macOS 스택 | Cordova 단계 실행 (prepare, archive) |
| Codemagic | Linux | Mac mini 인스턴스 | Ionic Cordova 빌드가 저장되는 codemagic.yaml |
| Azure DevOps | ubuntu-latest |
macOS-latest |
인증서 및 프로필 설치를 위한 태스크 |
기능별 상세 내용은 다음을 참조하세요 Cordova 앱용 CI/CD 플랫폼 비교.
5단계: Cordova용 Live Updates
Live Updates를 사용하면 스토어 심사 없이 HTML, CSS, JavaScript 수정 사항을 배포할 수 있습니다. App Center CodePush는 2025년 3월에 종료되었고, Appflow Live Updates는 2027년 12월 31일에 종료되며, 기존 커뮤니티 기반의 code push 플러그인들은 더 이상 유지보수되지 않습니다. 안정적인 대안은 Capgo Live Update 사용하기 를 사용하여 @capgo/cordova-updater 플러그인입니다. 이 플러그인은 Capacitor updater와 동일한 백엔드, 채널 및 CLI를 사용합니다.
요구 사항
- Cordova CLI 12 이상
cordova-android13 이상 및/또는cordova-ios7 이상 (플러그인이 기본https://localhost/및app://localhost/scheme을 사용함)- 사용하지 마세요
cordova-plugin-ionic-webview와 함께 사용하지 마세요. Ionic WebView는 Cordova의 scheme handler를 우회하므로 다운로드된 bundle이 로드되지 않습니다.
플러그인 설치
Capacitor 방식과 동일하게 Capgo 앱을 생성한 후, 다음 Capgo 앱 ID로 플러그인을 설치하세요:
cordova plugin add @capgo/cordova-updater \
--variable APP_ID=com.example.app \
--variable DEFAULT_CHANNEL=production
cordova prepare android ios
DEFAULT_CHANNEL, UPDATE_URL, 그리고 AUTO_UPDATE 는 선택적인 설치 변수입니다. 변경 시 cordova prepare 를 다시 실행하세요.
매 실행 시마다 확인하세요
로컬에서 deviceready, 해당 플러그인은 다음과 같이 사용 가능합니다 cordova.plugins.Updater. notifyAppReady() 매 실행 시 적용됩니다. 그렇지 않으면 플러그인은 새 번들을 손상된 것으로 간주하고 롤백합니다:
document.addEventListener('deviceready', async () => {
const { Updater } = cordova.plugins
await Updater.notifyAppReady()
const latest = await Updater.getLatest()
if (latest.url && !latest.error) {
const bundle = await Updater.download({
url: latest.url,
version: latest.version,
checksum: latest.checksum,
})
await Updater.next({ id: bundle.id })
}
})
CI에서 번들 upload 하기
웹 전용 변경 사항의 경우, 웹 레이어를 빌드하고 upload 하는 job을 추가하세요:
live-update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- run: bun run build
- run: bunx @capgo/cli@latest bundle upload --channel=production
env:
CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }}
Live Update는 웹 번들만 교체합니다. Cordova 플러그인을 추가, 삭제 또는 업그레이드하려면 여전히 스토어용 Native Build가 필요합니다. Capgo는 Live Update 번들에 기록된 네이티브 플러그인을 프로젝트와 비교하여 호환되지 않는 upload를 감지합니다. 자세한 내용은 live update 호환성을 참고하세요. 전체 설정 방법은 Cordova updater 문서.
빌드 측면에서는, Capgo 빌드 문서에서는 Capacitor 프로젝트만 다룹니다 (생성된 native project를) cap sync업로드합니다). 위에서 보셨듯이 Cordova 바이너리는 CI에서 계속 빌드됩니다.
선택 사항: 나중에 Capacitor로 마이그레이션하기
파이프라인과 Live Update를 사용하기 위해 반드시 Cordova를 떠날 필요는 없습니다. 마이그레이션은 별개의 결정 사항이며, 다음과 같은 경우에 가치가 있습니다:
- macOS runner를 유지 관리하지 않고 iOS 빌드를 하고 싶을 때. Capgo Build를 사용하면 Linux CI 작업에서 Capacitor 앱을 컴파일할 수 있습니다.
- 의존 중인 플러그인의 Cordova 업데이트가 중단되었지만, Capacitor 대응 플러그인이 있는 경우.
- 플랫폼을 직접 패치하지 않고도 Swift Package Manager 지원과 최신 Android Gradle Plugin 버전을 사용하고 싶을 때.
마이그레이션을 통해 다음을 유지할 수 있습니다: www code. Capacitor을 추가하고, 플러그인을 옮기면 됩니다 (대부분의 Cordova 플러그인은 그대로 작동합니다). Capacitor 대응 native 플러그인이 있는 몇 가지만 교체하세요. Capgo 앱 ID와 채널은 그대로 유지되며, updater plugin만 @capgo/cordova-updater 대상 @capgo/capacitor-updater에서 변경됩니다. 단계별 가이드는 Cordova에서 Capacitor로 마이그레이션하기, 그리고 Cordova에서 Capacitor로 전환하는 솔루션 페이지 도움받는 방법을 다룹니다. 마이그레이션 후에는 위 CI가 웹 빌드로 축소되며, cap sync, 그리고 build request 설명된 대로 플랫폼당 Capacitor 앱을 위한 CI/CD 플랫폼 비교.
CI 환경에서의 Cordova 빌드 트러블슈팅
| 오류 | 원인 및 해결 방법 |
|---|---|
Current working directory is not a Cordova-based project |
config.xml 또는 www/ 누락되었습니다. Cordova 명령을 실행하기 전에 웹 빌드를 먼저 실행하거나, 빈 www/. |
No platforms added to this project |
플랫폼이 목록에 없습니다. package.json. Run cordova platform add 커밋하세요. package.json. |
Could not find an installed version of Gradle |
최근 cordova-android 는 Gradle wrapper를 사용합니다. Gradle을 전역으로 설치하기보다 플랫폼을 업데이트하세요. |
Unsupported class file major version |
러너의 JDK가 다음 버전과 일치하지 않습니다: cordova-android 를 사용하여 버전을 고정하세요. setup-java. |
No signing certificate "iOS Distribution" found |
사용 Apple Distribution 를 ID로 지정하고 다음을 확인하세요: .p12 에 개인 키가 포함되어 있는지 확인하세요. |
errSecInternalComponent |
키체인이 잠겨 있거나 파티션 목록이 누락되었습니다. 키체인 설정 명령어를 다시 실행하세요. |
| Upload 거부됨: SDK 버전이 너무 오래되었습니다. | Runner 이미지가 Xcode 26 미만 버전을 사용 중입니다. 선택하거나 macos-26 Xcode 버전을 설정하세요. xcode-select. |
업로드 시 Play 업로드: Version code has already been used |
버전 올리기 android-versionCode 모든 업로드 시 적용됩니다. |
요약
2026년의 Cordova 파이프라인은 플랫폼별 CI 작업으로 구성됩니다. Android AAB를 위한 Linux runner, IPA를 위한 Xcode 26 기반 macOS runner, 생성된 build.json를 위한 secrets, 그리고 양쪽 스토어용 API 키가 필요합니다. 어떤 메인스트림 CI에서도 실행 가능합니다. 웹 전용 수정을 위한 @capgo/cordova-updater 및 bundle upload job을 추가하면 Appflow나 App Center가 제공하던 빌드, 스토어, OTA 기능을 모두 갖추게 됩니다. Capacitor로의 전환은 나중을 위한 선택지일 뿐, 필수 사항은 아닙니다.