- Published on
Flutter iOS 빌드 실패 - Podfile·CocoaPods 충돌 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서로 다른 팀원이 만든 Podfile, 플러그인 버전 업, Xcode 업데이트가 겹치면 Flutter iOS 빌드는 생각보다 쉽게 무너집니다. 특히 CocoaPods는 로컬 Ruby, Pod repo, Xcode 설정, iOS Deployment Target 같은 외부 요인의 영향을 크게 받기 때문에 “어제는 됐는데 오늘은 안 됨”이 자주 발생합니다.
이 글은 Flutter iOS 빌드 실패 중에서도 Podfile·CocoaPods 충돌로 발생하는 케이스를 중심으로, 원인 분류와 해결 루틴을 제공합니다. 핵심은 무작정 pod install을 반복하기보다, 상태를 초기화하고(캐시/잠금/워크스페이스), 충돌 지점을 좁히고, Podfile을 최소 변경으로 안정화하는 것입니다.
증상 패턴: 어떤 로그가 나오면 Podfile·Pods 문제인가
다음과 같은 메시지는 대체로 CocoaPods 또는 Podfile 레벨에서 충돌이 났다는 신호입니다.
CocoaPods could not find compatible versions for pod ...The sandbox is not in sync with the Podfile.lockSpecs satisfying the ... dependency were found, but they required a higher minimum deployment targetUnable to find a specification for ...Module 'XXX' not found(특정 플러그인/Pod만 누락)- Xcode에서
Runner타겟 빌드 중ld: library not found또는framework not found
이때 Flutter 쪽 로그(flutter build ios)만 보고 해결하려고 하면 시간이 늘어납니다. iOS 폴더에서 CocoaPods 상태를 먼저 정리하는 게 빠릅니다.
문제의 본질: Podfile, Podfile.lock, Pods 디렉터리의 “불일치”
CocoaPods는 크게 3가지 상태가 맞아야 안정적으로 동작합니다.
Podfile: 어떤 Pod를 어떤 방식으로 설치할지 선언Podfile.lock: 설치된 Pod의 확정 버전(재현성)Pods/및Runner.xcworkspace: 실제 설치 산출물
Flutter 플러그인 버전이 바뀌면 iOS 쪽 Pod 의존성도 바뀌는데, 이때 Podfile.lock이 과거 상태를 강제하거나, Pods/가 깨진 상태로 남아 있으면 충돌이 발생합니다.
1단계: “초기화 루틴”으로 상태를 깨끗하게 만든다
가장 먼저, iOS 폴더에서 아래 순서로 상태를 정리합니다.
flutter clean
rm -rf ios/Pods ios/Podfile.lock ios/Runner.xcworkspace
rm -rf ~/Library/Developer/Xcode/DerivedData
cd ios
pod deintegrate
pod repo update
pod install
cd ..
flutter pub get
flutter build ios
왜 이렇게 많이 지우나
Podfile.lock은 의존성 버전을 고정합니다. 플러그인 업데이트 후에도 예전 버전을 고정하고 있으면 충돌이 반복됩니다.Runner.xcworkspace는 Pods 통합 결과물입니다. 이게 오래된 상태면 Xcode가 잘못된 참조를 잡습니다.DerivedData는 Xcode 캐시입니다. 빌드 스크립트나 모듈 캐시가 꼬이면 “없는 모듈” 같은 오류가 남습니다.
팀 환경에서 재현되는 문제라면, 이 초기화 루틴을 먼저 수행해 “진짜 충돌”인지 “캐시 불일치”인지 분리하세요.
2단계: iOS Deployment Target 충돌 해결
가장 흔한 충돌은 특정 Pod가 더 높은 iOS 최소 버전을 요구하는 경우입니다.
로그 예시(부등호는 코드로 감쌈):
required a higher minimum deployment target
해결 방법: Podfile에서 platform 버전 상향
ios/Podfile 상단에 다음처럼 설정합니다.
platform :ios, '13.0'
그리고 Xcode에서도 Runner 타겟의 Deployment Target이 동일하거나 더 높아야 합니다.
- Xcode
Runner타겟 Build Settings또는General탭iOS Deployment Target
주의
- Flutter/플러그인 조합에 따라 iOS 12 지원이 끊긴 경우가 많습니다.
- 무조건 낮추려 하기보다, 플러그인 릴리즈 노트에서 최소 iOS 버전을 확인하는 편이 안전합니다.
3단계: use_frameworks! / use_modular_headers! 충돌
Firebase, gRPC, 일부 네이티브 SDK를 포함하면 use_frameworks! 설정 때문에 헤더/모듈 문제가 터지는 경우가 많습니다.
증상
- Swift Pod는 되는데 Objective-C Pod에서 헤더를 못 찾음
Module not found또는non-modular header inside framework module
대표적인 안정화 조합
Flutter 기본 템플릿은 대체로 정석 구성이지만, 프로젝트가 커지면 아래처럼 명시적으로 정리하는 게 도움이 됩니다.
# ios/Podfile
platform :ios, '13.0'
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "Generated.xcconfig must exist. Run flutter pub get"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
# 필요 시 한 가지 전략만 선택해서 적용하세요.
# use_frameworks!
# use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
실무 팁
use_frameworks!를 켜면 프레임워크 방식으로 링크되면서 일부 Pod의 빌드 방식이 바뀝니다. 필요하지 않다면 끄는 게 안정적입니다.- 반대로 Swift 기반 SDK를 반드시 써야 해서
use_frameworks!가 필요하다면, 특정 Pod만 예외 처리하는 구성이 필요할 수 있습니다(이 경우는 프로젝트마다 달라 최소 변경 원칙으로 접근).
4단계: Podfile.lock 충돌과 팀/CI 재현성
팀에서 흔한 상황은 다음입니다.
- A는
pod install로 정상 - B는 같은 브랜치인데
pod install에서 버전 충돌 - CI는 더 자주 실패
원인은 대부분 Podfile.lock 커밋 정책과 CocoaPods repo 상태 차이입니다.
권장 정책
- 앱 프로젝트는
Podfile.lock을 커밋해 버전 재현성을 확보 - 다만 Flutter 플러그인 업데이트로 iOS 의존성이 변했으면,
Podfile.lock도 함께 업데이트해서 커밋
CI에서 안정화 체크리스트
- macOS 러너의 CocoaPods 버전 고정(예: Bundler 사용)
pod repo update를 무조건 돌리기보다, 필요 시점에만 수행
CI 시간을 줄이면서도 재현성을 높이는 방법론은 병렬화/캐시 전략과도 연결됩니다. 빌드 파이프라인 최적화 관점은 GitHub Actions 매트릭스로 CI 시간 50% 줄이기도 함께 참고하면 좋습니다.
5단계: Apple Silicon(M1/M2) + Ruby/CocoaPods 환경 이슈
M 시리즈 맥에서 iOS 빌드가 유독 흔들리는 이유는, CocoaPods가 Ruby/네이티브 확장에 의존하고 아키텍처 차이(arm64, x86_64)가 개입하기 때문입니다.
증상 예시
ffigem 설치 실패pod install중 네이티브 확장 빌드 실패
대응 전략
- 시스템 Ruby 대신
rbenv또는asdf로 Ruby 버전 고정 - CocoaPods도
gem install cocoapods단독 설치보다 Bundler로 잠금
Gemfile 예시:
source 'https://rubygems.org'
gem 'cocoapods', '1.15.2'
설치/실행:
cd ios
bundle install
bundle exec pod install
이렇게 하면 팀/CI에서 CocoaPods 버전 차이로 인한 미묘한 충돌을 크게 줄일 수 있습니다.
6단계: Xcode 빌드 설정 충돌(특히 시뮬레이터 arm64)
일부 오래된 Pod는 시뮬레이터 arm64를 제대로 지원하지 못해 링크 에러가 납니다. 이때는 Runner 타겟이 아니라 Pods 프로젝트 설정을 조정해야 해결되는 경우가 있습니다.
post_install에서 시뮬레이터 arm64 제외 예시(필요할 때만 사용):
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
# Apple Silicon 시뮬레이터에서 특정 Pod가 깨질 때만 임시로 사용
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64'
end
end
end
주의
- 이 설정은 “근본 해결”이라기보다 호환성 우회입니다.
- Pod/SDK 업데이트로 해결 가능한지 먼저 확인하고, 마지막 수단으로 적용하세요.
7단계: 자주 나오는 에러별 빠른 처방전
The sandbox is not in sync with the Podfile.lock
cd ios
pod install
그래도 안 되면 초기화 루틴(Pods, lock, workspace 삭제)로 되돌립니다.
Unable to find a specification for ...
cd ios
pod repo update
pod install
사내 네트워크/프록시 환경이면 CocoaPods CDN 접근이 막혀 발생할 수도 있습니다.
CocoaPods could not find compatible versions for pod ...
- 플러그인 버전 업으로 요구 버전이 바뀐 경우가 많음
Podfile.lock삭제 후 재설치로 해결되는지 먼저 확인
rm -f ios/Podfile.lock
rm -rf ios/Pods ios/Runner.xcworkspace
cd ios
pod install
재발 방지: “Podfile은 건드리지 말고, 건드릴 땐 최소로”
Flutter iOS 빌드가 불안정한 팀의 공통점은 Podfile이 프로젝트마다 제각각 커스터마이징되어 있고, 누가 왜 바꿨는지 맥락이 없는 경우가 많습니다.
권장하는 운영 방식은 다음과 같습니다.
- Podfile 수정은 반드시 PR에 근거(왜 필요한지, 어떤 에러를 해결하는지) 남기기
post_install해킹(아키텍처 제외, 빌드 세팅 강제)은 주석과 함께 최소화- 플러그인 업데이트 시 iOS 최소 버전, Xcode 버전, CocoaPods 버전까지 함께 점검
협업 중 설정 충돌을 줄이는 관점에서는 Git 충돌 자동화도 도움이 됩니다. Podfile/lock 충돌이 잦다면 Git rebase 충돌 자동 해결 - rerere 실무 설정을 같이 적용해 반복 충돌 비용을 낮출 수 있습니다.
마무리 체크리스트
iOS 빌드가 깨졌을 때 아래 순서대로 확인하면, 대부분의 Podfile·CocoaPods 충돌은 빠르게 정리됩니다.
Pods,Podfile.lock,Runner.xcworkspace,DerivedData초기화 후 재설치platform :ios, 'XX.X'와 Xcode Deployment Target 일치use_frameworks!/use_modular_headers!도입 여부 점검(필요 최소)- CocoaPods/Ruby 버전 고정(Bundler 권장)
- Apple Silicon 시뮬레이터 아키텍처 문제는 최후에
EXCLUDED_ARCHS로 우회
이 루틴을 팀 문서로 고정해두면, “환경 문제인지 코드 문제인지”를 빠르게 분리할 수 있고, Flutter 플러그인 업데이트 주기에도 iOS 빌드 안정성을 유지하기 쉬워집니다.