- Published on
Flutter iOS 빌드 실패? CocoaPods Podfile 7가지
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서론
Flutter로 iOS 빌드를 하다 보면 pod install 단계에서 멈추거나, Xcode에서 아카이브가 실패하거나, 시뮬레이터만 되고 실기기에서만 죽는 식의 “재현성 낮은” 문제가 종종 발생합니다. 이때 로그를 끝까지 읽어보면 대부분 CocoaPods/Podfile 쪽 설정 충돌로 귀결됩니다.
이 글에서는 Flutter iOS 빌드 실패를 유발하는 Podfile 관련 7가지 대표 원인을 “증상 → 원인 → 해결” 형태로 정리합니다. 단순히 pod deintegrate 같은 처방만 나열하지 않고, 왜 문제가 생기는지와 어떤 설정이 안전한지를 기준으로 설명합니다.
장애를 디버깅할 때는 체크리스트가 큰 도움이 됩니다. 비슷한 방식으로 원인을 좁혀가는 접근이 필요하다면, 인프라 장애 체크리스트 스타일의 글도 참고해보세요: K8s ImagePullBackOff·ErrImagePull 원인 12가지
0) 먼저 확인할 기본 전제(환경/정리 커맨드)
아래는 Podfile을 건드리기 전에 “캐시/락/파생 파일” 때문에 생기는 가짜 오류를 제거하는 최소 루틴입니다.
# 프로젝트 루트
flutter clean
rm -rf ios/Pods ios/Podfile.lock ios/.symlinks
# CocoaPods 캐시가 꼬인 경우(필요할 때만)
pod cache clean --all
cd ios
pod repo update
pod install --verbose
그리고 Xcode에서 빌드한다면, 반드시 Runner.xcworkspace를 열어야 합니다.
1) iOS Deployment Target 불일치(가장 흔함)
증상
Specs satisfying the ... were found, but they required a higher minimum deployment target.- 특정 플러그인(예: Firebase, GoogleMaps, Sentry 등) 추가 후 갑자기
pod install실패
원인
Pod가 요구하는 최소 iOS 버전과, Podfile/프로젝트 설정의 platform :ios, '...' 값이 다릅니다. Flutter 프로젝트는 오래된 템플릿에서 생성된 경우 11.0 같은 값이 남아 있는 경우가 많습니다.
해결
Podfile에서 platform을 올리고, Xcode 프로젝트의 Deployment Target도 일치시킵니다.
# ios/Podfile
platform :ios, '13.0'
# Flutter 기본 include
require File.expand_path(File.join('..', 'Flutter', 'podhelper'), __FILE__)
추가로, Runner 타겟과 Pods-Runner 타겟이 서로 다른 iOS 버전을 가리키면 또 꼬일 수 있으니 Xcode의 Build Settings > iOS Deployment Target도 확인하세요.
2) use_frameworks! / use_modular_headers! 충돌
증상
Swift pods cannot yet be integrated as static libraries류 오류Include of non-modular header inside framework module오류- 특정 Pod만 추가하면 컴파일 에러가 폭발
원인
CocoaPods는 크게 “정적 라이브러리 기반 통합”과 “framework 기반 통합”에서 헤더 처리 방식이 달라집니다.
use_frameworks!를 켜면 Pod들이 framework로 통합되며, Swift/ObjC 혼합 환경에서 헤더 모듈화 문제가 발생할 수 있습니다.use_modular_headers!는 모듈 헤더로 강제하여 일부 비모듈 헤더 문제를 줄이지만, 반대로 특정 Pod에서는 문제가 될 수 있습니다.
Flutter 플러그인 조합에 따라 정답이 달라져서, “무조건 켜라/끄라”가 아니라 필요한 경우에만 최소 옵션으로 가는 것이 안정적입니다.
해결 가이드
- 특별한 이유가 없다면
use_frameworks!를 먼저 제거하고 시도합니다. Firebase등에서 요구하거나 Swift Pod 통합 이슈가 있으면use_frameworks! :linkage => :static을 검토합니다.
target 'Runner' do
# 가능하면 기본(미사용) 유지
# use_frameworks!
# Swift/특정 SDK가 필요할 때만
# use_frameworks! :linkage => :static
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
use_frameworks!를 켠 뒤 갑자기 빌드가 깨졌다면, 가장 먼저 되돌려서 원인 범위를 줄이세요.
3) post_install 훅에서 Flutter 기본 설정을 덮어써서 실패
증상
Undefined symbols for architecture arm64(특정 플러그인 심볼 미해결)ld: framework not found또는Module not found류- Debug는 되는데 Archive만 실패
원인
Podfile의 post_install에서 EXCLUDED_ARCHS, OTHER_LDFLAGS, SWIFT_VERSION 등을 일괄 변경하면서 Flutter가 생성한 설정과 충돌합니다. 특히 인터넷에서 복붙한 스니펫이 Runner/Pods 타겟 전체에 과격하게 적용되는 경우가 많습니다.
해결
Flutter 권장 훅 호출을 유지하고, 필요한 설정만 “조건부”로 최소 적용합니다.
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
# 예: Apple Silicon 시뮬레이터에서만 임시 회피가 필요할 때
target.build_configurations.each do |config|
if config.name == 'Debug'
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64'
end
end
end
end
핵심은 flutter_additional_ios_build_settings(target)를 제거/누락하지 않는 것입니다. 이 호출이 빠지면 Flutter 엔진/플러그인 쪽 링크 설정이 깨지는 경우가 많습니다.
4) Podfile.lock/Pods 캐시 불일치로 “내 컴퓨터만” 깨짐
증상
- 팀원은 되는데 내 로컬만
pod install이 다르게 동작 - CI에서는 되는데 로컬에서만
Checksum/Version관련 오류 The sandbox is not in sync with the Podfile.lock경고 후 빌드 실패
원인
CocoaPods는 Podfile.lock과 Pods/ 디렉터리 상태를 강하게 결합합니다. flutter pub get으로 플러그인 버전이 바뀌었는데 lock/Pods가 예전 상태면 샌드박스가 불일치합니다.
해결
일관된 재설치를 수행합니다.
cd ios
rm -rf Pods Podfile.lock
pod install
팀 프로젝트라면 Podfile.lock을 커밋하는 정책이 일반적입니다(재현성). 다만 플러그인 업데이트 시점에 lock을 함께 갱신해야 합니다.
5) CocoaPods 소스(repo) 설정/네트워크 이슈로 설치 실패
증상
Unable to find a specification for ...CDN: trunk Repo update failed/SSL/timed out류
원인
사내 프록시/방화벽, 오래된 CocoaPods, 혹은 source가 잘못 지정된 Podfile로 인해 trunk CDN 접근이 불안정해집니다. 또한 pod repo update가 안 된 상태에서 신규 버전 Pod를 찾지 못하기도 합니다.
해결
- CocoaPods 업데이트
- repo 업데이트
- Podfile에 불필요한
source를 남발하지 않기
sudo gem install cocoapods
pod repo update
Podfile에 특정 사설 spec repo를 써야 한다면, trunk와 함께 명시하되 순서/접근성을 점검하세요.
6) Xcode/Swift 버전과 Pod의 Swift 설정 불일치
증상
Swift compiler error가 Pods 타겟에서 발생Building for iOS Simulator, but linking in object file built for iOS같은 혼종 오류
원인
Xcode를 올렸는데(예: Xcode 15/16), 일부 Pod가 요구하는 Swift 버전/빌드 설정과 충돌할 수 있습니다. 특히 오래된 Pod가 SWIFT_VERSION을 명시하지 않거나, 프로젝트가 강제로 낮은 Swift 버전을 사용하도록 설정되어 있으면 문제가 커집니다.
해결
Podfile post_install에서 필요한 경우에만 Swift 버전을 통일합니다(무조건 덮어쓰기는 위험).
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
# 특정 Pod만 예외 처리하는 방식이 더 안전
if target.name.start_with?('SomeLegacyPod')
config.build_settings['SWIFT_VERSION'] = '5.0'
end
end
end
end
또한 Xcode에서 Build Settings > Build Libraries for Distribution 같은 옵션이 켜져 있으면 일부 Pod에서 ABI/모듈 이슈가 날 수 있으니, 문제 발생 시 해당 옵션도 점검하세요.
7) 아키텍처/시뮬레이터(Apple Silicon) 관련 설정이 Podfile에 하드코딩됨
증상
- M1/M2/M3에서 시뮬레이터 빌드만 실패
ld: building for iOS Simulator, but linking in dylib built for iOS또는arm64관련 오류
원인
과거에는 Apple Silicon 전환 초기에 EXCLUDED_ARCHS를 Podfile에 하드코딩해 회피하는 경우가 많았습니다. 시간이 지나면서 많은 Pod가 정상 지원을 시작했는데도, 예전 회피 코드가 남아 오히려 현재 환경에서 문제를 일으킵니다.
해결
- 가능한 한 최신 Pod/플러그인 버전으로 올리고
EXCLUDED_ARCHS는 “필요한 경우 + Debug 시뮬레이터 한정”으로 최소화합니다.
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
# 최후의 수단: Debug + Simulator에만 제한
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64' if config.name == 'Debug'
end
end
end
만약 Release/Archive까지 이 설정이 들어가면, App Store 제출용 바이너리에서 예상치 못한 링크 문제가 생길 수 있으니 적용 범위를 좁히는 것이 중요합니다.
실전: “Podfile 안전 템플릿” 예시
아래는 Flutter iOS에서 비교적 안전하게 시작할 수 있는 Podfile 형태입니다. 플러그인 요구사항이 생길 때마다 옵션을 추가하되, 전역 강제는 피하는 방향입니다.
platform :ios, '13.0'
# CocoaPods 통계 비활성화(빌드 약간 빨라짐)
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
require File.expand_path(File.join('..', 'Flutter', 'podhelper'), __FILE__)
flutter_ios_podfile_setup
target 'Runner' do
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
# 필요한 경우에만 최소한으로 추가
target.build_configurations.each do |config|
if config.name == 'Debug'
# Apple Silicon 시뮬레이터 이슈가 있을 때만 사용
# config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64'
end
end
end
end
로그를 “원인별로” 읽는 팁
Podfile 문제는 로그가 길어서 감으로 때려 맞히기 쉽습니다. 다음처럼 분류하면 훨씬 빨리 좁혀집니다.
- pod install 단계에서 실패: iOS 최소 버전, spec repo, 네트워크, Pod 간 의존성 충돌 가능성이 큼
- Xcode 컴파일 단계에서 실패: 헤더 모듈화(
use_frameworks!/use_modular_headers!), Swift 버전, 빌드 세팅 덮어쓰기(post_install) 가능성이 큼 - Archive에서만 실패: Release 설정/Bitcode/서명/링커 플래그/
EXCLUDED_ARCHS범위 문제 가능성이 큼
이런 식의 “체크리스트 기반 디버깅”은 다른 분야에서도 동일하게 통합니다. 문제를 빠르게 재현/격리하는 관점은 다음 글의 접근과도 유사합니다: Assistants API v2 run이 queued나 in_progress에 멈출 때 실전 디버깅 체크리스트
결론
Flutter iOS 빌드 실패는 겉으로는 Xcode 오류처럼 보이지만, 실제로는 **Podfile의 작은 선택(플랫폼 버전, framework 통합 방식, post_install 훅, 아키텍처 예외 처리)**이 누적되어 터지는 경우가 많습니다.
정리하면 다음 순서가 가장 효율적입니다.
platform :ios와 Xcode Deployment Target 정합성 확보use_frameworks!/use_modular_headers!는 “필요할 때만”post_install은 Flutter 기본 설정을 유지한 채 최소 변경- lock/Pods 불일치면 과감히 재설치
- Apple Silicon 예외처리는 Debug+Simulator로 제한
위 7가지를 체크해도 해결이 안 된다면, 그때는 특정 Pod/플러그인 이름과 에러 로그의 “첫 번째 실패 지점”을 기준으로 더 좁혀야 합니다(대부분 첫 에러가 진짜 원인입니다).