일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 데드락
- 알고리즘
- 비동기
- 앨런
- struct
- 동기화
- Codable
- Linked List
- deadlock
- Swift
- COLOR
- 상호배제
- Algorithm
- IOS
- decode
- 오브젝트
- @state
- Apple Developer Academy
- 인프런
- 프로세스 스케줄링
- 동시성
- UserDefaults
- scrollview
- forEach
- core data
- 100 days of SwiftUI
- 가상 메모리
- 운영체제
- async
- SwiftUI
Archives
- Today
- Total
기어가더라도 제대로
[iOS - Swift] CoreData 용어 정리 및 CRUD 사용법 본문
목차
- core data 는 무엇?
- DB 와 다른가? ORM은 무엇인가?
- Application's Model Layer
- Data Model 만들기
- Entity 란?
- Core Data Model로 부터 Swift Class 만들기(3가지 방법)
- Core Data Stack 이란? 그리고 생성하기
- NSManagedObjectModel
- NSManagedObjectContext
- NSPersistentStoreCoordinator
- NSPersistentContainer
- 뷰와 연결하기
- 사례 예시: Business, Department, Employee :: Core Data Model
- Get, Add, Delete, Update
Core Data는 무엇인가?
- 앱의 모델 계층
- 객체 그래프 관리 프레임워크
- 메모리에 있는 관계 그대로 디스크에 쓰고 싶을 때 사용한다.
DB와 다른것인가? ORM 은 무엇인가?
- 엄밀히는 DB는 아니다. Core Data 프레임워크에서 제공하는 기능 중에 하나가 DB 기능이다.
- DB 저장 + DB 검색
- ORM 은 DB에 CRUD를 할 때 Swift 언어에서 하는 방식처럼 사용하게 해주는 기능이다.
- 복잡한 쿼리문을 배울 필요 없이 간편하게 api로 db와 통신 가능
Application의 Model Layer
- Domain 계층의 Model 객체들을 Device도 이해하는 객체로 변환한 것이라 이해하면 편하다.
Data Model 만들기
- Command N → Data Model
Entity란?
- Entities 에 들어가는 하나의 클래스
- Entity 하나는 편하게 DB에서 Table 하나라고 보면 된다.
- Attributes, Relationships, Fetched Properties 는 컬럼 역할
- Attributes: 클래스에 속한 프로퍼티
- RelationShips: Entity간에 관계를 나타냄
Attributes에서 배열타입 설정
- repeats 라는 프로퍼티에 [Int] 타입을 설정하고 싶은 경우
- Attribute 레코드 클릭 -> Type을 "Transformable" 로 변경 -> Inspector -> Attribute - Custom Class -> "바꾸고 싶은 타입으로 설정"
Relationships에서 배열 타입 설정
- Person Entity가 Family에 여럿 있을 수 있는 경우
members: [Person]
- Family 기준 → Members 프로퍼티에 Person 배열이 있다. inverse 는 Person 기준에서 family는 어떤 프로퍼티에 있는가 를 정의
- Person 기준 → family 프로퍼티에 Family가 있다. Family에서 Person은 members 프로퍼티에 속한다.
- 서로의 역관계도 설정을 해놓아야 변화를 감지할 때 양방향으로 전달
- Family Entity는 여러개의 Person을 가질 것이니까 Relationships에서 항목을 하나 클릭하고
- 해당 로우 클릭 → Inspector → Type → “To One”에서 “To Many” 로 변경
- members에서 여러 Person으로 그래프가 갈 수 있도록 설정
custom type 을 attribute의 type 으로 채택하기
- 위의 unit 속성을 Unit 이라는 enum 케이스 타입으로 채택할 것임
- type 과 사용할 enum 의 Int16 타입을 맞추고, Objective-C 에서 사용가능하다는 키워드를 달아줘야함
- 기본으로 설정되는 Optional 값을 체크 해제하고 기본값을 설정
@objc
public enum Unit: Int16 {
case hour = 0
case minute = 1
case km = 2
case times = 3
}
- 위의 Unit 이란 타입을 프로퍼티로 사용할 부모 Entity로 가서
- Inspecter ->. Class -> Codegen -> Manual/None
- Navigator Bar -> Editor -> Create NSManagedObject Subclass ... : 로 하면 자동으로 두가지 파일이 생성됨
- Notice+CoreDataProperties 에서 extension으로 unit 프로퍼티에 대한 타입을 지정해줄 수 있음
extension Notice : Identifiable {
@NSManaged public var unit: Unit
}
- 추가 팁: 기존에 시뮬레이터에서 구동중인 데이터가 있고 이를 커스텀 타입으로 변경했을 때 오류가 나는데, 이것을 마이그레이션 하는 것도 재밌는 학습이 될거라 생각합니다.
- 미세먼지 팁: 위에서 enum 을 했는데 struct 는 되나 검색을 해보니, 지원하지 않는다고 하네요. 그래서 방법이 Data 타입은 지원해주니, struct 를 Data 로 형변환해서 저장하는 방법이 일단은 떠오릅니다.
Core Data Model로 부터 Swift Class를 만드는 3가지 방법
- Data Model Inspector → Entity → Class → Codegen
- 여기서 3가지 방식으로 Swift Class 를 만들어주는 방법이 존재한다.
- Xcode Editor에서 만들어준 Entity를 어떻게 Swift 에서 Class 처럼 사용할지 설명한다.
- 각 방식간에 전환 설정은 꼭 클린 빌드 폴더를 해야 전환이 된다.
- Class Definition
- 별다르게 하지 않아도 Swift 타입으로 인식
- 해당 타입 안에서 메서드나 기타 수정 불가
- Manual/None
- 수동으로 Entity에 해당하는 Swift Class 파일을 생성
- 이 모드로 전환 후 Xcode 상단 Bar → Editor → Create NSManagedObject Subclass
- 자동으로 파일 추가됨
- Category/Extension
- Class Definition 처럼 자동으로 추가가 되지만, 추가로 선언하고 메서드나 연산 프로퍼티에 대해서
- Extension 으로 추가
Core Data Stack 이란? 그 생성법
- 4가지 객체가 있는데, 가장 중요한 것은 Context이다.
- ORM답게 Context의 변화를 감지하고 DB에 자동으로 전달하기 때문이다.
- 개발자가 엄격하게 DB의 CRUD 문법을 전달하지 않아도 된다.
- Core Data 내부에 SQL 기능을 편리하게 사용할 수 있도록 도와주는 도구의 집합이다.
NSManagedObjectModel
- 객체의 그래프 관리가 Core Data의 주 업무라고 했는데, 그 그래프를 담당한다.
.xcdatamodeld
에 있는 Entity들의 프로그래밍적인 표현이다.- 주로 사용하진 않는다.
NSManagedObjectContext
- managed object에 변화를 추적하고 조작하는 객체 공간
- 데이터 베이스에 있는 객체를 보고 접근하게 해주는 Window
- 개발자가 주로 사용
- 항목의 생성, 수정, 삭제, 검색등의 작업을 한다.
- Core Data Class에 생긴 변화를 추적한다.
NSPersistentStoreCoordinator
- Context와 persistent store의 소통을 돕기 위해 모델을 사용하는 coordinator
- Persistent Store를 관리
- 실제 데이터베이스를 관리해 store로부터 앱의 타입의 인스턴스들을 저장하고 fetch 한다.
- Context의 변화를 읽고 실제 store에 저장하거나 검색하는 일을 대신 해주는 기특한 녀석
- 자주 사용하진 않는다.
NSPersistentContainer
- 위의 3개, 즉 Core Data Stack을 캡슐화한 컨테이너이다.
- 이 클래스에 프로퍼티로 위에 3녀석이 자리하고 있다.
- Core Data Stack의 생성과 관리를 간단하게 해준다.
- Core Data의 DB라고 할 수 있다.
뷰와 연결하기
- persistent container 만 만들어주면 core data stack 모두를 사용할 수 있다.
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "appName")
container.loadPersistentStores { _, error in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
}
}
.xcdatamodeld
의 파일 이름과 container의 이름이 같게 컨테이너를 만든다.- persistent store load 명령을 내린다.
- SwiftUI에서는 ~App.swift 파일에서 사용
데이터를 디스크에 저장하는 메소드 만들기
// ~ PersistenceController
func saveContext() {
let context = container.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
- persistent container에 viewContext를 통해 데이터베이스에 접근
context.hasChanges
를 사용해 변화가 있을 때 저장
앱의 나머지 부분에 연결하기
struct CoreDataPracticeApp: App {
let persistenceController = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}
- context를
.managedObejctContext
라는 키값으로 전달한다. - SwiftUI 앱의 다른 부분에서도
@Environment
에 접근하여 사용 가능하다.
뷰에서 managed object context 사용하기
// ContentView
@Environment(\.managedObjectContext) var managedObjectContext
func saveContext() {
do {
try managedObjectContext.save()
} catch {
print("Error saving managed object context: \(error)")
}
}
- 변화를 저장하기 위해서 항상
managedObjectContext.save()
를 해야한다.
객체를 fetch 해오기
@FetchRequest(
entity: Movie.entity(),
sortDescriptors: [
NSSortDescriptor(keyPath: \Movie.title, ascending: true)
],
predicate: NSPredicate(format: "genre contains 'Action'")
) var movies: FetchedResults<Movie>
@FetchRequest
: 데이터에 변경이 생길 때마다 계속 fetch 해오는 속성- entity: 가져올 entity
- sortDescriptors: entity의 인스턴스들을 어떤 기준으로 정렬해 가져올 것인지 설정
- Movie.title을 기준으로 오름차순으로 가져옴
- predicate: 일종의 필터링 과정. Movie.genre 에서 “Action” 이 있는 객체만 걸러짐
- NSPredicate 문법이 복잡하고 다양해서 따로 검색을 해보는 것을 추천
CRUD 메소드 구현
// ContentView
func addMovie(title: String, genre: String, releaseDate: Date) {
let newMovie = Movie(context: managedObjectContext)
newMovie.title = title
newMovie.genre = genre
newMovie.releaseDate = releaseDate
saveContext()
}
func deleteMovie(at offsets: IndexSet) {
offsets.forEach { index in
let movie = self.movies[index]
self.managedObjectContext.delete(movie)
}
saveContext()
}
- 생성 및 삭제 후 saveContext() 해줌
예시) Business, Department, Employee - Core Data Model sample
- Reference Video: https://www.youtube.com/watch?v=huRKU-TAD3g&t=967s&ab_channel=SwiftfulThinking
- 모두 정확한 동작이 되지 안을 수 있다.
[Add, Fetch] Entity - Business 회사 가져오기
class RelationshipViewModel: ObservableObject {
let manager = CoreDataManager.instance
@Published var businesses: [BusinessEntity] = []
@Published var departments: [DepartmentEntity] = []
@Published var employees: [EmployeeEntity] = []
init() {
getBusinesses
}
func addBusiness() {
let newBusiness = BusinessEntity(context: manager.context)
newBusiness.name = "Apple"
//비지니스에 부서 추가하기
//newBusiness.departments = []
//비지니스에 직원 추가하기
//newBusiness.employees = []
//회사에 부서 추가하기
//newBusiness.addToDepartments(value: DepartmentEntity)
//비지니스에 직원 추가하기
//newBusiness.addToEmployees(value: EmployeeEntity)
save()
}
func getBusinesses() {
let request = NSFetchRequest<BusinessEntity>(entityName: "BusinessEntity")
do {
businesses = try manager.context.fetch(request)
} catch let error {
print("Error fetching. \(error.localizedDescription)")
}
}
func save() {
manager.save()
}
}
[Fetch] Entity - 회사에 속한 부서들과 직원들 가져오기
struct BusinessView: View {
let entity: BusinessEntity
var body: some View {
VStack {
Text("Name: \(entity.name ?? "")")
.bold()
if let departments = entity.departments?.allObjects as? [DepartmentEntity] {
Text("Departments:")
.bold()
ForEach(departments) { department in
Text(department.name ?? "")
}
}
if let employees = entity.employees?.allObjects as? [EmployeeEntity] {
Text("Employees: ")
.bold()
ForEach(employees) { employee in
Text(employee.name ?? "")
}
}
}
}
}
[Delete] Entity - 부서 삭제
// ~ RelationshipViewModel
func deleteDepartment() {
let department = departments[2]
manager.context.delete(department)
save()
}
[Update] Entity - 회사의 정보 수정
// ~ RelationshipViewModel
func updateBusiness() {
let existingBusiness = businesses[2]
existingBusiness.addDepartments(departments[1])
save()
}
Entity 자체가 삭제되었을 때 Relationships 보존 전략
- Data Model 에서
- Entity: DepartmentEntity
- Relationships: employees
- Delete Rule 설정으로 데이터를 삭제할 때 관계된 것들을 어떻게 남겨둘지 삭제할지 전략을 세울 수 있다.
Nullify
"Finance" 부서가 삭제되면 소속 직원인 "Emily" 는 자신이 속한 부서만 Null로 될 뿐 employeeEntity로 남아있다.
Cascade
"Finance" 부서가 삭제되면 모든 소속 직원 EmployeeEntity 가 삭제된다. 즉, "Emily" 도 삭제된다.
Deny
- 부서에 속한 모든 employees 가 없어질 때까지 해당 부서의 삭제를 거부함
- 이 경우 emily가 있기 때문에 "Finance" 부서 자체의 삭제가 거절되고 에러를 반환
출처
https://velog.io/@nala/iOS-Core-Data는-대체-무엇인가
https://velog.io/@nala/iOS-SwiftUI에서-CoreData-써보기#1-3-core-data-model-file로부터-swift-class를-생성한다
https://www.youtube.com/watch?v=huRKU-TAD3g&t=967s&ab_channel=SwiftfulThinking
https://stackoverflow.com/questions/26900302/swift-storing-states-in-coredata-with-enums
'Swift - 데이터베이스' 카테고리의 다른 글
[iOS-CoreData] Unit Test 해보기 (0) | 2023.02.03 |
---|---|
[iOS-CoreData]Repository Pattern with CoreData (1) | 2023.02.01 |
[iOS-Swift] CoreData Object가 유일하다고 보장하기(feat. constraints) (0) | 2022.12.04 |
[iOS-Swift] CoreData, NSManagedObject Subclass 에서 옵셔널 제거하기 (0) | 2022.12.02 |
[iOS-Swift] CoreData Model의 Entity가 Hashable - \.self 가 가능해지는 이유 (0) | 2022.12.01 |
Comments