← 모든 글

On Demand Resources 지원 중단: Background Assets가 요구하는 비용

Apple은 10년 묵은 콘텐츠 전송 시스템을 13개 단어짜리 문장 하나로 정리했습니다. “On Demand Resources와 NSBundleResourceRequest API는 지원 중단됩니다. 대신 Background Assets를 사용하세요.”1

이 한 문장이 항목의 전부입니다. 레이더 170066290을 달고 Deprecations 아래에 놓여 있으며, 서로 다른 릴리스 노트 세 곳에 토씨 하나 다르지 않게 반복됩니다.123 Apple은 제거 버전도, 이 지원 중단에 맞춘 마이그레이션 안내서도, macOS나 watchOS 릴리스 노트에 짝이 되는 언급도 내놓지 않았습니다.

일은 마지막 네 단어에 숨어 있습니다. Background Assets는 하나가 아닙니다. 호스팅 모델이 다르고, 최소 배포 타깃이 다르고, 마이그레이션 비용이 대략 10배 차이로 벌어지는 세 가지 구성으로 갈라집니다. 이 선택은 코드 한 줄을 쓰기 전에 이루어지며, 잘못 고르는 것이 값비싼 실수입니다.

TL;DR

  • Apple은 iOS 및 iPadOS 27, tvOS 27, visionOS 27 릴리스 노트에서 On Demand Resources와 NSBundleResourceRequest를 지원 중단했습니다.123 ODR은 계속 동작합니다. Apple은 제거 시점을 밝히지 않았고, 지원 중단된 API는 여전히 출시 빌드에 담기며 태그도 그대로 해석합니다.
  • API 자체의 가용성 데이터는 여섯 개 플랫폼에서 지원 중단을 표시하는데, 이는 어떤 릴리스 노트가 언급한 것보다 두 개 많습니다. Mac Catalyst와 watchOS입니다.4 Background Assets는 watchOS 가용성을 아예 게시하지 않으므로, watchOS는 문서화된 대체 수단 없이 지원 중단만 받게 됩니다.56
  • “대신 Background Assets를 사용하세요”는 세 갈래로 갈립니다. Apple이 호스팅하는 관리형, 직접 호스팅하는 관리형, 그리고 비관리형입니다. 관리형 두 경로는 모두 최소 배포 타깃으로 iOS 26.0을 요구합니다.78 26.0 미만에서는 비관리형 경로만 열려 있으며, 이 경로는 파일을 직접 호스팅하고 매니페스트 형식을 직접 만들고 직접 파싱할 것을 요구합니다.9
  • 디스크 관리는 뒤집힙니다. ODR에서는 시스템이 쓰이지 않는 태그 리소스를 정리했고, setPreservationPriority(_:forTags:)는 그 순서에 대한 힌트로 받아들여졌습니다.410 Background Assets는 코드가 remove(assetPackWithID:)를 호출하기 전까지 내려받은 팩을 전부 기기에 남겨 둡니다.11
  • Apple은 압축된 에셋 팩을 최대 200GB, 앱 레코드당 최대 200개까지 호스팅하며, 이 한도는 앱이 제공하는 모든 플랫폼에 걸쳐 공유되고, 업로드는 App Store Connect로, 심사는 빌드와 별도로 진행됩니다.1213

Apple이 정확히 무엇을 지원 중단했는가

이 항목에서 무게를 지니는 세부 사항은 셋인데, 항목 자체는 그중 무엇도 말해주지 않습니다.

적용 범위는 릴리스 노트보다 넓습니다. 이 항목을 담은 문서는 셋입니다. iOS 및 iPadOS 27, tvOS 27, visionOS 27이고, 각각 On Demand Resources 제목 아래 Deprecations 소제목에 놓여 있으며, 모두 같은 레이더 번호를 인용합니다.123 macOS 27과 watchOS 27 노트는 이에 대해 아무 말도 하지 않습니다.

API의 가용성 주석은 더 넓은 이야기를 들려줍니다. NSBundleResourceRequest는 iOS 9.0에 도입되어 27.0에서 지원 중단된 것으로 표기되고, iPadOS 9.0, Mac Catalyst 13.1, tvOS 9.0, visionOS 1.0, watchOS 2.0에도 동일한 27.0 지원 중단이 붙어 있습니다.4 메타데이터에는 여섯 플랫폼, 노트 전체에서 이름이 불린 것은 넷입니다. Bundle의 보존 우선순위 메서드 두 개도 같은 여섯 플랫폼 주석을 답니다.10 릴리스 노트만 보고 점검하면 Mac Catalyst와 watchOS 항목을 통째로 놓칩니다.

Mac Catalyst 항목은 명목상의 것입니다. Apple은 NSBundleResourceRequest가 “Mac Catalyst로 빌드한 Mac 앱에서 온 호출을 무시한다”고 문서화해 두었으므로, 이 지원 중단은 거기서 애초에 아무 일도 하지 않던 API를 거둬들이는 셈입니다.4 watchOS 항목은 명목상의 것이 아닙니다. Background Assets는 iOS 16.0, iPadOS 16.0, Mac Catalyst 16.0, macOS 13.0, tvOS 18.4, visionOS 2.4의 가용성을 나열하며 watchOS 행은 없습니다.5 Apple은 Apple이 호스팅하는 에셋 팩이 “watchOS를 제외한 모든 플랫폼에서 App Store를 통해 배포되는 앱에 사용 가능”하다고 분명히 밝힙니다.6 ODR을 쓰는 watchOS 앱은 지원 중단 경고를 받고, 옮겨 갈 대체 수단은 받지 못합니다.

강제력은 제가 다룬 27 사이클의 세 가지 변화 중 가장 약합니다. 지원 중단은 지원 중단일 뿐입니다. Apple은 제거 버전도, 제출 관문도, 런타임 실패도 내놓지 않았습니다. 같은 사이클의 씬 생명주기 의무화와 비교해 보세요. 거기서는 새 SDK로 빌드한 앱이 “실행되지 않습니다”. 런치 스크린 요구사항에서는 App Store가 빌드를 아예 거부합니다. ODR은 둘 중 어느 쪽도 하지 않습니다. 기존 태그는 계속 해석되고, 기존 호출은 계속 리소스를 반환하며, 이미 출시된 바이너리는 계속 동작합니다. 도착한 것은 컴파일러 경고와 아무도 읽을 수 없는 시계뿐입니다.

