Wednesday, August 28, 2019

Xcode 빌드 성공 후 앱 크래시? 'Image not found' 오류, 원인부터 해결까지 총정리

iOS 앱 개발 여정에서 가장 당혹스러운 순간 중 하나는 바로 Xcode에서 'Build Succeeded'라는 반가운 메시지를 보고 기쁘게 앱을 실행했을 때, 스플래시 스크린이 보이자마자 혹은 그 직전에 아무런 경고 없이 앱이 종료되는 현상을 마주했을 때입니다. 개발자는 방금까지 성공적으로 빌드되었던 프로젝트이기에 더욱 원인을 파악하기 힘들어 막막함을 느끼게 됩니다. 이때 Xcode의 디버그 콘솔을 확인해보면, 개발자들을 오랫동안 괴롭혀 온 악명 높은 로그를 발견하게 될 가능성이 높습니다.


dyld: Library not loaded: @rpath/SomeFramework.framework/SomeFramework
  Referenced from: /var/containers/Bundle/Application/YOUR_APP_UUID/YourApp.app/YourApp
  Reason: image not found

바로 'dyld: Library not loaded: ... Reason: image not found' 오류입니다. 이 메시지는 '라이브러리를 로드하지 못했습니다. 원인: 이미지를 찾을 수 없습니다.'라고 직역할 수 있습니다. 처음 이 오류를 접하는 개발자는 '이미지(Image)? 내가 추가한 아이콘이나 이미지 파일에 문제가 생겼나?'라고 오해하기 쉽습니다. 하지만 여기서 말하는 'Image'는 우리가 흔히 생각하는 JPEG나 PNG 같은 그림 파일이 아닙니다. 이 오류의 정확한 의미와 원인을 깊이 있게 이해하고, 다양한 시나리오에 맞는 해결책을 찾아가는 과정을 자세히 알아보겠습니다.

1. 오류의 근원: 'dyld'와 'Image'의 정체 파헤치기

문제를 해결하기 위해선 먼저 오류 메시지에 등장하는 핵심 용어들의 의미를 정확히 알아야 합니다. 'dyld'와 'image', 이 두 단어에 모든 힌트가 담겨 있습니다.

dyld (The Dynamic Linker)란 무엇인가?

dyld는 'dynamic linker' 또는 'dynamic loader'의 줄임말로, macOS와 iOS 운영체제의 핵심적인 부분입니다. 앱을 실행하면 가장 먼저 실행되는 프로세스 중 하나로, 다음과 같은 중요한 역할을 수행합니다.

  • 실행 파일 로딩: 사용자가 탭한 앱의 메인 실행 파일을 메모리에 올립니다.
  • 의존성 해결: 앱이 작동하는 데 필요한 다양한 동적 라이브러리(Dynamic Libraries)와 프레임워크(Frameworks)를 찾아서 함께 메모리에 로드합니다.
  • 심볼 바인딩: 앱 코드에서 호출하는 라이브러리 함수나 변수들을 실제 메모리 주소에 연결(binding)해 줍니다.

쉽게 비유하자면, dyld는 복잡한 조립식 가구를 만들기 전에 설계도(앱 실행 파일)를 보고, 필요한 모든 부품(라이브러리, 프레임워크)들이 제대로 있는지 확인하고 제자리에 가져다 놓는 일꾼과 같습니다. 만약 이 일꾼이 설계도에 명시된 특정 부품을 찾지 못하면, 조립을 시작할 수조차 없으므로 작업을 중단하고 "부품을 찾을 수 없습니다!"라고 외치게 됩니다. 이것이 바로 'Library not loaded' 오류의 본질입니다.

'Image not found'에서 'Image'란 무엇인가?

앞서 언급했듯이, 여기서 말하는 'Image'는 그래픽 파일이 아닙니다. 운영체제와 링커의 관점에서 'Image'는 메모리에 매핑될 수 있는 실행 가능한 바이너리 파일 단위를 의미합니다. 대표적인 예시는 다음과 같습니다.

  • Executable: 앱의 메인 실행 파일 (예: YourApp.app/YourApp)
  • Dynamic Library: 동적 라이브러리 파일 (.dylib)
  • Framework: 코드와 리소스를 묶어놓은 번들 형태의 동적 라이브러리 (.framework)

