천원의 개발

iOS Realm Migration(마이그레이션) 하는 법 본문

iOS&Swift🍎/iOS

iOS Realm Migration(마이그레이션) 하는 법

천 원 2022. 10. 13. 22:21

마이그레이션?

  - Realm 데이터베이스를 사용하여 개발하다 보면 데이터베이스의 스키마 구조를 변경해야 할 경우가 생기는데 이런 경우 앱 충돌이 발생합니다. 개발 시에는 앱을 새로 삭제 후 실행해 주면 문제없이 작동하지만, 앱을 출시한 상태에서 스키마 구조를 변경하고 싶을 때 사용하는 게 마이그레이션 입니다.

Schema Version

  - 마이그레이션을 관리 하기 위한 스키마 버전입니다. 처음 상태는 0으로 시작합니다.

  - 컬럼의 추가나 삭제 같은 동작은 Schema Version을 증가 시 키는 것만으로 마이그레이션이 이루어집니다.

 

스키마 버전을 출력해주는 코드입니다.

  do {
      let version = try schemaVersionAtURL(fileURL!)
      print("Schema Version: \(version)") // Schema Version: 0
  } catch {
      print(error)
  }

 

컬럼 추가하기

현재 데이터베이스의 스키마입니다.

class User: Object {
    @Persisted var name: String
    @Persisted var address: String
    @Persisted var age: Int

    @Persisted(primaryKey: true) var objectId: ObjectId

    convenience init(name: String, address: String, age: Int) {
        self.init()
        self.name = name
        self.address = address
        self.age = age
    }
}

 

여기에 phoneNumber를 컬럼에 추가 해줍니다.

class User: Object {
    @Persisted var name: String
    @Persisted var address: String
    @Persisted var age: Int
    @Persisted var phoneNumber: Int

    @Persisted(primaryKey: true) var objectId: ObjectId

    convenience init(name: String, address: String, age: Int) {
        self.init()
        self.name = name
        self.address = address
        self.age = age
    }
}

 

 

그런 후 AppDelegate의 didFinishLaunchingWithOptions에서 아래 코드를 실행해 줍니다.

단순히 컬럼을 추가 해주는 동작은 schemaVersion을 하나 올려 주기만 해도 이루어집니다.

let config = Realm.Configuration(schemaVersion: 1)  // schemaVersion을 1로 올림
Realm.Configuration.defaultConfiguration = config

phone 컬럼이 추가된 모습입니다.

 

하지만 위에 코드를 사용하면 직관적이지 못하기 때문에 migrationBlock을 활용해 줍시다.

여기서 if 문을 통해서 schemaVersion 0 > schemaVersion 1 로 갈 때 동작을 수행할 수 있지만 단순히 컬럼을 추가해주었기 때문에 아무런 동작을 하지 않아도 됩니다.

let config = Realm.Configuration(schemaVersion: 1) { migration, oldSchemaVersion in
    if oldSchemaVersion < 1 { // version 0 > 1 phoneNumber 컬럼 추가
    
    }
} // schemaVersion을 1로 올림
Realm.Configuration.defaultConfiguration = config

 

삭제 또한 같은 방식으로 해주면 됩니다.

 

 

프로퍼티명 변경 하기

User 스키마에서 address -> location 으로 프로퍼티명을 변경 하고싶다면 renameProperty 메서드를 사용해서 변경 할 수 있습니다.

먼저 스키마를 변경 해줍니다.

class User: Object {
    @Persisted var name: String
    @Persisted var location: String // 변경
    @Persisted var age: Int
    @Persisted var phoneNumber: Int
    
    @Persisted(primaryKey: true) var objectId: ObjectId
    
    convenience init(name: String, address: String, age: Int) {
        self.init()
        self.name = name
        self.location = address
        self.age = age
    }
}

 

schemaVersion -> 2로 올려줍니다.

renameProperty를 활용하여 프로퍼티명을 변경 해줍니다.

let config = Realm.Configuration(schemaVersion: 2) { migration, oldSchemaVersion in
    if oldSchemaVersion < 1 { // version 0 > 1 phoneNumber 컬럼 추가

    }

    if oldSchemaVersion < 2 { // version 1 > 2 address -> location 프로퍼티명 변경
        migration.renameProperty(onType: User.className(), from: "address", to: "location")
    }
}
Realm.Configuration.defaultConfiguration = config

 location으로 변경된 모습

 

 

 

Linear Migrations

  - 마이그레이션시에 schemaVersion의 업데이트를 순서대로 적용해야 합니다. 예를 들면 schemaVersion 0을 사용하는 사용자가 앱을 업데이트하게 되면 schemaVersion 0 > 1 phoneNumber 컬럼을 추가하는 과정과 schemaVersion 1 > 2  location 으로 프로티명을 변경 하는 과정을 모두 거쳐야 하는 것입니다. 그래서 우리는 else if 문을 사용하는 것이 아닌 if 문을 중첩 사용하여 Linear 형태로 Migration 해줍니다.

 

 

 

컬럼 생성 시 초깃값

컬럼에 초기값을 포함하여 추가를 해보겠습니다.

먼저 테이블을 변경 해줍니다.

class User: Object {
    @Persisted var name: String
    @Persisted var location: String
    @Persisted var age: Int
    @Persisted var phoneNumber: Int
    @Persisted var introduce: String // 초기값 포함하여 추가
    
    @Persisted(primaryKey: true) var objectId: ObjectId
    
    convenience init(name: String, address: String, age: Int) {
        self.init()
        self.name = name
        self.location = address
        self.age = age
    }
}

 

가장 먼저 스키마 버전을 3으로 올려줍니다. 

enumerateObjects 메서드를 활용하여 초깃값을 부여해줍니다.

let config = Realm.Configuration(schemaVersion: 3) { migration, oldSchemaVersion in
    if oldSchemaVersion < 1 { // version 0 > 1 phoneNumber 컬럼 추가

    }

    if oldSchemaVersion < 2 { // version 1 > 2 address -> location 프로퍼티명 변경
        migration.renameProperty(onType: User.className(), from: "address", to: "location")
    }

    if oldSchemaVersion < 3 { // version 2 > 3 introduce 컬럼 생성 및 초기값 부여
        migration.enumerateObjects(ofType: User.className()) { oldObject, newObject in
            guard let new = newObject else {return}
            guard let old = oldObject else {return}

            new["introduce"] = "안녕하세요 \(old["name"]!)입니다. \(old["location"]!)에서 왔습니다."
        }
    }
}
Realm.Configuration.defaultConfiguration = config

 

introduce가 정상적으로 추가 된 모습입니다.

 

 

 

 

마이그레이션은 코드가 지저분하고 한번 사용하면 변경하지 못하기 때문에 처음 스키마를 설계 시에 신중하게 하고 마이그레이션을 하게 되더라도 최소화하는 방향으로 개발하는 것이 좋습니다.

 

여기까지 Realm Migration 정리였습니다.

 

 

 

 

 

출처: 

https://www.mongodb.com/docs/realm/sdk/swift/model-data/change-an-object-model/#std-label-ios-modify-an-object-schema   

🌱SeSAC iOS 2기 69회차 강의자료