apple/iOS, UIKit, Documentation

[iOS] CoreData 정리 이론 (1/2)

lgvv 2023. 12. 10. 19:27

[iOS] CoreData 정리 이론 (1/2)

 

CoreData에 대해서 알아봅시다.

1편에서는 개념과 이론을 중점으로 서술하며, 코드 설명은 2편에서 이어집니다.


[iOS] CoreData 정리 이론 (1/2):  https://rldd.tistory.com/587

 

Core Data란?

- 코코아 개발 환경을 통해 제공하는 인 메모리(In-Memory) 방식의 데이터 관리 프레임워크.

- 이를 통해 데이터베이스 한경과 유사하게 데이터 CRUD과정을 수행할 수 있음.

- Apple 생태계 환경(iOS, macOS 등)을 지원하고 objc와 swift언어에서 모두 사용할 수 있음.

- 인 메모리 방식 기반이긴 하나, 내부적으로는 SQLite 같은 영구 저장소에 보조적으로 데이터를 저장할 수 있기 때문에 앱이 종료되더라도 데이터가 삭제되지 않음.


* 인 메모리(In-Memory) 방식이란?

 - HDD, SSD 등의 보조기억 장치에 저장하는 데이터베이스 방식과는 다르게 CPU는 Main Memory에 저장된 데이터에만 직접 접근할 수 있음. 따라서 데이터 처리 속도가 매우 빠름.

 - 메모리 내부에서 L1 cache, L2 cache, L3 cache, Paging 알고리즘 등이 있으며, 추가지식은 운영체제 관련부분을 찾아보기를 바람.

 

 

- Core Data의 저장구조

SQLite Core Data
데이터베이스 파일 데이터 모델 파일
테이블 엔티티
칼럼 어트리뷰트
외래키 + 조인 릴레이션

 

 

Core Data는 데이터베이스가 아니다.

- Core Data는 인 메모리 방식이지만, 데이터를 영구적으로 저장할 수 있다는 점, 검색 및 쿼리를 지원. 그러나 데이터베이스는 아님.

- Core Data는 여러 계층에서 소통하는 다양한 객체로 구성되어 있으며, 각 계층의 역할 뿐만아니라, 이들 사이의 관계를 파악해야함.

- 데이터 저장에 관련된 기능을 제공하는 프레임워크.

 

Core Data 구조

Core Data 구조

 

- Managed Object: 데이터를 저장하기 위해 생성하는 인스턴스

관계형 데이터베이스는 테이블의 행이나 레코드 정도로 생각. Core Data는 모든 레코드를 객체화하여 다룸. 테이블의 행 하나하나에 해당하는 레코드가 Core Data 독립된 객체로 동작. 이때 레코드를 구성하는 각 칼럼들은 관리 객체의 속성이 됨. 

 

ex) 만약 회사 직원이라는 레코드를 읽어올 경우.

- 회사 직원이라는 객체에 대응하는  구조를 가진 관리 객체가 만들어지고 이 관리 객체는 (이름, 닉네임, 코드, 입사일, 재직상태) 등의 속성을 가지는 식.

 

회사 직원 레코드 n개를 읽기 위해서는 n개의 관리 객체가 필요하며, 새로운 객체를 데이터를 담을 객체가 생성되어야 한다.

Core Data에서 사용되는 관리 객체는 NSManagedObject 클래스나 또는 그 하위 클래스의 인스턴스이며, 생성된 관리 객체들은 모두 관리 객체 컨텍스트에 담겨 관리.

 

- Managed Object Context: Core Data 가장 핵심적인 객체로 크게 두가지 역할을 담당.

(역할 1): CRUD 기능

 - 이 역할은 모두 컨텍스트에서 이루어짐. 

 - 컨텍스트란? Core Data는 메모리에 로드된 상태로 처리되는데, 메모리를 컨텍스트라고 함.

(역할 2): Persistent Store Coordinator 및 Persistent Store Coordinator에 대한 관리

- Persistent Store Coordinator: 컨텍스트(메모리)와 직접 데이터를 주고 받으면서 저장소들의 접근을 저장하고 해당 저장소에 대한 입출력 담당.

 

코디네이터를 단일 컨테스트와 연결되어 애플리케이션이 전달하는 각종 요청을 처리. 예를 들어 필요한 객체가 아직 컨텍스트에 로드되지 않았다면, 코디네이터는 컨텍스트로 부터 요청을 받아 영구 저장소에서 데이터를 찾고, 이를 컨텍스트를 찾고, 이를 컨텍스트에 전달하여 메모리에 로드하는 방식.

이 과정에서 코디네이터는 미리 정의된 관리 객체 모델을 사용하여 인스턴스를 생성하고 여기에 읽어온 데이터를 담아 전달하는데 이렇게 생성된 인스턴스가 바로 관리 객체라고 부름.

 

