iOS 빌드가 터졌어요 (Xcode의 저주와 CocoaPods 디버깅)
1. "어제는 됐는데 오늘은 안 돼요."
Flutter 개발자가 가장 두려워하는 순간입니다. 코드는 한 줄도 안 고쳤습니다. 그냥 자고 일어났을 뿐인데, 갑자기 빌드가 빨간 에러를 토해냅니다.
ld: symbol(s) not found for architecture arm64
Sandbox: rsync.samba(...) deny(1) file-write-create
Command PhaseScriptExecution failed with a nonzero exit code
로그는 외계어 같습니다. 맥북을 던지고 싶겠지만, 잠깐 참으세요. 이것은 여러분의 잘못이 아닙니다. Xcode와 CocoaPods의 복잡한 의존성 관계가 꼬였을 뿐입니다.
2. 원리 이해 - 왜 자꾸 꼬일까?
Flutter의 iOS 빌드는 단순히 "컴파일" 한 번으로 끝나지 않습니다. 3단계의 복잡한 파이프라인을 거칩니다.
- Flutter Build: Dart 코드를 AOT(Ahead-of-Time) 컴파일하여 네이티브 바이너리(
App.framework)로 만듭니다. - CocoaPods Install:
pubspec.yaml에 정의된 플러그인들이 사용하는 네이티브 iOS 라이브러리(Map, WebView, Firebase 등)를 다운로드하고, 의존성을 연결합니다. - Xcode Build: 위 두 가지 결과물과 여러분의 Runner 프로젝트를 합쳐서, 서명(Signing)을 하고 최종
Runner.app을 만듭니다.
대부분의 문제는 2번과 3번 사이에서 발생합니다. CocoaPods가 다운로드한 라이브러리 버전과 Xcode가 기대하는 설정이 다르거나, 이전 빌드의 찌꺼기(Cache/Artifacts)가 남아있어서 충돌이 나는 것입니다.
3. 해결책 1 - 국룰 3단 콤보 (Deep Clean)
가장 무식하지만, 80%의 문제를 해결하는 "성스러운 의식"입니다.
어설프게 flutter clean만 하지 말고, iOS 의존성까지 뿌리째 뽑아야 합니다.
# 1. Flutter 프로젝트 루트에서 시작
flutter clean
# 2. iOS 폴더로 이동
cd ios
# 3. CocoaPods 캐시 삭제 (가장 중요)
rm -rf Pods
rm Podfile.lock
# 4. 의존성 재설치 (Repo 업데이트 포함)
# M1/M2 맥이라면 arch -x86_64를 붙여야 할 수도 있음
pod install --repo-update
# 5. 다시 빌드
cd ..
flutter pub get
flutter run
이걸로 해결되면 다행입니다. 안 된다면 이제 진짜 디버깅을 시작해야 합니다.
4. 해결책 2 - 애플 실리콘 (M1/M2/M3) 아키텍처 충돌
애플 실리콘 맥을 쓰는데 Excluded Architectures 또는 symbol not found for architecture arm64 에러가 뜬다면?
범인은 "오래된 라이브러리"입니다.
어떤 라이브러리가 인텔(x86_64)용 바이너리만 갖고 있고, arm64(애플 실리콘)용 바이너리가 없어서 시뮬레이터 빌드가 실패하는 것입니다.
해결법 - Podfile 수정
강제로 시뮬레이터 빌드에서 arm64를 제외시켜서, 로제타(Rosetta)로 돌리게 만들어야 합니다.
ios/Podfile의 post_install 블록에 아래 코드를 추가하세요.
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
# 추가된 부분
target.build_configurations.each do |config|
# 시뮬레이터(iphonesimulator) 빌드일 때만 arm64 제외
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = "arm64"
end
end
end
단, 이 방법은 임시방편입니다. 장기적으로는 해당 라이브러리 개발자에게 "arm64 지원해주세요"라고 이슈를 남기거나, 라이브러리를 교체해야 합니다.
5. 해결책 3 - 서명(Signing) 및 프로비저닝 프로파일
Code Signing Error, Provisioning Profile doesn't match, Entitlements mismatch.
이 에러들은 "애플이 이 앱을 누가 만들었는지 인증할 수 없다"는 뜻입니다.
터미널에서는 해결이 불가능합니다. 무조건 Xcode를 열어야 합니다.
ios/Runner.xcworkspace파일을 더블클릭해서 엽니다. (.xcodeproj아님!)- 왼쪽 네비게이터 최상단의
Runner(파란 아이콘) 클릭. - TARGETS에서
Runner선택 ->Signing & Capabilities탭 클릭. - Team: 여기서 붉은색 경고가 뜨면 로그인이 풀린 겁니다. 다시 로그인해서 팀을 선택하세요.
- Bundle Identifier: 전 세계에서 유일해야 합니다.
6. 해결책 4 - Ruby 버전 대환장 파티
pod install을 쳤는데 ffi, ethon, gem 관련 루비 에러가 쏟아진다면?
MacOS에는 기본적으로 시스템 Ruby가 깔려있는데, 이것과 CocoaPods가 사용하는 Ruby 버전이 꼬인 겁니다.
해결법 - rbenv 사용
시스템 Ruby(system)를 쓰지 말고, rbenv로 별도의 버전을 설치해서 쓰세요.
# 1. rbenv 설치
brew install rbenv ruby-build
# 2. 호환성 좋은 Ruby 버전 설치 (예 - 2.7.6 또는 3.2.2)
rbenv install 3.2.2
rbenv global 3.2.2
# 3. CocoaPods 재설치
gem install cocoapods
# 4. 확인
which pod
# 결과가 /Users/내이름/.rbenv/shims/pod 여야 함.
# /usr/bin/pod 라면 시스템 루비를 쓰고 있는 것임.
핵심 용어 정리
1. Podfile vs Podfile.lock
- Podfile: "나는 Firebase랑 GoogleMaps가 필요해"라고 적는 주문서.
- Podfile.lock: 실제로 설치된 정확한 버전(
2.1.3)이 적힌 영수증.
2. Workspace (.xcworkspace) vs Project (.xcodeproj)
CocoaPods를 쓰면 반드시 .xcworkspace로 열어야 합니다.
Workspace는 Runner 프로젝트와 Pods 프로젝트(라이브러리들)를 하나로 묶어주는 거대한 폴더입니다.
3. Build Phases
Xcode가 빌드하는 순서입니다. Flutter는 여기에 Run Script를 끼워넣어 Dart 코드를 컴파일하고 에셋을 복사합니다.
4. Bitcode
애플이 앱을 기기에 맞춰 최적화하기 위해 사용하는 중간 코드입니다. 하지만 Xcode 14부터 Deprecated(사용 중단) 되었습니다. 빌드 설정에서 Enable Bitcode를 No로 설정해야 최신 라이브러리들과 충돌이 안 납니다.
8. 요약
- 3단 콤보 생활화:
flutter clean+rm -rf Pods+pod install은 만병통치약이다. - M1 맥:
Excluded Architectures = arm64설정이 필요한지 확인하라. - Xcode 확인: 서명 에러는 터미널 말고 Xcode GUI에서 해결하라.
- Workspace: 제발
.xcodeproj로 열지 말고.xcworkspace로 열어라.