이 파일들은 모두 Mach-O(Mach Object)라는 특정 파일 형식을 따릅니다. 따라서 'Reason: image not found'라는 메시지는 "dyld가 앱을 실행하기 위해 메모리에 올려야 할 특정 프레임워크나 동적 라이브러리 파일을 지정된 경로에서 찾지 못했습니다."라는 의미로 해석해야 합니다.

이제 오류의 의미를 명확히 이해했습니다. 앱은 빌드(컴파일 및 링크) 시점에는 "나는 'SomeFramework'라는 라이브러리를 사용할 거야"라고 약속했지만, 정작 앱이 실행되는 런타임 시점에는 dyld가 그 약속된 'SomeFramework' 파일을 찾을 수 없는 상황인 것입니다.

2. 왜 라이브러리를 찾지 못할까? 주요 원인 분석

빌드는 성공했는데 왜 런타임에 라이브러리를 찾지 못하는 걸까요? 이는 Xcode 프로젝트 설정의 '링크(Link)'와 '임베드(Embed)' 개념의 차이에서 비롯되는 경우가 대부분입니다. 다양한 원인이 존재하며, 가장 흔한 시나리오부터 복잡한 경우까지 순서대로 살펴보겠습니다.

원인 1: 프레임워크가 앱 번들에 포함되지 않은 경우 (가장 흔한 원인)

가장 빈번하게 발생하는 원인입니다. Xcode 프로젝트는 외부 프레임워크를 사용할 때 두 가지 주요 단계를 거칩니다.

  1. Linking (링크): 컴파일 시점에 프로젝트가 해당 프레임워크의 존재를 인지하고, 그 안에 있는 함수나 클래스들을 코드에서 사용할 수 있도록 연결하는 과정입니다. 'Link Binary With Libraries' 빌드 단계에서 이 작업이 이루어집니다. 링크만 된 상태에서는 컴파일러가 "아, 'SomeFramework'라는 게 있고, 그 안에 이런 기능들이 있구나"라고 인지만 할 뿐, 실제 프레임워크 파일이 최종 결과물인 앱(.app)에 포함되지는 않습니다.
  2. Embedding (임베드): 링크된 프레임워크를 실제 사용자에게 배포될 앱 번들(.app 파일 내부)에 복사하여 포함시키는 과정입니다. 이렇게 해야 사용자의 기기에서 앱이 실행될 때 dyld가 앱 내부에서 해당 프레임워크를 찾을 수 있습니다.

'Image not found' 오류는 대부분 '링크'는 되었지만 '임베드'가 누락되었을 때 발생합니다. 컴파일러는 프레임워크의 존재를 알고 있으니 빌드에 성공하지만, 막상 실행하려고 보니 앱 번들 안에 프레임워크 파일이 없어서 dyld가 실패하는 것입니다.

해결책: 'Frameworks, Libraries, and Embedded Content' 설정 확인

이 문제를 해결하는 가장 직접적인 방법은 Xcode의 Target 설정에서 해당 프레임워크를 '임베드'하도록 명시하는 것입니다.

  1. Xcode에서 프로젝트 네비게이터(좌측 패널)의 최상단에 있는 프로젝트 파일을 선택합니다.
  2. 중앙 에디터에서 TARGETS 목록의 해당 앱 타겟을 선택합니다.
  3. 상단 탭에서 General을 선택합니다.
  4. 아래로 스크롤하여 'Frameworks, Libraries, and Embedded Content' 섹션을 찾습니다.

이 목록에서 오류를 일으키는 프레임워크를 찾으십시오. 아마도 Embed 컬럼이 'Do Not Embed'로 설정되어 있을 것입니다. 이것이 문제의 원인입니다.

Xcode의 General 탭에서 Frameworks, Libraries, and Embedded Content 섹션을 보여주는 이미지. 특정 프레임워크의 Embed 설정을 'Embed & Sign'으로 변경하는 것을 강조.