- Managed Object Model: 컨텍스트(메모리)와 직접 데이터를 주고 받으면서 저장소들의 접근을 저장하고 해당 저장소에 대한 입출력 담당.

 

관리 객체 모델은 DB로 치자면 테이블의 구조를 정의하는 스키마에 해당. Core Data에서 테이블에 대응되는 엔티티의 구조를 정의하는 객체. 관리 객체 모델은 Xcode에서 설계한 엔티티로 생성.

 

- Persistent Object Store: Core Data를 사용할 때 데이터가 저장되는 저장소 환경.

내부적으로 Core Data는 데이터에 대한 변경사항을 해석하여 영구 저장소에 저장. 저장 가능한 유형으로 4가지 타입이 존재

타입 설명
인메모리 저장 타입(NSInmemoryStoreType) 메모리 기반의 저장소를 사용하는 방식으로, 사실상 영구 저장소를 사용하지 않 는 것과 동일합니다. 따라서 이 타입을 사용하면 앱을 종료했을 때 데이터가 보존되지 않습니다. 인메모리 저장소 타입은 주로 데이터 객체의 런타임 캐싱에 활용됩니다.
플랫 바이너리 저장소 타입(NSBinaryStoreType) 데이터를 단순 바이너리 파일 형식으로 저장하는 방식입니다. 데이터 조회 성능 을 개선할 수 있다는 장점이 있지만. 저장된 데이터의 크기가 커질수록 바이너 리 파일의 크기가 커지고 초기 로딩 시간도 늘어난다는 단점이 있습니다.
XML 저장소 타입(NSXMLStoreType) 파일에 데이터를 기록하는 면에서는 ②의 바이너리 파일과 유사하지만 바이너리 코드가 아니라 XML 방식으로 데이터를 변환하여 저장하는 방식입니다. 파일에 저장하는 것이기 때문에 부분적으로 저장되지 않고 항상 전체가 저장되거나 저장되지 않는 원자성을 됩니다. 나머지 방식에 비해 처리 속도가 느린 것이 단점으로 꼽히지만, 코드를 외부에서 직접 열어보고 판독할 수 있기 때문에 프로젝트 초기에 디버깅 및 저장 여부 확인이 필요할 때 많이 사용됩니다. 단, iOS 에서는 속도 및 성능의 제약 등으로 인해 사용할 수 없습니다.
SQLite 데이터베이스(XSSQLiteStoreType) 코어 데이터를 사용할 때 가장 많이 선택하는 영구 저장소 타입으로, 일부만 로딩하기 때문에 메모리에 객체 그래프가 완전히 로딩되어 있지 않을 수 있습니다. 다만 우리가 사용하는 범위에서는 크게 문제되지 않는 수준입니다. ios 프로젝트에서 코어 데이터가 기본으로 채택하는 방식입니다.

* 출처: 친절한 재은씨(실전편)

 

 

인 메모리(In-Memory) DB

변경 내역을 영구 저장소에 반영하는 과정을 보통 커밋(Commit) 또는 동기화(Synchronize)라고 부르는데, 커밋이 발생하기 전까지 변경된 데이터는 메모리에서만 존재.

 

장점: 온디스크(On-Disk) 방식처럼 매번 디스크에 직접 작성하거나 읽어오지 않아도 되기 때문에 I/O가 적게 발생.

또한 비즈니스 로직 수행 과정에서 발생하는 데이터의 변경을 모두 메모리 수준에서 처리한 후 최종 결과만 영구 저장소에 반영하면 되기 때문에 여러 번 반복해서 읽거나 쓰더라도 성능에 문제가 거의 생기지 않음.

 

물론 인메모리 방식이라도 코드를 작성하는 방식에 따라 성능의 저하가 생길 수도 있음. 영구 저장소로 SQLite를 사용하는 경우 디스크 Read / Write를 하는 SQLite의 기본 오버헤드에다 Core Data와 SQLite간 데이터 컨버전을 위한 오버헤드가 추가되기 때문에, 매 작업마다 커밋을 하게 되면 오히려 SQLite만 사용하는 것보다 느릴 수 있음. 실제로 벤치마크 상에서도 Core Data가 SQLite보다 약간 더 느리다는 평가를 받는데, 이는 위에서 설명한 오버헤드가 원인.

 

데이터의 교환 및 저장 메커니즘

영구 저장소와 메모리에 저장된 레코드 사이에 일어나는 데이터 교환 방식은 미러링(Mirroring)과 유사. 영구 저장소에서 레코드를 로딩할 때에는 저장된 레코드를 그대로 읽어 관리 객체로 만들어 내고, 반대로 관리 객체를 영구 저장소에 저장할 때에는 메모리상에서 수정된 객체가 그대로 영구 저장소에 반영되는 식. 따라서 데이터 항목 하나하나를 처리하기 위해 구문을 작성할 필요는 없으며, 프로퍼티 리스트를 저장할 때처럼 개별 데이터를 일일이 저장할 필요도 없음. 그냥 메모리상에서 컨텍스트에 로드된 관리 객체를 수정한 뒤 커밋하면 변경된 사항이 통째로 영구 저장소에 반영.

 

