[Cocoa] 코어데이터를 수동으로 적용하기

코어데이터 코어데이터 코어데이터. 쉽지도 않은 내용인데 이 블로그에서 최근에 코어데이터를 지긋지긋하게 많이도 다루는 것 같다. ㅠㅡㅠ 하지만 언젠가는 피가되고 살이될 코어데이터에 대한 내용이다.

이미 “간단한” 저장은 아주 손쉽게 Keyed Archiver를 사용하여 인코딩한 객체를 파일로 바로 저장하는 것은 살펴보았다. 하지만 만약, 저장한 주소록에 사람이 수백만명이라면 엄청나게 많은 데이터가 앱이 실행될 때 한번에 메모리로 로드되어 올라갈 것이다. (이것이 아카이빙으로 내용을 저장할 때의 한계이다. 많은 데이터는 결국 한 번에 로딩해서 안고 있어야 하는 부담이 있다.)

하지만 코어데이터는 굉장히 빠르게 영구저장소를 계속해서 액세스하고, 자동으로 차등저장 및 로딩을 지원하기 때문에 데이터세트가 어느 정도까지는 커져도 괜찮다. (적어도 나는 그렇게 알고 있다.)

iOS라면 UIManagedDocument를 사용하면 문서파일 자체를 코어데이터 영구저장소 파일(데이터베이스 파일)로 바로 사용할 수 있다. 이 내용은 이미 살펴본 바가 있는데, 문제는 NSManagedDocument 라는 것은 아직 공식적으로 존재하지 않는 클래스이다. (아 이런…) 결국 코코아 앱을 시작할 때 코어데이터를 적용해주지 않으면… 콸콸콸콸…

그래서, 만들어보기로 했다.
사실 코어데이터를 적용하여 시작한 앱 델리게이트 클래스를 적당히 수정하면 된다. 하지만 공부하는 차원에서 만들어보기로 한다. 그럼 여기서 다시 개념 정리의 복습이 있겠다.

1. managed object context

컨텍스트라고 줄여서 부르기도 하는 이것은 이를테면 메모리상에 존재하는 일종의 화이트보드 같은 것이다. 새 객체를 썼다가 없앨 수도 있고 이를 다시 영구저장소에 옮겨놓을 수도 있다. 대부분의 코어데이터 앱은 이 컨텍스트를 획득하기만 하면 컨텍스트가 마치 요술상자처럼 대부분의 일을 다 처리해준다.

2. manged object

managed…라고 특별히 ‘관리된다’라고 생각하기보다 이는 ‘코어데이터와 관계된’ 이라고 생각하는 편이 조금 더 도움이 될 것 같다. 코어데이터 모델 파일에서 디자인한 객체가 실제로 만들어진 형태라고 생각하면 된다.

3. managed object model

managed object 객체의 데이터 디자인에 대한 정보를 담고 있는 객체이다. 그러니까 객체 모양에 대해 기술하는 객체라는 의미인데, 중복된 정보같이 보이고 흔히 영문 레퍼런스를 볼 때는 심지어 managed object와 혼동을 유발하기도 한다. 이 객체는 실제로 여러 데이터를 조작하고 저장하고 하는 동작에는 영향을 미치지 않는다. 대신에 영구저장소에 기록되는 파일의 내부 구조 (스키마)가 managed object의 각 요소를 제대로 담을 수 있도록 하는데 쓰인다.

즉, managedObjectModel은 영구저장소 코디네이터가 사용하는 것이다. 또한 이 클래스는 인터페이스 빌더에서 디자인한 엔티티의 자료 모델 파일인 .xcdatamodel 파일을 컴파일한 .momd 파일로부터 만들어진다.

4. persistent store coordinator

컨텍스트는 메모리상에 존재하는 스크래치패드이다. 이 메모리상에서 놀고있는 데이터는 나중에 필요할 때 사용하기 위해 저장될 필요가 있다. 메모리 상에서 뛰어노는 컨텍스트에게 영구저장소와 인터페이스해주는 역할을 한다.

5. persistent store

SQLite, XML 등의 형태로 managed object 들이 기록되어 있는 파일 그 자체를 말한다.

목표

코어데이터를 사용하려면 어떤 객체를 “managed” 한 상태로 만들어서(1) 디스크에 쓸 수 있도록(2)하면 되는 것이다. 즉 (1)에서 managedObjectContext 객체가 필요하고, (2)에서 persistentStoreCoordinator가 필요하다. (이후 컨텍스트, 코디네이터로 표시한다.)

