Tuesday, March 29, 2022

Flutter iOS 빌드, 지긋지긋한 arm64 아키텍처 오류와 작별하는 법

Apple Silicon(M1, M2, M3 등)이 탑재된 Mac에서 Flutter로 iOS 앱을 개발하다 보면, 개발 여정의 마지막 단계인 빌드에서 예상치 못한 암초를 만날 때가 있습니다. 모든 코드가 완벽해 보이고 Android에서는 훌륭하게 작동하는데도, iOS 시뮬레이터를 실행하는 순간 Xcode는 차가운 오류 메시지를 뱉어냅니다. 바로 많은 개발자들의 골머리를 앓게 하는 그 유명한 오류입니다.

Flutter iOS 빌드 arm64 아키텍처 오류 메시지

ld: building for iOS Simulator, but linking in dylib built for iOS, file '...' for architecture arm64

Error (Xcode): Building for iOS Simulator, but linking in dylib built for iOS, file '...' for architecture arm64

Could not build the application for the simulator. Error launching application on iPhone 14 Pro.

이 메시지를 마주하면 당황스럽기 마련입니다. '분명 내 Mac은 arm64 아키텍처인데, 왜 arm64를 위한 빌드가 문제라는 거지?'라는 의문이 듭니다. 이 글에서는 이 오류가 발생하는 근본적인 원인부터 시작하여 가장 확실하고 영구적인 해결책, 그리고 급할 때 사용할 수 있는 우회 방법까지, arm64 아키텍처 충돌 문제를 완벽하게 해결하는 모든 방법을 깊이 있게 다룹니다.

1. 왜 이런 문제가 발생할까요? 근본 원인 파헤치기

문제를 해결하기 위해선 먼저 원인을 정확히 이해해야 합니다. 이 오류의 핵심에는 '아키텍처(Architecture)'라는 개념이 있습니다. 아키텍처는 CPU가 명령어를 처리하는 방식에 대한 규약으로, 컴퓨터의 근본적인 설계도와 같습니다.

1.1. CPU 아키텍처의 격동기: Intel에서 Apple Silicon으로

과거 Apple은 수년간 Intel의 CPU를 사용해왔습니다. 이 CPU들은 x86_64 (또는 AMD64)라는 아키텍처를 기반으로 합니다. 따라서 과거의 모든 macOS와 그 위에서 동작하는 프로그램들, 그리고 iOS 시뮬레이터 역시 x86_64 환경에서 작동하도록 설계되었습니다.

하지만 2020년, Apple은 M1 칩을 시작으로 자체 설계한 Apple Silicon으로의 대대적인 전환을 시작했습니다. 이 칩들은 아이폰과 아이패드에서 사용되던 arm64 아키텍처를 기반으로 합니다. 이 전환으로 인해 Mac은 엄청난 성능 향상과 전력 효율을 얻었지만, 개발자들에게는 두 개의 다른 아키텍처를 동시에 고려해야 하는 새로운 과제가 생겼습니다.

1.2. 아키텍처 충돌의 현장: 시뮬레이터와 실제 기기의 차이

우리가 마주한 오류는 바로 이 아키텍처의 차이 때문에 발생합니다. 각 빌드 대상의 아키텍처를 살펴봅시다.

  • 실제 iOS 기기 (아이폰, 아이패드): 항상 arm64 아키텍처를 사용합니다.
  • Intel 기반 Mac의 iOS 시뮬레이터: Mac 자체가 x86_64이므로, 시뮬레이터도 x86_64 환경으로 구동됩니다.
  • Apple Silicon 기반 Mac의 iOS 시뮬레이터: Mac 자체가 arm64이므로, 시뮬레이터도 네이티브 arm64 환경으로 구동됩니다.

문제는 바로 여기에 있습니다. 오류 메시지 "building for iOS Simulator, but linking in dylib built for iOS"를 해석해 보면, "iOS 시뮬레이터용으로 빌드하고 있는데, 실제 iOS 기기용으로 빌드된 라이브러리(dylib)를 연결하려고 하고 있다"는 뜻입니다.

더 구체적으로는, Apple Silicon Mac에서 시뮬레이터 빌드를 할 때, 어떤 라이브러리(주로 CocoaPods로 관리되는 외부 라이브러리)가 실제 기기용인 arm64 코드를 제공하는데, 빌드 시스템이 이를 시뮬레이터용 arm64 코드와 혼동하거나 잘못 연결하려고 시도하면서 충돌이 발생하는 것입니다. 빌드 시스템은 "이 arm64 코드는 실제 기기용인데, 왜 시뮬레이터 빌드에 넣으려고 해?"라며 거부하는 상황입니다.

