안녕하세요. 후르륵짭짭 입니다.
이번에는 Realm의 Migration에 대해 알아보려고 합니다.
** Migration이란 **
현업을 하다 보면 Migration이라는 말을 많이 듣게 됩니다.
Migration이란 쉽게 기존에 있던 것에서 다른 곳으로 옮기거나 소프트웨어의 업데이트가 있을 때, 구축되어 있는 데이터베이스의 변동이 있을 때 사용합니다.
즉, 기존의 있던 사항이 변경 된다고 할 때 마이그레이션이라는 말을 사용합니다.
** Realm Auto Migration **
class MigrationTestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
do {
let realm = try Realm()
print("realm = \(configuration.fileURL)")
let book = Book(firstName: "ali", userId: 23)
try! realm.write({
realm.add(book)
})
}
catch{
print(error.localizedDescription)
}
}
}
class Book : Object {
@objc dynamic var firstName = ""
@objc dynamic var lastName = ""
@objc dynamic var userId = 0
override static func primaryKey() -> String? {
return "userId"
}
convenience init(firstName : String, userId : Int){
self.init()
self.firstName = firstName
self.userId = userId
}
}
위와 같이 코드가 적혀있는 상태에서, Build and Run을 하면 SchmaVersion이 1인 Realm File이 생성이 됩니다.
그리고 나서 Book 이라는 Realm Class에 title을 추가고 Run 을 하면 Realm에 title이 추가되었다고 오류가 발생합니다.
이런 크래시가 발생한 것은 생성된 Realm 파일의 구조와 달라져서 크래시가 발생한 겁니다.
class Book : Object {
@objc dynamic var firstName = ""
@objc dynamic var lastName = ""
@objc dynamic var userId = 0
@objc dynamic var title = ""
override static func primaryKey() -> String? {
return "userId"
}
convenience init(firstName : String, userId : Int){
self.init()
self.firstName = firstName
self.userId = userId
}
}
이럴 경우에는 Realm Schema version을 2로 변경 해고 configuration으로 등록을 해주면
자동으로 구조 변경 된 것을 확인하고 생성된 Realm 파일의 구조를 변경해서 크래시가 발생하지 않고 사용할 수 있게 됩니다.
override func viewDidLoad() {
super.viewDidLoad()
do {
let configuration = Realm.Configuration(schemaVersion:2)
let realm = try Realm(configuration: configuration)
print("realm = \(configuration.fileURL)")
let book = Book(firstName: "ali", userId: 23)
try! realm.write({
realm.add(book)
})
}
catch{
print(error.localizedDescription)
}
}
** Realm Custom Migration **
Custom Migration은 Auto Migration이 할 수 있는 간단한 필드 추가에서 벗어나
필드 이름 변경 , 기존 데이터 값 추가 , 디폴트 관계 데이터 추가 등 여러 수작업이 필요할 때 사용합니다.
이는 Configuration 변경을 통해 가능하게 할 수 있습니다.
Realm.Configuration(schemaVersion:4) { migration, oldSchemaVersion in }
schemaVersion -> 현재 스키마 버전
migration -> Migration객체로 Realm 파일에 접근 할 수 있으며 Migration에 필요한 메소드 포함하고 있음
oldSchemaVersion -> 스키마 버전 비교
- Default 값 추가 -
위에 처럼 Auto Migration을 사용하면 안전하게 필드가 추가됩니다.
하지만 추가 되는 데이터들에게 Default값을 추가해야한다면 Custom Migration을 해줘야합니다.
class MigrationTestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
do {
let configuration = Realm.Configuration(schemaVersion: 2) { migration, oldSchemaVersion in
print("New Schema : \(migration.newSchema.description)")
print("old Schema : \(migration.oldSchema.description)")
//title의 Default값을 넣어준다.
migration.enumerateObjects(ofType: "Book") { oldObject, newObject in
newObject?["title"] = "default"
}
}
let realm = try Realm(configuration: configuration)
print("realm = \(configuration.fileURL)")
let book = Book(firstName: "ali", userId: 20)
try! realm.write({
realm.add(book)
})
}
catch{
print(error.localizedDescription)
}
}
}
- 필드 Name 변경 -
기존의 필드 이름이 변경이 되어야한다면 아래와 같이 변경해주면 됩니다.
이때 새로 작성되는 Schmaversion에는 Model의 명을 변경 해줘야합니다.
class Book : Object {
@objc dynamic var firstName = ""
@objc dynamic var lastName = ""
@objc dynamic var userId = 0
@objc dynamic var title : String? = nil
// @objc dynamic var currentUser = false
@objc dynamic var iscurrentUser = false
override static func primaryKey() -> String? {
return "userId"
}
convenience init(firstName : String, userId : Int){
self.init()
self.firstName = firstName
self.userId = userId
}
}
let configuration = Realm.Configuration(schemaVersion:2) { migration, oldSchemaVersion in
migration.enumerateObjects(ofType: "Book") { oldObject, newObject in
//SchemaVersion 비교
if oldSchemaVersion == 1 {
// oldObject에 title 필드 값이 없다면 Default값 넣어준다.
if oldObject?["title"] == nil {
newObject?["title"] = "migrated Value"
}
}
}
//currentUser로 되어 있는 필드 이름을 iscurrentUser로 변경
migration.renameProperty(onType: "Book", from: "currentUser", to: "iscurrentUser")
}
- 디폴트 관계 데이터 추가 및 통합 스키마 버전 관리 -
class Book : Object {
@objc dynamic var firstName = ""
@objc dynamic var lastName = ""
@objc dynamic var userId = 0
@objc dynamic var title : String? = nil
@objc dynamic var iscurrentUser = false
@objc dynamic var passport : Passport?
override static func primaryKey() -> String? {
return "userId"
}
convenience init(firstName : String, userId : Int){
self.init()
self.firstName = firstName
self.userId = userId
}
}
class Passport : Object {
@objc dynamic var passportNumber = ""
}
let configuration = Realm.Configuration(schemaVersion:3) { migration, oldSchemaVersion in
migration.enumerateObjects(ofType: "Book") { oldObject, newObject in
if oldSchemaVersion == 1 {
if oldObject?["title"] == nil {
newObject?["title"] = "migrated Value"
}
migration.renameProperty(onType: "Book", from: "currentUser", to: "iscurrentUser")
}
}
if oldSchemaVersion == 2 {
// 추가 되는 관계형 필드인 Passport에 디폴트 값을 추가할 때
migration.enumerateObjects(ofType: "Book") { oldObject, newObject in
let passport : MigrationObject = migration.create("Passport", value: ["passportNumber" : "Number"])
newObject?["passport"] = passport
}
}
}
위에 처럼 스키마 버전 관리를 하나씩 분기 처리를 하면 놓치는 부분이 많을 수 있기 때문에
아래 처럼 비교를 통해 해주는 것이 바람직합니다.
let configuration = Realm.Configuration(schemaVersion:4) { migration, oldSchemaVersion in
//이렇게 통합적으로 관리 해주는게 좋습니다.
if oldSchemaVersion < 4 {
migration.enumerateObjects(ofType: "Book") { oldObject, newObject in
if oldObject?["title"] == nil {
newObject?["title"] = "migrated Value"
}
migration.renameProperty(onType: "Book", from: "currentUser", to: "iscurrentUser")
}
migration.enumerateObjects(ofType: "Book") { oldObject, newObject in
let passport : MigrationObject = migration.create("Passport", value: ["passportNumber" : "Number"])
newObject?["passport"] = passport
}
}
Realm의 Migration에 대해 알아봤습니다.
DB의 Migration은 필수적인 항목이고 피해갈 수 없죠.
저도 많이 고생을 했습니다.
개인적인 생각이지만, 한번 DB 구조를 잘짜야지 Custiom Migration을 안하게 되고 효율적인 데이터베이스 구조를 만들 수 있습니다.
감사합니다.
참고 사이트 :
http://www.clusterdb.com/mongodb/migrating-your-ios-apps-realm-schema-in-production
https://ali-akhtar.medium.com/migration-with-realm-realmswift-part-6-11c3a7b24955
'DataBase > IOS - Realm' 카테고리의 다른 글
IOS) Realm - Compact FileSize 그리고 주의할 사항 (0) | 2022.07.24 |
---|---|
IOS)Realm - Multi Threading (0) | 2022.04.28 |
IOS)Realm - Configuration & Extension Realm Share - 1 (0) | 2022.02.13 |
IOS) Realm - CRUD Operation & Notification - 3 (2) | 2021.12.12 |
IOS) Realm - CRUD Operation & Notification - 2 (0) | 2021.12.04 |
댓글