이재용의 iOS

Package, Bundle 알아보기

2022년 12월 20일 • ☕️ 6 min read


이번 글은 Package와 Bundle에 대하여, 그리고 둘의 차이점에 대하여 알아보고자 합니다.

Package

Package는 파인더가 여러 리소스들을 하나의 유닛으로 묶어 사용자에게 디렉토리를 단일 파일처럼 보여지게 합니다. 여러 리소스들을 하나로 압축시키고 연결시키는 작업을 통해 사용자에게 편의성을 제공합니다. Xcode에서 앱을 하나 만들어봅니다. 그렇다면 .xcodeproj 파일이 생성되지만 엄밀히 말하면 이것은 파일이 아닌 디렉토리입니다. 그리고 파인더에 의해 생성되는 패키지입니다. 패키지는 아래와 같이 여러가지 종류들이 있습니다.

.app, .playground, .plugin, .xcodeproj etc…

Bundle과의 차이점을 비교할 때 더 확실히 이해할 수 있습니다.

Package 내용 보기

Package는 디렉토리이기 때문에 내부 컨텐츠에 접근이 가능합니다. 패키지를 오른쪽 클릭 후, “패키지 내용 보기” 항목을 선택하면 파인더에서 패키지 내부에 접근할 수 있습니다. 터미널에서도 경로를 통해 접근 가능합니다.

show-package-content

Package의 컨텐츠를 코드로 접근할 수 있습니다. 그리고 이 컨텐츠는 Package의 종류에 따라 접근하는 방식이 다릅니다.

  • Package가 Bundle 구조를 가지고 있을 때, Bundle을 통해 접근할 수 있습니다.

여기서 Bundle 구조와 Bundle은 다른 개념입니다. Bundle 구조에서 Bundle은 뒤에서 설명하겠지만 디렉토리이고, Bundle은 Swift3에서 NSBundle의 명칭이 바뀐 용어입니다. NSBundle은 Bundle 디렉토리 안에 포함된 리소스에 쉽게 접근할 수 있도록 도와주는 객체입니다.

  • Package가 도큐멘트일 경우, iOS에선 UIDocument를 통해 접근할 수 있습니다.

앱 번들에 포함되는 코드들은 .xcodeproj 패키지에 포함되어 있지 않습니다. 이전 글에서 알아보았지만 .phxproj파일은 프로젝트의 설정 파일로 파일에 대한 레퍼런스를 가지고 있습니다. 이를 실행하면 프로젝트가 실행되지만 프로젝트를 빌드할 때, Bundle 디렉토리가 생성됩니다.

Bundle

Bundle은 실행가능한 코드들과 해당 코드들이 사용하는 리소스들을 포함하는 디렉토리입니다. 코드와 리소스를 모으는 구조를 제공하여 개발자 경험을 우선시합니다. (Package는 사용자 경험을 우선시합니다.) 이 구조는 코드와 리소스들을 동적으로 로드하게 만들고 로컬리제이션 기능도 제공합니다.

Main Bundle

Xcode 프로젝트들은 Cmd+R을 통해 빌드할 때, Bundle을 생성합니다. 모든 프로젝트들은 각 애플리케이션마다 main bundle을 소유합니다. Main bundle은 현재 실행중인 코드가 포함된 주요 디렉토리입니다. 사용자가 앱을 실행하면 main bundle에서 즉시 필요한 코드와 리소스를 찾아 메모리에 로드합니다.

Main Bundle 위치

Main bundle의 위치를 알아보기 위해서 NSBundle 객체에 먼저 알아야합니다. NSBundle은 Bundle 디렉토리 안에 포함된 리소스에 쉽게 접근할 수 있도록 도와주는 객체입니다. Swift3에서 Bundle 객체로 명칭이 변경되었고 디렉토리를 의미하는 Bundle과는 헷갈리지 않도록 조심해야합니다. ⚠️

아래 코드를 통해 생성되는 Bundle의 경로를 알 수 있습니다.

Bundle.main.bundleURL
// file:///Users/jyong/Library/Developer/CoreSimulator/Devices/F0B3974A-4251-4C16-9EC7-F77795F0F77F/data/Containers/Bundle/Application/0A56C385-BB01-4C1D-AA9E-B328D2E44DFE/MiniApp.app/

Bundle.main.bundlePath
// /Users/jyong/Library/Developer/CoreSimulator/Devices/F0B3974A-4251-4C16-9EC7-F77795F0F77F/data/Containers/Bundle/Application/0A56C385-BB01-4C1D-AA9E-B328D2E44DFE/MiniApp.app

해당 경로를 따라가보면 아래 사진같이 MiniApp.app 응용 프로그램을 확인할 수 있습니다. 프로젝트를 빌드할 때 생성되는 main bundle입니다.

main-bundle-location

프로젝트를 빌드할 때 생성되는 Main bundle은 Project Build Configuration의 Signing 옵션에서 Bundle Identifier에 따라 구분됩니다. 같은 Bundle Identifier일 경우, 빌드할 때마다 덮어 씌워지고 다를 경우 새로운 Bundle을 생성합니다.

아래 사진에서 0A56C… 이름의 디렉토리는 Bundle Identifier가 “com.Mini”인 타겟을 빌드하여 생성된 디렉토리이고 2BA1A6C… 이름의 디렉토리는 “com.MiniApp”인 타겟을 빌드하여 생성된 디렉토리입니다.