그리고 이 과정은 코어데이터의 인프라에 따르면 다음과 같은 순서로 행해져야 함을 알 수 있다.

  1. 데이터모델 파일의 이름을 구한다. 이 파일은 컴파일 시에 앱 번들 내 리소스폴더에서 .momd 파일로 컴파일 된다.
  2. 데이터가 기록될 영구저장소 파일의 이름과 형식을 정한다. 특히 iOS에서는 XML타입은 지원하지 않고 오로지 SQLite 타입의 파일만 만들 수 있다.
  3. 컴파일된 모델파일(.momd)로부터 NSManagedObjectModel 객체를 생성한다.
  4. ManagedObjectModel로부터 NSPersistentStoreCoordinator 객체를 생성한다. 코디네이터 객체를 생성하는데 모델이 필요한 이유는 DB의 스키마를 어떤 방식으로 구성할지에 대한 정보가 필요하기 때문이다.
  5. 이제 실제 저장될 파일을 코디네이터에게 알려준다. (-addPersistentStoreWithType...)
  6. managedObjectContext 객체를 만든다음, 이 컨텍스트에게 코디네이터를 소개해준다.

영구저장소 관리자 클래스 만들기

이름이 너무 거창한데, 간단히 말해서 모델디자인 파일의 이름을 알려주면 그 파일에 맞도록 설정을 만들고, 앱 번들 내에 영구저장소 파일을 생성해 컨텍스트를 쓸 수 있도록 만들어주는 클래스이다.

1. 모델 객체 만들기

모델객체는 간단히 리소스 폴더에서 모델(이미 알고 있음)파일 이름과 같은 이름의 .momd 파일 (사실은 디렉터리이다.)을 사용하여, 이 내용을 토대로 모델 객체를 초기화하면 된다. 이는 프로퍼티로 설정해 두는 것이 나중에 사용하기 편하다.

-(NSManagedObjectModel*)model
{
    const NSString* modelName = @"Employee";
    if(!_model){
        NSURL *modelURL = [[NSBundle mainBundle] URLForResource:modelName withExtension:@"momd"];
        _model = [[NSManagedObjectModel alloc] initWithContentOfURL:modelURL];
    }
    return _model;
}

2. 코디네이터 만들기

코디네이터는 기본적으로 모델 객체를 기반으로 생성할 수 있다. 생성한 다음에는 번들 내 적당한 위치(문서 폴더나 리소스폴더)에 파일을 하나 정해, 이 파일을 코디네이터에 저장소로 추가한다. 그러면 코디네이터가 알아서 파일을 만들고, 모델의 내용을 기반으로 파일의 스키마를 잡아준다.

저장소로는 XML이나 SQLite가 모두 가능한데, iOS에서는 SQLite 만 쓸 수 있다. (아무래도 속도 때문인 듯)

-(NSPersistentStoreCoordinator*)coordinator
{
    NSFileManager* fileManager = [NSFileManager defaultManager];

    NSURL *dirURL = [[fileManager URLsForDirectory:NSDocumentDirectory 
                        inDomain:NSUserDomainMask]
                    objectAtIndex: 0];
    NSURL *storURL = [dirURL URLbyAppendingPathComponent:@"store.sqlite"];
    NSError *error;
    if(!_coordinator){
        _coordinator = [[NSPersistentStoreCoordinator alloc] initWithManageObjectModel:self.model];
        [_coordinator addPersistentStoreWithType:NSSQLiteStoreType
                    configuration:nil
                    URL:storURL
                    options:nil
                    error:&error];
        if(error){
            NSLog(@"Can't connect storage: %@", error);
            return nil;
    }
    return _coordinator;
}

3.컨텍스트만들기

컨텍스트는 쿨하게 그냥 만들고, 코디네이터만 세팅해준다.

-(NSManagedObjectContext*)context 
{
    if(!_context){
        _context = [[NSManagedObjectContext alloc] 
                        initWithConcurrencyType:NSMainQueueConcurrencyType];
        _context.persistentStoreCoordinator = self.coordinator;
        _context.undoManager = [[NSUndoManager alloc] init];
    }
    return _context;
}

첨부하는 예제파일에서는 initWithModelName: 으로 초기화한 후 managedObjectContext 프로퍼티를 통해 컨텍스트를 뽑아갈 수 있도록 했다.

소스코드 : http://www.box.com/s/i7du3y90j8pfeqz5hhiz

p.s. 코코아앱을 신규로 만들면, 이제는 코어데이터 체크 여부에 상관없이 코어데이터 프레임워크는 기본으로 link 되어 있다.