개발/안드로이드

Room DB 사용시 앱을 재시작할 때마다 데이터가 초기화 된다...?

스몰스테핑 2024. 6. 6. 16:56

 

Jetpack Compose 튜토리얼의 해당 과제를 수행 중에 있다.

 

https://developer.android.com/codelabs/basic-android-kotlin-compose-flight-search?hl=ko&continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fandroid-basics-compose-unit-6-pathway-3%3Fhl%3Dko%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fbasic-android-kotlin-compose-flight-search#0

 

프로젝트: Flight Search 앱 만들기  |  Android Developers

사용자가 항공편 출발 위치를 선택하고 좋아하는 항공편을 찾아보고 저장할 수 있는 앱을 만듭니다.

developer.android.com

 

외부 DB를 받아와 항공편을 검색해 Favorite 설정을 하면 메인화면에서 설정한 항목을 볼 수 있어야한다.

이를 위해 Room 데이터베이스를 읽고 쓸 수 있어야 했다.

 

저장하고 메인화면으로 나가는 과정에서 Favorite 설정한 데이터가 표시되지 않는 상황이 발생했다.

Android Studio에서 제공하는 Database Inspector를 통해 확인한 결과 데이터를 제대로 저장되는 것을 목격했고, DB쪽에 문제가 있음을 파악했다.

 

https://velog.io/@gogumi4502/Solutionroom-1

 

Solution[room](앱 Reload시 데이터 초기화)

✏️ Room을 공부하던 중 자꾸 데이터를 성공적으로 저장하는데 앱을 Reload하면 데이터가 삭제하는 문제가 발생하였습니다.❗️내가 Room을 확실히 이해하지 못해 발생한 문제로 Room을 사용하면

velog.io

 

나와 같은 프로젝트를 진행했던 사람이 똑같은 상황을 겪었었다.

하지만 읽어봐도 뭔가 설명은 모호했다.

 

.createFromAsset("database/flight_search.db")

 

아래 있는 코드라는 이것을 삭제하자니 입력되어있는 기존 데이터를 받아와 항공편 검색에 사용할 수가 없다.

 

 

.fallbackToDestructiveMigration()

 

이걸 삭제하면 되는 거였다.

 

https://stackoverflow.com/questions/76044041/how-do-i-make-inserted-data-persist-in-room-database

 

How do I make inserted data persist in Room database?