main-bundle-location

Main Bundle에 리소스 추가하기

Main bundle은 코드와 리소스를 포함하는 디렉토리입니다. Mini.app 패키지에 “패키지 내용 보기”를 통해 접근하면 아래와 같은 파일들이 있습니다.

  • executable code: ViewController, AppDelegate… 등과 같이 실행 가능한 코드들
    .exe 실행파일 형태로 존재하고 있습니다. nm 명령어를 통해 터미널에서 확인할 수 있습니다.
  • info.plist : 프로젝트의 빌드 세팅을 포함하는 리소스
  • images, sound, nib 파일과 같은 리소스

Main bundle은 image, sound 등과 같이 외부 리소스를 프로젝트에 추가하고 접근할 때 가장 흔히 사용됩니다. 이미지를 프로젝트 네비게이터에 드래그 드롭을 통해 추가할 경우 아래 창이 뜹니다.

add-resource

이미지 파일을 추가하는 방식에는 Create groups와 Create folder references 2가지가 있습니다. Create groups 옵션으로 추가한 모든 것들은 모두 Copy Bundle Resources항목에 별도 디렉토리 없이 바로 파일만 추가됩니다. 즉, Bundle에 따로 디렉토리 생성 없이 mainBundle의 root에 파일들이 복사됩니다. 같은 경로에 배치되는 것이 중요합니다. 이 리소스들은 모두 아래의 명령어를 통해 접근할 수 있습니다.

Bundle.main.url(forResource: "img", withExtension: "png")

⚠️ 같은 이름의 파일들을 Create groups로 추가할 경우 발생하는 오류

이제 자주 발생하는 경로 오류에 대해 이제 이유를 알 수 있습니다. 프로젝트에 같은 리소스 파일이름이 있으면 Multiple Commands produce 빌드 오류가 납니다. 이는 빌드를 통해 Bundle을 생성하는 과정에서 같은 경로에 같은 이름의 파일이 두개가 존재하면 안 되기 때문에 발생하는 오류입니다.

error-path

Create folder references 옵션을 이용해서 추가한 리소스같은 경우 project navigator에 일반적인 회색 디렉토리가 아닌 파란색 디렉토리가 추가됩니다. (아래 사진의 img 디렉토리와 같이)

create-folder-references

이렇게 추가된 디렉토리는 Copy Bundle Resources 항목에서도 디렉토리로 나타나고 접근 시에도 sub directory 파라미터를 사용해야합니다.

Bundle.main.url(forResource: "img", withExtension: "png", subdirectory: "img")

Copy Bundle Resources

Copy Bundle Resources는 프로젝트 Build Phases에서 확인할 수 있습니다. 이 항목은 Xcode가 mainBundle을 생성한 후, mainBundle에 들어가 있는 리소스 파일들 중 어느 리소스 파일을 앱에 추가할 지 결정할 때 사용됩니다. 즉, 이 항목에 없는 리소스는 Project navigator에 추가되어 있어도 실제로 Bundle에 추가되지 않습니다. 따로 main bundle의 경로로 불러와도 없기 때문에 nil이 나옵니다.

MiniApp.app은 Package? Bundle?

그렇다면 위에서 확인한 MiniApp.app은 패키지일까? 번들일까? 둘 다 입니다. 분명 위에서 프로젝트를 빌드하면 .app 확장자를 가진 main bundle이 생성된다고 했습니다. 하지만 오른쪽 클릭을 하면 “패키지 내용 보기”라는 항목을 볼 수 있습니다.


Package와 Bundle은 같은 의미로 언급될 때도 있습니다. 하지만 위에서 설명했듯이 다른 개념입니다.

Package는 사용자에게 제공하는 디렉토리입니다. 지금 Mac에서 응용 프로그램을 보면 모두 아이콘들이 놓여져 있습니다. 이는 모두 파인더가 단일 파일인 것처럼 사용자에게 제공하는 것일뿐 실제로는 디렉토리입니다. 단일 파일처럼 취급되는 이유는 일반 사용자가 Package에 부정적인 영향을 줄 수 있는 변경을 방지하기 위해서입니다. Bundle은 개발자에게 제공되는 디렉토리입니다. 단순히 폴더 역할 뿐만 아니라 위에서 설명한 여러 기능들을 추가로 제공합니다.

그럼 .app같은 경우엔 왜 같은 의미로 언급되는 것일까? 파인더는 아래의 조건 중 하나라도 해당된다면 디렉토리를 Package로 취급합니다.

  • 디렉토리가 .app, .bundle, .framework, plugin 등의 파일 확장자를 가진 파일인 경우
  • 디렉토리에 있는 Application이 Package타입을 나타내는 확장자가 있는 경우

위의 이유로 많은 유형의 Bundle이 Package입니다. 따라서 .app은 Package이자 Bundle이 됩니다.

요약

  • 번들은 하나의 앱(타겟)을 구성하는 코드와 리소스들을 관리하는 디렉토리이다.
  • 패키지는 사용자를 위해 단일 파일처럼 제공되는 디렉토리이다.
  • 리소스를 추가할 때, Create groups옵션은 번들의 root에 파일들이 복사하고 Create folder references옵션은 실제 파인더의 파일 시스템까지 복사되기 때문에 번들의 root에 디렉토리가 복사된다.

Reference