시점을 보면 뒷정리가 아니라 결정입니다. Apple은 대체 수단의 관리형 계층이 출시된 지 한 릴리스 만에, 네 개 플랫폼 이름에 걸쳐 이 API를 한꺼번에 지원 중단했습니다. 이 지원 중단은 비상 상황이 아니라, Apple이 길이를 밝히지 않은 마이그레이션 창이 열린 것으로 받아들이는 편이 맞습니다.

코드를 쓰기 전에 갈리는 분기

Apple의 프레임워크 개요는 두 가지 호스팅 모델과, “다운로드, 업데이트, 압축 등을 대신 처리해 주는” 관리형 기본값을 설명합니다. Apple 호스팅의 경우 “에셋을 App Store Connect에 업로드하고, 앱 빌드와 비슷한 방식으로 그곳에서 관리”합니다.5 Xcode의 Background Download 익스텐션 템플릿은 두 가지 타입을 직접 이름으로 제시합니다. Apple 호스팅 관리형과 자체 호스팅 비관리형입니다.911 세 번째 조합은 프로퍼티 리스트 키 안에 존재합니다. BAHasManagedAssetPacksYES로 두고 BAUsesAppleHosting은 켜지 않으면 직접 호스팅하는 관리형 에셋 팩이 선택되며, 이때 문서는 StoreKit의 StoreDownloaderExtension이 아니라 ManagedDownloaderExtension 프로토콜로 안내합니다.7814

Apple 호스팅 관리형 자체 호스팅 관리형 자체 호스팅 비관리형
최소 iOS 26.0715 26.0714 16.19
파일 호스팅 주체 Apple, App Store Connect 경유6 직접 직접
매니페스트 형식 Apple의 JSON 스키마11 Apple의 JSON 스키마11 직접 만들고 직접 파싱9
익스텐션 프로토콜 StoreDownloaderExtension716 ManagedDownloaderExtension714 BADownloaderExtension9
다운로드·업데이트·압축 시스템5 시스템5 직접9
프로퍼티 리스트 키 3개11 전 과정이 문서화되어 있지 않음7 최상위 4개, 그중 하나가 하위 3개를 포함9

대다수 팀에게는 배포 타깃이 이 선택을 대신 결정합니다. 관리형 진입점은 AssetPackManagerManagedDownloaderExtension부터 세 개의 프로퍼티 리스트 키까지 전부 최소 iOS 26.0을 요구합니다.781415 iOS 18을 지원하는 앱은 관리형 계층을 아예 채택할 수 없고, 선택지는 iOS 16.1까지 거슬러 올라가는 비관리형 경로이거나, 배포 타깃이 올라갈 때까지 지원 중단된 ODR에 머무는 것으로 좁혀집니다.9

비관리형 경로는 성격이 다른 일입니다. Apple이 제시한 순서는, 앱이 실행되기 전에 시스템이 BAManifestURL에서 매니페스트를 내려받아 그 파일을 익스텐션에 건네고, 다운로드 요청 집합을 돌려받는 것입니다.9 Apple은 역할 분담을 에두르지 않고 못박습니다. “자체 호스팅 비관리형 에셋의 매니페스트 파일을 (원하는 형식으로) 만드는 것은 여러분의 책임이며, 시스템에 URL과 파일 크기를 전달하도록 그 파일을 파싱하는 코드도 여러분의 몫입니다.”9 호스팅, CDN, 매니페스트 스키마, 파서, 도메인 허용 목록을 전부 직접 마련해야 합니다. “대신 Background Assets를 사용하세요”를 API 교체 정도로 읽는 사람은 관리형 계층을 읽으면서 비관리형 청구서를 인용하고 있는 셈입니다.

ODR이 대신 해주던 일, 이제 손으로 다시 만들어야 하는 것

ODR의 동작 세 가지에는 그대로 갈아 끼울 수 있는 대응물이 없습니다.

태그는 에셋 팩이 됩니다. ODR은 Xcode에서 할당한 문자열 태그로 콘텐츠를 식별했고, NSBundleResourceRequest(tags:)가 그중 하나 이상을 점유했습니다.4 Background Assets는 태그를 에셋 팩으로 대체합니다. 에셋 팩은 JSON 매니페스트가 기술하는 파일 디렉터리이며, 커맨드 라인 도구가 이를 .aar 아카이브로 압축합니다. 세분화 단위가 커지고, 할당 작업은 Xcode의 에셋 카탈로그를 떠나 여러분이 직접 관리하는 파일로 옮겨 갑니다.

Apple 호스팅은 별도의 출시 노선이 됩니다. ODR 콘텐츠는 빌드에 얹혀 함께 갔습니다. Apple이 호스팅하는 에셋 팩은 Transporter, altool, iTMSTransporter 또는 App Store Connect API를 통해 독립적으로 업로드하고, 앱과 별도로 App Review에 제출합니다.1113 이 분리 자체가 기능입니다. 빌드를 내보내지 않고도 새 콘텐츠를 출시할 수 있으니까요. 대가는 두 번째 제출 파이프라인, 두 번째 심사 대기열, 그리고 지켜봐야 할 두 번째 상태 집합입니다.

