일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 앱 성능 개선
- 스켈레톤 통합
- Privacy manifest
- 파노라마 뷰
- Android
- boilerplate 제거
- ios
- launch screen
- 360도 이미지 뷰어
- 360도 이미지
- 스켈레톤 UI
- privacyinfo.plist
- native
- React-Native
- panorama view
- 스플래시스크린
- 라이브러리 없이
- 리엑트 네이티브
- react-native-fast-image
- launchscreen
- 3b52.1
- 네이티브
- Skeleton UI
- 360도 뷰어
- react
- requirenativecomponent
- 리액트
- 리액트 네이티브
- Native Module
- React Native
- Today
- Total
Neoself의 기술 블로그
iOS Core Data 프레임워크로 데이터 구조화 및 유지하기 본문
오픈소스를 활용하지 않고 iOS 앱 개발 시, 내부에 데이터를 저장하여 활용하는 방법은 크게 2가지가 있습니다.
- UserDefault
- CoreData
비밀번호나 사용자 이메일 주소와 같은 단순한 키-값을 저장하는 데에는 이전에 말씀드렸던 UserDefaults나 Keychaion이 더 적합합니다. 하지만, 복잡한 데이터 관계가 필요하거나, 데이터의 변경에 대한 롤백, 즉 변경 추적이 필요한 경우에는 Core Data가 더 적합한 프레임워크가 됩니다.
Core Data가 그래서 뭐죠?
Core Data는 객체 그래프 관리자로서, iOS 어플리케이션에서 데이터를 구조화하고 유지하기 위한 프레임워크입니다. 여기서 Core Data는 DB 그 자체가 아닌, 객체 그래프, 즉 아래와 같이 객체들 간의 관계를 나타내는 연결된 네트워크를 관리하는 주체입니다.
// 엔티티 간 관계 예시
class Department: NSManagedObject {
@NSManaged var name: String
@NSManaged var employees: Set<Employee> // 1:N 관계
}
Core Data가 동작하기 위해선 아래와 같은 컴포넌트의 구성이 필요합니다.
1. NSManagedObjectModel
@NSManaged public var greeting: String?
@NSManaged public var id: UUID?
@NSManaged public var image: String?
@NSManaged public var mbti: String?
@NSManaged public var name: String?
.xcdatamodeld 파일에서 정의되며, 엔티티, 속성, 관계 등 데이터의 스키마를 정의하는 컴포넌트입니다.
2.NSPersistentStoreCoordinator
모델과 저장소 간의 중재자 역할입니다. 여기서 저장소의 경우 일반적으로 SQLite를 사용하지만, Binary, In-Memory 타입의 저장소 또한 지원이 됩니다.
3. NSManagedObjectContext
메모리 내의 작업 공간을 지칭하며, 객체의 CRUD 작업을 실질적으로 수행하는 공간입니다. 동시성 프로그래밍을 구현하는 DispatchQueue 도구와 유사하게 Core Data 프레임워크에서도 동시성 처리를 위한 Context 타입들을 제공합니다.
// Context 계층 구조 설정 예시
let mainContext = persistentContainer.viewContext
let privateContext = persistentContainer.newBackgroundContext()
let childContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
childContext.parent = mainContext
- Main Context(UI Context) : UI 업데이트를 위한 메인 스레드의 Context이며, 메인스레드와 동일하게 직접적인 사용자 상호작용을 처리합니다.
- Private Context: 백그라운드 작업을 위한 Context이며, 주로 무거운 데이터 처리 작업을 수행합니다.
- Child Context: 임시 작업을 위한 컨텍스트로 변경사항을 부모 Context에 선택적으로 저장할 수 있습니다.
// AppDelegate 내에 선언,
lazy var persistentContainer: NSPersistentContainer = {
// 1. Core Data 스택 설정을 단순화하는 컨테이너 클래스 생성
let container = NSPersistentContainer(name: "JGDD_MO") // 엔티티 정의 파일명과 동일해야함!
// 2. 저장소 로드
container.loadPersistentStores { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error)")
}
}
return container
}()
...
// 영구저장소의 데이터에 접근하기 위해 context 접근
final class DataManager {
private let context: NSManagedObjectContext
private init() {
self.context = appDelegate.persistentContainer.viewContext
}
...
위 코드는 NSPersistentContainer 클래스를 활용해 Core Data 스택을 자동으로 설정하고, 설정된 container로 영구 저장소를 접근하여 반환하는 코드와, 영구저장소의 데이터에 대한 CRUD를 위해 메인 컨텍스트를 접근하는 코드로 구성되어있습니다.
그럼 Core Data가 시스템 상에서는 어떤 원리로 동작하는 것일까요?
가장 먼저 SQLite로 이루어진 저장소, 즉 영구저장소에서 데이터를 가져오는 과정을 설명드리겠습니다.
이 과정에서 Core Data는 메모리 효율성을 위해 Faulting 매커니즘을 사용합니다.
// Fault 객체 예시
let profile = try context.fetch(fetchRequest).first
// 이 시점에서는 fault 상태 (최소한의 메타데이터만 로드)
print(profile.name) // 실제 데이터 접근 시점에 fault 해결
초기 fetch 시에는 객체의 ID와 메타데이터만 로드를 진행합니다. 그리고 실제로 객체의 속성을 접근할 경우에 필요한 데이터를 데이터 베이스로부터 로드하죠. 여기서 메모리 사용량 최적화를 위해 사용하지 않는 객체는 다시 fault 상태로 언제든지 전환이 가능합니다.
다음은 영구저장소에 데이터를 저장(write)하는 과정입니다.
func createProfile(name: String?, greeting: String?, mbti: String?, image: String?) throws -> JGDD_MO {
let profile = JGDD_MO(context: context)
profile.id = UUID()
profile.name = name
profile.greeting = greeting
profile.mbti = mbti
profile.image = image
do {
try context.save()
return profile
} catch {
context.rollback()
throw ProfileError.creationFailed
}
}
가장 먼저 NSManagedObject 인스턴스가 생성되며, 인스턴스의 속성들에 저장하고자 하는 데이터가 담기게 됩니다. 이때 NSManagedObjectContext는 이를 감지하며, save() 메서드를 호출하면 생성된 인스턴스가 영구 저장소로 기록됩니다.
아래 Core Data 프레임워크를 통해 영구저장소의 데이터에 접근하는 플로우를 보면 더 이해가 쉬울 것 같습니다.
ViewController 단에서 NSManagedObject를 생성하거나 변경하게 되면, NSManagedObjectContext는 변경사항을 자동으로 추적하고, save() 호출 시, 일괄 처리하게 됩니다. 그러면 NSPersistentStoreCoordinator는 객체 ID(URI)와 영구저장소의 실제 데이터 위치를 매핑합니다. 그 마지막으로, SQLite를 사용하는 DB에 Write 동작을 수행합니다. 수행이 완료되면, 컴포넌트가 거친 순서의 역순으로 상태값을 반환합니다.
참고 자료
- Apple Core Data Documentation
- WWDC Sessions on Core Data
감사합니다.
'개발지식 정리 > Swift' 카테고리의 다른 글
WatchOS에서의 CoreData 사용기 - 데이터 손실 없이 안정적으로 서버에 데이터 전송하기 (1) | 2024.10.25 |
---|---|
UIKit + MVC 아키텍처 패턴 사용기 (3) | 2024.10.24 |
Swift에서의 동시성 프로그래밍(Concurrent Programming) (2) | 2024.10.21 |
WCSession 사용기 - 아키텍처 구성 (2) | 2024.10.17 |
PhaseAnimator, Keyframes으로 고급 애니메이션 구현하기 (6) | 2024.10.16 |