![[Room DB] Room Database 마이그레이션 시 주의점 2가지](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwlDvW%2FbtrSL1z6mqm%2FYl9dY8FMrckUfoetmcINTk%2Fimg.png)
What is Room Database?
안드로이드에서 데이터를 저장할 때 에는 SharedPreferences 또는 DataStore를 주로 사용하지만 방대한 자료, 그룹화/분류 및 쿼리문이 필요해지는 등 이러한 상황에서는 Jetpack에서 제공하는 Room Database(이하 Room DB) 주로 사용하고 계신다고 생각됩니다.
상당한 양의 구조화된 데이터를 처리하는 앱은 데이터를 로컬에 유지하여 매우 큰 이익을 얻을 수 있습니다. 가장 일반적인 사용 사례는 기기가 네트워크에 액세스 할 수 없을 때도 사용자가 오프라인 상태로 계속 콘텐츠를 탐색할 수 있도록 관련 데이터를 캐시 하는 것입니다. Room 지속성 라이브러리는 SQLite를 완벽히 활용하면서 원활한 데이터베이스 액세스가 가능하도록 SQLite의 추상화 계층을 제공합니다. 특히 Room을 사용하면 다음과 같은 이점이 있습니다. SQL 쿼리의 컴파일 시간 확인 반복적이고 오류가 발생하기 쉬운 상용구 코드를 최소화하는 편의 주석 간소화된 데이터베이스 이전 경로 이러한 점을 고려할 때 SQLite API를 직접 사용하는 대신 Room을 사용하는 것이 좋습니다.
안드로이드 공식 문서 - Room을 사용하여 로컬 데이터베이스에 데이터 저장
사용하는 방법도 크게 어렵지 않고 LiveData, RX, Coroutine Flow 모두 지원을 하기에 비동기적인 작업으로 처리할 때에도 간편합니다.
또한 기본적인 Query문에 대해 Annotation을 사용을 통해 귀찮은 보일러플레이트적인 Query문을 정확하고 가독성 높게 구현이 가능합니다. (물론 이러한 요소 외에도 많은 장점이 존재합니다.)
그렇기에 여러분들도 아시다시피 간단하게 Entity를 만들고 이를 DAO를 통해 CRUD 처리를 할 수 있습니다.
그러나 이러한 간편함에 속아(?) Entity의 변화가 생겨 마이그레이션 하는 과정을 놓친다면 크리티컬한 이슈를 만들어 낼 수 있다는 치명적인 문제가 있습니다.
이러한 Entity에 변화가 생겼을 때에 대한 대처에서 크게 하는 실수 2가지에 대해 짧게 요약 정리해보겠습니다.
데이터 셋이 복잡하지 않은 경우, 데이터 객체 T를 저장하기 위해 Room DB를 사용하시던 분은 Proto DataStore를 통해 대체하실 수 있습니다.
자주 하는 실수 2가지
본문에 나오는 2가지 외에도 다양한 요소가 존재합니다만, 개인적으로 자주 겪은 실수... 2가지에 대해서 정리해보았습니다.
이 외에 추가로 경험하신 내용이나 추가되면 좋겠다 하는 부분이 있으시다면 댓글로 남겨주시면 감사하겠습니다 :)
Database Version
새로운 Entity가 생긴다면 문제가 없지만 기존 Entity의 변화가 생긴다면 Database의 version을 증가시켜야 합니다.
그렇지 않다면 기존 사용자가 Entity가 변경된 앱으로 업데이트하게 되는 경우 아래와 같은 오류를 만날 수 있습니다.
Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number.
You can simply fix this by increasing the version number.
실제 사용자는 해당 오류를 확인하지 못하고 앱이 죽게 됩니다.
이는 Entity 즉 테이블의 정보가 변경됨으로써 무결성을 보장할 수 없기에 경고를 하는 것입니다.
이를 해결하는 방법은
- 앱 데이터를 초기화하여 기존 가지고 있던 DB의 정보를 초기화시키거나
- Database의 version을 증가시키면 됩니다
그러나 1번의 방법은 UX를 고려하지 않는 무식한 방법이며, 2번을 수행한다 하더라도 구 버전 사용자가 신 버전으로 업데이트 시 오 작동을 할 가능성이 있기에 반드시 구 버전 사용 후 신 버전에서도 정상 작동되는지확인해야 합니다.
특히 1번의 경우, 개발하는 과정에서 version 올리는 것에 대한 귀찮음으로 "나중에 해야지"라는 생각을 하는 것은 매우 위험합니다.
이를 깜빡하고 출시를 하게 된다면 기존 모든 사용자들은 앱을 실행할 수 없는... 대 지옥을 맛보실 수 있습니다.
Room DB Migration
Entity의 변화가 생겨 마이그레이션 해야 하는 상황이 발생하는 경우 마이그레이션 코드를 작성하게 됩니다.
이때 중요한 점은 AOS 버전에 따라 SQL문 오류가 발생할 수 있습니다.
예시로 Entity의 필드 이름이 age에서 level로 변경되었다고 해보겠습니다.
일반적으로 생각한다면 아래와 같이 ALTER RENAME TO를 통해 쉽게 변경할 수 있습니다.
val migration_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE User RENAME COLUMN age TO level")
}
}
위의 마이그레이션 코드는 최신 AOS에서 정상적으로 작동되기에 '마이그레이션 완료!' 라 생각하고 출시/배포하실 수 있습니다.
그러나 AOS 9.0 미만에서는 해당 SQL문 실행 시 오류가 발생하게 됩니다.
이는 안드로이드 운영체제 버전에 따라 내장된 데이터베이스의 버전이 다르기에 발생하는 이슈입니다.
그렇기에 마이그레이션 코드를 작성한 경우에는 더더욱 구/신 운영체제에서 Room DB 업데이트 전/후 버전 모두 정상 작동되는지 확인을 반드시 해야 합니다.
위의 시나리오를 해결하는 꼼수 중 하나는 age가 아닌 level이 담긴 임시 테이블을 생성 후 해당 테이블로 기존 age를 level를 집어넣은 후 기존 테이블 삭제, 그리고 다시 기존 이름으로 테이블을 생성하여 임시 테이블의 정보를 집어넣고 임시 테이블을 삭제하는 방법입니다.
val migration_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE TempUser (id INTEGER NOT NULL PRIMARY KEY DEFAULT 0, level INTEGER NOT NULL DEFAULT 0)")
database.execSQL("INSERT INTO TempUser (id, level) SELECT id, age FROM User")
database.execSQL("DROP TABLE User")
database.execSQL("CREATE TABLE User (id INTEGER NOT NULL PRIMARY KEY DEFAULT 0, level INTEGER NOT NULL DEFAULT 0)")
database.execSQL("INSERT INTO User (id, level) SELECT id, level FROM TempUser")
database.execSQL("DROP TABLE TempUser")
}
}
위의 마이그레이션 로직을 정리하자면 아래와 같습니다.
- 변경된 내용의 임시 테이블 Create
- 임시 테이블에 기존 테이블의 정보를 Insert
- 기존 테이블 Delete
- 변경된 내용의 정보로 기존 이름의 테이블 Create
- 기존 이름의 테이블에 임시 테이블의 정보를 Insert
- 마지막으로 임시 테이블 Delete
마이그레이션 코드로 작성한 SQL문이 구/신 OS 모두 동일하게 동작된다면 문제가 없지만, 그렇지 않다면 해당 SQL문을 우회해야 작성해야 하기에 반드시 테스트가 필요합니다.
결론적으로
실제로는 더 많은 케이스가 있을 수 있겠지만, 개인적으로 초기 자주 실수하였던 2가지의 상황을 정리해보았습니다.
이처럼 기존 Entity 정보의 변화가 발생한다는 것은 무결성이 깨진다는 것이고, 이는 앱 업데이트 시 어떠한 이슈를 만들어낼지 모르는 시한폭탄입니다.
그렇기에 반드시 구 버전 사용 후 신 버전에서의 테스트가 필요하며, 오류 또는 필요에 의해 마이그레이션 코드를 작성하게 된다면 해당 마이그레이션 코드 역시 구/신 운영체제에서 구/신 버전 모두 정상 작동되는지 테스트가 필요합니다.
또한 개발 팀의 테스트뿐만 아니라 QA팀에서도 통합 테스트 과정에서 해당 시나리오를 테스트를 할 수 있도록 DB의 변동사항이 발생한다면 해당 부분을 적극적으로 테스트할 수 있도록 공유하시는 것을 추천드립니다.
'개발 > Android' 카테고리의 다른 글
안드로이드 Proto 설정 시 오류 (0) | 2023.08.14 |
---|---|
컴포즈 내비게이션 CreationExtras must have a value by `SAVED_STATE_REGISTRY_OWNER_KEY` 오류 (0) | 2022.12.07 |
PopupWindow에서 Jetpack Compose를 사용하는 방법 (1) | 2022.11.04 |
안드로이드 gRPC 개념 및 사용법 (2) | 2022.10.12 |
MQTT 안드로이드 코틀린 클라이언트 + 서버 만들기 (2) | 2022.09.27 |
상상하는 것을 소프트웨어로 구현하는 것을 좋아하는 청년
게시글이 마음에 드시나요? [ 공감❤️ ] 눌러주시면 큰 힘이 됩니다!