오류가 발생하는 라이브러리의 Embed 설정을 'Do Not Embed'에서 'Embed & Sign'으로 변경하는 것이 가장 일반적인 해결책입니다.

해당 프레임워크의 'Do Not Embed'를 클릭하여 드롭다운 메뉴를 열고, 'Embed & Sign'으로 변경해 주세요. ('Embed Without Signing'은 특별한 경우가 아니면 사용하지 않습니다. 일반적으로 프레임워크도 앱과 함께 서명되어야 합니다.)

만약 목록에 프레임워크가 아예 없다면, 섹션 하단의 '+' 버튼을 눌러 직접 추가한 후 'Embed & Sign'으로 설정하면 됩니다.

이 설정을 변경한 후, 프로젝트를 클린(Cmd + Shift + K)하고 다시 빌드하여 실행하면 대부분의 간단한 'Image not found' 오류는 해결됩니다.

원인 2: 잘못된 Runpath Search Paths (@rpath) 설정

만약 위 방법으로 해결되지 않았다면, 조금 더 깊은 레벨의 문제일 수 있습니다. 오류 로그를 다시 자세히 보면 @rpath/SomeFramework.framework와 같은 경로를 볼 수 있습니다. 여기서 @rpath는 무엇일까요?

@rpath는 'Runpath-dependent library'를 의미하며, dyld에게 "이 프레임워크를 찾기 위해 특정 디렉토리 목록을 검색해봐"라고 알려주는 일종의 플레이스홀더입니다. 앱 실행 파일은 내부에 "내가 사용하는 라이브러리들은 @rpath 경로에 있어"라는 정보를 가지고 있고, 동시에 dyld가 검색해야 할 @rpath의 실제 경로 목록도 지정해 줍니다.

일반적으로 iOS 앱의 경우, 임베드된 프레임워크는 앱 번들 내의 Frameworks라는 폴더에 위치합니다. 따라서 @rpath는 이 Frameworks 폴더를 가리키도록 설정되어야 합니다. 이 설정은 Xcode의 빌드 설정에서 관리됩니다.

해결책: 'Runpath Search Paths' 빌드 설정 확인

  1. 앱 타겟 설정에서 General 탭 대신 Build Settings 탭을 선택합니다.
  2. 상단의 검색창에 'Runpath Search Paths'를 입력합니다.
  3. Linking 섹션 아래에 해당 설정이 나타납니다.

이 설정값이 비어 있거나 잘못된 경로를 가리키고 있다면 문제가 발생합니다. 일반적으로 iOS 앱의 경우, 이 값은 다음과 같이 설정되어 있어야 합니다.


@executable_path/Frameworks

여기서 @executable_path는 앱의 메인 실행 파일이 위치한 디렉토리를 의미하는 또 다른 플레이스홀더입니다. 따라서 위 설정은 "앱 실행 파일이 있는 위치의 하위 폴더인 'Frameworks' 폴더를 검색하라"는 의미가 됩니다. 이 설정이 올바르게 되어 있는지 확인하고, 만약 비어 있다면 '+' 버튼을 눌러 위 값을 추가해 주세요.

원인 3: 아키텍처(Architecture) 불일치 문제

이 오류는 시뮬레이터에서는 잘 실행되던 앱이 실제 기기에서는 크래시하거나, 그 반대의 경우에 자주 발생합니다. 이는 프레임워크가 빌드된 CPU 아키텍처와 앱이 실행되는 환경의 아키텍처가 맞지 않기 때문입니다.

  • iOS 시뮬레이터:
    • Intel 기반 Mac: x86_64 아키텍처
    • Apple Silicon (M1/M2/...) 기반 Mac: arm64 아키텍처
  • 실제 iOS 기기 (iPhone, iPad): arm64 아키텍처 (과거에는 armv7 등도 있었으나 현재는 대부분 arm64)

만약 여러분이 사용하는 프레임워크가 arm64(실제 기기용)로만 빌드되었다면, Intel Mac의 시뮬레이터(x86_64)에서 실행할 때 `Image not found` 오류가 발생할 수 있습니다. dyld가 시뮬레이터 아키텍처에 맞는 버전의 프레임워크를 찾지 못하기 때문입니다.

