Search
Duplicate

(Swift) 코어 데이터 이해하기

간단소개
팔만코딩경 컨트리뷰터
ContributorNotionAccount
주제 / 분류
Swift
Scrap
태그
9 more properties
"아래 모든 내용은 애플 공식 문서를 참고하여 정리 및 구현한 내용입니다."

Core Data

Overview

한 개의 장치에서 데이터를 유지/캐시하거나, CloudKit을 통해 여러 장치에서
데이터를 동기화할 수 있도록 하는 프레임워크
Core Data를 사용하면, 오프라인에서 어플리케이션의 영구적인 데이터를 저장하고
일시적인 데이터를 캐시할 수 있습니다.
한 개의 iCloud 계정을 사용하는 다수의 기기에 데이터를 동기화하기 위해,
Core Data는 자동으로 CloudKit 컨테이너에 미러링합니다.
Core Data의 Data Model editor을 통해, 데이터의 타입과 관계를 정의하고,
정의된 각각의 클래스를 생성합니다.

Persistence

Core Data는 저장소에 데이터를 맵핑하는 세부 과정을 추상화합니다.
이는 Swift, Obj-C에서 데이터 베이스를 직접적으로 관리하지 않고,
쉽게 데이터를 저장하도록 해줍니다.

Undo and Redo of Individual or Batched Changes

Core Data의 뒤로가기(undo) 관리자는 데이터 변화를 추적하고,
개별의 데이터를 전체적으로 또는 그룹별로 롤백할 수 있습니다.
이 기능은 앱에서 redo, undo 기능을 쉽게 추가하도록 도와줍니다.

Background Data Tasks

객체에 JSON을 파싱(parsing)하는 등의 UI와 관련없는 데이터 작업을 백그라운드에서 수행합니다.
수행 후 데이터를 저장 / 캐시하여 서버 왕복을 줄일 수 있습니다.

View Synchronization

Core Data는 테이블 뷰 또는 컬렉션 뷰에 데이터 소스를 제공하여
유저의 뷰와 데이터를 동기화 상태로 유지하도록 도와줍니다.

Versioning and Migation

Core Data는 앱 발전(업데이트)에 따라 데이터 모델의 버전을 관리하고,
유저 데이터를 이전하는 매커니즘을 포함합니다.

Creating a Core Data Model

Overview

Core Data를 사용하기 위해서는 앱의 객체 구조를 담을 데이터 모델 파일을 생성해야 합니다.
데이터 모델 파일을 생성하는데에는 두 가지 방법이 있는데,
새 프로젝트를 생성할 때, 그리고 이미 생성된 프로젝트에 추가입니다.

Add Core Data to a New Xcode Project

먼저, 새 프로젝트를 생성할 때는 새 프로젝트 생성 창에서
하단 버튼 중 [Use Core Data]를 체크하여 프로젝트를 생성합니다.

Add Core Data Model to an Existing Project

이미 존재하는 프로젝트에 데이터 모델을 추가할 때는,
Xcode에서 File > New > File(Command + N)을 선택한 후,
iOS 템플릿에서 Data Model을 선택합니다.
위 두 가지 방법을 사용해서 Data Model을 생성하면, Project Navigator에서 파일을 확인할 수 있습니다.
(클래스 명은 Entity 명으로 설정되니, 파일 명은 Model, Data Model 등으로 설정하셔도 됩니다.)
참고자료

Configuring Entities

Entity란?

Entity는 이름, 속성(Attirbute), 관계 등을 포함한 객체(쉽게 이해하면 데이터로 활용되는 class)

Add Entities

Core Data model을 추가한 후, .xcdatamodeld 파일에서 아래와 같이 Entity를 추가한다.
좌측 하단에 있는 Add Entity 버튼을 누른다.
좌측 상단 ENTITIES List에 추가된 새 Entity의 이름을 바꿔준다.

Configure Entities

