- Published on
Flutter iOS 빌드 실패 - CocoaPods 충돌 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
Flutter로 iOS를 빌드하다 보면 어느 날 갑자기 pod install 단계에서 깨지거나, Xcode 아카이브에서만 실패하는 식의 문제가 반복됩니다. 대부분의 경우 원인은 CocoaPods 의존성 해석 결과가 프로젝트 상태와 어긋나면서 발생하는 Pod 충돌입니다. 특히 Flutter 플러그인은 iOS 네이티브 의존성을 함께 끌고 오기 때문에, 플러그인 업데이트나 Xcode/SDK 업그레이드 이후 잠복해 있던 불일치가 한 번에 터지곤 합니다.
이 글에서는 현장에서 가장 자주 만나는 충돌 패턴을 기준으로, 원인 진단 → 재현 가능한 정리(clean) → Podfile 조정 → 버전 고정/업데이트 전략 순서로 해결법을 정리합니다. Flutter 성능/빌드 파이프라인 전반 최적화 관점은 Dart로 Flutter 성능 최적화하는 실전 패턴도 함께 참고하면 좋습니다.
1) 증상으로 빠르게 분류하기
CocoaPods 충돌은 로그가 길고 산만하지만, 메시지 패턴이 반복됩니다.
A. CocoaPods could not find compatible versions for pod ...
- 특정 Pod 버전 제약이 서로 충돌
- 예:
Firebase/CoreOnly는FirebaseCore특정 버전 필요, 다른 플러그인이 다른 버전 요구
B. The sandbox is not in sync with the Podfile.lock
Pods/혹은Podfile.lock와 실제 설치 상태가 불일치- 대개 캐시/중간 산출물 꼬임
C. Specs satisfying the ... dependency were found, but they required a higher minimum deployment target
- iOS Deployment Target이 낮아서 Pod가 요구하는 최소 버전을 만족 못함
D. Apple Silicon에서 building for iOS Simulator, but linking in object file built for iOS
- 시뮬레이터 아키텍처(
arm64/x86_64)와 바이너리 아키텍처 불일치
이제부터는 “무조건 지우고 다시 설치”가 아니라, 무엇을 지우고 무엇을 고정해야 하는지를 단계별로 설명합니다.
2) 가장 안전한 표준 정리 절차(재현 가능한 clean)
아래는 충돌을 해결할 때 가장 먼저 수행하는 표준 루틴입니다. 핵심은 Flutter 산출물과 CocoaPods 산출물을 서로 다른 레이어로 보고 각각 정리하는 것입니다.
2.1 Flutter 레이어 정리
flutter clean
rm -rf .dart_tool
rm -rf build
flutter pub get
2.2 iOS Pods 레이어 정리
ios 디렉터리에서 실행합니다.
cd ios
rm -rf Pods
rm -f Podfile.lock
rm -rf ~/Library/Caches/CocoaPods
rm -rf ~/.cocoapods
pod repo update
pod install --verbose
Podfile.lock를 지우는 이유: 현재 상태에서 의존성 해석을 다시 하도록 강제CocoaPods캐시 제거는 시간이 들지만, “예전 spec/아티팩트가 남아 충돌”하는 케이스에 효과가 큽니다.
2.3 Xcode DerivedData 정리(아카이브만 실패할 때 특히 중요)
rm -rf ~/Library/Developer/Xcode/DerivedData
이 단계까지만으로도 sandbox not in sync 류의 문제는 상당수 해결됩니다. 그러나 버전 제약 충돌은 Podfile/플러그인 버전 정책을 손봐야 합니다.
3) Podfile에서 충돌을 줄이는 기본 설정
Flutter 기본 Podfile을 그대로 쓰다가, Xcode/SDK 업그레이드 이후 충돌이 늘어나는 경우가 많습니다. 아래는 실무에서 자주 쓰는 “안전한 기본값”입니다.
3.1 iOS Deployment Target 올리기
로그에 required a higher minimum deployment target가 보이면 가장 먼저 iOS 타겟부터 맞춥니다.
ios/Podfile 상단을 점검하세요.
platform :ios, '13.0'
- iOS 12 이하를 유지해야 하는 요구가 없다면, 최신 플러그인 생태계에서는
13.0이상이 충돌을 크게 줄입니다. - 플러그인이 iOS 14 이상을 요구하는 경우도 있으니, 실제 에러 메시지에서 요구 버전을 확인해 상향합니다.
3.2 use_frameworks! / use_modular_headers! 충돌
일부 Pod는 정적 라이브러리/동적 프레임워크 조건에 민감합니다. Flutter 플러그인 조합에 따라 다음과 같은 선택이 필요합니다.
- Swift 기반 Pod가 많고, 동적 프레임워크가 필요하면
use_frameworks!가 유리 - 헤더 모듈화 충돌이 있으면
use_modular_headers!가 도움이 될 때가 있음
예시(상황에 맞게 하나씩 적용하며 검증):
use_frameworks! :linkage => :static
use_modular_headers!
주의: 플러그인/Pod 조합에 따라 오히려 다른 충돌을 만들 수 있으니, 적용 후 pod install 로그를 기준으로 판단해야 합니다.
4) 버전 충돌의 핵심: Podfile.lock과 플러그인 버전 정책
4.1 Podfile.lock은 “재현성”을 위한 스냅샷
- CI/팀 개발에서
Podfile.lock을 커밋하는 경우가 많습니다. - 하지만 플러그인 버전을 올렸는데
Podfile.lock이 예전 Pod 버전을 강제하면,compatible versions충돌이 발생합니다.
해결 전략은 둘 중 하나입니다.
- 플러그인 업데이트 후
Podfile.lock도 함께 갱신
- 위의 정리 절차처럼
Podfile.lock삭제 후 재생성
- Pod 버전을 명시적으로 고정
- 특정 Pod가 깨지는 버전이 있고, 당장 플러그인 업그레이드가 어렵다면 임시로 고정
예시:
target 'Runner' do
# 예: 특정 버전에서만 빌드가 통과하는 경우 임시 고정
pod 'FirebaseCore', '10.25.0'
end
고정은 단기 처방입니다. 장기적으로는 플러그인/SDK 조합을 업데이트해 고정 해제를 목표로 하세요.
4.2 pod repo update vs pod install --repo-update
로컬 spec repo가 오래되어서 최신 Pod 버전을 못 찾는 경우가 있습니다.
cd ios
pod install --repo-update
- 팀마다
CocoaPods CDN설정/네트워크 환경이 달라 재현이 어려운 문제를 줄여줍니다.
5) Apple Silicon(arm64) 시뮬레이터 충돌 해결
M1/M2/M3 환경에서 특히 흔한 에러가 “시뮬레이터로 빌드하는데 디바이스용 바이너리를 링크했다”입니다. 원인은 크게 두 가지입니다.
- 오래된 바이너리 Pod가 시뮬레이터
arm64를 지원하지 않음 - 빌드 설정이 시뮬레이터 아키텍처를 잘못 제외/포함함
5.1 임시로 시뮬레이터에서 arm64 제외(레거시 Pod 대응)
Podfile의 post_install에 다음을 추가합니다.
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
# Apple Silicon 시뮬레이터에서 문제가 나는 레거시 Pod 대응
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64'
end
end
end
- 이 설정은 “시뮬레이터에서만”
arm64를 제외합니다. - 장기적으로는 해당 Pod/플러그인을 업데이트해
arm64지원 버전으로 올리는 것이 정석입니다.
5.2 Rosetta로 Xcode 실행은 최후의 수단
Intel 전용 바이너리가 섞여 있고 당장 업데이트가 불가능할 때만 고려합니다. 프로젝트가 커질수록 부작용(빌드 시간 증가, 환경 불일치)이 커집니다.
6) Xcode/Flutter 버전 업 이후 자주 터지는 케이스
6.1 Pods-Runner 관련 오류, 헤더/모듈 에러
- Xcode 업데이트로 컴파일러 설정이 바뀌면서, 이전에는 경고였던 것이 에러가 되는 경우가 있습니다.
- 이때는
ios/Runner.xcworkspace로 열었는지부터 확인하세요.Runner.xcodeproj로 열면 Pods 설정이 반영되지 않아 이상한 에러가 연쇄적으로 발생합니다.
6.2 Flutter.framework/Generated.xcconfig 관련
Flutter iOS 프로젝트는 Generated.xcconfig가 꼬이면 연쇄 실패가 납니다. 다음을 재생성합니다.
flutter pub get
cd ios
pod install
그래도 꼬이면 ios/Flutter/Generated.xcconfig가 git에 의해 잘못 관리되고 있지 않은지 확인하세요(보통은 자동 생성 파일).
7) CI에서만 발생하는 CocoaPods 충돌 줄이기
로컬에서는 되는데 CI에서만 실패한다면, “캐시가 중간 상태를 저장”했을 가능성이 큽니다. 특히 Pods/를 캐시할 때 문제가 자주 생깁니다.
- 권장:
~/.cocoapods전체를 캐시하기보다는,Podfile.lock기반으로 재설치하는 편이 안전합니다. - 캐시 점검 관점은 GitHub Actions 캐시가 안 먹을 때 키·경로 9분 점검에서 소개한 방식(키 설계, 경로 검증, 무효화 전략)을 그대로 적용할 수 있습니다.
간단한 CI 스크립트 예시:
flutter clean
flutter pub get
cd ios
pod install --repo-update
8) 최종 체크리스트(문제 재발 방지)
ios/Podfile의platform :ios가 플러그인 요구사항을 만족하는가Runner.xcworkspace로 열고 빌드하는가- 플러그인 업데이트 시
Podfile.lock갱신 정책이 팀에 합의되어 있는가 - Apple Silicon 시뮬레이터 아키텍처 이슈는
EXCLUDED_ARCHS로 임시 완화했는가(가능하면 업데이트로 제거) - CI 캐시가
Pods/중간 상태를 저장하고 있지 않은가
9) 자주 묻는 질문
Q1. Podfile.lock을 항상 지워도 되나?
단기적으로는 해결이 빠르지만, 팀/CI 재현성을 해칠 수 있습니다. 권장은 “충돌 해결 시에는 지우고 재생성”하되, 정상 상태에서는 Podfile.lock을 커밋해 재현성을 유지하는 방식입니다.
Q2. 충돌이 특정 플러그인 하나에서만 나면?
그 플러그인이 끌어오는 iOS Pod의 최소 iOS 버전, 혹은 특정 Pod 버전 제약이 원인인 경우가 많습니다. 플러그인 릴리즈 노트에서 iOS 최소 버전 변경 여부를 확인하고, 필요하면 플러그인을 한 버전 내리거나(임시), iOS 타겟을 올리세요.
CocoaPods 충돌은 “한 번 해결하면 끝”이 아니라, 플러그인/SDK/Xcode가 움직일 때마다 다시 나타날 수 있는 구성 관리 문제입니다. 위의 표준 정리 절차와 Podfile 기본 설정을 템플릿처럼 유지해두면, 원인 파악과 복구 시간이 크게 줄어듭니다.