App Group은 앱과 그 확장 기능 간에 데이터를 교환하기 위한 공유 컨테이너를 생성하는 iOS 메커니즘입니다. 이것이 없으면 각 프로세스(메인 앱, 위젯, 공유 확장)는 격리된 샌드박스에서 실행되며 인접한 구성 요소의 데이터에 접근할 수 없습니다. Apple Developer Documentation(2026)에 따르면, App Group은 entitlements에 지정된 단일 그룹 식별자를 통해 이 문제를 해결합니다. 이는 iOS 생태계에서 일관된 사용자 경험을 만들기 위한 기본 도구입니다.
핵심 요점
App Group은 동일한 개발자의 여러 프로세스에 공유 저장 영역을 제공하는 iOS 운영 체제 메커니즘입니다. 이것이 없으면 각 확장 기능은 자체 샌드박스에서 작동하며 다른 구성 요소가 기록한 데이터를 읽을 수 없습니다. App Group은 올바른 entitlements를 가진 모든 프로세스가 접근할 수 있는 디렉터리를 파일 시스템에 생성하여 이 격리 문제를 해결합니다.
그룹 식별자는 group. 접두사로 시작하며 프로젝트의 entitlements 파일에 지정됩니다. Xcode에서 이 식별자는 Capabilities를 통해 추가되며 시스템이 자동으로 앱 서명에 포함시킵니다. 구성 후 모든 그룹 구성원은 컨테이너 내 Library/Application Support/의 공유 디렉터리에 접근할 수 있습니다.
Apple WWDC 2020 Session 10026에 따르면, App Group은 세 가지 주요 시나리오를 다룹니다: NSUserDefaults를 통한 설정 동기화, FileManager를 통한 파일 공유, 단일 저장소를 통한 Core Data 공유입니다. 각 시나리오는 해당 API에 그룹 식별자만 지정하면 됩니다.
iOS는 com.apple.security.application-groups entitlement를 가진 프로세스가 처음 실행될 때 공유 컨테이너를 생성합니다. 물리적으로 컨테이너는 시스템 디렉터리에 위치하며, 그 경로는 메인 앱 및 확장 기능의 샌드박스와 다르지만 모든 권한이 있는 프로세스는 읽기 및 쓰기 권한을 가집니다.
Apple은 team ID와 코드 서명의 조합을 통해 App Group의 보안을 보장합니다. 동일한 개발자 인증서로 서명되지 않은 프로세스는 그룹 컨테이너에 접근할 수 없습니다. 이는 다른 개발자의 앱 간 데이터 유출을 방지합니다.
식별자 형식: group.<team-id>.<name>입니다. 예: group.ABC123DEFG.widget-data. 하나의 기기에 여러 그룹을 만들 수 있으며, 각 그룹은 고유한 구성원 집합을 가집니다. 하나의 앱이 동시에 여러 App Group에 속할 수 있으며, 이는 다른 확장 기능의 데이터를 분리하는 데 유용합니다.
// Get the App Group container URL
guard let containerURL = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.com.example.widget")
else { return }
// Create a file in the shared container
let fileURL = containerURL.appendingPathComponent("shared.data")
try "Hello from main app".write(to: fileURL, atomically: true, encoding: .utf8)
FileManager는 containerURL(forSecurityApplicationGroupIdentifier:) 메서드를 제공하여 공유 디렉터리의 URL을 반환합니다. 각 프로세스에서 이 메서드를 개별적으로 호출하는 것이 중요합니다. 경로는 물리적으로 동일하지만 각 프로세스는 자체 보안 관점에서 이를 봅니다.
Xcode에서 App Group을 활성화하는 것은 Capabilities에서 시작됩니다. App Groups 토글이 프로젝트에 entitlement를 추가합니다. Apple Developer Portal에서도 앱 식별자에 이 기능이 포함되어야 합니다. 이것이 없으면 실제 기기에서 코드 서명이 작동하지 않습니다.
Xcode에서 프로젝트를 열고 앱 대상을 선택한 후 Signing & Capabilities 탭으로 이동합니다. +를 클릭하고 App Groups를 선택합니다. 새 그룹을 만들거나 기존 그룹을 선택합니다. 공유 컨테이너에 접근해야 하는 각 확장 기능에 대해 동일한 단계를 반복합니다. 모든 참가자가 동일한 그룹 식별자를 사용해야 합니다.
Capabilities를 활성화하면 Xcode가 자동으로 com.apple.security.application-groups 키와 식별자 배열이 포함된 .entitlements 파일을 생성합니다. 모든 필수 확장 기능이 그룹 식별자에 올바른 team ID와 함께 이 파일을 가지고 있는지 확인하세요.
<!-- Example entitlements file -->
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.example.shared</string>
</array>
프로젝트에 여러 확장 기능(Today Widget, Share Extension, Notification Service)이 있는 경우, 각각 동일한 그룹으로 자체 entitlements 파일을 가져야 합니다. 대상에서 entitlements가 누락되면 확장 기능이 공유 데이터를 볼 수 없는 일반적인 원인입니다.
앱과 그 확장 기능 간에 데이터를 동기화하는 가장 간단한 방법은 suite name을 사용한 UserDefaults입니다. 표준 UserDefaults.standard 대신 App Group 식별자로 인스턴스를 생성하면 모든 프로세스가 동일한 설정을 읽습니다.
Apple Human Interface Guidelines에 따르면, 이 메커니즘은 상태 동기화에 적합합니다: 선택한 테마, 즐겨찾기, 온보딩 플래그 등. 그러나 대용량 데이터나 동시 쓰기 작업에는 적합하지 않습니다. 그러한 경우 파일이나 Core Data를 사용하세요.
// Write to UserDefaults App Group (main app)
let defaults = UserDefaults(suiteName: "group.com.example.shared")
defaults?.set(true, forKey: "isDarkMode")
defaults?.synchronize()
// Read from UserDefaults App Group (widget extension)
let sharedDefaults = UserDefaults(suiteName: "group.com.example.shared")
let isDark = sharedDefaults?.bool(forKey: "isDarkMode") ?? false
참고: suiteName을 사용한 UserDefaults 인스턴스는 각 프로세스에서 개별적으로 생성되지만 공유 컨테이너의 동일한 plist 파일에서 읽습니다. synchronize() 메서드는 즉시 디스크 쓰기를 보장합니다. iOS 13+에서는 시스템이 주기적으로 데이터를 동기화하므로 생략할 수 있습니다.
더 복잡한 시나리오의 경우 App Group은 공유 파일 시스템에 대한 접근을 제공합니다. 컨테이너 디렉터리에 생성된 모든 파일은 그룹의 모든 프로세스가 접근할 수 있습니다. 이를 통해 공유 Core Data 데이터베이스, 이미지 캐시 또는 임시 파일을 사용할 수 있습니다.
공유 Core Data 저장소를 사용하려면 App Group 디렉터리에 있는 .sqlite 파일 URL로 NSPersistentContainer를 생성합니다. 한 번에 하나의 프로세스만 데이터베이스에 쓰도록 하세요. 동시 쓰기는 데이터를 손상시킬 수 있습니다. 일반적인 아키텍처: 메인 앱이 쓰고 확장 기능이 읽습니다.
// Setup Core Data with App Group container
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "SharedModel")
guard let appGroupURL = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.com.example.shared")
else { return container }
let storeURL = appGroupURL.appendingPathComponent("SharedModel.sqlite")
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: storeURL)]
container.loadPersistentStores { _, error in
if let error = error { fatalError(error.localizedDescription) }
}
return container
}()
대안적인 접근 방식은 파일 기반 이미지 캐시입니다. NSCache는 메모리에만 존재하지만 App Group을 통해 캐시된 이미지를 공유 디렉터리의 디스크에 저장할 수 있습니다. 위젯 확장 기능은 동일한 파일에 접근하여 추가 네트워크 다운로드 없이 최신 데이터를 표시할 수 있습니다.
| 시나리오 | 권장 API | 데이터 크기 |
|---|---|---|
| 설정 및 플래그 | UserDefaults suiteName | 최대 1 KB |
| 이미지 캐시 | FileManager + NSCache | 최대 100 MB |
| 구조화된 데이터 | Core Data 저장소 | 최대 500 MB |
| 임시 파일 | FileManager 임시 | 최대 50 MB |
공유 파일로 작업할 때 시스템이 언제든지 확장 기능을 종료할 수 있음을 명심하세요. 여러 프로세스가 동시에 쓸 때 데이터 손상을 방지하기 위해 접근 조정에 NSFileCoordinator를 사용하세요.
자주 묻는 질문
App Group은 앱과 그 확장 기능 간에 데이터를 저장하기 위한 공유 컨테이너를 생성하는 iOS 메커니즘입니다. 동일한 그룹 식별자를 가진 프로세스가 파일 시스템의 단일 디렉터리를 통해 파일, UserDefaults 설정 및 Core Data 데이터를 교환할 수 있습니다.
Xcode에서 대상의 Capabilities를 열고 App Groups를 활성화한 후 group.* 형식의 식별자를 추가합니다. 그런 다음 공유 컨테이너에 접근해야 하는 각 확장 기능에 대해 동일한 단계를 반복합니다. 모든 참가자가 동일한 그룹 식별자를 사용하는지 확인하세요.
모든 유형의 iOS 확장 기능이 App Group을 지원합니다: Today Widget(위젯 확장), Share Extension, Notification Service Extension, Custom Keyboard 등. 각 확장 기능은 메인 앱과 동일한 그룹 식별자로 별도의 entitlements 파일을 가져야 합니다.
네, 하지만 앱이 동일한 개발자 인증서로 서명되고 동일한 team ID를 가진 경우에만 가능합니다. Apple은 코드 서명을 통해 보안을 보장합니다: 다른 개발자의 두 앱은 동일한 그룹 식별자로도 동일한 컨테이너에 접근할 수 없습니다.
작은 설정에는 suite name을 사용한 UserDefaults, 파일에는 FileManager, 구조화된 데이터에는 Core Data를 사용하세요. 이러한 모든 API는 App Group 디렉터리에서 작동합니다. 초기화 시 그룹 식별자만 지정하면 됩니다. Core Data의 경우 저장소 URL이 공유 디렉터리를 가리키는지 확인하세요.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.