Entity를 추가한 후 우측 Inspector bar의 data inspector을 보면 설정할 수 있는 부분있다.
내용이 많지만 내게 필요한 Codegen만 정리하고 넘어가려 한다.
Codegen
Code generation의 약자로 선택된 옵션에 따라 Entity를 지원하기 위해
managed object subclass와 프로퍼티 파일을 생성한다.
3가지 값을 선택할 수 있는데,
1.
Class Definition(기본 값)
생성된 로직이나 프로퍼티를 수정할 필요가 없는 경우 사용하는 옵션
이 옵션을 사용할 경우, 생성된 소스코드 파일이 프로젝트에 보이지 않는다.
Xcode는 클래스와 프로퍼티 파일을 빌드 할 때 생성하고, 빌드 디렉토리에 넣어놓는다.
2.
Category/Extension
Managed object subclass에 Logic을 추가하고 싶을 때 사용하는 옵션
이 옵션을 선택하면 Subclass 파일을 생성해야 하는데, 방법은 아래와 같다.
1.
상단 메뉴 바에서 Editor > Create NSManagedObject Subclass 선택
2.
해당 Entity를 선택하고 Next > Next > Create 버튼 클릭
3.
Create 버튼을 클릭하면, Class와 properties 파일이 생성되는데,
Properties 파일은 삭제한다.
(위 옵션에서도 프로퍼티는 빌드 할 때 생성되기 때문에)
생성하고 나면, 프로젝트 네비게이터에 아래와 같이 나타난다.
3.
Manual/None
Managed object subclass의 로직이나 프로퍼티를 수정해야 할 때, 사용하는 옵션
이름과 같이 이 옵션을 사용하면 Core Data는 파일을 생성하지 않고, 개발자가 수동(Manually)으로 파일을 생성해야한다.
파일 생성 방법은 Category/Extension과 같고, 프로퍼티 파일을 삭제하지 않고 사용한다.
참고자료
[Apple doc: Configuring Entities]
[Apple doc: Generating Code]

Configuring Attributes

Attribute란?

Entity에 들어가는 프로퍼티(class 내의 프로퍼티의 개념이랑 비슷하다.)

Add Attributes

Entiry를 선택하고 우측 하단의 Add Attributes 버튼을 누른다.
새로 추가한 Attribute의 이름을 설정하고, Type(기본값: Undefined)을 설정한다.
추가할 옵션이 있다면 우측 Data Model Inspector에서 확인하고 수정한다.

Setting Up a Core Data Stack

Core Data Model을 만든 후, 본격적인 구현은 Core Data Stack을 만들면서 부터 시작됩니다.

Core Data Stack

저도 Core Data Stack 부분 이해하는데, 시간이 오래걸렸는데,
이걸 이해 하셔야 Core Data를 알고 사용할 수 있습니다.
Core Data는 앱의 모델 계층을 관리, 유지하기 위해 몇가지 클래스를 제공합니다.
이러한 클래스를 통틀어 Core Data Stack이라 하는데, 아래와 같습니다.
1.
NSManagedObjectModel: Core Data Stack에서 접근할 데이터를 나타냅니다.
2.
NSManagedObjectContext
위 객체는 앱이 가장 많이 상호작용하는 객체이며, 앱 전체에 노출됩니다.
영구 저장소에서 객체를 가져올 때, 임시 복사본으로 가져와 수정할 수 있고 저장하지 않는 한 영구 저장소의 데이터는 변경되지 않는다.
모든 managed objects는 managed object context에 등록되어야 합니다.
컨텍스트(Context)를 사용하여 개체 그래프에 개체를 추가하고, 삭제할 수 있습니다.
컨텍스트는 각 개체의 Attrubutes와 개체 간의 관계의 변경 사항을 추적합니다.
추적을 통해 컨텍스트는 undo(뒤로가기)와 redo(다시하기) 기능을 제공할 수 있습니다.
3.
NSPersistentStoreCoordinator
영구 저장소(NSPersistentStore)에서 앱 타입의 인스턴스를 저장하고, 가져옵니다.
(영구 저장소는 디스크에 있을 수도, 메모리에 있을 수도 있습니다)
NSManagedObjectModel 개체가 초기화 된 후 생성됩니다.
위 객체는 영구 저장소에서 데이터를 인식하면
해당 데이터를 요청하는 NSManagedObjectContext로 전달합니다.
NSPersistentStore을 추가하는 호출은 비동기적으로 수행됩니다.
(동기적 수행 시, 예외상황에서 UI 스레드를 멈춰버리는 등의 상황이 발생할 수 있음)
4.
NSPersistentContainer
Core Data Stack 생성을 처리합니다.
(Model, Context, Coordinator을 한 번에 설정합니다.)
NSManagedObjectContext에 대한 접근과 여러 편리한 메서드를 제공합니다.
참고자료
그냥 Documentation만 찾으면... 내용이 부족하고, Documentation Archive를 찾아야합니다.