자동 정리는 여러분의 일이 되며, 이 역전이 가장 날카로운 변화입니다. ODR은 내려받은 콘텐츠를 시스템이 소유한 캐시로 취급했습니다. Apple은 “NSBundleResourceRequest 객체가 하나라도 해당 태그를 관리하고 있는 한, 시스템은 그 태그로 표시된 리소스를 기기 저장 공간에서 정리하려 시도하지 않는다”고 문서화했는데, 이는 정리를 하느냐 마느냐가 아니라 언제 하느냐에 대한 약속입니다.4 setPreservationPriority(_:forTags:)는 바로 “번들 안의 태그별 리소스 묶음을 정리할 상대적 순서를 시스템에 알려주는 힌트”를 주기 위해 존재했습니다.10 점유를 끝내면 시스템이 자기 일정에 따라 공간을 회수했고, 저장 공간 부족 처리는 Apple의 문제였습니다.

Background Assets는 이 소유권을 뒤집습니다. 시스템은 팩을 자동으로 최신 상태로 유지하고, checkForUpdates()는 서버에서 더 이상 유효하지 않게 된 팩을 제거합니다.15 하지만 두 메커니즘 중 어느 것도 여러분이 그냥 다 쓴 팩을 쫓아내지는 않습니다. Apple의 지시는 명시적입니다. “앱이 설치되어 있는 동안 시스템은 에셋 팩을 자동으로 제거하지 않습니다. 따라서 에셋 팩을 다 사용했다면 remove(assetPackWithID:) 메서드를 호출하세요.”11

범위에 묶인 참조 카운팅 패턴은, 호출할지 말지를 여러분이 판단해야 하는 명시적 삭제로 무너져 내립니다.

// ODR: claim a tag, use the file, release the claim.
// The system reclaims the space afterward on its own schedule.
let request = NSBundleResourceRequest(tags: ["Tutorial"])
try await request.beginAccessingResources()
let url = Bundle.main.url(forResource: "Introduction", withExtension: "m4v")
request.endAccessingResources()
import System  // url(for:) and contents(at:) take a FilePath

// Background Assets: ensure the pack, read the file, delete the pack.
// Nothing reclaims the space if you skip the last line.
let manager = AssetPackManager.shared
guard let pack = try await manager.manifest.assetPack(withID: "Tutorial") else { return }
try await manager.ensureLocalAvailability(of: pack, requireLatestVersion: false)
let url = try manager.url(for: "Videos/Introduction.m4v")
try await manager.remove(assetPackWithID: "Tutorial")

ODR의 정리 가정을 안고 있는 앱은 모두 그에 맞는 저장 정책을 새로 써야 합니다. 실패 양상은 조용합니다. 아무것도 크래시하지 않고, 아무 경고도 뜨지 않으며, 사용자가 저장 공간 목록에서 여러분의 앱을 알아챌 때까지 기기 저장 공간만 계속 불어납니다.

마이그레이션 작업 범위, 항목별로

Apple이 호스팅하는 관리형 경로에서는, Apple이 제시한 단계를 그대로 따라가면 다음 점검 목록이 나옵니다.

파일을 에셋 팩으로 묶고 팩마다 다운로드 정책을 고릅니다. 정책은 셋입니다. essential은 설치 중에 내려받고, App Store와 TestFlight, 홈 화면에서 사람들이 지켜보는 진행률에 반영됩니다. prefetch는 설치 중에 시작해 설치가 끝난 뒤에도 백그라운드에서 이어집니다. onDemand는 코드가 요청할 때만 내려받습니다.11 essentialprefetch에서는 중첩된 installationEventTypes 배열이 firstInstallation, subsequentUpdate 또는 둘 다를 받으므로, 튜토리얼 팩을 최초 설치 때만 내려받고 이후의 모든 업데이트에서는 건너뛰게 할 수 있습니다.11

팩마다 매니페스트를 작성합니다. Xcode가 주석이 달린 템플릿을 만들어 줍니다.

xcrun ba-package template -o Manifest.json
{
  "assetPackID": "Tutorial",
  "downloadPolicy": {
    "essential": {
      "installationEventTypes": ["firstInstallation"]
    }
  },
  "fileSelectors": [
    { "file": "Videos/Introduction.m4v" },
    { "directory": "Textures/Tutorial" }
  ],
  "platforms": ["<identifiers from the generated template comments>"]
}

파일 경로는 패키징 명령을 실행한 디렉터리를 기준으로 해석되며, 나중에 경로로 파일을 다시 읽을 때 이 점이 다시 중요해집니다.11 각 팩을 아카이브합니다.

xcrun ba-package Manifest.json -o Tutorial.aar

Application Extension 아래에 Background Download 익스텐션 타깃을 추가하고, 타입으로 Apple 호스팅 관리형을 고릅니다. 앱과 익스텐션 양쪽에 App Groups 기능을 추가하고 둘을 같은 그룹에 넣습니다. 그런 다음 앱 타깃에 프로퍼티 리스트 키 세 개를 추가합니다. BAAppGroupID, YES로 설정한 BAHasManagedAssetPacks, 그리고 YES로 설정한 BAUsesAppleHosting입니다. Apple은 Apple 호스팅 프로젝트에서는 나머지 Background Assets 키를 전부 생략하라고 안내합니다.11 걸려 넘어지기 쉬운 제약이 하나 있습니다. 짝이 되는 익스텐션 프로토콜을 함께 채택하지 않고 AssetPackManager만 채택하는 것은 Apple의 표현으로 “프로그래머 오류”입니다.15

파일을 다시 읽는 일은 병합된 네임스페이스를 통해 이루어집니다. Apple은 “모든 에셋 팩을 공유 네임스페이스로 자동 병합해, 마치 에셋 루트 폴더가 사용자의 기기에 그대로 붙여넣어진 것처럼 재구성”하므로, 코드는 어떤 팩이 어떤 파일을 담고 있는지 추적하지 않고 경로만으로 파일을 지정합니다.11 읽기는 기본적으로 메모리 매핑된 Data를 반환하며, 절차적 로딩을 위한 파일 디스크립터 방식도 있는데 이쪽은 직접 닫아야 합니다.11

