Core Data는 iOS 및 macOS 애플리케이션에서 객체 그래프를 관리하기 위한 Apple의 프레임워크입니다. 데이터 영속성, 변경 추적, 실행 취소 작업 및 NSFetchedResultsController를 통한 UI 통합을 제공합니다. Apple Developer 문서(2025)에 따르면 Core Data는 데이터베이스가 아니라 객체 모델링 계층으로, 기본적으로 SQLite를 영구 저장소로 사용하여 객체를 로드하고 저장합니다.
핵심 요점
Core Data는 Apple의 Cocoa Touch SDK의 일부인 객체 그래프 관리 및 영속성 프레임워크입니다. 데이터 작업을 위한 객체 지향 인터페이스를 제공합니다. 개발자는 엔티티, 속성 및 관계로 작업하고 Core Data는 이러한 객체를 내부적으로 관계형 데이터베이스 레코드로 변환합니다.
Core Data는 Mac OS X 10.4 Tiger(2005)에서 macOS용으로 도입되었고 iOS 3.0(2009)으로 포팅되었습니다. 20년이 넘는 기간 동안 프레임워크는 SQLite 위의 단순한 추상화 계층에서 NSPersistentCloudKitContainer를 통한 클라우드 동기화, 자동 컨텍스트 관리를 통한 멀티스레딩, Swift Concurrency를 통한 비동기 로딩을 갖춘 완전한 기능의 스택으로 진화했습니다.
Slack Community(2025)의 iOS 개발자 설문 조사에 따르면 Core Data는 상용 iOS 애플리케이션의 68%에서 로컬 데이터 저장소로 사용됩니다. 복잡성과 계층화된 아키텍처에 대한 비판에도 불구하고, 시스템과의 긴밀한 통합, 무료(SDK에 내장), iCloud 동기화 지원 덕분에 Apple 애플리케이션의 표준으로 남아 있습니다.
흔한 오해는 Core Data를 데이터베이스로 생각하는 것입니다. 프레임워크는 직접 SQL 쿼리를 실행하지 않으며 DBMS가 아닙니다. Core Data는 영속성을 위해 SQLite, Binary 또는 In-Memory 저장소를 사용할 수 있는 객체 그래프 관리 계층입니다. 비유: Core Data는 Apple 생태계를 위한 Hibernate 또는 Entity Framework와 같으며, 그 아래의 SQLite는 Hibernate 아래의 MySQL과 같습니다.
Core Data 스택은 NSManagedObjectModel(데이터 스키마), NSPersistentStoreCoordinator(저장소 코디네이터), NSManagedObjectContext(작업 컨텍스트) 및 NSPersistentContainer(iOS 10부터 세 가지를 결합하는 통합 컨테이너)의 네 가지 상호 연결된 구성 요소로 구성됩니다. NSPersistentContainer는 스택 생성 및 구성을 자동화합니다.
각 구성 요소는 엄격하게 정의된 기능을 수행합니다. NSManagedObjectModel은 엔티티 설명이 포함된 .xcdatamodeld 파일을 로드합니다. NSPersistentStoreCoordinator는 모델을 물리적 저장소 파일(SQLite)에 연결합니다. NSManagedObjectContext는 객체 작업을 위한 임시 영역을 제공합니다. 컨테이너는 모든 것을 단일 초기화 호출로 통합합니다.
SQLite(NSSQLiteStoreType)는 대부분의 애플리케이션에서 사용되는 표준 저장소입니다. 데이터는 ACID 트랜잭션 지원과 함께 단일 .sqlite 파일에 저장됩니다. Binary(NSBinaryStoreType)는 소규모 데이터 세트(최대 수백 개체)를 위한 바이너리 형식 저장소입니다. In-Memory(NSInMemoryStoreType)는 디스크 영속성 없이 RAM의 임시 저장소로, 테스트 및 캐싱에 사용됩니다.
| 저장소 유형 | 형식 | 성능 | 사용 시기 |
|---|---|---|---|
| SQLite | .sqlite | 높음 | 프로덕션 표준 선택 |
| Binary | .binary | 중간 | 소규모 데이터 세트 |
| In-Memory | RAM | 최대 | 테스트, 캐시, 임시 데이터 |
| CloudKit | iCloud | 네트워크 의존적 | 장치 간 동기화 |
저장소 유형은 NSPersistentStoreDescription 초기화 시 한 줄로 설정됩니다. 개발자는 객체 조작 코드를 변경하지 않고도 단위 테스트를 위해 SQLite에서 In-Memory로, 또는 iCloud 동기화를 위해 CloudKit으로 전환할 수 있습니다. Core Data는 통합 컨텍스트 API를 통해 저장소 유형 간의 차이를 추상화합니다.
NSManagedObject는 모든 Core Data 객체의 기본 클래스이며, 단일 엔티티 레코드를 나타냅니다. 각 관리 객체는 고유한 NSManagedObjectID(영구 식별자)를 가지며, 컨텍스트에 바인딩되고 KVO(키-값 관찰)를 통해 변경 사항을 추적합니다. 개발자는 NSManagedObject 하위 클래스를 만들어 형식화된 엔티티 속성을 정의합니다.
NSManagedObjectContext는 모든 객체 작업을 위한 작업 영역을 제공하는 Core Data의 중앙 구성 요소입니다. 컨텍스트는 추가, 삭제 및 변경 사항을 추적하고(변경 추적), undoManager를 통해 작업 실행 취소를 지원하며, 저장 알림을 받을 때 다른 컨텍스트의 변경 사항을 자동으로 병합합니다.
비공개 큐 규칙: NSManagedObjectContext는 .privateQueueConcurrencyType 또는 .mainQueueConcurrencyType으로 생성됩니다. 메인 컨텍스트는 기본 UI 스레드에 바인딩되고, 비공개 컨텍스트는 백그라운드 큐에서 실행됩니다. 각 컨텍스트는 자체 큐에서만 사용해야 합니다. 다른 스레드에서 관리 객체에 액세스하면 충돌이 발생합니다. parentContext를 사용하면 비동기 쓰기를 위한 컨텍스트 계층 구조를 구성할 수 있습니다.
struct CoreDataStack {
let container: NSPersistentContainer
init(name: String) {
container = NSPersistentContainer(name: name)
container.loadPersistentStores { _, error in
if let error = error {
fatalError("Failed to load store: \(error)")
}
}
container.viewContext.automaticallyMergesChangesFromParent = true
}
func backgroundContext() -> NSManagedObjectContext {
let context = container.newBackgroundContext()
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return context
}
}
NSPersistentContainer는 자동으로 viewContext(메인 큐)를 생성하고 백그라운드 작업을 위해 newBackgroundContext()를 제공합니다. automaticallyMergesChangesFromParent = true를 설정하면 viewContext가 백그라운드 컨텍스트가 저장할 때 변경 사항을 자동으로 가져와 수동 데이터 다시 가져오기 없이 UI를 업데이트합니다.
NSPersistentStoreCoordinator는 물리적 데이터 저장소를 관리합니다. 파일을 열고, 모델을 기반으로 SQLite 테이블을 생성하며, 스키마 변경 시 마이그레이션을 수행합니다. NSSQLiteStoreType으로 NSPersistentStoreDescription을 초기화하면 Core Data는 .xcdatamodeld 모델과 일치하는 스키마의 SQLite 파일을 생성합니다.
Core Data는 SELECT/INSERT/UPDATE를 통한 표준 SQL 쿼리를 사용하지 않습니다. 대신 모델과 NSFetchRequest를 통해 수행된 쿼리를 기반으로 내부 SQL 명령을 생성합니다. 개발자는 시작 인수 -com.apple.CoreData.SQLDebug 1을 사용하여 SQL 로깅을 활성화하고 쿼리 성능을 디버깅할 수 있습니다.
경량 마이그레이션은 새 속성 추가, optional/required 플래그 변경 또는 renamingID를 사용한 이름 변경 시 SQLite 스키마를 자동으로 업데이트하는 프로세스입니다. 엔티티 병합 또는 분할과 같은 근본적인 스키마 변경에는 무거운 마이그레이션이 필요하며 사용자 정의 NSMigrationManager를 통해 수행됩니다.
let description = NSPersistentStoreDescription()
description.url = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)
.first?
.appendingPathComponent("Model.sqlite")
description.setOption(true as NSNumber,
forKey: NSMigratePersistentStoresAutomaticallyOption)
description.setOption(true as NSNumber,
forKey: NSInferMappingModelAutomaticallyOption)
let container = NSPersistentContainer(name: "AppModel")
container.persistentStoreDescriptions = [description]
container.loadPersistentStores { _, error in
if let error = error { print("Migration error: \(error)") }
}
NSMigratePersistentStoresAutomaticallyOption 및 NSInferMappingModelAutomaticallyOption을 통한 자동 마이그레이션 설정은 Core Data가 모델의 새 버전에서 속성이나 엔티티가 추가될 때 SQLite 파일을 독립적으로 업데이트할 수 있게 합니다. 마이그레이션이 불가능한 경우 저장소 코디네이터는 이유 설명과 함께 오류를 throw합니다. 개발자는 NSMigrationManager를 통해 사용자 정의 마이그레이션을 구현해야 합니다.
NSFetchRequest는 Core Data에서 객체를 가져오는 기본 도구입니다. 요청에는 엔티티 이름, 조건자(필터), 정렬 설명자, 제한 및 오프셋이 포함됩니다. 결과는 NSManagedObject 또는 형식화된 하위 클래스의 배열로 반환됩니다. NSPredicate는 AND, OR, IN, LIKE 및 하위 쿼리를 사용한 복잡한 조건을 지원합니다.
NSBatchDeleteRequest는 각 객체를 메모리에 로드하지 않고 대량으로 삭제하는 효율적인 방법입니다. 요청은 SQLite 수준에서 실행되어 관리 객체 컨텍스트를 우회하고 완료 후에만 컨텍스트를 업데이트합니다. 업데이트(NSBatchUpdateRequest) 및 삽입(NSBatchInsertRequest)을 위한 유사한 배치 요청이 있습니다.
CRUD(생성, 읽기, 업데이트, 삭제)는 Core Data에서 컨텍스트 메서드(insert, fetch, save, delete)를 통해 수행됩니다. 모든 변경 사항은 context.save()가 호출될 때까지 임시입니다. 이 메서드는 변경 사항을 영구 SQLite 저장소에 저장합니다. 저장 오류 시 컨텍스트는 재시도를 위해 변경된 상태로 유지됩니다.
let context = container.viewContext
// 생성
let user = User(context: context)
user.id = 42
user.name = "Alice"
// 읽기
let request = User.fetchRequest()
request.predicate = NSPredicate(format: "name CONTAINS %@", "Ali")
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
let results = try context.fetch(request)
// 업데이트
results.first?.name = "Alice Updated"
// 삭제
if let first = results.first { context.delete(first) }
// 저장
try context.save()
컨텍스트 저장(context.save())은 중요한 작업입니다. save가 호출되지 않으면 모든 변경 사항은 메모리에만 남습니다. 컨텍스트는 hasChanges 상태를 추적하며 저장 전에 확인할 수 있습니다. 백그라운드 작업의 경우 자체 저장 기능이 있는 newBackgroundContext를 사용하고 UI의 경우 타이머 또는 앱이 백그라운드로 전환될 때 자동 저장되는 viewContext를 사용합니다.
첫 번째 사례 — NSPersistentCloudKitContainer를 사용하여 iCloud를 통해 사용자 장치 간 데이터를 동기화합니다. 클라우드 동기화는 영구 저장소 설명에 CloudKit 옵션을 추가하여 활성화됩니다. Core Data는 동기화 충돌을 자동으로 관리하고 다른 장치의 변경 사항을 병합합니다.
두 번째 사례 — 큰 테이블에서 조건자 없이 fetchRequest를 사용하지 마십시오. 모든 무조건 가져오기는 모든 엔티티 객체를 메모리에 로드하여 높은 RAM 소비와 UI 지연을 초래합니다. 항상 조건자와 제한을 사용하십시오. 페이지네이션을 위해 NSFetchRequest에서 fetchLimit와 fetchOffset을 사용하십시오.
세 번째 사례 — 다중 스레드 액세스에서 충돌을 해결하기 위해 mergePolicy를 구성하십시오. NSMergeByPropertyObjectTrumpMergePolicy는 마지막으로 저장된 컨텍스트에서 충돌하는 속성을 업데이트합니다. NSRollbackMergePolicy는 충돌 시 현재 컨텍스트 변경 사항을 폐기합니다. 정책 선택은 애플리케이션의 비즈니스 로직에 따라 달라집니다.
네 번째 사례 — 테이블 및 컬렉션 통합을 위해 NSFetchedResultsController를 사용하십시오. NSManagedObjectContextDidSave 알림을 자동으로 구독하고 필요한 객체만 로드하며(폴팅), UITableView 애니메이션을 위한 적절한 인덱스 경로와 함께 삽입, 삭제 및 이동을 델리게이트에 알립니다.
폴팅은 Core Data의 지연 로딩 메커니즘입니다. 가져오기 요청에 의해 반환된 관리 객체는 폴트 상태에 있습니다. 속성이 완전히 로드되지 않고 식별자만 로드됩니다. 전체 로딩(폴트 발생)은 속성에 처음 액세스할 때 발생합니다. 관계 프리페칭(setRelationshipKeyPathsForPrefetching)은 관련 객체를 미리 로드하여 N+1 쿼리를 방지합니다.
extension UserRepository {
func fetchUsersWithPosts() throws -> [User] {
let request = User.fetchRequest()
request.predicate = NSPredicate(format: "isActive == YES")
request.relationshipKeyPathsForPrefetching = ["posts"]
request.returnsObjectsAsFaults = false
request.fetchBatchSize = 20
let context = container.viewContext
return try context.fetch(request)
}
}
fetchBatchSize = 20은 Core Data가 전체 테이블을 한 번에 로드하지 않고 20개 객체의 배치로 데이터를 로드(화면 표시용)하도록 합니다. returnsObjectsAsFaults = false 플래그는 모든 사용자 속성이 즉시 로드되도록 보장하며 직접 표시에 유용합니다. “posts” 관계의 프리페칭은 게시물에 액세스할 때 각 사용자에 대한 별도 쿼리를 방지합니다.
자주 묻는 질문
아니요, Core Data는 객체 그래프 관리 프레임워크입니다. 객체 작업, 변경 추적 및 영속화를 위한 API를 제공합니다. Core Data 내부의 데이터베이스(기본적으로 SQLite)를 프레임워크 자체와 혼동해서는 안 됩니다. Core Data는 ORM이지 DBMS가 아닙니다.
네, Core Data는 SQLite, Binary 및 In-Memory의 세 가지 저장소 유형을 지원합니다. 저장소 유형은 NSPersistentStoreDescription을 통해 설정됩니다. In-Memory 저장소는 데이터를 디스크에 유지하지 않으며 단위 테스트에 적합합니다. Binary 저장소는 소형 객체 집합을 위한 레거시 형식입니다.
경량 마이그레이션의 경우 NSMigratePersistentStoresAutomaticallyOption과 NSInferMappingModelAutomaticallyOption을 활성화하십시오. 복잡한 변경의 경우 Xcode를 통해 Mapping Model(.xcmappingmodel)을 생성하십시오. CloudKit 저장소(NSPersistentCloudKitContainer)는 iCloud 서버와 스키마를 동기화할 때 자동으로 마이그레이션을 지원합니다.
SwiftData는 Swift Macros와 Swift Concurrency를 사용하여 Core Data 위에 구축된 새로운 Apple 프레임워크(iOS 17+)입니다. SwiftData의 구문은 더 간단합니다. 엔티티는 @Model 매크로로, 컨텍스트는 @Environment(\.modelContext)로 설명됩니다. 내부적으로 SwiftData는 동일한 Core Data 스택과 SQLite를 사용합니다.
시작 인수 -com.apple.CoreData.SQLDebug 1을 활성화하십시오. Core Data가 모든 SQL 쿼리와 그 지속 시간을 Xcode 콘솔에 출력합니다. 프로파일링을 위해 Core Data 템플릿과 함께 Instruments를 사용하면 폴트 요청 수, 객체 로드 시간 및 컨텍스트 저장 기간을 확인할 수 있습니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.