I am struggling with inserting permanent data into my prepopulated database. Here is the code that adds the data into the database dialogBinding.btnYes.setOnClickListener { val cantoneseWord =

stackoverflow.com

https://stackoverflow.com/questions/72832362/room-database-is-recreated-at-app-restart

 

Room database is recreated at app restart

Using Android API 32 and Room 2.4.2 Room database is created like this companion object { @Volatile private var INSTANCE: AppDatabase? = null fun getDatabase(context: Context): AppData...

stackoverflow.com

 

이 외에도 외국에서도 나와 같은 사례가 많았던 모양이다.

 

그래서 정확히 .fallbackToDestructiveMigration()이 뭐하는 녀석인지도 모르고 추가했다는 것인데,

 

https://developer.android.com/training/data-storage/room/migrating-db-versions

 

Room 데이터베이스 이전  |  Android Developers

Room 라이브러리를 사용하여 데이터베이스를 안전하게 이전하는 방법 알아보기

developer.android.com

 

공식 문서를 읽어보면 Room이 기기의 기존 데이터베이스를 현재 버전으로 업그레이드하기 위한 이전 경로를 찾을 수 없으면  IllegalStateException이 발생하고, 이전 경로가 누락되었을 때 기존 데이터를 잃어도 괜찮다면 데이터베이스 생성 시 다음과 같이 fallbackToDestructiveMigration() 빌더 메서드를 호출하라고 되어 있다.

Room.databaseBuilder(applicationContext, MyDb::class.java, "database-name")
        .fallbackToDestructiveMigration()
        .build()

 

이 옵션을 설정하면 Room이 이전을 실행하려고 하고, 정의된 이전 경로가 없을 때 사용자의 데이터베이스의 테이블에서 모든 데이터를 영구적으로 삭제한다고한다.

 

이것 때문임이 확실하긴 하다.

하지만 이전에 Room DB를 사용한 튜토리얼 프로젝트들이나 작업했던 것들 전부  해당 옵션이 추가 되어있는데 이 프로젝트에서만 문제가 발생하는게 모호해서 좀 더 찾아봤다.

 

https://stackoverflow.com/questions/76118471/room-create-from-asset-fallbacktodestructivemigration-recreating-the-database

 

Room create from asset + fallbackToDestructiveMigration recreating the database on each request

Using Room, I load my database from assets (an already populated database). I do plan to update the database content very often, so every time the version is increased, the old database the user has

stackoverflow.com

 

위 게시물에서도 단순히 fallbackToDestructiveMigration을 제거하면 해결되는 문제지만, 이럴 경우 데이터베이스와 데이터베이스 버전을 업데이트하면 마이그레이션이 제공되지 않기에 충돌이 발생하기 때문에 다음 릴리스부터 데이터베이스 마이그레이션을 수행할 수 없기에 addMigrations()을 추가하거나 충돌을 맞이할 수 밖에 없다는 것.

 

allowMainThreadQueries()

 

그렇기에 저 위 링크의 답변에선 애초에 마이그레이션 계획이 없다면 addMigrations() 할 수 있는 유일한 방법은 try/catch를 사용해 조건부로 fallbackToDestructiveMigration을 호출하고, 두 개의 정수(현재 데이터베이스의 버전, 새 데이터베이스의 버전)을 사용해 위 코드와 DataStore를 사용해 데이터베이스 마이그레이션이 필요한 릴리스가 있을 때마다 새로 작업을 시도하는 코드를 작성한 것 같다.

 

하지만 allowMainThreadQueries()는 프로덕션 앱에서는 사용해선 안되며 테스트 및 단순성 목적으로만 사용하라고 한다. 해당 메서드는 Room은 기본적으로 Main Thread에서 접근할 수 없지만, 저 메서드를 통해 만들어진 Instance에선 접근이 가능하기 때문이다. Database에 접근하는 작업은 무거운 작업이기에 사용자 활동을 장기간 멈출 수 있기에 Main Thread에서 접근하는 것이 아닌 coroutine으로 접근하기에 사용하지 않는게 좋다는 것 같다.

 

 

+++

github에 flightsearch 프로젝트를 쳐보니 나와 같은 문제에 직면했는지 database class 파일에 fallbackToDestructiveMigration이 없는 혹은, 주석처리해둔 사람들도 여럿 보였다.

그냥 쓴 사람들도 있는데 뭐가 정환히 문제인진 모르겠다.

 

+++

공식문서는 Room 2.2.0 이상에서 API 메서드를 사용하여 초기화 시 기기 파일 시스템의 사전 패키징된 데이터베이스 파일의 콘텐츠로 Room 데이터베이스를 미리 채울 수 있는 방법도 소개하고 있다.

 

https://developer.android.com/training/data-storage/room/prepopulate?hl=ko#migrations

 

Room 데이터베이스 미리 채우기  |  Android Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. Room 데이터베이스 미리 채우기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 때로 특정 데이터 세트

developer.android.com

 

 

추가 자료:

 

https://android-dev.tistory.com/entry/AndroidKotlin-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-Room-Database-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B03-Migration

 

[Android/Kotlin] 안드로이드 Room Database 사용하기(3) Migration

안녕하세요. 오늘은 Room Database를 사용하다가 Migration이 필요할 때 Migration 하는 방법에 대해서 알아보겠습니다. 테이블을 추가하거나 컬럼을 변경하는 경우에 유용하게 사용할 수 있습니다. 오늘

android-dev.tistory.com

 

https://velog.io/@thd0427/ROOM-DB%EB%A5%BC-Migration-%ED%95%98%EC%9E%90

 

ROOM DB를 Migration 하자

💡 리브랜딩과 함께 카테고리 개편 작업을 하며 Room DB Migration을 했던 이야기

velog.io

 

https://velog.io/@jjddww/Database-AAC-Room-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98

 

[Database, AAC] Room 데이터베이스 마이그레이션

Room 엔티티 클래스와 기본 데이터베이스 테이블을 수정해야 할 경우, 기기 내에 이미 존재하는 데이터베이스의 유저 데이터를 보존하는 것이 중요.

velog.io

 

https://medium.com/androiddevelopers/room-auto-migrations-d5370b0ca6eb

 

Room auto-migrations

Easily move your tables between rooms

medium.com