Initailize a Persistent Container

일반적으로 Core Data는 앱이 실행될 때, 초기화 시킵니다.
우선, persistent container를 처음 호출할 때 까지 초기화를 지연시키도록 lazy 변수로 선언해줍니다.
프로젝트를 생성할 때, Core Data를 선택했다면, 아래 코드는 자동으로 생성되지만,
선택하지 않았다면 직접 작성해야합니다.
1.
AppDelegate 파일에 NSPersistentContainer 타입의 lazy 변수를 선언합니다.
2.
변수에 persistent container 인스턴스를 생성하고, 데이터 모델 파일 이름과 이니셜라이저를 넘겨줍니다.
3.
loadPersistentStores 메서드를 통해, 영구(Persistent) 저장소를 불러옵니다.
만약 저장소가 없다면, 생성합니다.
import CoreData class AppDelegate: UIResponder, UIApplicationDelegate { ... lazy var persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "DataModel") container.loadPersistentStores { description, error in if let error = error { fatalError("Unable to load persistent stores: \(error)") } } return container }() ... }
Swift
복사
persistent container가 한번 생성되면 model, context, store coordinator
각 인스턴스에 대한 참조를 managedObjectModel, viewContext, persistentStoreCoordinator 프로퍼티에 담는다.

Pass a Persistent Container Reference to a View Controller

UI에서 container을 참조하기 위해, container의 참조를 view로 넘겨줍니다.
우선, Core Data를 import하고, persistent container을 참조하기 위해
앱의 root view controller(가장 아래 뷰(메인 뷰인 경우가 많음))에 아래와 같이 변수를 선언합니다.
import UIKit import CoreData class ViewController: UIViewController { // 아래 코드와 같이 persistent container를 참조하는 변수 선언 var container: NSPersistentContainer! override func viewDidLoad() { super.viewDidLoad() guard container != nil else { fatalError("This view needs a persistent container.") } // 이 아래 코드는 persistent container가 사용 가능한 경우 실행됩니다. } }
Swift
복사
AppDelegate 파일로 돌아와서 application(_:didFinishLaungchingWithOptions:) 메서드 내부에
해당 앱의 root view controller 타입으로 다운 캐스팅한 window?.rootViewController를
rootVC 변수에 넣습니다.
약간 말이 어렵지만, window?.rootViewController를 ViewController(제일 아래 뷰) 타입으로
다운 캐스팅(ViewController의 프로퍼티인 container을 사용하기 위해)해서 변수에 넣어준다고 이해하면 좋습니다.
class AppDelegate: UIResponder, UIApplicationDelegate { ... func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { if let rootVC = window?.rooViewController } ... }
Swift
복사

Subclass the Persistent Container

NSPersistentContainer은 자식 클래스(subclass)로 사용되는 걸 선호합니다.
자식 클래스는 Core Data와 연관된 코드를 편리하게 넣을 수 있는 장소입니다.
import CoreData // PersistentContainer이 NSPersistentContainer의 자식 클래스(subclass) // 이렇게 선언하면 편하게 saveContext 함수를 사용할 수 있다. class PersistentContainer: NSPersistentContainer { func saveContext(backgroundContext: NSManagedObjectContext? = nil) { let context = backgroundContext ?? viewContext guard context.hasChanges else { return } do { try context.save() } catch let error as NSError { print("Error: \(error), \(error.userInfo)") } } }
Swift
복사
추후에 Core Data를 백그라운드에서 받아올 때, 참조할 문서 [Using Core Data in the Background]