해결책: 프레임워크 아키텍처 확인 및 Universal Framework 사용

터미널을 사용하여 프레임워크가 어떤 아키텍처를 지원하는지 확인할 수 있습니다.


# 프레임워크의 실제 바이너리 파일 경로로 이동해야 합니다.
cd SomeFramework.framework
lipo -info SomeFramework

이 명령을 실행했을 때 나오는 결과로 지원 아키텍처를 알 수 있습니다.

  • Architectures in the fat file: SomeFramework are: x86_64 arm64 -> Universal Framework로, 시뮬레이터와 기기 모두 지원합니다. (가장 이상적)
  • Non-fat file: SomeFramework is architecture: arm64 -> 실제 기기용으로만 빌드되었습니다.
  • Non-fat file: SomeFramework is architecture: x86_64 -> Intel 시뮬레이터용으로만 빌드되었습니다.

만약 아키텍처가 맞지 않는다면, 해결책은 프레임워크를 제공한 곳에 문의하여 Universal Framework(또는 XCFramework)를 받거나, 소스 코드가 있다면 직접 모든 필요한 아키텍처를 포함하여 빌드해야 합니다. Xcode 빌드 설정의 'Excluded Architectures' 항목을 조절하여 특정 아키텍처를 제외하고 빌드하는 임시방편도 있지만, 근본적인 해결책은 아닙니다.

원인 4: 의존성 관리 도구(CocoaPods, Carthage, SPM) 설정 오류

대부분의 개발자들은 외부 라이브러리를 관리하기 위해 CocoaPods, Carthage, Swift Package Manager(SPM)와 같은 도구를 사용합니다. 이 도구들은 위에서 설명한 임베드 및 경로 설정을 자동으로 처리해주지만, 가끔 설정이 꼬이면서 문제가 발생할 수 있습니다.

  • CocoaPods:
    • pod install 또는 pod update를 실행한 후 프로젝트를 클린하고 다시 빌드해 보세요.
    • `Podfile`에서 프레임워크에 use_frameworks! 옵션이 올바르게 사용되었는지 확인하세요.
    • Build Phases에 '[CP] Embed Pods Frameworks' 스크립트가 제대로 포함되어 있고 실행되는지 확인하세요.
  • Carthage:
    • Carthage는 프레임워크를 빌드만 해줄 뿐, 프로젝트에 통합하는 것은 수동입니다. '원인 1'에서 설명한 것처럼 프레임워크를 'Frameworks, Libraries, and Embedded Content'에 수동으로 추가하고 'Embed & Sign'으로 설정했는지 다시 확인하세요.
    • Build Phases에 Carthage가 요구하는 copy-frameworks 스크립트가 올바르게 설정되었는지 확인하세요.
  • Swift Package Manager (SPM):
    • SPM은 가장 자동화가 잘 되어 있어 문제가 적지만, 가끔 캐시 문제로 오류가 발생할 수 있습니다. Xcode의 'File' 메뉴에서 'Packages' > 'Reset Package Caches' 또는 'Resolve Package Versions'를 실행해 보세요.
    • 라이브러리가 동적(dynamic) 라이브러리로 제공되는지, 정적(static) 라이브러리로 제공되는지 확인하고, 프로젝트 설정과 맞는지 점검이 필요할 수 있습니다.

3. 체계적인 문제 해결을 위한 진단 절차