로컬 테스트에는 따로 예산을 잡아둘 만한 설정 비용이 따라붙습니다. Background Assets는 모든 다운로드에 HTTPS를 사용하므로 모의 서버에 인증서가 필요합니다. Apple이 문서화한 경로는 키체인 접근으로 자체 서명 루트 CA를 만들고, Apple Configurator로 그 CA를 담은 프로파일을 만들고, 테스트 기기마다 그 프로파일을 설치해 신뢰하도록 설정하고, 서버의 IP 주소나 호스트 이름과 정확히 일치하는 이름의 SSL 리프 인증서를 발급하고, 서버를 띄운 뒤, 기기마다 개발자 설정에서 URL 재정의를 지정하는 순서로 이어집니다.17

xcrun ba-serve --host localhost Tutorial.aar HighQualityTextures.aar

이 순서는 그 자체로 하나의 작업으로 잡아 두세요. On Demand Resources는 어느 단계에서도 여러분의 서버를 필요로 하지 않았습니다. Apple은 그것을 “App Store에 호스팅된 콘텐츠”의 관리자로 설명하며, 기기에 없는 리소스는 “App Store에 요청”됩니다.4 마이그레이션한다는 것은 다운로드를 단 한 번 시험해 보기 전에 호스팅을, 또는 신뢰할 수 있는 그 모의본을 먼저 세워 둔다는 뜻입니다. 두 작업 흐름을 나란히 놓고 읽어 보면, 마이그레이션 첫날을 잡아먹는 것은 API 채택이 아니라 인증서 체인일 겁니다.

계획의 기준이 될 숫자

Apple은 Apple이 호스팅하는 팩에 대해 두 가지 상한을 공표합니다. 총 200GB, 그리고 앱 레코드당 에셋 팩 200개입니다. 둘 다 “앱이 제공하는 모든 플랫폼에 걸쳐 공유”됩니다.12 Apple은 이 총량을 TestFlight 또는 App Store 배포 자격이 있는 버전들 가운데 최대 크기를 기준으로 계산하며, Awaiting Upload, Processing, Failed, 그리고 완전히 Superseded 상태인 버전은 제외하고, 한도의 80%에 도달하면 메일을 보냅니다. 팩을 아카이브하면 App Store에 게시 중인 버전까지 포함해 그 팩의 모든 버전이 사라지면서 공간이 회수됩니다.12

비관리형 경로는 이 상한을, 여러분이 직접 설정하는 최상위 프로퍼티 리스트 키 네 개로 대체합니다. 그중 하나는 다시 세 개를 담은 딕셔너리이고, 압축본과 비압축본의 구분은 진짜 함정입니다. BADownloadAllowanceBAEssentialDownloadAllowance는 다운로드 크기를 제한하며 압축된 값을 받습니다. BAMaxInstallSizeBAEssentialMaxInstallSize는 설치된 크기를 제한하며 압축하지 않은 값을 받습니다.9 Apple은 설치 크기 키에 경고를 붙여 두었는데, 실은 제품 차원의 경고입니다. “App Store는 이 키로 제품 페이지에 앱 크기를 표시하므로 정확한 값을 넣으세요 … 필요한 디스크 공간을 부풀리지 마세요.”18 BADownloadDomainAllowList가 이 집합을 마무리하며, 와일드카드를 위해 앞에 별표를 선택적으로 붙일 수 있는 DNS 형식 도메인을 받습니다.9

계획에 들어가야 할 숫자가 하나 더 있는데, 이것은 산문이 아니라 가용성 메타데이터에서 나옵니다. AssetPackManager는 iOS 26.0에 출시됐고, 그 멤버 중 넷은 이미 지원 중단을 달고 있습니다. ensureLocalAvailability(of:)status(ofAssetPackWithID:)가 26.4에서, 이어서 assetPack(withID:)allAssetPacks가 27.0에서입니다.19 조회, 상태, 다운로드 호출이 각각 두 번의 마이너 릴리스에 걸쳐 한 번씩 자리를 옮긴 셈입니다. Apple이 지금 가리키는 경로, 즉 매니저의 manifest 프로퍼티를 거치는 경로는 27.0 SDK에서 베타 주석을 달고 있고, 일괄 처리용 ensureLocalAvailability(of:requireLatestVersions:)도 마찬가지입니다.19 게다가 Apple 자신의 Apple 호스팅 안내서는 여전히 지원 중단된 호출 두 개를 시연합니다.11 따라서 실질적인 하한은 이 계층의 명목상 26.0보다 위에 있습니다. 앞에서 보여준 다운로드 경로를 지원 중단 경고 없이 작성하려면 27.0을 타깃으로 잡아야 하고, 그러기 위해 베타 주석이 붙은 심볼을 받아들여야 합니다. 채택하라고 지시받은 대체 수단이, 그것이 대체하는 API보다 더 어리고 더 빠르게 움직이고 있습니다.

이 지원 중단에 실제로 걸리는 쪽

영향을 받는 집단은 좁고, 저는 제 코드에서 그 집단을 찾지 못했습니다. 출시 중인 프로젝트 일곱 개에서 NSBundleResourceRequest, beginAccessingResources, setPreservationPriority, ON_DEMAND_RESOURCES 관련 빌드 설정 일체, knownAssetTags, 그리고 에셋 카탈로그 태그 구성을 검색했습니다. 일곱 개 전부, 모든 패턴에서 0건이었습니다. 제 프로젝트 디렉터리 아래의 모든 Swift, Objective-C, 프로퍼티 리스트, pbxproj 파일을 저장소 전체로 훑어도 어떤 ODR 심볼도 나오지 않았고, Background Assets 역시 나오지 않았습니다.20

이 빈 결과에는 짚어둘 만한 원인이 있고, 그 원인은 일반화됩니다. ODR은 콘텐츠가 코드를 압도하는 앱을 위해 존재합니다. 제 에셋 카탈로그 중 가장 큰 것은 Return의 66.6MB이고, 나머지 앱은 전부 8MB 미만이며 Water와 Yawara는 100KB에도 미치지 않습니다.20 이 정도 크기에서 콘텐츠를 팩으로 쪼개는 일은, 아무도 불평하지 않는 다운로드를 아끼자고 네트워크 실패 양상과 저장 정책, 두 번째 심사 파이프라인을 더하는 셈입니다. Background Assets가 정말로 필요한 앱은 레벨 콘텐츠를 가진 게임, 큰 머신러닝 모델을 함께 배포하는 앱, 그리고 언어별 영상을 담은 앱이며, 이는 정확히 Apple이 iOS 27의 현지화 에셋 팩 지원으로 겨냥한 집단입니다.21

