본문 바로가기

iOS

iOS 데이터 저장 방식 알아보기 - Core Data

반응형

안녕하세요 :)

이번에는 Core Data에대해 알아보도록 하겠습니다!

앱을 개발하다 보면 사용자의 입력 데이터를 저장해야하거나, 앱에서 사용할 데이터를 로컬에 저장해 오프라인에서도 이용할 수 있어야하는 상황이 있습니다.

이런 작업을 간단하고 효율적으로 처리하도록 돕는 것이 바로 Core Data입니다

이전 데이터 방식들보다 조금 어려울 수 있어요! 하지만 같이 천천히 알아봅시다☺️

 

 

이외 다른 저장 방식에 대한 글을 보고싶으시다면 아래 링크를 참고해주세요!

iOS 데이터 저장 방식 알아보기 - 개념편

iOS 데이터 저장 방식 알아보기 - UserDefaults

iOS 데이터 저장 방식 알아보기 - File System


Core Data란?

 

 

애플에서 제공하는 강력한 데이터 관리 도구로, 데이터를 저장, 관리, 조회, 수정하는 모든 과정을 지원합니다.

단순한 데이터 저장을 넘어 데이터베이스와 유사한 방식으로 데이터를 처리할 수 있도록 돕고, 앱 성능 최적화와 여러 기기 간 데이터 동기화 기능도 제공합니다.

즉, Core Data는 개발자의 작업을 크게 간소화하며, 데이터 관리의 수준을 한 단계 끌어올려줍니다!


Core Data의 주요 기능과 역할

위에서 CoreData는 데이터를 저장, 조회, 수정, 동기화하는 데 필요한 거의 모든 기능을 제공한다고 했죠??

이 기능들을 사용하면 복잡한 데이터 구조도 쉽게 다룰 수 있고, 앱의 데이터 처리 효율을 높일 수 있습니다!

 

오호.. 그럼 Core Data의 주요 기능과 활용 방법을 좀 더 자세 살펴봅시다!!

 

1. 데이터 저장 및 관리

  • 데이터를 SQLite, XML, 메모리 등 다양한 저장소에 저장하고 관리
  • 앱 내 로컬 저장뿐만 아니라 데이터 캐싱 기능도 지원해, 네트워크 요청 없이 데이터를 빠르게 읽어올 수 있음
  • 예시: 메모 앱에서 작성한 메모를 SQLite에 저장해 앱을 재실행해도 데이터 유지

 

2. 관계형 데이터 모델링

  • 엔터티(Entity) 간의 관계를 설정하고 데이터를 체계적으로 연결할 수 있음
  • 복잡한 데이터 구조도 모델링을 통해 쉽게 관리할 수 있음
  • 예시: 쇼핑 리스트 앱에서 "카테고리(Category)"와 "상품(Item)" 간의 관계를 설정해, 카테고리별로 상품을 깔끔히 정리

 

3. 데이터 변경 추적 및 실행 취소

 

 

  • 데이터 변경 이력을 자동으로 관리하며, 실행 취소(Undo)재실행(Redo) 기능을 제공
  • 사용자가 실수로 데이터를 수정하거나 삭제해도 복원할 수 있어 안전한 데이터 관리가 가능
  • 예시: 텍스트 편집기 앱에서 작업 이력을 추적해 사용자가 이전 상태로 되돌아 감

 

4. 데이터 동기화

 

 

  • CloudKit과 통합하여 여러 기기 간 데이터를 동기화할 수 있음
  • 네트워크 연결이 없어도 데이터를 오프라인에 저장했다가, 연결이 복구되면 자동으로 동기화
  • 예시:사용자가 iPhone에서 작성한 메모가 iPad에서도 자동으로 동기화되는 메모 앱

Core Data Stack

Core Data Stack은 Core Data의 핵심 구조로, 데이터를 저장, 관리, 조회하는 데 필요한 모든 환경을 제공합니다.

이 스택은 데이터를 처리하는 데 필요한 객체를 연결해 개발자가 데이터를 효과적으로 다룰 수 있도록 도와줍니다.

 

