저번 Core Data (1) 글에서 Core Data Model을 생성할 때 .xcdatamodeld 파일을 만들고 Entity를 추가, 각 엔터티에 attribute 추가하는 등의 작업을 했다. 그때 Entity에 대해 다양한 설정을 해주는 부분에서 codegen 항목에 있는 category/extension, manual/none, class definition 각 옵션이 어떤걸 의미하는지 정확히 이해하지 못해서 나중에 다시 설명하는 글을 쓰겠다고 넘겼는데 이번 글에서 이 부분을 설명해 보겠다.
[iOS] Core Data (1) | Persistence
[iOS] Core Data (3) | codegen(generating code) 이해해보자!
# 🥨 Entity란?
codegen 항목은 Entity, Attribute를 각각 클래스화, 프로퍼티화시켜주기 위해 class를 자동으로 만들어줄지 수동으로 만들어 줄지를 결정하는 항목이다.
그러니 codegen을 이해하려면 일단 Entity에 대한 이해가 필요하다. Entity는 CoreData가 관리하는 객체라고 이해하면 되는데, 우리가 일반 swift코드를 다룰 때 class와 비슷한 개념이라고 생각하면 된다. 그렇담 Attribute를 추가하는 게 우리가 class 정의할 때 추가해 주는 프로퍼티와 비슷하겠죠?
우리는 core data model을 만들 때 Entity를 추가하고 Attribute까지 설정해주고 나서 이 엔터티에 대해 클래스화를 어떻게 시켜줄 것인가를 고민해야 한다. 이때 볼 수 있는 게 바로 XCode 우측의 inspector 창이다. 여기서 class라는 카터고리에 Name, Module, Codegen 이 있는데 다른 건 이름으로도 예상가는 항목이니 codegen을 이해해 보자.
# 🥨 codegen (generating code)
https://developer.apple.com/documentation/coredata/modeling_data/generating_code
Generating Code | Apple Developer Documentation
Automatically or manually generate managed object subclasses from entities.
developer.apple.com
codegen은 generating code에 대한 것이다. 이렇게 이야기하면 전혀 설명이 되지 않겠지.. 내가 처음에 이해하지 못한 것처럼..
generating code 직역하면 “코드 생성”. 쉽게 풀어서 설명하면 “엔터티에 대해 ManagedObject를 상속하는 하위 클래스를 자동 또는 수동으로 생성하는 것 “을 뜻합니다.
그렇다면 또 ManagedObject는 뭔데..?
모든 CoreData의 객체(엔터티)가 상속하는 베이스 클래스. 즉, 엔터티를 클래스화 시킬 때 ManagedObject를 상속한 클래스로 만들어야 하는 것이죠.
# 🥨 class 파일과 properties 파일
우리가 Entity를 사용하기 위해 “클래스화” 시키고 내부 attribute를 객체 내 ”프로퍼티화” 시키기 위해서는 엔터티이름+CoreDataClass.swift, 엔터티이름+CoreDataProperties.swift 두 가지 파일이 필요합니다. (정확하게는 이 두 가지 파일들과 그 내부에 있는 코드들이 중요한 것이죠)
각각 파일은 무엇이며 어떨 때 필요할까요?
엔터티이름+CoreDataClass.swift은 엔터티를 클래스화 하기 위한 class가 정의되어 있고 추가적인 메서드나 비즈니스로직을 추가할 때필요합니다. 엔터티이름+CoreDataProperties.swift는 class의 extension이 정의되어 있고 그 내부에 속성이 선언되어 있고, 속성 및 관계에 대한 사용자 지정 코드를 추가할 수 있으며, 속성에 대한 추가 로직, 계산 속성, 메서드 등을 작성하는 게 가능하다고 한다.
프로퍼티 파일(엔터티이름+CoreDataProperties.swift)은 우리가 attribute를 추가/삭제해 주었을 때 자동으로 업데이트된다고 합니다. 즉, 우리가 어트리뷰트를 추가해 주었다고 해서 이 파일을 일일이 수정해 줄 필요가 없다는 것이죠!
// Row+CoreDataClass.swift
import Foundation
import CoreData
@objc(Row)
public class Row: NSManagedObject {
}
// Row+CoreDataProperties.swift
import Foundation
import CoreData
extension Row {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Row> {
return NSFetchRequest<Row>(entityName: "Row")
}
@NSManaged public var number: Int16
}
extension Row : Identifiable {
}
저희가 모델 만들 때 attribute로 추가해 준 number라는 attribute가 엔터티이름+CoreDataProperties.swift 파일에 @NSManaged public var 이렇게 프로퍼티로 정의되어 있는 게 보이시죠??
이렇게 엔터티를 클래스화 하기 위해 필요한 두 가지 파일을 살펴보았는데 codegen 항목에서 어떤 옵션을 고르는지에 따라 이 두 가지 파일들을 우리가 작성해주어야 하는지, 아니면 자동으로 생성되어 XCode가 알아서 Coredata에 대한 파일들을 연결해 주는지가 달라집니다.
이게 바로 우리가 codegen옵션 중 하나를 선택하는 이유!!
# 🥨 codegen 세 가지 옵션들의 차이점
일단 codegen에는 이렇게 세 가지 종류가 있다는 것을 볼 수 있고,
이것들이 어떤 차이를 가지는지 알아보자
class definition
core data가 생성한 managedObject의 서브클래스(엔터티 클래스)& 프로퍼티 가 자동생성되어 위에서 살펴본 두 가지 파일(엔터티이름+CoreDataClass.swift, 엔터티이름+CoreDataProperties.swift )을 편집할 필요가 없음
프로젝트 내 아무 데서나 바로 인스턴스를 생성해도 에러가 나지 않는 이유! 이기도 하다 :)
let row = Row()
category/ extension
managedObject 서브클래스(엔터티 클래스)에 메서드 혹은 비즈니스 로직 등 추가해서 클래스 파일(엔터티이름+CoreDataClass.swift 파일)을 우리가 제어하고 싶을 때 사용한다. 프로퍼티 파일(엔터티이름+CoreDataProperties.swift 파일)은 제너레이팅 해주기 때문에 class definition옵션과 동일하게 attribute 추가/삭제됐을 때 자동으로 업데이트해줘서 따로 관리해주지 않아도 된다.
일단 class definition 옵션이 아니고서는 파일을 우리가 만들어줘야 하는데 이건 (editor - create NSManagedObject Subclass…) 경로로 만들 수 있다.
만약 옵션을 category/ extension으로 바꿨는데 아무런 파일을 생성해주지 않으면 이런 에러가 나면서 빌드가 안될 것이다.
manual/none
클래스 파일, 프로퍼티 파일 두 개 파일 모두 제너레이팅 해주지 않는다. 즉, XCode는 core data의 엔터티에 대해 아무것도 연결해주지 않는 것이다. 이 옵션을 entity class 직접 생성해 주고 extension까지 직접 생성해서 클래스와 프로퍼티를 모두 우리가 제어해주어야 한다..!
# 🥨 category/extension 옵션은 어떻게 활용할 수 있을까?
maual/none 은 처음부터 끝까지 개발자가 코어데이터에 대한 코드들을 짜고 연결해주어야 하기 때문에 엄두가 나지 않았고 category/extension 옵션은 어떤 상황에서 쓸 수 있는지 활용 코드를 알고 싶어서 시도해 보았다.
일단 위에서 말한 것처럼 (editor - create NSManagedObject Subclass…) 경로로 클래스와 프로퍼티 파일을 만들었다. 하지만 category/extension옵션은 프로퍼티 파일(엔터티이름+CoreDataProperties.swift )에 대해서는 Xcode가 알아서 제너레이팅 해주고 우리는 클래스 파일(엔터티이름+CoreDataClass.swift)만 생성하고 수정해 주면 되기 때문에 프로퍼티 파일은 삭제해 주자.
클래스 파일을 어떻게 활용해 줄 수 있을까, 어떤 로직을 넣어줄 수 있을까 생각하다가
엔터티를 가져오기 위해 forEntityName이라는 프로퍼티에 String 타입으로 엔터티 이름("Row")을 적어주었어야 했었다. 이렇게 String으로 적어주는 것은 오류가 날 가능성이 있기 때문에 이것을 클래스 파일에서 Row 타입 내에 캡슐화 해줄 수 있을 것 같아 간단하지만 타입 저장 속성으로 클래스 안에 정의해 보았다!
lazy var entity = NSEntityDescription.entity(forEntityName: "Row", in: context)
// Row+CoreDataClass.swift
import Foundation
import CoreData
@objc(Row)
public class Row: NSManagedObject {
static let name = "Row"
}
그러면 entity를 불러올 때 "Row"라는 String이 아니라 Row라는 타입의 name 속성에 접근해서 사용해 줄 수 있기 때문이다!
lazy var entity = NSEntityDescription.entity(forEntityName: Row.name, in: context)
그리고 필요할 때 row 개수에 대한 정보를 텍스트로 받을 수 있도록 계산 속성을 추가해 보았습니다 ㅎㅎ
import Foundation
import CoreData
@objc(Row)
public class Row: NSManagedObject {
static let name = "Row"
var currentRowNumberNotice : String {
return "지금 row의 갯수는 \(String(self.number))입니다"
}
}
이렇게 활용하는 것이 적당한 예시인지는 잘 모르겠지만.. 뭐 이런 식으로 함수, 프로퍼티를 작성해서 사용해 주면 되는 것 아닐까요?? ㅎㅎ
이번 글에서는 swift에서 core data를 사용하며 객체(entity)와 프로퍼티(attribute)를 설정해 주기 위한 codegen(generating code)의 옵션들과 차이점들에 대해 알아보았는데 이제 좀 감이 잡히시나요??
실제 프로젝트에서는 어느 정도의 깊이로 core data를 사용하는지도 궁금하네요!
이후 core data에 대해 추가적으로 알게 되는 게 있으면 또 글 써볼게요!
'iOS' 카테고리의 다른 글
[iOS] image asset의 크기 1x ,2x, 3x 사용하는 이유 | scale factor, 해상도(Resolution), pixel, point (0) | 2024.05.22 |
---|---|
[iOS] 앱의 시작지점 @main (1) | 2024.02.11 |
[iOS] Core Data (2) | CRUD (1) | 2023.12.10 |
[iOS] Core Data (1) | Persistence (1) | 2023.12.09 |
[iOS] handoff가 도대체 뭐야 | (feat. TaskManagement, NSUserActivity) (3) | 2023.12.02 |