다양한 원인을 살펴보았으니, 이제 실제 문제에 부딪혔을 때 따라할 수 있는 체계적인 진단 절차를 정리해 보겠습니다.

  1. 오류 로그 정밀 분석: 크래시 로그에서 'Library not loaded' 뒤에 나오는 프레임워크 이름(예: `SomeFramework.framework`)을 정확히 확인합니다. 이것이 문제의 대상입니다.
  2. 가장 흔한 원인부터 확인 (Embed 설정):
    • Xcode Target > General > 'Frameworks, Libraries, and Embedded Content' 섹션으로 이동합니다.
    • 문제의 프레임워크를 찾아 Embed 설정이 'Embed & Sign'으로 되어 있는지 확인합니다. 아니라면 변경합니다.
    • 프로젝트 클린 (Cmd + Shift + K) 후 다시 빌드하여 테스트합니다. (대부분 여기서 해결됩니다.)
  3. Runpath 설정 확인:
    • 1단계로 해결되지 않았다면, Target > Build Settings에서 'Runpath Search Paths'를 검색합니다.
    • 값이 @executable_path/Frameworks를 포함하고 있는지 확인합니다. 없다면 추가합니다.
    • 다시 클린 빌드 후 테스트합니다.
  4. 의존성 관리 도구 재설정:
    • CocoaPods 사용자: 터미널에서 pod deintegrate 실행 후 pod install을 다시 실행하여 프로젝트 설정을 초기화합니다.
    • SPM 사용자: Xcode 메뉴에서 패키지 캐시를 리셋합니다.
    • Carthage 사용자: carthage update를 다시 실행하고, 프로젝트에 프레임워크를 추가하는 과정을 다시 한번 점검합니다.
  5. Derived Data 삭제: Xcode의 설정이 꼬였을 가능성을 배제하기 위해, Derived Data 폴더를 삭제하는 것이 도움이 될 수 있습니다. Xcode의 'File' > 'Project Settings' 또는 'Workspace Settings'에서 Derived Data 경로를 찾아 해당 폴더를 통째로 삭제한 후 Xcode를 재시작하고 다시 빌드합니다.
  6. 아키텍처 문제 의심:
    • 특정 환경(시뮬레이터 또는 기기)에서만 문제가 발생한다면 아키텍처 불일치일 가능성이 높습니다.
    • 터미널에서 lipo -info 명령으로 프레임워크의 지원 아키텍처를 확인하고, 현재 빌드하려는 환경의 아키텍처가 포함되어 있는지 확인합니다.
  7. (고급) otool로 확인: otool은 Mach-O 파일을 분석하는 강력한 커맨드라인 도구입니다. 앱 실행 파일이 어떤 경로로 라이브러리를 찾으려고 하는지 직접 확인할 수 있습니다.
    
    # .app 파일이 생성되는 경로로 이동해야 합니다. (보통 DerivedData 폴더 안)
    otool -L YourApp.app/YourApp
            
    이 명령은 앱이 의존하는 모든 라이브러리와 dyld가 찾으려고 시도할 경로를 보여줍니다. 여기서 문제의 프레임워크 경로가 @rpath/...로 제대로 표시되는지, 다른 이상한 경로는 없는지 확인할 수 있습니다.

결론: 단순한 실수를 넘어 시스템의 이해로

'dyld: Library not loaded: Reason: image not found' 오류는 처음 마주하면 매우 당황스럽고 복잡해 보이지만, 그 본질을 파고들면 iOS/macOS 앱의 빌드 및 실행 프로세스에 대한 깊은 이해를 얻을 수 있는 좋은 기회가 됩니다. 대부분의 경우, 이 문제는 프레임워크를 '링크'만 하고 최종 앱 번들에 '임베드'하는 것을 잊어버린 단순한 설정 실수에서 비롯됩니다.

하지만 때로는 런패스(runpath)나 아키텍처, 코드 서명과 같은 더 복잡한 시스템 레벨의 지식을 요구하기도 합니다. 오늘 살펴본 다양한 원인과 체계적인 진단 절차를 따라 차근차근 점검해 나간다면, 어떤 상황에서 이 오류를 만나더라도 자신감 있게 문제를 해결하고, 더 나아가 이러한 문제를 미연에 방지하는 견고한 프로젝트를 구성할 수 있게 될 것입니다.

빌드가 성공했다고 끝이 아닙니다. 우리의 코드가 사용자의 기기에서 성공적으로 로드되고 실행되는 그 순간까지, 개발자의 책임은 계속됩니다. 이 오류는 바로 그 책임의 중요성을 일깨워주는 값진 교훈이라 할 수 있습니다. 다음에 이 오류를 만난다면, 당황하지 말고 dyld의 여정을 따라가며 잃어버린 'Image'를 찾아내는 탐정이 되어보시기 바랍니다.


0 개의 댓글:

Post a Comment