뭐.. 장황하게 적었지만 그냥 Core Data를 사용하려먼 필요한 클래스들을 설정하고 준비해야한다는 뜻입니다!

그 필요한 클래스들을 통틀어 Core Data Stack이라고 하는거구요!!

그렇다면 어떤 주요 클래스들로 구성되는지 알아볼까요?!

 

 

NSManagedObjectModel

  • 앱의 데이터 모델(.xcdatamodeld)을 나타냄
  • 엔터티(Entity), 속성(Attribute), 관계(Relationship)을 정의

NSPersistentStoreCoordinator

  • Core Data와 실제 데이터 저장소(SQLite, XML 등)를 연결하는 역할
  • 데이터 저장소의 종류에 따라 데이터를 적절히 저장하거나 가져옴

NSManagedObjectContext

  • 데이터를 읽거나 쓰는 작업 공간으로, 변경 사항을 추적
  • 변경된 데이터를 저장소에 저장하거나 취소할 수 있음

NSPersistentContainer

  • Core Data 스택의 모든 구성 요소(NSManagedObjectModel, NSPersistentStoreCoordinator, NSManagedObjectContext)를 한 번에 설정해주는 편리한 래퍼
  • 간단한 설정으로 Core Data를 사용할 수 있게 도와줌

Core Data Stack 초기화 및 관리 방법

Core Data Stack을 구성하는 클래스에대해서 알아보았으니, 구성하는 방법에대해서도 알아야겠죠?

방법은 크게 두가지로 나뉩니다.

두 가지 방법 모두 데이터 일과성 유지, 앱 전역 사용 등을 보장하기 위해 Singleton 패턴을 사용합니다

 

  1. NSPersistentContainer 사용 (간단한 설정)
  2. 수동 구성 (복잡한 설정)

 

1. NSPersistentContainer 사용

// Core Data와 관련된 모든 기능을 캡슐화하기 위한 관찰 가능한 클래스를 정의합니다.
class CoreDataStack: ObservableObject {
    static let shared = CoreDataStack()
    
    // 처음 사용할 때까지 초기화를 미루기 위해 lazy 변수로 지속 컨테이너를 생성합니다.
    lazy var persistentContainer: NSPersistentContainer = {
        
        // 데이터 모델 파일 이름을 컨테이너의 초기화 메서드에 전달합니다.
        let container = NSPersistentContainer(name: "DataModel")
        
        // 모든 지속 저장소를 로드합니다. 저장소가 없으면 생성합니다.
        container.loadPersistentStores { _, error in
            if let error {
                // 에러를 적절히 처리합니다. 그러나 개발 중에는 `fatalError(_:file:line:)`를 사용하는 것이 유용합니다.
                fatalError("지속 저장소를 로드하지 못했습니다: \(error.localizedDescription)")
            }
        }
        return container
    }()
        
    private init() { }
}

 

Singleton 객체인(CoreDataStack.shard)를 통해 Core Data 스택을 관리하고, 내부적으로 NSPersistentContainer를 사용하여 Core Data Stack의 구성 요소를 자동으로 설정합니다

 

특징

  1.  간결함
    • NSPersistentContainer를 사용하여 Core Data의 전체 스택(NSManagedObjectModel, NSPersistentStoreCoordinator, NSManagedObjectContext)을 캡슐화
    • 개발자가 직접 NSManagedObjectModel, NSPersistentStoreCoordinator 등을 구성할 필요가 없음
  2. 자동화: 데이터 모델 로드, 저장소 연결 등을 자동으로 처리
  3. 편리성: SwiftUI와 잘 통합되며, viewContext를 바로 사용할 수 있음

 

주로 복잡한 설정이나 사용자 지정이 필요하지 않은 간단한 앱이나 기본적인 데이터 관리를 하는 프로젝트에 사용하기 적합하겠죠?!

수동 구성도 살펴 봅시다!

 

2. 수동 구성

class CoreDataStack {
    static let shared = CoreDataStack()

    private lazy var managedObjectModel: NSManagedObjectModel = {
        guard let modelURL = Bundle.main.url(forResource: "DataModel", withExtension: "momd") else {
            fatalError("데이터 모델을 찾을 수 없습니다.")
        }
        return NSManagedObjectModel(contentsOf: modelURL)!
    }()