이 문제를 해결하려면 빌드 시스템에게 "Apple Silicon Mac에서 시뮬레이터를 빌드할 때는, arm64 아키텍처를 제외하고 대신 x86_64 아키텍처로 빌드해줘"라고 명확하게 지시해야 합니다. 이렇게 하면 Mac에 내장된 Rosetta 2라는 번역기가 x86_64 코드를 arm64 환경에서 실행할 수 있도록 자동으로 처리해주어 문제를 우회할 수 있습니다.

2. 가장 확실하고 영구적인 해결책: Podfile 수정

이 문제를 해결하는 가장 권장되는 방법은 Flutter 프로젝트의 ios/Podfile을 수정하는 것입니다. 이 방법이 가장 좋은 이유는 다음과 같습니다.

  • 자동화: pod install 명령을 실행할 때마다 이 설정이 자동으로 모든 Pod(외부 라이브러리)에 적용됩니다.
  • 일관성: 팀원들과 프로젝트를 공유할 때, 다른 팀원들도 별도의 설정 없이 동일한 빌드 환경을 유지할 수 있습니다.
  • 지속성: Xcode 설정을 직접 바꾸는 것은 때때로 Flutter나 CocoaPods에 의해 덮어씌워질 수 있지만, Podfile 설정은 그렇지 않습니다.

아래 단계를 따라 Podfile을 수정하세요.

2.1. Podfile 열기

프로젝트의 루트 디렉토리에서 ios 폴더 안에 있는 Podfile을 텍스트 에디터나 IDE로 엽니다.


# VSCode를 사용한다면
code your_project_name/ios/Podfile

2.2. post_install 스크립트 추가 또는 수정

Podfile의 가장 마지막 부분, end 키워드 바로 위에 다음 코드를 추가합니다. 만약 post_install 블록이 이미 존재한다면, 해당 블록의 내용만 아래 코드로 교체하거나 적절히 병합합니다.


# Podfile 맨 아래에 추가합니다.
post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    target.build_configurations.each do |config|
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0' # 예시: 최소 배포 버전을 명시적으로 설정할 수 있습니다.
      config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64'
    end
  end
end

2.3. 코드 상세 분석 (이해를 돕기 위해)

위 코드가 정확히 무슨 일을 하는지 이해하면 더욱 좋습니다.

  • post_install do |installer| ... end: 이 블록은 CocoaPods가 모든 라이브러리(Pod) 설치를 마친 '후에' 실행될 스크립트를 정의합니다.
  • installer.pods_project.targets.each do |target| ... end: 설치된 모든 Pod(Firebase, Alamofire 등)을 하나씩 순회합니다.
  • target.build_configurations.each do |config| ... end: 각 Pod의 빌드 구성('Debug', 'Release' 등)을 순회합니다.
  • config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64': 이것이 핵심입니다.
    • config.build_settings: 해당 Pod의 Xcode 빌드 설정을 의미합니다.
    • EXCLUDED_ARCHS: '제외할 아키텍처'를 지정하는 설정 키입니다.
    • [sdk=iphonesimulator*]: 이 설정이 적용될 조건을 명시합니다. 즉, '빌드 대상 SDK가 iOS 시뮬레이터일 경우에만' 이라는 뜻입니다. *는 모든 버전의 시뮬레이터 SDK를 의미합니다.
    • = 'arm64': iOS 시뮬레이터용으로 빌드할 때, arm64 아키텍처를 빌드에서 제외하라고 명시적으로 지시합니다.

결론적으로, 이 스크립트는 "프로젝트에 포함된 모든 외부 라이브러리에 대해, iOS 시뮬레이터로 빌드할 때는 arm64 아키텍처를 사용하지 마세요" 라는 규칙을 자동으로 적용시켜주는 역할을 합니다.

참고: 예전 자료에서는 i386도 추가하라는 가이드가 있지만, 최신 Xcode와 Flutter 환경에서는 32비트 시뮬레이터 지원이 거의 필요 없으므로 arm64만 제외하는 것이 더 깔끔하고 일반적입니다.

2.4. 설정 적용 및 재빌드

