- Published on
Flutter iOS 빌드 실패 - Podfile.lock 충돌 해결 가이드
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서론
Flutter로 iOS 빌드를 하다 보면 어느 날 갑자기 pod install 단계에서 멈추거나, Xcode 빌드가 CocoaPods could not find compatible versions 류의 메시지로 실패하는 경우가 있습니다. 특히 팀 개발/CI 환경에서 자주 등장하는 원인이 Podfile.lock 충돌입니다. 겉으로는 “pods 버전이 안 맞는다”처럼 보이지만, 실제로는 Flutter 플러그인 버전, CocoaPods 리포 스펙, Ruby/CocoaPods 버전, iOS deployment target, Xcode/SDK가 얽히면서 Podfile.lock이 일관성을 잃는 케이스가 많습니다.
이 글에서는 Podfile.lock이 무엇을 잠그는지부터, 충돌이 생기는 대표 패턴, 그리고 로컬/CI에서 안전하게 해결하는 절차와 재발 방지 체크리스트까지 정리합니다. iOS 서명/코드사인 이슈까지 함께 겪고 있다면 기존 글인 Flutter iOS 빌드 실패? Signing·CocoaPods 10분 해결도 같이 보면 문제 범위를 빠르게 좁힐 수 있습니다.
Podfile.lock 충돌이란 무엇인가
CocoaPods에서 Podfile.lock은 현재 프로젝트가 의존하는 Pod들의 정확한 버전과 해시(체크섬), spec repo 정보를 고정합니다. 즉, 같은 Podfile이라도 Podfile.lock이 다르면 설치되는 Pod 버전이 달라질 수 있고, 반대로 Podfile.lock은 “이 버전으로 설치해야 한다”는 강한 제약이 됩니다.
Flutter iOS 프로젝트는 보통 ios/Podfile에서 flutter_install_all_ios_pods(또는 유사 스크립트)를 통해 플러그인 Pod들을 동적으로 구성합니다. 이때 다음이 바뀌면 Podfile.lock과 현실이 어긋나기 쉽습니다.
- Flutter 플러그인 버전 변경(예: Firebase 계열, permission_handler 등)
- iOS 최소 버전 변경(예: iOS 11 → 12)
- Xcode 업그레이드로 인한 SDK 변화
- CocoaPods 버전 차이(1.11.x vs 1.15.x)
- Spec repo 업데이트 여부(
pod repo update) use_frameworks!,use_modular_headers!같은 설정 변경
결과적으로 pod install이 다음처럼 실패합니다.
CocoaPods could not find compatible versions for pod "XYZ"The dependency 'ABC' is not used in any concrete target.Specs satisfying the 'XYZ (= 1.2.3)' dependency were found, but they required a higher minimum deployment target.- CI에서만 실패(로컬에서는 성공): lockfile이 특정 머신의 CocoaPods/Repo 상태에 종속됨
가장 흔한 충돌 시나리오 5가지
1) 플러그인 업데이트로 Pod 버전 제약이 바뀜
pubspec.lock에서 플러그인이 업데이트되면, iOS 쪽 Pod 의존성이 바뀌는 경우가 많습니다. 그런데 팀원이 기존 Podfile.lock을 그대로 커밋해두면 다른 개발자/CI가 pod install 시 lockfile에 맞추려다가 실패합니다.
2) iOS deployment target이 낮아서 lockfile 요구를 못 맞춤
예를 들어 lockfile은 Firebase/CoreOnly 10.x를 잡고 있는데, 해당 버전이 iOS 12 이상을 요구하면 iOS 11 타겟에서는 충돌이 납니다.
3) CocoaPods 버전 차이로 해석이 달라짐
CocoaPods는 버전이 올라가며 resolver가 달라지고, Podfile.lock 포맷/체크섬 처리도 영향을 받을 수 있습니다. 로컬은 1.15, CI는 1.11이면 “같은 lockfile”이더라도 설치 결과가 달라질 수 있습니다.
4) Spec repo가 오래되어 원하는 Pod 버전을 못 찾음
pod install은 spec repo에서 버전을 찾습니다. repo가 오래되면 lockfile이 요구하는 버전이 있어도 “없다”고 판단합니다.
5) 멀티 타겟/Runner 설정 변경으로 lockfile이 꼬임
Runner 외에 Notification Service Extension 등을 추가하면, Pod 타겟 구성이 바뀌고 lockfile이 재생성되어야 합니다.
빠른 진단: 무엇이 충돌하는지 확인하는 법
아래 명령으로 어떤 Pod가 문제인지 먼저 좁힙니다.
cd ios
pod install --verbose
그리고 lockfile과 Podfile 설정을 함께 확인합니다.
ios/Podfile.lock에서 문제 Pod의 버전이 무엇인지ios/Podfile에서 iOS 최소버전/프레임워크 설정이 무엇인지pubspec.lock에서 플러그인 버전이 최근 바뀌었는지
추가로 CocoaPods 환경 자체를 확인합니다.
pod --version
ruby -v
xcodebuild -version
CI에서만 재현된다면, CI에서 위 버전들이 로컬과 동일한지 비교하는 것이 1순위입니다.
해결 전략: “잠금(lock)”을 어디까지 신뢰할 것인가
Podfile.lock 충돌을 해결하는 방식은 크게 두 가지입니다.
- lockfile을 신뢰하고, 환경(spec repo, deployment target, CocoaPods 버전)을 lockfile에 맞춘다
- lockfile을 재생성해서 현재 Flutter/플러그인 상태에 맞게 잠금을 새로 건다
일반적으로 Flutter 프로젝트는 플러그인 의존성이 자주 변하므로, 충돌이 발생했다면 재생성(2) 이 정답인 경우가 많습니다. 다만 팀/CI 일관성을 위해 재생성 후에는 반드시 lockfile을 커밋하고, CI도 그 lockfile을 기준으로 설치하도록 맞추는 것이 좋습니다.
로컬에서 10분 해결: 안전한 재생성 절차
아래 순서가 가장 실패율이 낮습니다.
1) Flutter 아티팩트 정리
flutter clean
rm -rf ~/.pub-cache/hosted
flutter pub get
~/.pub-cache까지 지우는 건 과한 경우도 있지만, 플러그인 캐시가 꼬인 상황에서는 효과가 큽니다(특히 브랜치 이동을 자주 하는 팀).
2) iOS Pods 완전 정리 후 재설치
cd ios
rm -rf Pods
rm -f Podfile.lock
rm -rf ~/Library/Developer/Xcode/DerivedData
pod repo update
pod install
cd ..
Pods/와Podfile.lock을 함께 지워야 “새 resolver 결과”가 반영됩니다.pod repo update는 spec repo 미스매치 문제를 제거합니다.
3) Xcode에서 빌드 확인
open ios/Runner.xcworkspace
반드시 .xcworkspace를 열어 빌드합니다. .xcodeproj로 열면 Pods가 링크되지 않아 다른 오류로 보일 수 있습니다.
iOS 최소버전(iOS deployment target) 충돌 해결
많은 충돌이 “Pod가 더 높은 iOS 버전을 요구”해서 발생합니다. 이 경우 Podfile에서 플랫폼 버전을 올려야 합니다.
ios/Podfile 예시:
platform :ios, '12.0'
# Flutter default
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
# 일부 Pod가 낮은 타겟으로 잡히는 것을 방지
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
end
end
end
여기서 중요한 포인트:
platform :ios, '12.0'만 올려도 해결되는 경우가 많지만,- 특정 Pod 타겟이 여전히 낮게 잡히는 케이스가 있어
post_install에서 강제하는 방식이 실무에서 자주 쓰입니다.
단, iOS 최소버전을 올리면 지원 기기 범위가 줄어드니 앱 정책과 맞는지 확인해야 합니다.
CI에서만 터지는 Podfile.lock 충돌: 재현성과 고정 전략
CI에서 pod install이 불안정하면, 보통 다음 중 하나입니다.
- CocoaPods 버전이 로컬과 다름
- Spec repo 업데이트가 매번 달라짐
- 캐시 전략이 lockfile과 충돌
CocoaPods 버전 고정(권장)
Bundler로 CocoaPods 버전을 고정하면 팀/CI 일관성이 크게 올라갑니다.
ios/Gemfile 예시:
source 'https://rubygems.org'
gem 'cocoapods', '1.15.2'
설치/실행:
cd ios
bundle install
bundle exec pod install
CI에서도 bundle exec pod install을 사용하세요.
Spec repo 업데이트를 통제
매 빌드마다 pod repo update를 하면 최신 스펙을 받아오지만, 반대로 “오늘은 되고 내일은 안 되는” 변동성이 생길 수 있습니다. lockfile을 신뢰하는 전략이라면 보통은:
- 기본은
pod install만 수행 - 필요할 때만
pod repo update또는pod install --repo-update
처럼 운영하는 편이 안정적입니다.
캐시를 쓴다면 “키”를 lockfile 기반으로
GitHub Actions/GitLab CI에서 Pods/ 캐시를 쓰는 경우, 캐시 키를 Podfile.lock 해시로 잡지 않으면 충돌이 계속 반복됩니다. CI 권한/인증 이슈까지 겹치면 디버깅이 길어지니, CI 자체 트러블슈팅 경험이 있다면 GitHub Actions OIDC로 AWS 배포 권한 오류 해결처럼 “환경 차이에서 오는 실패”를 구조적으로 줄이는 접근이 도움이 됩니다.
팀 개발에서의 정답: Podfile.lock을 커밋할까?
결론부터 말하면 대부분의 앱 프로젝트는 ios/Podfile.lock을 커밋하는 것이 좋습니다.
- 장점: 팀원/CI가 동일한 Pod 버전을 설치 → 재현성 증가
- 단점: 플러그인 업데이트 시 lockfile 충돌이 자주 발생 → 하지만 이건 “변경을 명시적으로 관리”하는 비용
다만 다음 조건이면 예외를 고려할 수 있습니다.
- 라이브러리/SDK 형태로 배포하며, 소비자 프로젝트에서 Pods를 해결해야 하는 경우
- 내부 규칙상 iOS 의존성을 lock하지 않는 경우
일반적인 Flutter 앱(서비스 앱)이라면 커밋이 정석입니다.
자주 묻는 에러 메시지별 처방전
Specs satisfying the ... were found, but they required a higher minimum deployment target.
platform :ios올리기post_install로IPHONEOS_DEPLOYMENT_TARGET강제- 그래도 안 되면 플러그인/Pod 버전 다운그레이드(최후 수단)
CocoaPods could not find compatible versions for pod ...
Podfile.lock삭제 후 재생성pod repo update또는pod install --repo-update- CocoaPods 버전 고정(Bundler)
The sandbox is not in sync with the Podfile.lock.
pod install재실행Pods/삭제 후pod install
재발 방지 체크리스트
pod --version을 팀/CI에서 고정(Bundler 권장)- Flutter 플러그인 업데이트 시:
flutter pub get후 iOS에서pod install까지 수행- 변경된
Podfile.lock을 함께 커밋
- iOS 최소버전 변경은
Podfile, Xcode 프로젝트 설정, 그리고 필요 시post_install까지 일관되게 반영 - CI 캐시 키는
Podfile.lock해시 기반으로 설계
마무리
Podfile.lock 충돌은 단순히 파일 하나의 문제가 아니라, Flutter 플러그인 생태계 + CocoaPods 해석기 + iOS 타겟 설정 + 팀/CI 환경 차이가 만나면서 발생하는 전형적인 “재현성 문제”입니다. 해결의 핵심은 두 가지입니다.
- 충돌이 나면 과감하게 Pods/lockfile을 정리하고 “현재 상태에 맞게” 재생성한다
- 재생성한 결과를 팀과 CI가 동일하게 따라가도록(버전 고정/캐시 키/설정) 시스템을 맞춘다
위 절차대로 해도 여전히 빌드가 실패한다면, Pod 충돌이 아니라 서명/프로비저닝/빌드 설정 문제일 가능성도 있으니 Flutter iOS 빌드 실패? Signing·CocoaPods 10분 해결에서 체크 범위를 확장해보는 것을 권합니다.