- Published on
Flutter iOS 빌드 Podfile.lock 충돌 해결 가이드
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서론
Flutter로 iOS 빌드를 하다 보면 어느 날 갑자기 pod install이 실패하거나 Xcode 빌드가 깨지면서 로그에 Podfile.lock 관련 충돌 메시지가 등장하는 경우가 많습니다. 특히 팀에서 여러 명이 작업하거나, CI에서 캐시를 쓰거나, 플러그인 버전을 올린 직후라면 재현도 애매하고 “내 로컬에서는 되는데?” 같은 상황이 쉽게 만들어집니다.
이 글에서는 Podfile.lock 충돌이 왜 생기는지(구조와 동작 원리), 어떤 로그가 어떤 원인으로 이어지는지, 그리고 가장 안전하게 복구하는 순서(로컬/팀/CI 관점)를 정리합니다. Flutter 프로젝트 특성(Generated.xcconfig, Flutter pod, plugin symlink) 때문에 CocoaPods만 아는 상태로 접근하면 계속 같은 문제가 반복될 수 있어, Flutter iOS 빌드 파이프라인까지 함께 설명합니다.
참고로 동일 주제의 심화 글도 함께 읽으면 좋습니다: Flutter iOS 빌드 실패 - CocoaPods·Podfile.lock 충돌 해결
Podfile.lock 충돌이란 무엇인가
Podfile vs Podfile.lock의 역할
Podfile: “어떤 Pod를 어떤 조건으로 설치할지”를 선언합니다. 예: platform, use_frameworks, 특정 pod 버전 핀 등Podfile.lock: 실제로 “이번 설치에서 확정된 Pod 버전 조합”을 기록합니다. 팀/CI에서 동일한 의존성 그래프를 재현하기 위한 잠금 파일입니다.
CocoaPods는 기본적으로 다음을 목표로 합니다.
Podfile.lock이 존재하면 가능한 한 그 버전을 유지Podfile/spec repo 변화로 인해 불일치가 생기면 오류 또는 재해석(resolution)pod update는 의도적으로 lock을 갱신
Flutter iOS 프로젝트에서는 ios/Podfile이 Flutter tooling에 의해 특정 템플릿 구조를 갖고 있고, 플러그인 의존성이 .symlinks/plugins/... 경로로 연결되면서 Pod 구성이 자주 변합니다. 즉, lock이 조금만 어긋나도 충돌이 발생하기 쉬운 환경입니다.
대표 증상과 로그 패턴
1) The sandbox is not in sync with the Podfile.lock
보통 Pods/ 디렉터리(샌드박스) 내부 상태와 Podfile.lock이 가리키는 설치 결과가 다를 때 발생합니다.
원인 후보:
Pods/가 부분적으로 깨졌거나, git에서 Pods를 제외하고 lock만 남아 설치가 중간에 실패- Xcode에서 build setting/xcconfig가 꼬여서
pod install이 적용되지 않음 - CI 캐시가 lock 갱신과 맞지 않음
2) CocoaPods could not find compatible versions for pod ...
특정 Pod가 요구하는 버전 범위와 lock에 고정된 버전이 충돌합니다.
원인 후보:
- Flutter plugin 업데이트로 iOS native dependency가 바뀜
- iOS deployment target이 낮아서 최신 Pod 요구조건을 만족 못함
- Ruby/CocoaPods 버전 차이로 resolution 결과가 달라짐
3) Podfile.lock changed, but the lockfile is not committed
팀 규칙 또는 CI에서 lockfile 변경을 허용하지 않을 때 발생합니다. 즉, 현재 상태에서 pod install을 하면 lock이 변경되는데, 이를 커밋하지 않았거나 커밋되면 안 되는 정책이 걸려있습니다.
Flutter iOS에서 충돌이 잦은 근본 원인
1) Flutter plugin이 iOS Pod 의존성을 “간접”으로 바꾼다
예를 들어 firebase_messaging을 업데이트하면 내부적으로 Firebase/Messaging Pod 버전 요구가 바뀔 수 있습니다. Flutter 레벨에서는 pubspec.lock만 변했지만, iOS 레벨에서는 Pod 그래프가 달라져 Podfile.lock이 재해석되어야 합니다.
2) iOS deployment target 불일치
Pod는 점점 더 높은 iOS 최소 버전을 요구합니다. platform :ios, '11.0' 같은 오래된 설정이 남아 있으면 특정 Pod 업데이트가 막히고, lock은 유지하려다 충돌이 납니다.
3) CocoaPods/Ruby 환경 차이
개발자 A는 CocoaPods 1.11, 개발자 B는 1.14를 쓰면 같은 Podfile이라도 resolution 결과가 달라질 수 있습니다(특히 repo 업데이트, CDN, spec resolution 과정에서).
4) CI 캐시와 lockfile의 불일치
CI에서 Pods/ 또는 ~/.cocoapods 캐시를 저장해두고 lockfile이 바뀌면 “샌드박스 불일치”가 쉽게 납니다.
가장 안전한 해결 절차(로컬)
아래 순서는 “원인 미상” 상태에서 가장 재현성 높게 복구하는 방법입니다.
1) Flutter/Dart 의존성 정리
flutter clean
rm -rf .dart_tool
flutter pub get
flutter clean은 iOS 빌드 산출물과 일부 캐시를 제거합니다.- 플러그인 목록이 바뀌었을 수 있으니
pub get으로 먼저 Dart 레벨을 확정합니다.
2) iOS Pods 완전 재설치
cd ios
rm -rf Pods
rm -f Podfile.lock
rm -rf ~/Library/Developer/Xcode/DerivedData
pod repo update
pod install --verbose
cd ..
설명:
Pods/와Podfile.lock을 함께 제거하면 “현재 Podfile + 현재 plugin 상태” 기준으로 lock을 새로 생성합니다.pod repo update는 spec이 오래되어 생기는 “못 찾음/호환 불가”를 줄입니다.DerivedData삭제는 Xcode가 이전 Pods 설정을 물고 있는 경우를 제거합니다.
3) iOS 최소 버전/Podfile 점검
ios/Podfile의 platform이 너무 낮으면 충돌이 반복됩니다. 예:
platform :ios, '13.0'
# Flutter 기본 템플릿 구조 유지 권장
install! 'cocoapods', :disable_input_output_paths => true
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
주의:
use_frameworks!/use_modular_headers!는 플러그인 조합에 따라 필요/불필요가 갈립니다. 이미 동작하던 프로젝트라면 무작정 토글하지 말고, 충돌 로그에 따라 조정하세요.- iOS 최소 버전은 앱 정책/지원 OS에 맞춰 올리되, 올린 뒤에는 Xcode 프로젝트의 Deployment Target도 함께 맞추는 것이 안전합니다.
4) 다시 빌드
flutter build ios --release
# 또는
flutter run
“lock은 유지하고 Pods만 맞추고 싶다”는 경우
팀에서 Podfile.lock을 기준으로 재현성을 강하게 유지한다면(일반적으로 권장), lock을 삭제하지 말고 샌드박스만 맞추는 접근이 좋습니다.
cd ios
rm -rf Pods
pod install
여기서도 The sandbox is not in sync...가 나오면, 대개 Pods/가 아니라 Podfile.lock 자체가 현재 Podfile/plugin 상태와 충돌하는 것입니다. 그때는 lock 갱신이 필요합니다.
lock 갱신이 필요한 상황과 올바른 갱신 방법
1) pod update는 최소 단위로
전체 업데이트는 의존성 지옥을 부를 수 있습니다. 충돌 Pod만 지정 업데이트하세요.
cd ios
pod update Firebase/Messaging
pod install
또는 특정 Pod 이름이 로그에 뜨면 그 Pod부터 업데이트합니다.
2) Flutter plugin 업데이트 후 권장 루틴
flutter pub upgrade
flutter clean
flutter pub get
cd ios
pod repo update
pod install
이 과정에서 Podfile.lock이 변경되면, 변경된 lock을 커밋해 팀과 CI가 동일 조합을 사용하도록 맞추는 것이 일반적으로 정답입니다.
Git 협업에서 Podfile.lock을 어떻게 다룰까
결론: Podfile.lock은 커밋하는 편이 안전하다
iOS 앱 프로젝트에서는 Podfile.lock을 커밋해 재현성을 확보하는 것이 일반적입니다. Flutter도 iOS 네이티브 빌드가 포함되므로 동일합니다.
다만 다음을 팀 규칙으로 정해두면 충돌이 줄어듭니다.
- Flutter plugin 업데이트(PR)에는
pubspec.lock과ios/Podfile.lock변경이 함께 들어가야 한다 - lock 충돌이 났을 때 “누가 맞는가”가 아니라 “CI가 성공하는 조합”을 기준으로 lock을 확정한다
- CocoaPods 버전을 팀에서 고정한다(예:
Gemfile사용)
CocoaPods 버전 고정(Gemfile) 예시
ios/Gemfile:
source 'https://rubygems.org'
gem 'cocoapods', '1.14.3'
설치/실행:
cd ios
bundle install
bundle exec pod install
이렇게 하면 개발자마다 CocoaPods 버전이 달라서 생기는 lock 미세 차이를 크게 줄일 수 있습니다.
CI에서 Podfile.lock 충돌을 줄이는 체크리스트
1) 캐시 키에 Podfile.lock 해시를 포함
예: GitHub Actions라면 ios/Podfile.lock이 바뀌면 Pods 캐시가 무효화되도록 키를 구성합니다.
2) pod install은 항상 실행
캐시를 쓰더라도 pod install을 생략하면 샌드박스 불일치가 발생할 수 있습니다. 캐시는 “속도”를 위한 것이지 “설치 생략”을 위한 것이 아닙니다.
3) Flutter와 CocoaPods 단계 순서 고정
flutter pub get→cd ios && pod install→xcodebuild/flutter build ios
순서가 뒤섞이면 plugin symlink 상태가 달라져 lock 충돌이 재현되기도 합니다.
CI 권한/설정 이슈가 섞이면 원인 파악이 더 어려워지는데, 토큰/권한 문제로 파이프라인이 깨지는 케이스는 별도로 정리해 둔 글이 있습니다: GitHub Actions GITHUB_TOKEN 403 권한 오류 해결
자주 묻는 질문(실전)
Q1. Podfile.lock을 삭제해도 되나?
개인 로컬에서 “일단 빌드 복구”가 목적이면 삭제 후 재생성은 빠른 해결책입니다. 하지만 팀/CI가 있는 프로젝트라면, 삭제로 인해 lock이 크게 변할 수 있으므로 PR 단위로 의존성 변경을 명확히 하고 lock 변경을 함께 커밋하는 방식이 바람직합니다.
Q2. pod repo update는 매번 해야 하나?
매번 필수는 아니지만, 특정 Pod를 찾지 못하거나 호환 버전 탐색이 실패할 때는 가장 먼저 해볼 가치가 있습니다. CI에서는 주기적으로 spec 업데이트를 하거나, CocoaPods CDN을 안정적으로 쓰도록 네트워크 환경을 보장하는 편이 좋습니다.
Q3. Apple Silicon(M1/M2)에서만 충돌이 나요
아키텍처 차이로 인해 네이티브 바이너리/빌드 설정이 달라지는 경우가 있습니다. 다만 Podfile.lock 충돌 자체는 대부분 “버전/샌드박스/해석 결과” 문제이므로, 위의 정리 절차(특히 Pods 재설치 + CocoaPods 버전 고정)가 먼저입니다.
결론: 재현성(락)과 복구 절차를 팀 규칙으로 만들기
Flutter iOS 빌드에서 Podfile.lock 충돌은 우연이 아니라 구조적으로 자주 발생합니다. 플러그인 업데이트가 iOS 네이티브 의존성을 간접 변경하고, 개발자/CI 환경 차이가 resolution 결과를 흔들기 때문입니다.
정리하면 다음 3가지만 팀 규칙으로 잡아도 충돌 빈도가 크게 줄어듭니다.
pubspec.lock변경이 있으면ios/Podfile.lock변경도 함께 리뷰한다- CocoaPods 버전을
Gemfile로 고정하고bundle exec pod install을 표준으로 삼는다 - CI 캐시는
Podfile.lock해시 기반으로 무효화하고,pod install은 항상 실행한다
문제가 재발한다면 위의 “완전 재설치 루틴”을 그대로 실행해 상태를 초기화한 뒤, 어떤 변경이 lock을 흔드는지(플러그인 업데이트, iOS 최소 버전, Podspec 요구조건)를 역추적하는 방식이 가장 빠릅니다.