Android에서 앱 아이콘만 바꿨을 뿐인데 Cordova 빌드가 갑자기 실패하는 경우가 꽤 있습니다. 이때는 “아이콘 작업” 자체보다, 그 결과로 생성/복사된 리소스가 Android 리소스 규칙을 어기거나(이름, 밀도, 포맷), Adaptive Icon 설정과 엇갈리거나, 캐시/중복 리소스 때문에 aapt2가 막히는 패턴이 많습니다.

Broken app icon metaphor with Android and gear symbols

아래는 빌드 실패 로그부터 원인을 좁혀 가장 흔한 케이스를 빠르게 해결하는 순서입니다.

1) 먼저 실패 지점을 로그에서 고정하기(어느 작업에서 깨졌나)

같은 “Gradle 빌드 실패”라도 실패 지점이 다르면 원인도 달라집니다. Cordova로 빌드할 때는 우선 로그를 조금 더 자세히 뽑아 “어느 task에서 실패했는지”부터 고정하세요.

  • 권장: cordova build android --verbose
  • 추가로 필요하면: --stacktrace --info (CI라면 로그 길이가 늘어나는 점만 주의)

아이콘/리소스 계열은 대개 아래 작업에서 터집니다.

  • :app:processDebugResources / :app:processReleaseResources
  • :app:mergeDebugResources
  • 로그에 aapt2, resource, Android resource linking failed가 함께 등장

여기까지 확인했으면, 이제 메시지에서 “무슨 리소스”를 지목하는지(파일 경로/리소스명/라인)를 잡습니다. 대부분은 실패 메시지에 문제 파일 경로가 박혀 있습니다.

2) 흔한 실패 로그 패턴 5가지(아이콘 변경 직후 특히 자주)

Magnifying glass over log document and resource folder icons

  • Android resource linking failed: 리소스 참조가 깨졌거나(없음), XML이 잘못되었거나, Adaptive Icon 관련 설정이 어긋난 경우가 많습니다.
  • error: file name must contain only [a-z0-9_.]: 아이콘 파일명에 대문자/하이픈/공백이 섞인 케이스가 매우 흔합니다.
  • duplicate resources / Duplicate resources: 같은 이름의 아이콘이 여러 폴더(또는 다른 플러그인/스크립트)에서 동시에 들어온 경우입니다.
  • PNG crunching / error: failed to read PNG signature: 손상된 PNG, 잘못 저장된 PNG(특히 색상 프로파일/인터레이스 등)로 터질 수 있습니다.
  • adaptive icon 관련: ic_launcher.xml이 참조하는 foreground/background 리소스가 없거나 타입이 안 맞는 경우입니다.

이제부터는 이 패턴에 맞춰 “확인 지점 → 조치”로 이동하면 됩니다.

3) 해결 단계 A: 리소스 파일명/경로 규칙부터 정리(가장 빠른 승부처)

아이콘 리소스는 대부분 platforms/android/app/src/main/res/ 아래에 들어갑니다. 먼저 이 경로에서 아래를 점검하세요.

  • 파일명 규칙: 소문자/숫자/언더스코어만 사용(예: ic_launcher.png). 대문자, 공백, 하이픈이 있으면 바로 실패합니다.
  • mipmap/drawable 위치: 런처 아이콘은 보통 mipmap-*에 있어야 합니다. 작업 도중 drawable로 섞여 들어가면 참조가 꼬일 수 있습니다.
  • 밀도 폴더 정합성: mipmap-hdpi/xhdpi/xxhdpi/xxxhdpi 등 여러 폴더에 같은 이름이 있어야 하는데 일부만 있거나, 다른 포맷이 섞이면 경고/오류로 이어질 수 있습니다.
  • 중복 파일: 같은 이름의 아이콘이 mipmap과 drawable 양쪽에 있거나, 다른 폴더에 중복되면 Duplicate resources가 납니다.

파일명/위치가 정리되면, 다음은 “Adaptive Icon(안드로이드 8.0+)”이 맞는지 확인합니다.

4) 해결 단계 B: Adaptive Icon(아이콘 XML)과 실제 리소스 매칭 확인

Layered adaptive icon concept with foreground and background symbols

