- Published on
Flutter iOS 빌드 실패 - Podfile.lock 충돌 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서론
Flutter로 iOS 빌드를 하다 보면 어느 날 갑자기 pod install 단계에서 멈추거나, CI에서는 실패하는데 로컬에서는 되는 묘한 상황을 만나게 됩니다. 그 중심에 자주 있는 것이 Podfile.lock 충돌입니다. 특히 팀 작업(여러 개발자), 플러그인 업데이트, Xcode/Flutter 업그레이드가 겹치면 Podfile.lock이 “내가 아는 상태”와 “프로젝트가 요구하는 상태” 사이에서 꼬이기 쉽습니다.
이 글에서는 Flutter iOS 빌드 실패를 유발하는 Podfile.lock 충돌을 원인별로 분해하고, 가장 안전한 해결 순서(로컬/CI 공통), 그리고 재발 방지 체크리스트까지 정리합니다. Git에서 락 파일 충돌을 다루는 관점은 Git rebase 충돌 자동해결 - rerere·autosquash 실전과도 연결되니 함께 읽어두면 팀 생산성에 도움이 됩니다.
Podfile.lock이 왜 중요한가
CocoaPods에서 Podfile.lock은 “이번에 설치된 Pod들의 정확한 버전 스냅샷”입니다.
Podfile은 의존성 요구사항(예:~> 1.2)을 정의Podfile.lock은 해결된 결과(예:1.2.7)를 고정
Flutter iOS 프로젝트에서는 ios/Podfile이 Flutter 플러그인들이 생성하는 Podspec들과 맞물려 동작합니다. 즉, flutter pub get으로 플러그인 버전이 바뀌면, iOS 쪽 Pod 의존성 그래프도 바뀔 수 있습니다.
문제는 다음 상황에서 발생합니다.
- 팀원이
Podfile.lock을 업데이트했는데 내 브랜치에선 다른 플러그인/Pod 조합 - CocoaPods 버전 차이로 dependency resolver 결과가 달라짐
Pods/디렉터리와Podfile.lock이 서로 다른 상태(캐시/중간 산출물 불일치)use_frameworks!,use_modular_headers!같은 설정 변경으로 Pod 통합 방식이 달라짐
증상 패턴: 어떤 에러가 뜨나
아래는 Podfile.lock 충돌/불일치에서 흔히 보는 패턴입니다.
1) “The sandbox is not in sync with the Podfile.lock”
Pods/폴더(샌드박스)와Podfile.lock이 맞지 않다는 뜻- 보통
Pods/가 오래됐거나,pod install이 중간에 실패했거나, 브랜치 전환 후 정리가 안 됐을 때
2) “CocoaPods could not find compatible versions for pod …”
Podfile.lock에 고정된 버전과 현재 요구사항이 충돌- 플러그인 업데이트로 특정 Pod의 최소 버전이 올라갔는데 락이 예전 버전을 잡고 있는 경우가 많음
3) Git 머지/리베이스에서 Podfile.lock 충돌
Podfile.lock은 텍스트지만 “의존성 그래프 결과물”이라 수동 병합이 어렵습니다.- 충돌을 억지로 해결하면
pod install에서 더 큰 불일치가 터질 수 있습니다.
원인 진단: 무엇이 바뀌었는지 먼저 확인
해결 전에 “누가 무엇을 바꿨는지”를 확인하면 불필요한 전체 초기화(=시간 낭비)를 줄일 수 있습니다.
변경점 체크
pubspec.lock변경 여부- Flutter 플러그인 버전이 바뀌면 iOS Pod 의존성도 바뀔 확률이 큼
ios/Podfile변경 여부- 플랫폼 버전(iOS deployment target),
use_frameworks!옵션 등
- 플랫폼 버전(iOS deployment target),
- CocoaPods 버전
- 팀/CI가 서로 다른 CocoaPods 버전을 쓰면 lockfile 포맷/해석이 달라질 수 있음
아래 명령으로 로컬 환경을 기록해두면 팀 디버깅이 빨라집니다.
flutter --version
ruby --version
pod --version
xcodebuild -version
가장 안전한 해결 절차(로컬)
아래 순서는 “최소 파괴 → 필요 시 초기화”로 진행합니다.
1) 기본 정리: Flutter/Pods 캐시 정합성 맞추기
flutter clean
rm -rf ios/Pods ios/.symlinks ios/Flutter/Flutter.framework ios/Flutter/Flutter.podspec
rm -f ios/Podfile.lock
flutter pub get
ios/.symlinks는 Flutter가 플러그인 Podspec 연결을 위해 쓰는 심볼릭 링크 묶음입니다.Podfile.lock을 지우는 이유는 “현재 pubspec 기준으로 다시 해결”하기 위함입니다.
이후 iOS 디렉터리에서 설치:
cd ios
pod repo update
pod install
cd ..
대부분은 여기서 해결됩니다.
2) 그래도 실패하면: Pod 캐시/DerivedData까지 정리
특정 Pod가 꼬이거나 Xcode 캐시가 남아 빌드가 계속 실패하는 경우가 있습니다.
rm -rf ~/Library/Caches/CocoaPods
rm -rf ~/Library/Developer/Xcode/DerivedData
cd ios
pod deintegrate
pod install
cd ..
pod deintegrate는 Xcode 프로젝트에서 CocoaPods 통합 흔적을 제거합니다.- 이후
pod install로 깨끗하게 재통합합니다.
3) “호환 버전” 에러가 계속되면: 플러그인/플랫폼 타깃 점검
예를 들어 어떤 플러그인이 iOS 13 이상을 요구하는데 platform :ios, '12.0'이면 충돌이 납니다.
ios/Podfile에서 플랫폼 버전을 올리고 다시 설치합니다.
# ios/Podfile
platform :ios, '13.0'
그 다음:
cd ios
pod install
cd ..
Git 충돌에서의 정석: Podfile.lock은 “재생성”이 안전하다
Podfile.lock이 충돌 났을 때 가장 안전한 방법은 다음 중 하나입니다.
- 한쪽(lockfile)을 선택하고 그 상태에서
pod install로 재정렬 - 또는 lockfile을 삭제 후 재생성(팀 합의가 있을 때)
실무에서 추천하는 방식은 “pubspec.lock + Podfile.lock을 함께 움직이게” 만드는 것입니다.
전략 A: feature 브랜치에서 lockfile 재생성
- 충돌 해결 시
Podfile.lock을 삭제하고 병합 - 병합 결과 기준으로 로컬에서
pod install실행 - 생성된
Podfile.lock을 커밋
git checkout your-branch
# 충돌 해결 중 Podfile.lock은 제거
rm -f ios/Podfile.lock
flutter pub get
cd ios && pod install && cd ..
git add ios/Podfile.lock
git commit -m "Regenerate Podfile.lock after merge"
전략 B: rerere로 반복 충돌 비용 줄이기
Podfile.lock은 자주 충돌합니다. Git의 rerere를 켜두면 동일 패턴 충돌을 자동으로 기억/적용해줍니다. 자세한 사용법은 Git rebase 충돌 자동해결 - rerere·autosquash 실전를 참고하세요.
CI에서만 실패하는 경우: 버전 고정이 핵심
로컬에서는 되는데 GitHub Actions/Jenkins에서만 실패한다면 보통 환경 차이입니다.
1) CocoaPods 버전 고정
CocoaPods는 RubyGem이므로, CI에서는 버전을 명시적으로 고정하는 편이 안전합니다.
Gemfile을 iOS 폴더 또는 repo 루트에 두고:
# Gemfile
source 'https://rubygems.org'
gem 'cocoapods', '1.14.3'
CI에서는:
bundle install
cd ios
bundle exec pod install
이렇게 하면 팀/CI가 동일한 resolver를 쓰게 되어 lockfile 충돌/불일치가 크게 줄어듭니다.
2) 캐시를 쓴다면 “키”에 lockfile을 포함
CI에서 CocoaPods 캐시를 쓰는 경우, 캐시 키에 Podfile.lock 해시가 들어가야 합니다. 그렇지 않으면 “이전 lockfile 기준 Pods 캐시”가 재사용되어 샌드박스 불일치가 생깁니다.
(빌드 캐시/동시성 이슈는 인프라/CI 전반의 문제로 번지기도 합니다. GitHub Actions에서 동시 실행/취소 정책을 정리하는 글인 GitHub Actions 동시 실행 막힘 해결 - concurrency·cancel-in-progress도 참고할 만합니다.)
자주 묻는 질문(실전 포인트)
Q1. Podfile.lock을 git에 커밋해야 하나?
대부분의 앱 프로젝트는 커밋하는 것이 권장됩니다.
- 팀원/CI가 동일한 Pod 버전을 설치하도록 보장
- 재현 가능한 빌드(특히 릴리즈/핫픽스)에 유리
단, lockfile을 커밋하면 “충돌 관리”가 필수이므로, 위에서 말한 재생성 전략과 버전 고정이 함께 필요합니다.
Q2. pod update는 언제 쓰나?
pod update는 lockfile을 무시하고 가능한 최신 버전으로 올릴 수 있어, 문제를 해결하기도 하지만 더 큰 변경을 유발할 수 있습니다. 원칙적으로는:
- 단순 충돌/불일치 해결:
pod install중심 - 특정 Pod 버전 올려야 함:
pod update SomePod처럼 대상 Pod만 제한
Q3. Apple Silicon(M1/M2)에서만 꼬인다
아키텍처/네이티브 바이너리 이슈가 섞이면 Podfile.lock 문제가 아닌데도 pod 단계에서 터지는 것처럼 보일 수 있습니다. 이 경우는:
- Ruby/CocoaPods 설치 방식(rbenv, asdf)
- Xcode command line tools 경로
- 시뮬레이터/디바이스 빌드 타깃
등을 같이 점검해야 합니다. 다만 “샌드박스 불일치”류의 메시지가 명확하다면 먼저 lockfile/Pods 정합성부터 해결하는 게 맞습니다.
재발 방지 체크리스트
팀에서 한 번 정리해두면 같은 유형의 iOS 빌드 실패가 크게 줄어듭니다.
- CocoaPods 버전 고정:
Gemfile+bundle exec pod install - Flutter 플러그인 변경 시 규칙:
pubspec.lock변경되면pod install후Podfile.lock도 함께 커밋 - 브랜치 전환 후 iOS 빌드 실패 시 1차 처방:
rm -rf ios/Pods ios/.symlinks && pod install - CI 캐시 키에 Podfile.lock 포함
- Podfile.lock 충돌은 수동 병합보다 재생성이 안전
결론
Flutter iOS 빌드에서 Podfile.lock 충돌은 “원인”이라기보다 의존성 그래프가 바뀌었는데 정합성이 깨진 결과인 경우가 많습니다. 가장 효과적인 해결책은 (1) Flutter 플러그인 상태를 먼저 확정(flutter pub get), (2) Pods/lockfile을 정리하고, (3) 동일한 CocoaPods 버전으로 pod install을 재실행해 정합성을 맞추는 것입니다.
팀/CI까지 포함해 재발을 막으려면 CocoaPods 버전 고정과 캐시 키 설계, 그리고 **lockfile 충돌 처리 규칙(재생성 우선)**을 표준화하는 것이 핵심입니다.