배경
- 코어데이터 과연 어떻게 쓰는 녀석인가? 조금 더 잘 알기 위해 공부해 보았다.
Core Data Stack
-
코어 데이터 스택이란 코어 데이터가 데이터를 관리하고 저장하는 데 필요한 모든 구성 요소를 의미한다.

-
Model(NSManagedObjectModel)이 Entity, 즉 모델을 표현하는 객체
-
NSPersistentStoreCoordinator가 영구 저장소 관리를 담당하는 객체
-
NSManagedObjectContext가 명령을 수행하는 객체
-
위 세 가지 타입들을 캡슐화하고 Core Data Stack 전체를 나타내는 하나의 타입이 NSPersistentContainer!
Stack 설정하기
- 따라서 코어데이터 사용을 위해서는 NSPersistentContainer를 먼저 생성해야 한다.
- 이후 컨테이너에서 persistent store를 로드한다.
let container = NSPersistentCloudKitContainer(name: "CoreDataCloudKitDemo")
container.loadPersistentStores { _, error in
}
container.viewContext
container.newBackgroundTask()
- 명령 패턴을 사용하는 코어 데이터는 어떤 작업을 수행하려면 context가 반드시 필요하다.
- fetch request, save 등의 작업들을 모두 context를 통해 작업.
- context는 작업 수행을 위해 persistentStoreCoordinator를 알고 있어야 하고
- persistentStoreCoordinator는 저장소를 이해하기 위해 ManagedObjectModel을 알고 있어야 한다.
- 이처럼 각 타입들은 상호 의존성이 존재하며, PersistentContainer는 각 타입들을 캡슐화하고 Core Data Stack 전체를 나타내는 하나의 타입을 제공함!
앱에 데이터 추가하기
- 몇 개 정도의 객체만 필요하다면 NSManagedObject 서브클래스의 초기자 사용
context.perform {
let post = Post(context: context)
post.title = "Hello, world!"
try? context.save()
}
- 수백, 수천, 수만 개의 객체들을 오버헤드 없이 집어넣고 싶다면?
- batch insertion 사용

- batch insertion에는 몇 가지 주의점이 있음
- unique constraints가 있다면 새로운 값으로 대체됨 (중복 방지)
- relationship 설정에는 사용될 수 없음
- batch insertion은 contextDidSaveNotification을 발생시키지 않기 때문에 알아서 처리해줘야 함
객체 가져오기
- NSManagedObject 서브클래스에 정의된 fetchRequest 함수 활용
- predicate 사용해 원하는 결과만 가져오기
- 만약 viewContext 활용 중이고 객체에 대한 변경을 곧바로 반영하고 싶다면 combine 사용 가능

- sortDescriptor, fetchBatchSize 활용해 커스텀 정렬 / 요청 크기를 지정 가능
- 요청 크기 지정은 관리하는 객체 갯수가 매우 많을 경우 필수적인 옵션. 성능에 많은 영향을 준다.
Live Queries
-
코어 데이터는 NSFetchedResultsController의 형태로 live query 지원
Live Query?
실시간으로 데이터를 업데이트하거나 쿼리 결과를 지속적으로 반영하는 방식의 쿼리
데이터의 변동을 실시간으로 반영하는 경우 사용
-
컨트롤러의 delegate result들 활용
-
2019년부터는 DiffableDataSource 사용 편하게 하기 위한 델리게이트 메소드도 지원
Derived Attributes
-
비정규화를 통해 성능 향상시킬 수 있음
-
블로그 포스트 생각해보자. 포스트마다 태그가 있을 때 태그별 포스트 갯수를 알고 싶다.
- 만약 매번 fetch Request로 같은 태그를 가진 포스트들을 가져오고 그 총 갯수를 구하면 비효율적이고 성능 낭비
- 이럴 땐 Tag 모델에 postCounts 어트리뷰트 추가해주면 편하다.
-
그런데 이 postCounts에 대한 조작을 직접 수행하면 버그 없이 잘 작성할 수 있을지 장담 못함.
-
이 때 Derived Attributes 활용
-
코어데이터에 의해 관리되는 메타데이터

-
모델 Entity -> attribute로 추가 가능
-
또는 NSDerivedAttributeDescription으로 추가 가능
-
Derived 체크하고, Derivation에 문법 준수해서 작성해주면 끝. 나머지는 코어데이터가 알아서...
PersistentHistory
Testing
- 성능 목표를 알고 테스트하기
- 수만 개의 객체를 예상하는 앱은 선형 알고리즘 사용이 가능할 수 있지만, 수백만 개의 객체를 예상하는 앱은 로그 시간보다 느리면 앱 사용이 불가능해진다.
- 따라서 성능 데이터셋을 사용해 모든 통합 테스트 실행하는 것이 좋음.
- named in-memory 저장소 활용하기
- 빠르게 동작하는 것이 중요한 단위 테스트에서 SQLiteStore가 지원하는 named in-memory 저장소 활용하면 좋음
- 일반 in-memory도 좋지만 notification 필요하면 named in-memory 사용하면 됨

Sanitizer
- address sanitizer
- thread sanitizer
- undefined behavior sanitizer
출처
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/PersistentStoreFeatures.html
https://developer.apple.com/videos/play/wwdc2019/230