배포 타깃은 그림을 한층 선명하게 만들며, 어떤 코드베이스에서든 가장 먼저 확인해야 할 숫자입니다. 제 iOS 앱 여섯 개 중 다섯 개는 이미 iOS 26.0 이상이므로 관리형 계층을 오늘 바로 쓸 수 있습니다. Ace Citizenship은 일부 타깃이 여전히 17.0과 17.5를 선언하고 있어 관리형 계층이 배제되고, 비관리형 경로나 지원 중단된 ODR만 남습니다.20 무엇이든 설계하기 전에 이 확인부터 하세요. 여러분에게 실제로 제안된 마이그레이션이 무엇인지를 결정하는 것은 에셋 용량이 아니라 최소 배포 타깃입니다.

자주 묻는 질문

iOS 27에서 On Demand Resources가 작동을 멈추나요?

아닙니다. Apple은 ODR과 NSBundleResourceRequest를 지원 중단으로 표시했을 뿐, 제거 버전도, 제출 관문도, 런타임 실패도 내놓지 않았습니다.1 지원 중단된 API는 계속 동작하고, 기존 태그도 계속 해석됩니다. 여러분이 받는 것은 컴파일러 경고입니다. 같은 사이클의 다른 파괴적 변경들과 비교해 보면 성격이 분명해집니다. 씬 생명주기 의무화는 앱을 “실행되지 않게” 만들고, 런치 스크린 요구사항은 App Store가 빌드를 거부하게 만듭니다. ODR의 지원 중단은 문을 닫는 것이 아니라 시계를 돌리기 시작하는 쪽입니다.

이 지원 중단은 어떤 플랫폼을 포함하나요?

릴리스 노트는 문서 세 곳에서 네 개 플랫폼을 지목합니다. iOS 및 iPadOS 27, tvOS 27, visionOS 27이고, 모두 레이더 170066290을 인용합니다.123 API의 가용성 메타데이터는 그보다 넓어서, NSBundleResourceRequestBundle의 보존 우선순위 메서드를 여섯 개 플랫폼에서 27.0 지원 중단으로 표시하며 Mac Catalyst와 watchOS를 더합니다.410 Mac Catalyst는 형식적인 이야기입니다. Apple이 이 클래스가 Catalyst 앱에서 온 호출을 무시한다고 문서화해 두었기 때문입니다.4 watchOS는 형식적인 이야기가 아닙니다. Background Assets는 watchOS 가용성을 게시하지 않고, Apple이 호스팅하는 팩은 “watchOS를 제외한 모든 플랫폼”에서 사용 가능하므로, watchOS 앱에는 지원 중단된 API만 남고 문서화된 후속 수단은 없습니다.56

앱이 iOS 18을 지원하는데 마이그레이션할 수 있나요?

관리형 계층으로는 안 됩니다. AssetPackManager, ManagedDownloaderExtension, BAHasManagedAssetPacks, BAUsesAppleHosting은 모두 iOS 26.0을 요구합니다.781415 그 하한선 아래에는 선택지가 둘 남습니다. 비관리형 경로는 iOS 16.1부터 동작하지만, 에셋을 직접 호스팅하고, 매니페스트 형식을 직접 정의하고, 익스텐션에서 그것을 파싱하고, 프로퍼티 리스트에 다운로드 허용량과 도메인 허용 목록을 선언할 것을 요구합니다.9 그게 아니라면 배포 타깃이 26.0에 도달할 때까지 지원 중단된 ODR을 계속 쓰면 되고, Apple이 제거 시점을 밝히지 않았으니 현재로서는 그것도 허용됩니다.

ODR 코드를 그대로 옮겨오면 무엇이 조용히 망가지나요?

저장 공간입니다. ODR에서는 시스템이 쓰이지 않는 태그 콘텐츠를 정리했고, setPreservationPriority(_:forTags:)는 순서에 대한 힌트만 줬습니다.410 Background Assets는 앱이 설치되어 있는 동안 여러분이 다 쓴 팩을 절대 제거하지 않으므로, remove(assetPackWithID:)를 호출하기 전까지 그 공간은 여러분 몫입니다.11 명시적 삭제를 더하지 않은 채 예전의 beginAccessingResourcesendAccessingResources 패턴을 그대로 흉내 낸 코드는 디스크를 무한정 새게 만들며, 테스트에서 잡아낼 크래시도 경고도 남기지 않습니다. 다운로드 코드를 쓰기 전에 정리 정책부터 쓰세요.

핵심 정리

iOS 개발자라면: - 무엇보다 최소 배포 타깃을 먼저 확인하세요. iOS 26.0 아래라면 관리형 계층은 여러분에게 존재하지 않으며, “대신 Background Assets를 사용하세요”는 비관리형 경로에서 호스팅과 매니페스트 형식과 파서를 직접 만들라는 뜻이 됩니다.79 - 정리 정책을 마이그레이션의 일부로 함께 쓰세요. remove(assetPackWithID:)에는 자동으로 대응하는 짝이 없고, 점유를 놓고 시스템을 믿던 ODR 시절의 습관은 저장 공간이 소리 없이 불어나게 만듭니다.11

대용량 콘텐츠를 출시하는 팀이라면: - 로컬 테스트 환경 구성을 코드와 별도로 예산에 잡으세요. Apple이 문서화한 경로는 자체 서명 루트 CA, 기기마다 설치하고 신뢰해야 하는 Apple Configurator 프로파일, 서버 호스트 이름과 일치하는 SSL 리프 인증서, 그리고 기기별 URL 재정의를 거칩니다.17 - 200GB와 앱 레코드당 200개 팩을 기준으로 계획하세요. 이 한도는 앱이 출시되는 모든 플랫폼에 걸쳐 공유되며, 80%에 도달하면 Apple이 보내는 메일을 주시하세요.12 공간을 되찾으려고 아카이브하면 App Store에 게시 중인 버전까지 사라집니다.12

