-
Core Data Context 이해와 활용 - 기본Apple🍎/CoreData 2024. 1. 11. 02:24
Context가 뭐냐?
- Context는 Core Data Stack에서 제일 위에 있는 레이어이다.
- Context는 Application Model의 코드와 persistent store, data model을 연결하는 역할을 한다.
- Context를 사용하여 엔티티들을 저장 및 변경한다.
객체를 엔티티로서 DB에 저장 및 변경하기 위해서는 해당 객체는 특정 Context에 속해 있어야 한다.
왜냐하면 Context는 DB로부터 객체를 fetch하는 것 뿐만아니라 자신에게 속해 있는 객체를 추적 및 변경감지 역할을 수행하기 때문이다.Context가 하는 일
- 새로운 엔티티 만들기
- 엔티티 fetch 하기 (DB로부터 가져오기)
- 엔티티 변경 감지하기
- 유효성 검사
- undo / redo 동작 핸들링
- persistent에 바로 데이터 변경하지 않게 데이터 변경점 분리하기
Context 만들기
- 메인 쓰레드에서 사용되는 메인 Context.
let context = persistentContainer.viewContext
- 새로운 Context 만들기
let newContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
- 백그라운 쓰레드에서 사용할 private Context
let privateContext = persistentContainer.newBackgroundContext( )
- 모든 Core Data 객체들은 하나의 Context에 속한다.
- 따라서 모든 Core Data 객체들은 자신이 속해있는 Context가 무언지를 나타내는
managedObjectContext
프로퍼티를 가지고 있다.
Context에서 Data 다루기
Context에 새로운 엔티티 넣기 - Insert New Objectes
첫번째 방법
NSEntityDescription
의 static 메서드를 이용하면 바로 이름으로 선택한NSEntityDescription
(클래스)에 대한NSManagedObject
(객체)를 새로 만들어 지정한 Context에 넣어준다.
let newObject = NSEntityDescription.insertNewObject(forEntityName: "Song", into: context)
두번째 방법
- data model로부터 선택한
NSEntityDescription
을 가져온다음에 - 이걸로 직접
NSManagedObject
만들어서 지정한 Context에 넣기
let songEntityDescription = appDelegate.persistentContainer.managedObjectModel.entitiesByName["Song"]! let newObject = NSManagedObject(entity: songEntityDescription, insertInto: context)
일반적으로는 생성할 엔티티 지정과 생성을 동시에하는 첫번째 방법을 사용하지만
여러 Store가 있거나 수동으로 NSEntityDescription을 생성하려는 경우에는 두번째 방법을 사용한다.- 위와 같은 방법으로 새로운
NSManagedObject
(Context가 관리한는 객체, 엔티티)를 얻었으면 data Model에 맞게 generated 된 클래스에 맞게 타입 캐스팅해주기
let song = newObject as? Song
- 타입 캐스팅한 후 해당 클래스의 프로퍼티나 메서드를 활용하여 엔티티에 대한 작업을 할 수 있다.
song.title = "Signal"
- 엔티티에 대한 작업 완료해서 저장하고 싶으면 context의 save 호출
try context.save( )
Context로 저장한 데이터 불러오기 - Fetching Objects
Fetch Request
- Store(data가 저장되어 있는 곳)으로부터 저장한 데이터를 가져오는 작업을 Fetch라고 한다.
- 어떻게 fetch를 해서 가져올지를 결정하기 위해 fetch request 객체를 사용한다.
- 즉 fetch request 객체를 통해 DB에 날릴 쿼리를 결정한다.
- 가장 기본적인 fetch 방법
// Store에 요청할 내용을 담은 FetchRequest 만들기 let fetchRequest = NSFetchRequest<Song>(entityName: "Song") // context를 통해 fetchRequest를 Store에 전달하기 let songs = try context.fetch(fetchRequest) // Store에 있는 모든 “Song” 엔티티를 가져온다.
fetchRequest는 요청 내용을 담는 객체일 뿐이고 실제 요청은 Context를 통해서 한다.
- fetchRequest 종류
// 바로 NSManagedObject 서브 클래스로 가져오기 let fetchRequest = NSFetchRequest<Song>(entityName: "Song") // 일단 general 하게 가져온 다음, 이후에 타입 캐스팅하기 let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Song")
- fetchRequest method
let fetchRequest = Song.fetchRequest( )
NSManagedObject
서브 클래스에fetchRequest( )
메서드를 이용하여 간단하게 fetchRequest를 만들 수도 있다.클래스 자동 생성을 선택시
XCode가 엔티티들에 대응하는 클래스를 자동으로 만들어주는데
거기에 이미 각 엔티티를 위한 fetchRequest를 만드는 메서드가 클래스단에 붙어 있다.@nonobjc public class func fetchRequest() -> NSFetchRequest<Song> { return NSFetchRequest<Song>(entityName: "Song") }
Fetch Request 객체 만들때 직접 String 적지 말고 그냥 클래스단에 붙어 있는거를 호출해서 쓰면 typo를 예방할 수 있다.
Fetch Request에 조건 추가하기 - Add Predicate
- 그냥 fetchRequest를 전달하면 지정한 유형의 모든 엔티티들을 Store로부터 가져온다.
NSPredicate
를 이용하면 구체적인 조건을 달아 딱 원하는 엔티티만 가져올수 있다.
// Class 단 fetchRequest( ) 메서드로 fetchRequest 만들고 let fetchRequest = Song.fetchRequest() // 추가할 조건 NSPredicate로 설정한다음에 let predicate = NSPredicate(format: "name = %@", "Time") // fetchRequest에 predicate 추가해주기 fetchRequest.predicate = predicate // 이름에 Time들어간 엔티티들만 가져옴
Fetch할 때 정리해서 가져오기 - Sorting
- 그냥 fetchRequest를 전달하면 엔티티들을 순서가 없이 무작위로 가져온다.
- 가져온다음에 정렬하는 방법도 있지만 이왕이면 가져올때부터 정렬되있는게 좋지 않겠는가?
NSSortDescriptor
를 사용하면 정렬에 사용할 key와 순서를 fetchRequest에 전달할 수 있다.
// creationDate attribute value를 기준으로 오름차순으로 정렬하기 let dateSortDescriptor = NSSortDescriptor(key: "creationDate", ascending: true) // fetchReqeust에 sortDescriptors로 정렬 조건 추가해주기 songsFetchRequest.sortDescriptors = [dateSortDescriptor]
NSSortDescriptor
가 배열로 되어 있어 정렬조건을 여러개 추가할 수 있다.
let dateSortDescriptor = NSSortDescriptor(key: "creationDate", ascending: true) let nameSortDescriptor = NSSortDescriptor(key: "name", ascending: true) songsFetchRequest.sortDescriptors = [dateSortDescriptor, nameSortDescriptor]
Fetch할 때 범위 지정하기 - Fetch Offset and Limit
NSFetchReqeust
에fetchOffset
을 사용하면 지정한 offset부터 엔티티들을 가져올 수 있다.NSFetchReqeust
에fetchLimit
를 사용하면 지정한 개수만큼만 엔티티들을 가져올 수 있다.
request.fetchOffset = 10 // 10번째 row 부터 request.fetchLimit = 3 // 3개 만 가져오기
Fetch할때 Context까지 아니면 Store만
- Context는 Scratch Pad처럼 엔티티에 대해 작업한 내용을 임시로 가지고 있는 곳으로 이곳의 작업 내용들은 아직 Store에 반영을 할지 안할지 결정하지 않은 상태이다.
- Store로부터 fetch를 해서 저장한 엔티티들을 Context에 가져온 뒤 여기에 새로운 엔티티를 추가하고 기존의 엔티티의 값을 변경하는 등에 작업을 해도 save를 하기 전까지는 Context에서만 일어난 일이지 아직 정식으로 Store에 반영되지 않은 상태이다.
- Context 상에서 일어난 변화를 실제로 반영하기 위해서는 Context에 save를 호출해야한다
- fetch 를 수행할 때 default로는 Save Data (실제 Store에 저장된 내용) + UnSaved Data (Context에서 일어났지만 Store까지는 아직 반영하지 않은 내용) 모두를 가져온다.
- 만약에 fetch를 할때 실제 Store에 저장되어 있는 내용만 (Unsaved Data를 빼고 Saved Data만)가져오고 싶다면
NSFetchRequest
의includePendingChanges
를 false 로 바꾸면 된다.
Entity ID로 Fetch하기
- 우리가 각 엔티티에 id 속성을 따로 넣는 것과는 별개로 Core Data 객체들은 Store 내부에서 서로를 구분하기 위해 자체 내부 ID가 존재한다.
objectID
라는 프로퍼티로NSManagedObjectID
타입이다.objectID
는 서로 다른 Context에서 동일한 엔티티를 식별할 때 주로 사용된다.
// context에 해당 ID의 엔티티가 있는지 확인 let newSong = try context.existingObject(with: objectID)
Context에서 엔티티 삭제하기
일단 삭제하려는 엔티티
NSManagedObject
가 메모리 위에 올라와 있으면delete
로 삭제 가능context.delete(song)
'Apple🍎 > CoreData' 카테고리의 다른 글
Core Data Context 이해와 활용 - 심화 (0) 2024.01.15 데이터 모델 개념을 기초로 엔티티와 클래스 관계 알아보기 (0) 2024.01.09 Core Data 뜯어 보기 (0) 2024.01.04 엔티티와 컨텍스트 (0) 2024.01.02