    private lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
        let coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
        let url = self.applicationDocumentsDirectory.appendingPathComponent("CoreData.sqlite")
        do {
            try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
        } catch {
            fatalError("Persistent Store 추가 실패: \(error)")
        }
        return coordinator
    }()

    lazy var managedObjectContext: NSManagedObjectContext = {
        let coordinator = self.persistentStoreCoordinator
        let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
        context.persistentStoreCoordinator = coordinator
        return context
    }()

    private lazy var applicationDocumentsDirectory: URL = {
        let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return urls[0]
    }()
}

 

수동 방식도 Singleton객체(CoreDataStack.shared)를 통해 Core Data 스택을 관리하지만, 모든 구성 요소는 수동으로 설정합니다.

NSPersistentContainer처럼 자동으로 처리되지 않 각 객체를 직접 초기화하고 연결해야겠네요 ㅎㅎ

 

특징

  1. 세부적인 제어
    • NSManagedObjectModel, NSPersistentStoreCoordinator, NSManagedObjectContext 등을 직접 설정
    • 데이터 모델 파일 로드 및 저장소 위치 지정 등을 수동으로 처리
  2. 유연성: 개발자가 모든 세부 사항을 설정할 수 있으므로, 복잡한 설정이나 고급 기능을 사용할 때 유리
  3. 복잡성: 설정 코드가 많아지고, 실수를 할 가능성이 높음

그렇다면 수동 구성은 고급 사용자 정의가 필요한 경우인 복잡한 데이터 모델 구조를 가지거나, 저장소 동작을 세부적으로 조정해야하는 프로젝트에 적합하겠네요!

 

요약

특징 NSPersistentContainer 설정 수동 설정
설정 난이도 쉬움 어려움
코드 길이 짧음
유연성 제한적 (자동 처리) 매우 유연함 (세부 설정 가능)
사용 편의성 초보자 및 SwiftUI 친화적 고급 사용자에게 적합
추천 상황 단순한 앱, 기본 데이터 관리 복잡한 설정, 고급 기능 필요 시

 

초보자이거나 기본적인 데이터 관리만 필요하다면?

  • NSPersistentContainer 기반의 첫 번째 코드가 적합
  • 설정이 간단하고 SwiftUI와의 통합도 원활

복잡한 설정이 필요하거나 저장소 동작을 커스터마이징해야 한다면?

  • 두 번째 코드처럼 NSPersistentStoreCoordinator 등을 직접 설정하는 방식을 선택

Core Data의 한계 

1. 대량 데이터 처리의 성능 문제

  • Core Data는 수백만 개의 레코드를 처리할 때 성능이 저하될 수 있음
  • 복잡한 쿼리 최적화가 어렵고, 검색 속도가 느려질 수 있음
  • 해결 방법: 페이징(Paging)을 사용하거나 필요한 경우 SQLite를 직접 사용하는 것을 고려하기

2. 멀티스레드 작업의 어려움

  • NSManagedObjectContext는 특정 스레드에서만 사용 가능하며, 데이터 전달이 까다로움
  • 해결 방법: 백그라운드 작업을 위한 별도 Context를 생성하고, 작업 완료 후 병합(Merge) 처리하기

3. 네트워크 기반 동기화 제약

  • Core Data는 기본적으로 로컬 저장소로 설계되었음
  • 해결 방법: 간단한 앱은 iCloud 동기화를, 실시간 동기화는 Firebase나 Realm을 고려하기

4. 데이터 모델 변경 시 복잡성 증가

  • 데이터 구조를 변경할 때 마이그레이션(Migration) 작업이 필요함
  • 해결 방법: 초기 데이터 모델 설계 시 변경 가능성을 고려하고, 자동 마이그레이션 옵션을 적극 활용하기

잠깐! 마이그레이션이 뭔가 싶죠?!! 아래에서 자세히 설명해드리겠습니다!


Core Data의 데이터 마이그레이션