릴리스 매니저라면: - Apple이 호스팅하는 팩은 두 번째 제출 파이프라인을 뜻합니다. Transporter나 App Store Connect API로 업로드하고, 빌드와 독립적으로 버전을 관리하며, App Review에도 따로 제출합니다.1113 심사 주기에 맞춰 인력을 배치하세요. - 이번 사이클에 이 마이그레이션을 강제하는 것은 아무것도 없습니다. 실제 강제력을 지닌 씬 생명주기 의무화런치 스크린 요구사항 뒤로 ODR을 배치하고, Apple이 제거 버전을 공표하면 다시 들여다보세요.


27 사이클은 파괴적 변경들을 이빨이 얼마나 날카로운지에 따라 계속 줄 세우고 있습니다. 씬 생명주기는 앱이 실행되는 것을 막고, 런치 스크린은 빌드가 출시되는 것을 막으며, ODR은 그저 시계를 돌리기 시작할 뿐입니다. 어느 것이 어느 쪽인지 아는 것이 이 사이클을 잘 쓰는 방법입니다. 더 작은 표면에서 같은 패턴을 보고 싶다면 Image Playground에서 ImageCreator가 사라진 일을 참고하세요. 시리즈 전체 허브는 Apple 생태계 시리즈입니다.

참고 문헌


  1. Apple, iOS & iPadOS 27 Release Notes, On Demand Resources 항목의 Deprecations(레이더 170066290): “On Demand Resources와 NSBundleResourceRequest API는 지원 중단됩니다. 대신 Background Assets를 사용하세요.” 2026년 7월 25일 Apple 문서 JSON 데이터와 대조해 확인했습니다. 이 항목이 전문이며, 릴리스 노트에는 On Demand Resources에 대한 제거 버전도, 제출 요건도, 런타임 실패를 뜻하는 문구도 없습니다. 

  2. Apple, tvOS 27 Release Notes, On Demand Resources 항목의 Deprecations(레이더 170066290). iOS 및 iPadOS 노트와 문구가 동일합니다. 

  3. Apple, visionOS 27 Release Notes, On Demand Resources 항목의 Deprecations(레이더 170066290). 문구가 동일합니다. macOS 27과 watchOS 27 릴리스 노트에는 대응하는 항목이 없으며, 2026년 7월 25일 해당 문서 JSON 데이터를 검색해 확인했습니다. 

  4. Apple, NSBundleResourceRequest, Foundation. 가용성: iOS 9.0, iPadOS 9.0, Mac Catalyst 13.1, tvOS 9.0, visionOS 1.0, watchOS 2.0이며 각각 27.0에서 지원 중단됐습니다. 태그 모델(“개발 중에 태그라고 불리는 문자열 식별자를 만들어 온디맨드 리소스를 식별합니다”), 정리 동작(“NSBundleResourceRequest 객체가 하나라도 해당 태그를 관리하고 있는 한, 시스템은 그 태그로 표시된 리소스를 기기 저장 공간에서 정리하려 시도하지 않습니다”), Mac Catalyst 관련 언급(“이 클래스는 Mac Catalyst로 빌드한 Mac 앱에서 온 호출을 무시합니다”), 그리고 일회용 제약의 출처입니다. 멤버 API인 init(tags:), beginAccessingResources(completionHandler:), endAccessingResources()도 동일하게 여섯 플랫폼에서 27.0 지원 중단을 답니다. 

  5. Apple, Background Assets, 프레임워크 개요. 가용성: iOS 16.0, iPadOS 16.0, Mac Catalyst 16.0, macOS 13.0, tvOS 18.4, visionOS 2.4이며 watchOS 행은 없습니다. “관리형 Background Assets의 기본 구현은 다운로드, 업데이트, 압축 등을 대신 처리해 줍니다”와 “Apple 호스팅 Background Assets를 선택하면 에셋을 App Store Connect에 업로드하고, 앱 빌드와 비슷한 방식으로 그곳에서 관리합니다”의 출처입니다. 

  6. Apple, Creating managed asset packs, Background Assets. “Apple 호스팅 Background Assets는 압축된 에셋을 최대 200GB까지 호스팅할 수 있으며, watchOS를 제외한 모든 플랫폼에서 App Store를 통해 배포되는 앱에 사용 가능합니다”의 출처입니다. 

  7. Apple, BAHasManagedAssetPacks, Information Property List 레퍼런스. 불리언이며 iOS 26.0, iPadOS 26.0, macOS 26.0, tvOS 26.0, visionOS 26.0입니다. 익스텐션 프로토콜 분기의 출처이기도 합니다. “BAUsesAppleHosting 키를 YES로 설정했다면 StoreKit의 StoreDownloaderExtension 프로토콜을 사용하고, 그렇지 않다면 Background Assets의 ManagedDownloaderExtension 프로토콜을 사용하세요.” 이 분기가 바로 안내 문서들이 전 과정을 다루지 않는 관리형 자체 호스팅 조합을 문서화하고 있습니다. 

  8. Apple, BAUsesAppleHostingBAAppGroupID, Information Property List 레퍼런스. 둘 다 iOS 26.0, iPadOS 26.0, macOS 26.0, tvOS 26.0, visionOS 26.0으로 표기됩니다. 다만 BAAppGroupID, BAHasManagedAssetPacks, BAUsesAppleHosting은 다른 모든 플랫폼의 26.0 행과 나란히 Mac Catalyst 16.0이라는 이례적인 행을 달고 있습니다. 관리형 계층의 iOS 26.0 하한선은 비관리형 경로에서도 필요한 BAAppGroupID가 아니라 AssetPackManager, ManagedDownloaderExtension, BAHasManagedAssetPacks에 근거합니다. 

  9. Apple, Configuring an unmanaged Background Assets project, Background Assets. 자체 호스팅 비관리형 익스텐션 타입, 두 타깃 모두에 필요한 App Groups 요건, 설치 및 업데이트 시 다운로드 순서, 그리고 책임 소재를 밝힌 문장의 출처입니다. “자체 호스팅 비관리형 에셋의 매니페스트 파일을 (원하는 형식으로) 만드는 것은 여러분의 책임이며, 시스템에 URL과 파일 크기를 전달하도록 그 파일을 파싱하는 코드도 여러분의 몫입니다.” BAManifestURL, BAInitialDownloadRestrictions, BADownloadAllowanceBAEssentialDownloadAllowance(압축 크기), BAMaxInstallSizeBAEssentialMaxInstallSize(비압축 크기), BADownloadDomainAllowList(DNS 형식 도메인, 와일드카드를 위한 선택적 선행 별표)의 출처이기도 합니다. BADownloaderExtension은 iOS 16.1, iPadOS 16.1, Mac Catalyst 16.1, macOS 13.0, tvOS 18.4, visionOS 2.4부터, BAManifestURL은 iOS 16.1부터 사용할 수 있습니다. 

  10. Apple, setPreservationPriority(_:forTags:)preservationPriority(forTag:), Foundation. 둘 다 iOS 9.0에 도입되어 iOS, iPadOS, Mac Catalyst, tvOS, visionOS, watchOS에서 27.0에 지원 중단됐습니다. “번들 안의 태그별 리소스 묶음을 정리할 상대적 순서를 시스템에 알려주는 힌트”의 출처입니다. 

  11. Apple, Downloading Apple-hosted asset packs, Background Assets, 그리고 Creating managed asset packs. Background Download 익스텐션 템플릿과 그 Apple 호스팅 관리형 타입, 공유 앱 그룹 요건, “나머지 Background Assets 정보 프로퍼티 리스트 키는 전부 생략하라”는 지시가 붙은 세 개 키 프로퍼티 리스트 구성, xcrun ba-package templatexcrun ba-package 명령, 매니페스트 키(assetPackID, downloadPolicy, firstInstallationsubsequentUpdate를 받는 installationEventTypes, filedirectory를 갖는 fileSelectors, platforms), 세 가지 다운로드 정책(essential, prefetch, onDemand)과 그 설치 동작, 경로가 패키징 디렉터리 기준으로 해석된다는 규칙, 병합된 네임스페이스(“시스템은 모든 에셋 팩을 공유 네임스페이스로 자동 병합해, 마치 에셋 루트 폴더가 사용자의 기기에 그대로 붙여넣어진 것처럼 재구성합니다”), 메모리 매핑된 Data 읽기와 파일 디스크립터 방식, 업로드 경로(Transporter, altool, iTMSTransporter, App Store Connect API), 그리고 저장 관련 지시의 출처입니다. “앱이 설치되어 있는 동안 시스템은 에셋 팩을 자동으로 제거하지 않습니다. 따라서 에셋 팩을 다 사용했다면 remove(assetPackWithID:) 메서드를 호출하세요.” 이 글의 다운로드 예제는 Apple의 가용성 데이터에서 27.0에 지원 중단으로 표시된 assetPack(withID:)과 26.4에 지원 중단된 ensureLocalAvailability(of:)를 모두 호출합니다(주석 19 참고). 

  12. Apple, Apple-hosted asset pack size limits, App Store Connect 도움말. 에셋 팩 총량 200GB, 에셋 팩 개수 200개. “App Store Connect의 앱 레코드에 업로드된 모든 에셋 팩의 크기 사용량 합계”라는 설명, 계산 방식(TestFlight 또는 App Store 배포 자격이 있는 버전들에 대한 최대 크기 기준이며 Awaiting Upload, Processing, Failed, 완전히 Superseded 상태의 버전은 제외), 80% 도달 시 메일 알림, “이 한도는 앱이 제공하는 모든 플랫폼에 걸쳐 공유됩니다”, 그리고 아카이브 동작(“TestFlight에서 테스트 중인 버전과 App Store에 게시된 버전을 포함해 에셋 팩의 모든 버전을 App Store Connect에서 제거합니다”)의 출처입니다. 

  13. Apple, Overview of Apple-hosted asset packs, App Store Connect 도움말. 네 단계 작업 흐름(패키징, 업로드, TestFlight 테스트, App Review 제출), 에셋 팩이 앱 빌드와 독립적이라는 점, 그리고 관리형 Apple 호스팅 에셋의 지원 플랫폼 목록(iOS 26 이상, iPadOS 26 이상, macOS 26 이상, tvOS 26 이상, visionOS 26 이상)의 출처입니다. 

  14. Apple, ManagedDownloaderExtension, Background Assets. 프로토콜이며 나열된 여섯 플랫폼 모두에서 iOS 26.0 이상입니다. 상속받은 BADownloaderExtension의 모든 요구사항에 기본 구현을 제공하며, 선택적으로 backgroundDownload(_:didReceive:)를 구현하는 경우를 제외하고는 상속된 요구사항을 직접 구현하지 말라는 경고가 붙어 있습니다. 

  15. Apple, AssetPackManager, Background Assets. 액터이며 iOS 26.0, iPadOS 26.0, Mac Catalyst 26.0, macOS 26.0, tvOS 26.0, visionOS 26.0입니다. 옵트인 안내(“코드가 공유 매니저를 처음 참조하는 순간, Background Assets는 앱이 에셋 팩의 자동 시스템 관리를 선택한 것으로 간주합니다”)와 짝 요건, 즉 짝이 되는 관리형 익스텐션 프로토콜 없이 매니저만 채택하는 것은 “프로그래머 오류”라는 서술의 출처입니다. 또한 “서버에서 최신 에셋 팩 정보를 가져와 오래된 에셋 팩을 업데이트하고 더 이상 유효하지 않은 에셋 팩을 제거”하는 checkForUpdates(), 그리고 remove(assetPackWithID:), url(for:)(nonisolated이며 FilePath를 받아 URL을 반환), statusUpdates(forAssetPackWithID:)의 출처이기도 하며, 이들 중 지원 중단된 것은 없습니다. AssetPackManagedBackgroundAssetsError도 동일하게 iOS 26.0 가용성을 갖습니다. 지원 중단된 매니저 멤버는 주석 19를 참고하세요. 

  16. Apple, StoreDownloaderExtension, StoreKit. ManagedDownloaderExtension을 상속해 더 좁게 구체화한 프로토콜이며 iOS 26.0, iPadOS 26.0, macOS 26.0, tvOS 26.0, visionOS 26.0에서 사용할 수 있습니다. 

  17. Apple, Testing asset packs locally, Background Assets. HTTPS 요건, 키체인 접근을 통한 루트 CA 생성 절차, Apple Configurator 프로파일 생성과 기기별 설치 및 신뢰 설정 단계, 서버와 일치하는 “유효한 IP 주소, 호스트 이름 또는 도메인 이름”이어야 하는 SSL 리프 인증서, xcrun ba-serve 명령, 그리고 개발자 설정의 URL 재정의(iOS, iPadOS, tvOS, visionOS에서는 설정 > 개발자 > Development Overrides, macOS에서는 xcrun ba-serve url-override)의 출처입니다. 

  18. Apple, BAEssentialMaxInstallSizeBAMaxInstallSize, Information Property List 레퍼런스. BAEssentialMaxInstallSize는 iOS 18.0부터, BAMaxInstallSize는 iOS 16.0부터입니다. 둘 다 다음 문구를 담고 있습니다. “App Store는 이 키로 제품 페이지에 앱 크기를 표시하므로 정확한 값을 넣으세요. 에셋을 압축했다면 파일의 비압축 크기를 이 값으로 사용하세요. 필요한 디스크 공간을 부풀리지 마세요.” 둘 다 Background Assets 사용 시 필수로 표시되어 있습니다. 

  19. AssetPackManager 멤버에 대한 Apple 가용성 메타데이터이며, 2026년 7월 25일 Apple 문서 JSON 데이터에서 읽었습니다. 지원 중단된 멤버는 모두 iOS 26.0에 도입됐습니다. ensureLocalAvailability(of:)는 26.4에서 지원 중단, status(ofAssetPackWithID:)는 26.4에서 status(relativeTo:)로 대체되며 지원 중단, assetPack(withID:)는 27.0에서 “매니저의 manifest 프로퍼티 값에 대해 assetPack(withID:)을 호출하세요”라는 안내와 함께 지원 중단, allAssetPacks는 27.0에서 매니페스트의 assetPacks 프로퍼티로 대체되며 지원 중단됐습니다. iOS 27.0에 도입되어 현재 SDK에서 베타로 표시된 대체 심볼은 manifest, AssetPackManifest.assetPack(withID:)(동기 방식이며 옵셔널 AssetPack을 반환), ensureLocalAvailability(of:requireLatestVersions:)입니다. ensureLocalAvailability(of:requireLatestVersion:)는 26.4에 등장했으며 지원 중단되지 않았습니다. Apple의 Apple 호스팅 안내서가 여전히 지원 중단된 호출 두 개, 즉 assetPack(withID:)ensureLocalAvailability(of:)를 시연한다는 관찰은 저자의 것으로, 같은 날짜에 글 본문과 심볼 가용성을 대조해 확인했습니다. 

  20. 2026년 7월 25일 저자가 실제 배포 중인 프로젝트 일곱 개(Reps, Return, Banana List, Ace Citizenship, Water, ResumeGeni, Yawara)를 대상으로 조사했습니다. build, DerivedData, .build, Pods, .git을 제외한 모든 Swift, Objective-C, 헤더, 프로퍼티 리스트, project.pbxproj 파일에서 NSBundleResourceRequest, beginAccessingResources, setPreservationPriority, On Demand Resources, ON_DEMAND_RESOURCES 관련 빌드 설정 일체, knownAssetTags, ASSETCATALOG_COMPILER_*TAG 설정을 검색했습니다. 모든 프로젝트에서 모든 패턴에 대해 일치 항목이 0건이었고, Background Assets 심볼에 대해서도 0건이었습니다. 에셋 카탈로그 총량은 빌드 산출물, fastlane 스크린샷, 리포트 디렉터리를 제외한 모든 .xcassets 디렉터리에 대해 du로 측정했습니다. Return 66.62MB, Ace Citizenship 7.81MB, Banana List 2.04MB, Reps 1.66MB, Water 0.04MB, Yawara 0.01MB입니다. 각 project.pbxproj에서 읽은 IPHONEOS_DEPLOYMENT_TARGET 값은 Reps 26.0과 26.2, Return 26.1, Banana List 26.0, Water 26.0, Yawara 26.5, Ace Citizenship은 타깃별로 17.0, 17.5, 26.1입니다. 

  21. Apple, Reducing download and storage demands with localized asset packs, Background Assets. macOS 27, iOS 27, tvOS 27, visionOS 27에서 에셋 팩에 추가된 language 지정, BCP-47 식별자 규칙(언어·지역·문자 서브태그만 허용하며 변형이나 확장은 불가), Xcode 27 템플릿의 language 키의 출처입니다. 

관련 게시물

canOpenURL 지원 중단: 대신 무엇을 호출해야 하나

Apple은 세 문장으로 canOpenURL의 지원을 중단하면서 스킴 허용 목록 상한을 25개로 절반 줄였습니다. 대체 수단과, 그 대체 수단이 끝내 할 수 없는 검사 하나를 정리합니다.

19 분 소요

App Store의 소셜 미디어 체크박스, 그리고 그 대가

9월부터 시행되는 App Store 규칙에 따라 모든 제출물은 소셜 미디어 기능 여부를 신고해야 합니다. 13세 미만 예외 조항의 대가는 entitlement 하나, API 하나, 그리고 분기 처리 하나입니다.

20 분 소요