Core Data에 대해 아라보자 (2) - Core Data Stack

Yang Si Yeon·2022년 1월 31일
1

CoreData

목록 보기
2/3

Core Data Stack은 Core Data의 핵심으로 Core Data를 움직이는 객체들의 모음이다. 공식문서에서는 앱의 모델 계층을 관리하고 유지한다고 설명한다.

Stack의 구성

  • NSManagedObejctModel
  • NSPersistentStore
  • NSPersistentStoreCoordinator
  • NSManagedObjectContext

Managed object model

NSManagedObjectModel은 앱에서 쓰이는 데이터 모델의 object 타입 (entity), 가질 수 있는 속성 (attribute), 이들 간의 관계(relation)를 나타낸다. Core Data Stack의 다른 곳에서는 해당 model을 사용해 object를 만들고 속성을 저장하고 데이터를 저장한다.

NSManagedObjectModel을 데이터베이스 스키마로 생각할 수도 있지만, 이것은 Core Data Stack이 내부에서 SQLite를 사용하는 경우에만 해당될 수 있다. Core Data는 SQLite 이외에 다양한 영구 저장소를 사용하기 때문에 데이터베이스 스키마 보단 좀 더 일반적인 용어로 생각하는게 좋다.

Persistent store

NSPersistentStore는 데이터를 읽고 쓰는 역할을 한다. Core Data는 기본적으로 4가지 유형의 NSPersistentStore를 제공하는데 3개는 atomic 성질을 띄고 1개는 non-atomic 성질을 띈다.

  • atomic 영구 저장소
    : 읽기 또는 쓰기 작업을 수행하기 전에 영구 저장소를 완전히 역직렬화하고 메모리에 로드해야 함
  • non-atomic 영구 저장소
    : 필요에 따라 자체 chunk를 메모리에 로드할 수 있음
  • 직렬화(Serialization)란?
    : 객체를 직렬화하여 전송 가능한 형태로 만드는 것.
    객체들의 데이터를 연속적인 데이터로 변형하여 Stream을 통해 데이터를 읽을 수 있도록 해주며 주로 객체들을 통째로 파일로 저장 및 전송하고 싶을때 사용
  • 역직렬화(Deserialization)란?
    직렬화된 파일 등을 역으로 직렬화하여 다시 객체의 형태로 만드는 것.
    저장된 파일을 읽거나 전송된 Stream 데이터를 읽어 원래 객체의 형태로 복원

아래는 저장소의 종류이다.

1. NSSQLiteStoreType

SQLite 데이터베이스에 의해 지원되며, Core Data가 지원하는 유일한 non-atomic 저장소로 가볍고 효율적인 메모리 공간을 제공한다. 대부분의 iOS 프로젝트에 적합하고 Xcode의 Core Data 템플릿은 기본적으로 이 저장소 타입을 사용한다.

2. NSXMLStoreType

XML 파일로 지원되며 모든 저장소 타입 중 읽기 가장 쉽다. 하지만 atomic 저장소이므로 메모리 공간이 클 수 있다. OS X에서만 해당 타입을 사용할 수 있다.

3. NSBinaryStoreType

바이너리 데이터 파일로 지원되며, NSXMLStoreType과 마찬가지로 atomic 저장소이기 때문에 전체 바이너리 파일을 메모리에 로드해야 작업을 수행할 수 있다. 실제 애플 어플리케이션에서는 이 타입을 사용하는 것을 거의 찾을 수 없다.

4. NSInMemoryStoreType

메모리 내 영구 저장소 타입으로, 해당 타입은 실제로 영구적이지 않다. 앱을 종료하거나 디바이스를 끄면 해당 유형에 저장된 데이터가 사라진다. 이 저장소는 단위 테스트나 캐싱에 도움이 될 수 있다.

JSON 파일이나 CSV 파일로 지원되는 영구 저장소는 NSIncrementalStore를 상속받아 만들 수 있다. 해당 옵션이 궁금하다면 Apple의 Incremental Store Programming Guide 참고.

Persistent Store Coodinator

NSPersistentStoreCoordinator는 object model과 store(저장소)를 연결하는 다리 역할을 한다. NSManagedObjectModel을 이용하여 entity를 만들고, 이미 entity가 존재하고 있다면 NSPersistentStore로 부터 데이터를 로드한다.

NSPersistentStoreCoordinator는 실제 DB 작업을 수행하는 곳이며, NSManagedObjectContext의 요청에 대한 답을 주고, data에 대한 validation도 수행한다.

