푸시(예: FCM)를 붙이다 보면 “Android는 gradle 설정이 필요하고, iOS는 다른 설정이 필요”해서 분기를 넣어야 하는 순간이 자주 옵니다. 문제는 분기를 잘못 넣으면 Android 빌드가 gradle 단계에서 깨지거나, 반대로 iOS 쪽 설정이 Android에 섞여 애매한 에러를 만들기도 합니다.
아래는 빌드 로그 → 흔한 원인 → 해결 단계 순서로 정리한 실전 가이드입니다.
먼저 “어디에서(gradle? plugin.xml? config.xml?)” 문제가 났는지부터 좁히면 훨씬 빨리 끝납니다.
1) 빌드 로그에서 분기 실패를 의심해야 하는 신호
Android 빌드가 실패할 때, 아래 패턴이면 “플러그인/설정이 Android에 맞게 분기되지 않았다” 쪽을 먼저 의심합니다.
- Execution failed for task ':app:processDebugGoogleServices' (google-services.json 누락/경로 문제, 혹은 google-services 플러그인 적용 위치 문제)
- Could not find method googleServices() 또는 com.google.gms.google-services 관련 적용 에러 (Gradle plugin 적용 방식/버전 불일치)
- Manifest merger failed (AndroidManifest.xml 쪽에 iOS 키/값을 넣었다기보단, Android에 들어오면 안 되는 설정이 섞였거나 권한/서비스 중복)
- Duplicate class, Program type already present (의존성 중복, AndroidX/지원 라이브러리 혼재, 여러 푸시 플러그인 동시 설치)
로그를 볼 때는 “Cordova가 생성한 Android 프로젝트 기준”으로 판단하는 게 좋아서, 보통 platforms/android 아래의 에러 라인을 따라갑니다.
2) 흔한 원인: Android와 iOS 설정을 같은 곳에 한 번에 넣은 경우
Cordova에서 분기 실수는 대체로 아래 3곳에서 나옵니다.
- config.xml에 플랫폼 구분 없이 preference / edit-config / resource-file을 넣음
- plugin.xml에서 <platform name="android"> / <platform name="ios">를 나누지 않고 파일/설정을 공용으로 처리
- 빌드 스크립트나 후처리 스크립트(after_prepare 등)에서 OS 구분 없이 파일을 복사/수정
특히 푸시는 Android는 gradle 의존성 + google-services.json + Firebase 관련 설정 조합이 핵심이고, iOS는 entitlements / capabilities(APNs) / GoogleService-Info.plist처럼 완전히 결이 달라서 “공용 처리”가 잘 안 맞습니다.
3) 해결 단계 A: config.xml에서 Android/iOS를 확실히 분리하기
가장 먼저 확인할 것은 config.xml입니다. 푸시 플러그인 문서대로 따라 하다가 공용 영역에 설정을 넣어버리면, 의도치 않게 양 플랫폼에 섞입니다.
체크 포인트는 단순합니다.
- Android 전용 항목은 반드시 <platform name="android"> 블록 안에만 존재하는지
- iOS 전용 항목은 반드시 <platform name="ios"> 블록 안에만 존재하는지
- 공용(preference 등)은 정말로 양쪽에 안전한 값인지 (예: Push 관련 키가 OS별로 다르면 공용에 두지 않기)
실무에서는 “공용 설정을 최소화하고, 플랫폼 블록을 넓게 쓰는 방식”이 사고가 적습니다. 한 번이라도 꼬였으면, 설정을 정리한 뒤 platform rm android → platform add android로 재생성해서 반영이 깔끔한지 확인하는 편이 좋습니다(단, platforms 폴더에 직접 수정한 내용이 있으면 먼저 백업하세요).
4) 해결 단계 B: gradle 쪽 에러는 ‘버전/적용 위치/파일 유무’부터 점검
푸시가 얽힌 gradle 에러는 원인이 다양하지만, 분기 관점에서 보면 아래 순서로 정리하면 대부분 빠르게 걸러집니다.
- google-services.json 존재/위치: 플러그인 가이드가 요구하는 경로에 실제로 들어갔는지(프로젝트 루트가 아니라 platforms/android/app 쪽을 요구하는 경우가 많습니다)
- Google Services Gradle Plugin 적용 위치: app 레벨(build.gradle)인지 프로젝트 레벨인지, Cordova-android 버전에 맞는 방식인지
- 버전 호환: cordova-android 버전 ↔ Android Gradle Plugin ↔ Google Services Plugin ↔ compileSdk/targetSdk 조합이 맞는지
- 중복 의존성: 다른 플러그인이 Firebase/Play-services를 또 끌고 오면서 충돌하는지
빌드 로그에서 “어느 build.gradle에서 터졌는지”가 나오므로, 해당 파일이 platforms/android 안의 어떤 위치인지 먼저 확인하세요. Cordova 버전에 따라 gradle 파일 구조가 조금씩 다르고, 플러그인이 기대하는 구조도 달라서 “적용 위치가 어긋난” 문제가 자주 생깁니다.
5) 해결 단계 C: 플러그인 레벨(plugin.xml/hooks)에서 OS 분기 규칙을 강제하기
팀/프로젝트가 커질수록 “사람이 config.xml을 잘 관리해주길 기대”하기가 어렵습니다. 이럴 때는 플러그인 레벨에서 분기 규칙을 강하게 두는 편이 안정적입니다.
- 플러그인에서 Android에만 필요한 파일은 <platform name="android"> 아래에만 배치
- iOS 전용 파일(예: GoogleService-Info.plist)은 <platform name="ios"> 아래에만 배치
- 후처리 스크립트(hook)를 쓴다면, 실행 시점에 platform 값을 확인하고 Android일 때만 gradle 수정/파일 복사를 수행
특히 “한 번은 빌드되고 한 번은 깨지는” 유형은, hook이 모든 플랫폼에서 돌아가면서 엉뚱한 파일을 만들거나 덮어쓰는 경우가 많습니다. 그런 경우 hook에서 OS 체크가 확실한지부터 보세요.
6) 재발 방지 체크리스트(빌드 전 2분 점검)
- config.xml에서 push 관련 설정이 플랫폼 블록 밖(공용)에 남아있지 않은가
- platforms/android를 직접 수정한 이력이 있다면, 재생성 시 사라져도 괜찮은가(필요하면 hook로 이동)
- google-services.json이 Android 쪽에만 들어가 있고, 경로가 맞는가
- 푸시/분석/로그 관련 플러그인이 여러 개일 때 Firebase 의존성이 중복되지 않는가
- CI에서 Android/iOS를 각각 빌드할 때, 환경변수/시크릿 파일이 플랫폼별로 분리되어 주입되는가
체크리스트를 통과했는데도 깨진다면, 마지막으로 “플러그인을 하나씩 비활성/제거해서” 충돌 지점을 좁히는 방식이 가장 빠릅니다(푸시는 의존성이 많아서 한 번에 맞추기가 어렵습니다).
마무리
푸시는 기능 자체보다도 “플랫폼별 설정을 깔끔하게 분리”하는 게 핵심이고, Android는 특히 gradle 단계에서 그 결과가 바로 드러납니다.
빌드 로그에서 터진 지점을 기준으로 config.xml → gradle(버전/적용 위치/파일 유무) → plugin/hook 분기 순서로 점검하면, 대부분은 큰 삽질 없이 정리됩니다.