관리 객체를 저장소에 커밋할 때는 차등 저장(Differncial Save) 메커니즘이 사용됨.

매번 데이터 전체를 커밋하는 대신 마지막 저장 이후 변경된 부분만 커밋하는 방식으로 빠르고 가볍게 저장할 수 있다는 장점.

ex) 깃플로우에서 feature의 마지막 커밋과 develop을 비교하여 저장하는 방식과 유사

 

Core Data를 사용하면 CRUD작업 이후 save() 메소드를 호출하면 메모리상의 관리 객체 변경 내용이 그대로 영구 저장소에 커밋됨.

이를 커밋하지 않고 앱을 종료할 경우 데이터의 변경 내역을 반영되지 않은 채로 버려지기 때문에 변경 사항이 발생했다면 적절한 시기에 바드시 save()를 호출하여 변경된 내역을 커밋해주어야 함.

 

 

코어 데이터의 한계

 

(한계 1) 데이터를 메모리에 로딩하는 과정 없이 작업이 불가.


Core Data에서는 많은 수의 객체들을 수정하거나 삭제할 경우 NSFetchRequest 객체에 정의된 retureDistinctResults, propertiesToFetch 등의 속성을 적극 활용하여 객체의 전체를 불러오는걸 지양하고, 주기적으로 NSManagedObjectContext #refresh(_: mergeChanges:) 메소드를 호출하여 변경 내역이 없는 객체들을 해제하며, 컨텍스트를 영구 저장소에 커밋한 후에는 로딩된 모든 객체를 메모리에서 해재해주는 방법 등을 사용하여 메모리를 효율적으로 관리.

 

(한계 2) 데이터 로직을 다루기에 한계가 존재.

 

Core Data의 구조적 특성 때문. 특히 관리 객체를 서브클래싱 하는 경우, 데이터 프로퍼티에 대한 오버라이드 가능하여 Unique한 키에 상위 클래스에서 저장한 것을 확인할 방법이 없음.

 

(한계 3) 멀티 스레드, 멀티 유저를 지원하지 않음.

 

Core Data는 원칙적으로 싱글 스레드를 지원. 싱글 스레드만 지원하는 이유는 단일 작업에서 처리 성능을 향상시키기 위함. Lock을 통해 Critical Section을 보호하며, 이는 성능 저하의 원인이 되기도 함.

 

만약 어쩔 수 없이 멀티 스레딩 환경에서 사용해야 하는 경우에는, 관리 객체와 컨텍스트의 사용에 주의. 이들 객체는 기본적으로 싱글 스레드 상황만을 가정하여 제작되어 멀티스레드로부터 안전하지 않기 때문.

 

 

코어 데이터의 객체 모델링

Entity(관계형 데이터베이스에서 테이블)는 데이터가 저장될 구조 또는 형식. 내부 구성은 어트리뷰트(Attibuted)릴레이션(Relation), 그리고 페치 속성(Fetched Properties)로 이루어짐.

 - 어트리뷰트: 엔티티의 하위 속성들을 정의

 - 릴레이션: 다른 엔티티와의 관계를 정의

 - 페치속성: 데이터 검색 시 반복 사용되는 요청이나 값만 바꾸어 사용하는 비슷한 요청들을 미리 템플릿 형태로 만들어 놓는 것을 의미

 

 엔티티의 내용을 클래스 형태로 생성하면 NSManagedObject 혹은 그의 서브클래스가 되지만 엔티티 자체는 NSEntitiyDescription 클래스로 표현됨. NSEntitiyDescription은 추상 클래스로서 엔티티의 구조를 설명한다는 의미를 지님. 이떄의 설명은 엔티티의 각 언트리뷰트와 릴레이션을 가르킴. 

 즉, 데이터 추가가 필요하면, NSEntitiyDescription을 이용하여 인스턴스를 생성하면 그 결과로 NSManagedObject 객체 그의 서브 클래스 객체가 만들어지면 이를 컨텍스트가 인식하여 관리.

 

 

 

'apple > iOS, UIKit, Documentation' 카테고리의 다른 글

[iOS] SwiftData in UIKit  (0) 2023.12.15
[iOS] CoreData 정리 2 실습 (2/2)  (0) 2023.12.10
[Swift] plain ol' data(POD)  (0) 2023.08.08
[UIKit] UILabel Inset  (0) 2023.06.23
[Swift] 커링(Currying)  (1) 2023.02.28