Android 8.0(Oreo)부터는 런처 아이콘이 Adaptive Icon 구조로 구성되는 경우가 많습니다. 이때 보통 mipmap-anydpi-v26/ic_launcher.xml 같은 파일이 있고, 그 안에서 foreground/background를 참조합니다.

빌드 로그에 ic_launcher.xml 또는 anydpi-v26 폴더가 언급되면 아래를 확인하세요.

  • ic_launcher.xml이 참조하는 리소스 존재 여부: 예) @mipmap/ic_launcher_foreground, @color/ic_launcher_background가 실제로 존재하는지
  • 리소스 타입 일치: foreground를 mipmap으로 참조해놓고 실제는 drawable에 있는 등 타입/위치 불일치
  • v26 폴더만 남고 구형 mipmap이 없는 상태: 일부 런처/디바이스 조합에서 문제가 되거나, build variant에 따라 참조가 갈릴 수 있습니다.

특히 “아이콘 생성 도구”가 만들어준 결과물을 그대로 넣었을 때, foreground/background 파일명에 대문자가 섞이거나(예: IcLauncherForeground.png), 파일이 한 폴더에만 생성되는 경우가 있어 빌드가 깨질 수 있습니다.

5) 해결 단계 C: Cordova config.xml 아이콘 정의와 실제 생성물 충돌 정리

Cordova는 config.xml<icon> 정의를 보고 플랫폼별 리소스를 생성/복사합니다. 그런데 수동으로 platforms/android의 res를 만지거나, 아이콘 플러그인/스크립트가 추가로 개입하면 충돌이 생깁니다.

  • 원칙: 가능하면 config.xml만을 소스로 삼고, platforms/android 아래는 “생성 결과물”로 취급합니다.
  • 아이콘을 여러 경로로 동시에 넣고 있지 않은지 확인(예: config.xml + 별도 복사 스크립트 + resources/android/icon/ 폴더)
  • 빌드 variant(디버그/릴리즈)마다 다른 아이콘을 넣는 커스텀 로직이 있다면, 특정 variant에서만 리소스가 누락될 수 있습니다.

로그에서 mergeReleaseResources만 실패한다면, 릴리즈에서만 적용되는 설정/스크립트가 있는지 같이 의심해보는 게 좋습니다.

6) 해결 단계 D: 캐시/생성물 초기화 체크리스트(마지막에 한 번에 정리)

아이콘 변경은 “파일이 바뀐 것”인데도 Gradle 캐시/증분 빌드 때문에 이전 리소스가 남아 충돌하는 경우가 있습니다. 아래 체크리스트를 순서대로 적용하면, 원인을 깨끗하게 분리하기가 쉽습니다.

  • platforms/android를 직접 수정했다면: 해당 수정 이력은 버리고, 설정은 config.xml 쪽으로 옮길지 결정
  • cordova platform rm androidcordova platform add android로 재생성(아이콘도 다시 반영)
  • 빌드 폴더 정리: platforms/android/app/build 삭제 후 재빌드
  • 그래도 이상하면 Gradle 캐시 영향 확인(팀/CI 환경에서만 재현된다면 특히): 동일 환경에서 깨끗한 워크스페이스로 재현되는지 체크
  • 마지막으로 같은 리소스명이 다른 위치에 중복되어 있지 않은지 전체 검색

중요한 건 “정리를 했는데도 실패”가 아니라, 정리 후 실패 로그가 어떻게 바뀌는지입니다. 로그가 바뀌면 원인이 좁혀진 겁니다(예: duplicate → missing resource처럼).

마무리

아이콘 변경 후 Gradle 빌드 실패는 대부분 aapt2 리소스 처리 단계에서 힌트를 줍니다. 실패한 task와 지목된 파일 경로를 먼저 잡고, 파일명 규칙/중복/Adaptive Icon 매칭/생성물 초기화 순으로 처리하면 시간을 크게 줄일 수 있습니다.

그래도 해결이 안 되면, 실패 로그에서 딱 한 줄(처음으로 resource 경로가 등장하는 지점)을 기준으로 문제 파일을 특정해보세요. 그 한 줄이 거의 항상 답을 들고 있습니다.