Develop
Android Room Migration 완벽 가이드 — 버전 업그레이드 시 데이터 손실 방지
Android Room 데이터베이스 버전 업그레이드 시 Migration 객체 작성법, fallbackToDestructiveMigration 주의사항, AutoMigration 활용법을 실전 경험과 함께 정리하였다.
- ·Room은 데이터베이스 버전이 바뀌면 반드시 Migration 객체 또는 fallbackToDestructiveMigration이 있어야 앱이 크래시 없이 열린다
- ·AutoMigration은 Room 2.4.0부터 지원하며 컬럼 추가·삭제·이름 변경을 어노테이션으로 처리한다
- ·fallbackToDestructiveMigration은 기존 데이터를 모두 삭제하고 새 스키마로 재생성한다
- ·Room의 exportSchema = true로 설정하면 스키마 JSON이 저장돼 Migration 작성 시 참고할 수 있다
사내 앱에서 Room 버전을 올리면서 Migration 객체를 누락한 채 배포한 적이 있다. 개발 기기에서는 앱을 새로 설치해 테스트했기 때문에 문제를 발견하지 못했고, 업데이트한 사용자 기기에서 앱 실행 즉시 크래시가 발생했다. 핫픽스로 fallbackToDestructiveMigration을 추가했지만 사용자 데이터가 날아가는 최악의 결과를 맛봤다. 이후 exportSchema를 켜고 Migration 단위 테스트를 자동화하는 방식으로 재발을 막았다.
Room Migration 기본 개념과 작성 방법
Room Migration 객체를 직접 작성해야 하는 경우와 작성 방법
Room이 데이터베이스를 열 때 현재 설치된 DB 버전과 앱 코드의 버전 번호를 비교한다. 버전이 다르면 해당 경로의 Migration 객체를 찾아 SQL을 실행한다. Migration(1, 2) { database -> database.execSQL("ALTER TABLE user ADD COLUMN nickname TEXT") } 형태로 작성하며 시작 버전에서 끝 버전으로 가는 SQL 변환 로직을 담는다. 버전 1→2→3처럼 여러 단계가 있을 때 각 구간의 Migration을 모두 등록해야 하며, 1→3을 바로 처리하는 Migration을 추가하면 중간 버전을 건너뛸 수 있어 업데이트 경로가 단순해진다. exportSchema = true로 설정해 스키마 JSON 파일을 버전 관리에 포함하면 이전 스키마를 참조하면서 Migration SQL을 작성할 수 있다. 스키마 파일 없이 컬럼 추가를 기억에 의존해 작성하다 보면 오타나 누락이 생겨 크래시로 이어진다.
@Database(
entities = [User::class],
version = 2,
exportSchema = true
)
abstract class AppDatabase : RoomDatabase() {
companion object {
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE user ADD COLUMN nickname TEXT")
}
}
}
}Room AutoMigration으로 단순 스키마 변경을 자동 처리하는 방법
Room 2.4.0부터 지원하는 AutoMigration은 컬럼 추가처럼 단순한 변경을 어노테이션 하나로 처리한다. @Database 어노테이션에 autoMigrations = [AutoMigration(from = 2, to = 3)]을 추가하고 버전을 올리면 Room이 스키마 JSON 파일을 비교해 ALTER TABLE 구문을 자동 생성한다. 컬럼 이름 변경은 @RenameColumn, 컬럼 삭제는 @DeleteColumn 어노테이션을 AutoMigrationSpec 클래스에 붙이면 처리된다. 테이블 삭제나 테이블 이름 변경처럼 AutoMigration이 지원하지 않는 복잡한 변경은 여전히 수동 Migration 객체가 필요하다. 프로젝트에서 AutoMigration을 도입한 뒤 단순 컬럼 추가 작업에서 Migration 코드를 작성하는 시간이 크게 줄었고 실수로 인한 크래시도 없어졌다.
Room Migration 테스트 자동화
Room MigrationTestHelper로 Migration 단위 테스트 작성하는 방법
Migration을 작성했더라도 테스트 없이는 문제를 사전에 발견하기 어렵다. Room은 androidx.room:room-testing 라이브러리에 MigrationTestHelper를 제공한다. @RunWith(AndroidJUnit4::class)로 계측 테스트를 설정하고 helper.createDatabase(TEST_DB, 1)로 이전 버전 DB를 생성한 뒤 helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2)를 호출하면 Migration 실행 후 스키마가 기대와 일치하는지 자동으로 검증한다. 마이그레이션 테스트는 에뮬레이터나 실기기에서 실행해야 하며 CI 파이프라인에 포함시켜 버전 변경 시 자동으로 검증되도록 구성하는 것이 좋다. 이 테스트 하나가 있었다면 앞서 말한 크래시 배포를 막을 수 있었다.
Room Migration 운영 시 주의사항
fallbackToDestructiveMigration 사용 전 반드시 알아야 할 Room 데이터 손실 위험
fallbackToDestructiveMigration()은 Migration 객체가 없는 경로로 업그레이드가 시도될 때 기존 데이터베이스를 삭제하고 새 스키마로 재생성하는 안전장치다. 개발 중 스키마가 자주 바뀌는 초반에는 편리하지만, 출시 후에는 절대 의존해서는 안 된다. 특히 사용자가 여러 버전을 건너뛰며 업데이트하는 경우 준비하지 않은 Migration 경로가 생길 수 있고, fallbackToDestructiveMigration이 활성화돼 있으면 그 경로에서 데이터가 조용히 삭제된다. 안전한 접근 방법은 모든 가능한 버전 구간에 Migration을 등록하거나, fallbackToDestructiveMigrationFrom(특정 버전)으로 특정 구간에만 제한적으로 허용하는 것이다. 사용자 데이터가 중요한 앱이라면 출시 빌드에서 fallbackToDestructiveMigration을 완전히 제거하고 테스트로 Migration 완전성을 보장하는 것이 최선이다.
자주 묻는 질문
Room에서 테이블을 완전히 삭제하려면 Migration을 어떻게 작성하나요?+
database.execSQL("DROP TABLE IF EXISTS old_table")을 Migration 블록에 추가하면 됩니다. AutoMigration의 @DeleteTable 어노테이션으로도 처리 가능합니다.
Room 데이터베이스를 백업하고 복원하는 방법이 있나요?+
Android 12 이상에서는 WorkManager와 BackupAgent를 조합하거나 db.openHelper.writableDatabase.path로 파일 경로를 얻어 외부 저장소에 복사하는 방식을 사용할 수 있습니다.
Room 데이터베이스 파일 경로는 어디에 있나요?+
/data/data/패키지명/databases/ 아래에 있습니다. Device File Explorer나 adb pull 명령으로 꺼내 SQLite 브라우저로 확인할 수 있습니다.