Managed object context

코드를 작성할 땐 항상 NSManagedObjectContext를 사용하기 때문에 context가 작동하는 방식을 꼭 이해해야한다.

  • managed object context 내에서 Core Data object에 대한 모든 작업을 수행한다.
  • 데이터를 변경한 후 context에서 save()를 호출해야 데이터가 저장된다.
  • context는 managed object를 가지고 작업을 하는 in-memory scratchpad(스크래치패드 메모리)이다.
  • context는 object를 만들거나 가져오는 생명주기를 관리한다. 이 생명주기 관리에는 오류(faulting), 역 관계(inversion relationship) 핸들링, 유효성 검사(validation)과 같은 강력한 기능이 포함되어 있다.
  • managed object는 연결된 context 없이 존재할 수 없다. 따라서 모든 managed object는 아래와 같이 접근할 수 있는 context에 대한 참조를 유지한다.
let managedContext = employee.managedObjectContext
  • managed object가 특정 context와 연결되면 생명주기 동안 같은 context와 연결된 상태로 유지된다.
  • 애플리케이션은 하나 이상의 context를 사용할 수 있다. context는 disk에 있는 데이터에 대한 in-memory scratchpad 이므로, 실제로 동일한 Core Data object를 2개의 서로 다른 context에 동시에 로드할 수 있다.
  • context와 managed object는 thread-safe 하지않고, 이들이 생성된 동일한 thread에서만 context 및 managed object와 상호 작용할 수 있다.

Persistent Container ⭐️

NSPersistentContainer는 iOS 10부터 제공되며, 위 4가지 클래스를 모두 orchestrate하는 클래스이다. 우리는 4개의 stack 컴포넌트를 모두 연결하기 위해 보일러 플레이트 코드를 작성하지 않고, NSPersistentContainer를 초기화 한 뒤 저장소를 로드하면된다.

@available(iOS 10.0, *)
open class NSPersistentContainer : NSObject {
    open var name: String { get }
    open var viewContext: NSManagedObjectContext { get }
    open var managedObjectModel: NSManagedObjectModel { get }
    open var persistentStoreCoordinator: NSPersistentStoreCoordinator { get }

    // Creates a container using the model named `name` in the main bundle
    public convenience init(name: String)

    public init(name: String, managedObjectModel model: NSManagedObjectModel)

    // Load stores from the storeDescriptions property that have not already been successfully added to the container. The completion handler is called once for each store that succeeds or fails.
    open func loadPersistentStores(completionHandler block: @escaping (NSPersistentStoreDescription, Error?) -> Void)
}

Xcode에서 제공해주는 Core Data 템플릿을 이용하지 않고 직접 Core Data Stack을 구축해서 Core Data를 사용할 수 있다. Core Data가 어떻게 작동하는지 정말 알고 싶다면 자신의 Core Data Stack을 구축하는게 필수라고 한다.

자신의 Core Data Stack을 구축한다는게 엄청 대단한 일처럼 느껴질 수 있지만,

  • 원래 AppDelegate를 통해 접근할 수 있는 persistentContainer를 가지며
  • context를 save하는 함수를 가지는

커스텀 wrapper를 만든다는 의미인 것 같다.

Stack Object

class CoreDataStack {
	private let modelName: String
	
	lazy var managedContext: NSManagedObjectContext = {
		return self.storeContainer.viewContext
	}()
	
	init(modelName: String) {
		self.modelName = modelName
	}
	
	private lazy var storeContainer: NSPersistentContainer = {
		let container = NSPersistentContainer(name: self.modelName)
		container.loadPersistentStores { _, error in
			if let error = error as NSError? {
				print("Unresolved error \(error), \(error.userInfo)")
			}
		}
		return container
	}()
	
	func saveContext() {
		guard managedContext.hasChanges else { return }
		
		do {
			try managedContext.save()
		} catch let error as NSError {
			print("Unresolved error \(error), \(error.userInfo)")
		}
	}
}

CoreDataStack 생성은 아래와 같이 할 수 있다.

lazy var coreDataStack = CoreDataStack(modelName: "ModelName")

해당 포스팅에서는 class의 이름을 CoreDataStack이라고 명명했지만 다른 포스팅에서는 PersistentManager, PersistentContainer등 다양한 이름으로 쓰인다.


참고

https://aroundck.tistory.com/4733
raywenderlich - Core Data by Tutorials

profile
가장 젊은 지금, 내가 성장하는 데에 쓰자

0개의 댓글