일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 함수
- Git
- xcode
- Realm
- Background
- 30개프로젝트로배우는iOS앱개발withSwift초격자패키지Online
- 직장인자기계발
- Threading
- 프로퍼티 관찰자
- 패캠챌린지
- RawValue
- ios
- 기본문법
- TableView
- 직장인인강
- 패스트캠퍼스
- 독학
- cancelAsyncWrite
- swift
- 옵셔널 체이닝
- userDefaults
- Optional Chaining
- switch
- 열거형
- 패스트캠퍼스후기
- beginAsyncWrite
- 문자열 보간법
- SeSAC
- enum
- 인스펙터
- Today
- Total
아삭아삭 iOS 개발
[Swift] Realm Migration - 컬럼 추가/삭제/이름변경/결합 본문
어제 수업시간에 배웠던 realm migration 내용을 정리해보고, 연습 프로젝트에 실습한 내용을 기록했습니다.
틀린 내용이 있을 수 있으며 댓글을 통한 피드백은 언제나 환영입니다~
Migration
- Realm 데이터 설계 이후 데이터를 갱신할 때 필요한 과정
- ex) 새로운 테이블이 추가되거나
- ex) 기존 테이블의 컬럼을 삭제/추가/이름변경/타입변경 등이 필요할 때 사용
- 데이터베이스에서 스키마 버전을 관하는데 사용됨
- 별도로 지정하지 않는 한 realm schema 버전은 자동으로 0 설정
- 업데이트시 상위 버전으로만 가능
deleteRealmIfMigrationNeeded
개발자가 앱을 개발하거나 디버깅시 realm을 수정할 때마다 해당 수정된 버전의 realm으로 테스트를 하려면
시뮬레이터 앱을 매번 삭제&재설치하고 Realm Browser도 닫고 다시 열어주어야 했습니다.
하지만 deleteRealmIfMigrationNeeded 옵션을 사용하면 매번 재설치를 해야하는 귀찮은 작업을 안 해도 됩니다!
왜냐하면 기존 스키마랑 현재 스키마가 불일치해서 migration이 필요한 경우에
해당 옵션이 자동으로 기존의 schema를 제거해주기 때문입니다.
적용하는 방법은 appDelegate에서 마이그레이션 함수를 작성할 때 아래처럼 해당 옵션값을 true로 주면 끝입니다.
extension AppDelegate {
func aboutRealmMigration() {
let config = Realm.Configuration(schemaVersion: 1, deleteRealmIfMigrationNeeded: true) // 여기!
Realm.Configuration.defaultConfiguration = config
}
}
주의할 점! 릴리즈 버전에는 해당 옵셩을 사용하면 안되는 점 참고해주세요~
Linear Migrations
schema를 상세하게 업데이트 해주기 위해서는 AppDelegate내에서 migrationBlock를 사용해서 수동으로 업데이트를 해주어야 합니다.
이 때, migrationBlock이 중첩되거나 스킵되어지는 상황을 피하기 위해 아래처럼 모든 버전별로 if 구문을 통해 구분을 해줍니다.
extension AppDelegate {
func realmMigrationSetting() {
let config = Realm.Configuration(schemaVersion: 2) { migration, oldSchemaVersion in
if oldSchemaVersion < 1 { } // 버전이 1보다 아래일 경우 구분
if oldSchemaVersion < 2 { } // 버전이 2보다 아래일 경우 구분
}
Realm.Configuration.defaultConfiguration = config
}
}
이렇게 버전별로 세세하게 나눠줌으로써 사용자의 현재 버전이 과거 몇이든 상관없이
모두 적절한 순서를 거쳐 최신 버전으로 업데이트가 될 수 있습니다.
버전을 구분해줄 때 else if가 아닌 if 로 구성한 이유 또한모든 케이스의 버전 block을 하나하나 다 체크해주고 지나가기 위함이라고 하니 참고해주세요~
< 연습 프로젝트에 실습한 내용 >
1) 컬럼 추가/삭제
기존에 작업하던 메모장 프로젝트에서 아래처럼 likeNum 변수를 추가했다가, 삭제해보았습니다.
단순히 항목을 추가하거나 삭제했을 경우에는 manually 작업해줄 코드는 없으며
개발자가 버전을 올려주기만 하면 똑똑한 xcode가 자동으로 migration을 진행해줍니다.
// Relam model
class UserMemo: Object {
@Persisted var memoTitle: String
@Persisted var memoContent: String?
@Persisted var memoDate = Date()
@Persisted var pin: Bool
@Persisted var likeNum: Int // 좋아요 클릭수 항목을 추가했다가 삭제했음!
@Persisted(primaryKey: true) var objectId: ObjectId
convenience init(memoTitle: String, memoContent: String?, memoDate: Date) {
self.init()
self.memoTitle = memoTitle
self.memoContent = memoContent
self.memoDate = memoDate
self.pin = false
}
}elegate
// AppDelegate
extension AppDelegate {
func realmMigrationSetting() {
let config = Realm.Configuration(schemaVersion: 2) { migration, oldSchemaVersion in
if oldSchemaVersion < 1 { } // ver1. 단순히 컬럼 추가만 했으므로, 별도 코드 필요 x
if oldSchemaVersion < 2 { } // ver2. 단순히 컬럼 삭제만 했으므로, 별도 코드 필요 x
}
Realm.Configuration.defaultConfiguration = config
}
}
2) 컬럼명 변경
migration을 통해 컬럼명을 변경할 수도 있습니다.
단순히 컬럼을 추가하거나 삭제하는게 아닌 컬럼명을 변경하는 작업을 해줄 때에는 migration.renameProperty(onType:from:to)메서드를 활용해서 manually작업을 해주어야 합니다.
한번에 여러 컬럼에 대한 변경작업을 괜히 해보고 싶어서
한 번 4개 컬럼명 전체를 다 변경해보았는데요, 정상적으로 변경은 되었지만..
다른 뷰컨이나 realm repository 함수내에서 사용됐던 컬럼명도 다 변경된 이름으로 적용해주어야 합니다.
(but 이렇게 한번에 여러 컬럼에 대해 데이터를 다 변경하는게 안전하지는 않은 방법이라고 합니다!)
class UserMemo: Object {
@Persisted var title: String //제목(필수)
@Persisted var content: String? // 내용(옵션)
@Persisted var writtenDate = Date() // 작성 날짜(필수)
@Persisted var fixed: Bool // 고정여부(필수)
@Persisted(primaryKey: true) var objectId: ObjectId
convenience init(memoTitle: String, memoContent: String?, memoDate: Date) {
self.init()
self.title = memoTitle
self.content = memoContent
self.writtenDate = memoDate
self.fixed = false
}
}
extension AppDelegate {
func realmMigrationSetting() {
let config = Realm.Configuration(schemaVersion: 3) { migration, oldSchemaVersion in
if oldSchemaVersion < 1 { }
if oldSchemaVersion < 2 { }
// v3. 컬럼명 4개를 변경해주었으므로, 코드 작업을 해줌
if oldSchemaVersion < 3 {
migration.renameProperty(onType: UserMemo.className(), from: "memoTitle", to: "title")
migration.renameProperty(onType: UserMemo.className(), from: "memoContent", to: "content")
migration.renameProperty(onType: UserMemo.className(), from: "memoDate", to: "writtenDate")
migration.renameProperty(onType: UserMemo.className(), from: "pin", to: "fixed")
}
}
Realm.Configuration.defaultConfiguration = config
}
}
3) 두 컬럼을 결합해서 신규컬럼으로 추가
기존의 두 컬럼을 결합해서 신규 컬럼으로 추가할 경우에도 enumerateObjects을 사용합니다.
title과 content 컬럼을 결합해서 userDescription이라는 신규 컬럼으로 추가해보았습니다.
class UserMemo: Object {
.
.
@Persisted var userDescription : String // title + content 결합한 컬럼
.
.
}
extension AppDelegate {
func realmMigrationSetting() {
let config = Realm.Configuration(schemaVersion: 4) { migration, oldSchemaVersion in
.
.
// v4. 기존 컬럼 2개를 결합하여 신규컬럼 생성, 코드 작업을 해줌
if oldSchemaVersion < 4 {
migration.enumerateObjects(ofType: UserMemo.className()) { oldObject, newObject in
guard let new = newObject else { return }
guard let old = oldObject else { return }
new["userDescription"] = "제목은 \(old["title"])이고, 내용은 \(old["content"])입니다!!!"
}
}
}
Realm.Configuration.defaultConfiguration = config
}
}
4) 기본값이 있는 컬럼 추가
신규 컬럼으로 추가시 기본값이 존재할 경우에도 enumerateObjects 메서드를 활용합니다.
user라는 기본값을 갖는 author 컬럼을 추가해보았습니다.
class UserMemo: Object {
.
.
@Persisted var author: String // 메모 저자 - 기본값 존재
.
.
}
extension AppDelegate {
func realmMigrationSetting() {
let config = Realm.Configuration(schemaVersion: 5) { migration, oldSchemaVersion in
.
.
// v5. 신규 컬럼 1개를 추가하는데 기본값이 존재함, 코드 작업을 해줌
if oldSchemaVersion < 5 {
migration.enumerateObjects(ofType: UserMemo.className()) { oldObject, newObject in
guard let new = newObject else { return }
new["author"] = "user"
}
}
}
Realm.Configuration.defaultConfiguration = config
}
}
5) (시도) 기존컬럼의 컬럼명, 타입, 기본값 동시에 변경
위에 4번까지 차근차근 migration 연습을 해보다가, 한 컬럼에 대해 이름, 타입, 기본값이 한번에 바뀔 수 있을지가 갑자기 궁금해졌습니다.
그래서 renameProperty로 컬럼명을 변경해주고, enumerateObjects로 타입과 기본값을 새로 할당해보았는데..
.. 에러가 났습니다!ㅜㅜ
왜 그럴까 생각해보니 타입변경 관련해서는 (수업실습 때와 달리) realm model내에서 타입을 변경해주는 문구도 누락하기도 했고,단계적으로 바꾸는게 아니라 한번에 변경작업을 진행하려고 해서로 보였습니다.
나중에 이 여섯번째 시도 케이스에 대해 멘토님께 질문해보았고 아래와 같은 피드백을 받았습니다.
- 마이그레이션 작업 시에는 단계적으로 해주는게 안전
- 하나의 컬럼에 대해 다양한 변경을 하기에는 (가능하다고 할지라도) 안전하지 않은 코드로 참고하는 것이 좋음
- 실제로 보통 마이그레이션을 작업할 때 한 컬럼을 한번에 다양하게 바꾸는 케이스가 많이 없음
- 마이그레이션은 일반적으로 데이터를 보다 더 잘 정규화하기 위해 테이블을 쪼개거나, 신규 컬럼이 필요한 경우에 많이 사용됨
정말 그러고보니, 한 컬럼에 대해 이름도 바꾸고, 타입도 바꾸고, 기본값도 바꿀꺼면
아예 삭제하고 다른 컬럼이 들어오는게 차라리 나을 법할 수도 있겠다 싶었습니다.. 흠..
무튼 여섯번째 마이그레이션은 시도는 좋았으나, 실제 현업에서는 잘 쓰지 않는 케이스라는 것
그리고 마이그레이션을 주요하게 쓰는 경우에 대해 더 생각해볼 수 있었습니다!
https://www.mongodb.com/docs/realm/sdk/swift/model-data/change-an-object-model/
'Swift' 카테고리의 다른 글
[위클리 발표] Happy Realming 🤗(1)_Overview (0) | 2022.12.19 |
---|---|
[Swift] tableview cell 접었다 폈다 기능 구현하기_AutomaticDimension (0) | 2022.08.14 |
[Swift] Notification 알아보기 & Local Notification 적용 순서 정리 (0) | 2022.07.30 |
TableView는 언제, 왜 사용할까요? (0) | 2022.07.19 |
[Swift 독학] 패스트캠퍼스 챌린지 최종 후기_끝!! (0) | 2022.03.23 |