Podfile을 저장한 후, 터미널에서 다음 명령어를 순서대로 실행하여 기존 설정을 초기화하고 새 설정을 적용합니다.

  1. Flutter 프로젝트 클린:
    flutter clean
  2. iOS 디렉토리로 이동:
    cd ios
  3. 기존 Pods 설정 완전히 제거 (권장):
    pod deintegrate
  4. 새로운 설정으로 Pods 재설치:
    pod install --repo-update

    --repo-update 옵션은 Pod 레포지토리를 최신 상태로 업데이트한 후 설치를 진행하여 잠재적인 버전 문제를 예방합니다.

  5. 프로젝트 루트로 복귀:
    cd ..
  6. 앱 실행:
    flutter run

이제 앱이 문제없이 iOS 시뮬레이터에서 빌드되고 실행될 것입니다. 이 방법은 대부분의 arm64 관련 빌드 오류를 해결해 줍니다.

3. 보조 해결책: Xcode 프로젝트 설정 직접 변경

Podfile을 수정하는 방법이 가장 좋지만, 특정 상황에서는 Xcode에서 직접 설정을 변경해야 할 수도 있습니다. 예를 들어, Podfile 스크립트가 제대로 적용되지 않거나, 프로젝트의 메인 앱 타겟(Runner) 자체의 설정이 문제일 경우 이 방법을 사용할 수 있습니다. 다만 이 방법은 pod install 시 덮어씌워질 가능성이 있음을 인지해야 합니다.

3.1. Xcode 워크스페이스 열기

Finder에서 ios 폴더로 이동한 후, Runner.xcodeproj 파일이 아닌 Runner.xcworkspace 파일을 더블클릭하여 Xcode에서 엽니다. .xcworkspace 파일은 프로젝트와 Pods를 모두 포함하는 작업 공간입니다.

3.2. Excluded Architectures 설정 찾기

  1. 왼쪽의 프로젝트 탐색기에서 최상단에 있는 Runner 프로젝트를 선택합니다.
  2. 중앙 에디터 영역에서 PROJECT 섹션의 Runner가 아닌, TARGETS 섹션의 Runner를 선택합니다.
  3. 상단의 탭에서 Build Settings를 선택합니다.
  4. 우측 상단의 검색창에 Excluded Architectures라고 입력합니다.

3.3. arm64 아키텍처 추가

검색된 Excluded Architectures 항목을 찾아서 수정합니다.

Xcode Build Settings에서 Excluded Architectures 설정 변경
  1. Excluded Architectures 행을 더블클릭하거나 펼칩니다.
  2. DebugRelease 항목을 각각 펼칩니다.
  3. Any iOS Simulator SDK 옆에 있는 + 버튼을 누르고 arm64를 입력합니다. (만약 Any iOS Simulator SDK가 없다면, 직접 조건을 추가할 수 있습니다.)
  4. 같은 작업을 DebugRelease 모두에 적용해줍니다.

중요: 만약 오류 메시지가 특정 Pod(예: firebase_core)를 지목한다면, 왼쪽 프로젝트 탐색기에서 Pods 프로젝트를 펼쳐 해당 Pod의 타겟을 직접 선택한 후, 동일하게 Excluded Architectures 설정을 확인하고 수정해주어야 할 수도 있습니다.

이 방법을 시도한 후에도 마찬가지로 flutter clean 후 다시 빌드해보는 것이 좋습니다.

4. 임시방편 혹은 최후의 수단: Rosetta 2 사용하기

위의 방법들이 모두 통하지 않거나, 프로젝트 설정을 건드리기 곤란한 상황에서 급하게 빌드를 확인해야 할 때 사용할 수 있는 방법이 있습니다. 바로 Xcode 자체를 Intel 기반(x86_64) 앱으로 실행하도록 강제하는 것입니다. 이렇게 하면 Xcode 위에서 동작하는 시뮬레이터 또한 자연스럽게 x86_64 모드로 동작하게 되어 아키텍처 충돌이 원천적으로 발생하지 않습니다.

4.1. Rosetta 란?

Rosetta 2는 Apple Silicon Mac에서 Intel 기반 앱을 실행할 수 있도록 해주는 Apple의 공식 번역기(Translation Layer)입니다. 사용자가 별도로 신경 쓰지 않아도 백그라운드에서 실시간으로 x86_64 코드를 arm64 코드로 번역하여 실행해줍니다. 성능 저하가 약간 있지만 호환성은 매우 뛰어납니다.