Core Data를 사용하다 보면 앱의 요구 사항이 변경되면서 데이터 모델(Entity, Attribute 등)을 수정해야 할 때가 있습니다.

기존 데이터를 유지하면서 새 데이터 모델 구조로 이동하려면 데이터 마이그레이션이 필수입니다.

Core Data는 간단한 변경에 대해 자동 마이그레이션을 지원하며, 복잡한 변경의 경우 수동 마이그레이션을 제공합니다. 

 

1. 자동 마이그레이션

간단한 구조 변경에 적합하며, Core Data가 자동으로 처리합니다

자동으로 다 해주니 설정이 간단하며 유지보수가 용이하고 일반적인 데이터 구조 변경 시 빠르고 안정하다는 장점을 가지고 있지만,

속성 이름 변경, 관계 수정 등 복잡한 변경은 처리할 수 없습니다

 

아래와 같은 경우에는 자동 마이그레이션을 사용하면 좋아요!

  • 속성 추가/삭제
  • 새로운 엔터티(Entity) 추가

설정 방법

let options = [
    NSMigratePersistentStoresAutomaticallyOption: true,
    NSInferMappingModelAutomaticallyOption: true
]
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: options)

 

 

2. 수동 마이그레이션

복잡한 데이터 변경이 필요할 때 사용하며, 개발자가 매핑 규칙을 정의합니다.

수동으로 직접 하기때문에 복잡한 구조 변경도 처리 가능하고 사용자 정의 로직으로 데이터 가공이 가능하다는 장점을 가지고 있지만,

구현이 복잡하고 매필 모델 작성 시간이 소요된다는 점을 주의해야합니다

 

아래와 같은 경우 수동 마이그레이션을 사용하기 적합합니다

  • 속성 이름 변경
  • 관계(Relationship) 수정
  • 데이터 변환 작업 필요

설정 방법

let sourceModel = NSManagedObjectModel(contentsOf: sourceModelURL)
let destinationModel = NSManagedObjectModel(contentsOf: destinationModelURL)
let mappingModel = NSMappingModel(from: nil, forSourceModel: sourceModel, destinationModel: destinationModel)

let migrationManager = NSMigrationManager(sourceModel: sourceModel!, destinationModel: destinationModel!)
try migrationManager.migrateStore(from: sourceStoreURL, sourceType: NSSQLiteStoreType, options: nil, with: mappingModel, toDestinationURL: destinationStoreURL, destinationType: NSSQLiteStoreType, destinationOptions: nil)

 

요약

종류 자동 수동
적합한 변경 속성 추가/삭제, 간단한 엔터티 추가 속성 이름 변경, 관계 수정, 데이터 변환
설정 난이도 간단 복잡
유연성 제한적 유연

 

 

🤔 마이그레이션하기 전 체크해야하는건 뭘까?

  1. 데이터 모델 버전 관리
    • 변경 사항을 명확히 기록하고, 버전 번호를 관리
  2. 자동 vs 수동 결정
    • 변경 사항이 단순하면 자동, 복잡하면 수동 선택
  3. 데이터 무결성 확인
    • 마이그레이션 완료 후 데이터가 올바르게 변환되었는지 철저히 테스트

 

어느정도 훑어보았으니! 코드도 봐야겠죠??

내부 코드 설명까지 이번 글에 작성하면 너무 길어질 것 같아서 프로젝트 링크만 첨부하겠습니다.


예시 메모 프로젝트

코드가 궁금하신 분들은 아래 GitHub에 들어가셔서 보시면 되구요!

메모 추가/ 조회/ 삭제 정도의 기능만 만들어놨습니다!

부족한 부분이 많으니 조언은 환영합니다:)

 

MemoApp

 

iOS_Storage/CoreData at main · sojin2/iOS_Storage

Contribute to sojin2/iOS_Storage development by creating an account on GitHub.

github.com

 


 

이렇게 CoreData를 쭉 함께 알아보았는데요!

이해하시는데 조금의 도움이라도 되었으면 좋겠네용..

만약 틀린부분이 있다면! 편하게 댓글 달아주시면 감사하겠습니다!!!🙇🏻‍♀️

 

반응형