ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 데이터 모델 개념을 기초로 엔티티와 클래스 관계 알아보기
    Apple🍎/CoreData 2024. 1. 9. 00:53

    Entity와 Attribute는 대응하는 Class와 Property가 생긴다.

    보통 Core Data의 엔티티와 엔티티의 속성을 설정할 때는 Code Data Model Editor를 사용해왔다.
    여기서 Entity를 설정하면 실제 코드로는 어떻게 구현되는 것일까?

    Generated Classes

    • 데이터 모델안에서 엔티티와 속성이 어떻게 구성되는지 간단히 말하면
    • Editor에서 설정한 엔티티와 속성에 대응하는 클래스와 프로퍼티가 코드로 생성된다.

    How Is your Data Model Really Created.

    • Core Data Editor를 사용하여 엔티티와 속성을 만들면 데이터 모델은 정적이고 (컴파일 타임에 이미 모두 정해지고) 런타임에는 더이상 건들지 못한다고 생각할 수 있지만
    • Core Data는 생각보다 더 자유롭다. -> 런타임에서 코드를 통해 데이터 모델을 만들 수 있다.
    // Data model 만들기
    let model = NSManagedObjectModel( ) 
    
    // Entity 객체 하나 만들기
    let album = NSEntityDescription( ) 
    // 만든 Entity 이름 정하기
    album.name ="Album"
    
    // Attribute 객체 하나 만들기 
    let titleAttribute = NSAttributeDescription( )
    // 만든 Attribute 이름 정하기
    titleAttribute.name = "Title"
    // 만든 Attribute 타입 정하기 
    titleAttribute.attributeType =  .stringAttributeType
    
    // Entity에 Attribute 목록을 만들기 위해
    // 만든 Attribute들을 리스트로해서 프로퍼티로 넣기
    let properties = [titleAttribute]
    // 엔티티에 프로퍼티 맺어주기
    album.properties = properties
    
    // 만든 model에 완성된 엔티티 추가해주기
    model.entitiesn = [album]
    
    • 위와 같이 Core Data Editor를 사용하지 않고서도 직접 코드로 엔티티와 속성을 정의할 수 있다.

     

    직접 코드로 정의하는 방법을 보여준 이유는 이렇게 하라고 추천하는게 아니다. -> editor 써라.
    다만 data scheme(엔티티와 속성)이 런타임에 정해진다는 점에 주의 깊게 봐야한다.**
    컴파일 타임에 이미 정해져서 들어가는 게 아니고 코드가 돌아가면서 data scheme이 완성된다.

     

    Data model이 동적으로 생성된다. 즉 엔티티와 속성은 컴파일 타임에 정해지지 않는다. 근데 어떻게 이에 대응하는 클래스와 프로퍼티가 어떤 엔티티와 속성에 대응하는 지 알고 미리 존재할 수 있단 말인가?

     

    NSEntityDescription은 또 뭐야?

    • NSEntityDescription의 역할은 SQLite의 테이블의 역할을 한다.
    • SQLite에서 테이블은 앞으로 생성될 row의 이름과 해당 row의 속성(with type)을 정의하는 것처럼
    • NSEntityDescription은 앞으로 생성될 엔티티의 이름과 속성을 정의한다.

    -> 우리가 새로운 엔티티를 Data model에 추가하고 싶다면 새로운 NSEntityDescription을 만들고 관련된 attribute들을 이에 더해줘서 엔티티를 완성시킨뒤 이를 Data model에 추가하면 된다.

    • 또한 다음과 같이 data model로부터 어떤 엔티티들이 있는지 NSEntityDescription을 빼와서 볼 수도 있다.
    // data model 로부터 모든 NSEntityDescription 가져오기
    let entities : [NSEntityDescription] = PersistentController.shared.container. managedObjectModel.entities
    
    // data model 로부터 지정한 NSEntityDescription만 가져오기 
    let entity: NSEntityDescription = PersistentController.shared.container.mangedObjectModel.entitiesByName["Album"]!

    NSEntityDescription이 class(table)에 해당한다면 object(row)는 무엇이 될까???

     

    NSManagedObject는 각 데이터를 나타내는 개별 객체다.

    NSEntityDescription이 class라면 NSManagedObject는 특정 데이터를 저장하기 위해 이를 인스턴스화한 객체이다.

    예를 들어 학생이라는 NSEntityDescription는 class는 이름, 나이라는 property를 가지고 있으면 NSManagedObject는 class의 형태를 기반으로 해서 철수와 5살이라는 특정 데이터를 저장하는 객체이다.

    NSManagedObject를 이용해 실질적으로 데이터의 읽기, 저장, 수정, 삭제가 이루어진다.

     

    여기까지해서 큰그림

    • NSEntityDescription : 엔티티 이름 및 속성 정의
    • NSManagedObject : 각 개별 데이터를 저장
    • NSManagedObjectContext : 데이터의 변화를 추적

    NSEntityDescription을 기반으로해서 각 데이터를 저장하는 NSManagedObject들을 생성하고
    생성된 이들을 NSManagedObjectContext가 추적, 관찰, 관리하며 DB에로부터 읽기, 저장, 수정, 삭제, 한다.

    예를 들어 해바리가반 학생이라는 클래스가 있으면 해당 분류에 속하면서도 개별적인 데이터를 저장하기 위해 인스턴스 짱구, 철수, 맹구, 유리를 만들어서 데이터 관리에 이용한다.

    NSManagedObject에 접근 및 데이터 저장하기

    NSManagedObject를 일종의 딕셔너리로 생각해라 (키와 값을 가지고 있는 데이터 홀더)

    let allAttributes = myAlbum.entity.attributesByName
    • key 로 value 조회하기 ( 특정 속성에 대한 값 조회하기)
    let title = myAlbum.value(forKey: "title") as? String
    • key에 value 값 세팅하기 (특정 속성에 대한 값 설정하기)
    myAlbum.setValue("What is love", forKey: "title")
    • 왜 entity에 직접 접근해서 get과 set을 하지 않고 value(forKey: )setValue(. , forKey: ) 같은 메서드를 사용해 값을 읽고 세팅할까?
    // 아래와 같이 직접 엔티티에 get과 set을 하지 않고 왜 별도의 메서드를 이용하는가?
    let title = myAlbum.title
    myAlbum.title = "What is love"

     

    Managed Properties

    앞서 엔티티의 속성은 런타임에 정해진다고 했다. 그럼 코드에서 NSManagedObject의 프로퍼티에 접근및 설정을 할때는 아직 프로퍼티에 대응하는 속성이 정해지기 이전이다.
    즉 대응하는 속성이 뭐가 올지도 모르는데 먼저 프로퍼티의 값을 이용한 것이다. 어떻게 이게 가능할까???

    Object-C Runtime feature.

    • 다음 코드를 보면 Album class에 대한 생성자가 없다. 컴파일러가 징징댈거다.
    class Album {
        var title: String 
    }
    • @NSManaged를 프로퍼티 앞에 붙여주면 컴파일러가 조용해진다.
    class Album {
        @NSManaged var title: String
    }

     

    @NSManaged가 컴파일러에게 말해준다.
    “야 title 프로퍼티에 대한 getter, setter 런타임에 구현되니까 걱정마라.”

    다시말해서 @NSManaged가 붙어있으면 런타임때 Core Data가 Data Model에 설정해놓은 엔티티 - 속성을 바탕으로 이에 대응되는 클래스 - 프로퍼티에 getter, setter를 만들어준다.

    런타임때 프로퍼티에 대응하는 속성을 이어줄것을 보장하기 때문에 런타임이전(아직 엔티티에 속성이 세팅되기 이전)에도 프로퍼티에 대한 접근과 수정이 가능하다.

     

    Dynamic Dispatch는 Object-c 런타임만의 독특한 특징으로 메서드의 구현을 런타임까지 미룰수 있다.

    class Album : NSManagedObject {
        @NSManaged var title:  String
        @NSManaged var id : UUID
        @NSManaged var releaseDate: Date
    }

    위와 같이 클래스 - 프로퍼티가 작성되면 데이터 모델에서 이에 대응하는 엔티티-속성은 다음과 같을 것이다.

    런타임때 클래스 - 프로퍼티를 엔티티 - 속성과 맺어주기 때문에 당연히 이들의 이름은 반드시 동일해야한다.

     

    Entity Editor에서 Class 만드는 방법 선택하기

    앞에서 쭉 살펴본것처럼 엔티티에 대응하는 클래스가 존재해야한다.
    그리고 애플은 이 클래스를 만드는 방법을 3개 준비해놨다.

    Class Definition : XCode가 알아서 Class 만들어줌

    • 장점
      • 가장 간단하고 쉬운 방법이다.
      • Entity Editor에서 설정해놓은 엔티티와 속성을 기반으로 빌드할때마다 알아서 클래스를 만들어준다.
      • 클래스를 따로 관리할 필요가 없다.
    • 단점
      • 엔티티에 대한 별도의 설정이나 추가 메서드를 추가할 수 없다.

    Manual / None : 니가 직접 Class 만들어야됨

    • 처음부터 끝까지 다 작성하는 것은 힘들다. 그래서 애플은 Entity Editor의 설정을 기반으로 일반적인 클래스를 만들고 이를 손볼 수 있게 해주었다.
    • Editor -> Create NSManagedObject Subclass
    • 사용할 data model 선택
    • 선택한 data model에서 subclass 하고 싶은 엔티티 선택

    • XCode가 선택한 엔티티에 대해서 파일 두개 (Core DataProperties, CoreDataClass)를 만들어준다.
    • Entity + CoreDataProperties
    import Foundation
    import CoreData
    
    
    extension Album {
    
        @nonobjc public class func fetchRequest() -> NSFetchRequest<Album> {
            return NSFetchRequest<Album>(entityName: "Album")
        }
    
        @NSManaged public var title: String?
        @NSManaged public var id: UUID?
        @NSManaged public var releaseDate: Date?
    
    }
    
    extension Album : Identifiable {
    
    }
    • Entity + CoreDataClass
    import Foundation
    import CoreData
    
    @objc(Album)
    public class Album: NSManagedObject {
    
    }
    
    • 왜 두개나 만들어주냐 귀찮게 

    몇년 전까지만 해도 엔티티하나당 하나의 파일만 만들어줬다. 근데 프로젝트를 진행하다보면 

    1. 서브 클래스에 커스텀 메서드를 추가한다.

    2. 엔티티에 더 많은 속성을 추가할 수 있다.

     

    만약에 엔티티에 대한 커스텀 메서드를 잔뜩 작성해놓고나서 엔티티에 속성을 추가해서 generate를 다시하면 모든 파일에 덮어쓰기가 되버려서 작성해놓은 커스텀 메서드가 싹 날라가버렸다. 이를 해결하기 위해서 파일을 두개로 분리하였다.

     

    • EntityName + CoreDataProperties (extenstion) -> XCode generator에 의해서 update
    • EntityName + CoreDataClass (class) -> 개발자가 엔티티 커스텀시 사용

    -> Regenerate 해도 properties extension만 덮어쓰기하고 class는 안 건듬

    Category / Extension

    Data model에 변경이 있을 때마다 Xcode가 자동으로 extenstion을 업데이트해주는 동시에 개발자가 이용할 수 있는 클래스 또한 있는 옵션이다.

     


     

    Unleash Core Data: Fetching Data, Migrating, and Maintaining Persistent Stores

    Create apps with rich capabilities to receive, process, and intelligently store data that work across multiple devices in the Apple ecosystem. This book will show you how to organize your … - Selection from Unleash Core Data: Fetching Data, Migrating, a

    www.oreilly.com

    'Apple🍎 > CoreData' 카테고리의 다른 글

    Core Data Context 이해와 활용 - 심화  (0) 2024.01.15
    Core Data Context 이해와 활용 - 기본  (0) 2024.01.11
    Core Data 뜯어 보기  (0) 2024.01.04
    엔티티와 컨텍스트  (0) 2024.01.02

    댓글

Designed by Tistory.