4.2. Xcode를 Rosetta로 실행하는 방법

  1. Finder를 열고 응용 프로그램(Applications) 폴더로 이동합니다.
  2. Xcode.app을 찾습니다.
  3. Xcode 아이콘을 우클릭한 후 정보 가져오기(Get Info)를 선택합니다. (단축키: Cmd + I)
  4. 나타나는 정보 창에서 'Rosetta를 사용하여 열기(Open using Rosetta)' 체크박스를 선택합니다.

이 설정을 마치고 Xcode를 다시 실행하면, Xcode는 Rosetta 환경에서 동작하게 됩니다. 이제 VS Code나 Android Studio에서 flutter run을 실행하면, 이 Rosetta로 실행된 Xcode를 통해 시뮬레이터가 구동되므로 arm64 관련 빌드 오류가 발생하지 않습니다.

장점:

  • 가장 간단하고 빠릅니다. 프로젝트 코드를 전혀 수정할 필요가 없습니다.
  • 어떤 복잡한 라이브러리 의존성 문제도 일단은 우회할 수 있습니다.

단점:

  • Xcode와 시뮬레이터의 성능이 네이티브로 실행할 때보다 저하됩니다.
  • 이는 근본적인 해결책이 아닌 임시방편입니다. 문제의 원인을 해결한 것이 아니라 잠시 덮어둔 것에 불과합니다.
  • 네이티브 arm64 시뮬레이터에서만 발생하는 다른 문제를 놓칠 수 있습니다.

따라서 이 방법은 급한 디버깅이나 테스트에만 사용하고, 장기적으로는 Podfile을 수정하는 1번 방법을 통해 문제를 근본적으로 해결하는 것을 강력히 권장합니다.

5. 종합 문제 해결 흐름도

지금까지 설명한 내용들을 바탕으로, 문제 발생 시 어떤 순서로 접근하면 좋을지 정리해 보겠습니다.

  1. 1단계 (가장 먼저 시도): Podfile 수정
    • 가장 안정적이고 권장되는 방법입니다. ios/Podfilepost_install 스크립트를 추가하고 flutter clean, pod deintegrate, pod install --repo-update를 실행한 후 다시 빌드합니다.
  2. 2단계 (1단계 실패 시): Xcode 설정 직접 확인
    • Runner.xcworkspace를 열어 Runner 타겟과 문제가 되는 Pod 타겟의 Build Settings > Excluded Architecturesarm64가 시뮬레이터용으로 추가되었는지 확인합니다.
  3. 3단계 (그래도 실패 시): 환경 점검
    • Flutter, CocoaPods, Xcode가 최신 버전인지 확인합니다. 터미널에 flutter doctor -v를 입력하여 상태를 점검하고, 필요한 업데이트를 진행합니다. 때로는 오래된 버전의 툴 자체가 버그를 가지고 있을 수 있습니다.
  4. 4단계 (임시 해결): Rosetta 사용
    • 개발이 급하게 진행되어야 할 때, Xcode를 'Rosetta를 사용하여 열기'로 설정하여 일단 빌드 문제를 우회하고 개발을 계속 진행합니다. 추후 시간을 내어 근본 원인을 다시 파악해야 합니다.
  5. 5단계 (심층 분석): 특정 라이브러리 문제 조사
    • 오류 메시지가 특정 라이브러리를 명확히 지목한다면, 해당 라이브러리의 GitHub 저장소 'Issues' 탭을 검색해보세요. 전 세계의 다른 개발자들이 이미 같은 문제를 겪고 해결책이나 논의를 공유했을 가능성이 매우 높습니다.

마치며

Apple Silicon Mac에서 발생하는 Flutter iOS 빌드의 arm64 아키텍처 오류는 처음 마주하면 매우 당황스럽고 복잡해 보이지만, 그 원리를 이해하고 나면 명확한 해결책이 존재하는 문제입니다. 대부분의 경우 Podfile에 post_install 스크립트를 추가하는 것만으로도 문제는 깔끔하게 해결됩니다.

개발 과정에서 만나는 오류는 성장을 위한 디딤돌과 같습니다. 이 글을 통해 지긋지긋했던 빌드 오류와 작별하고, 다시 즐겁게 Flutter 앱 개발에 집중하실 수 있기를 바랍니다.


0 개의 댓글:

Post a Comment