스키마를 바꿀 때 기존 사용자 데이터를 옮기는 절차.
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE users ADD COLUMN phone TEXT")
}
}
Room.databaseBuilder(ctx, AppDatabase::class.java, "app.db")
.addMigrations(MIGRATION_1_2)
.build()
빠뜨리면 어떻게 되나
version 을 올렸는데 Migration 이 없으면
- IllegalStateException: A migration from 1 to 2 was required but not found
- 앱을 열자마자 크래시. 이미 배포된 사용자 전원이 겪는다
개발 중에 잘 되던 이유
- 앱을 지웠다 깔면 DB 가 새로 생성돼 마이그레이션 경로를 안 탄다
- 반드시 '구버전 설치 → 신버전 업데이트' 로 검증해야 한다
스키마 파일을 커밋한다
// build.gradle.kts
ksp { arg("room.schemaLocation", "$projectDir/schemas") }
schemas/1.json · 2.json 이 생성된다
- 마이그레이션 테스트가 이 파일을 읽어 실제로 검증한다
- 코드 리뷰에서 스키마 변경이 눈에 보인다
- git 에 반드시 커밋한다
마이그레이션을 테스트한다
@get:Rule val helper = MigrationTestHelper(
InstrumentationRegistry.getInstrumentation(), AppDatabase::class.java
)
@Test fun migrate1To2() {
helper.createDatabase(TEST_DB, 1).apply {
execSQL("INSERT INTO users VALUES (1, '홍길동', 'a@b.c')")
close()
}
val db = helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2)
// 데이터가 살아남았는지 확인
}
자동 마이그레이션
@Database(
entities = [UserEntity::class], version = 2,
autoMigrations = [AutoMigration(from = 1, to = 2)]
)
컬럼 추가처럼 단순한 변경은 Room 이 스키마 파일을 비교해 생성해 준다
애매한 변경(컬럼 삭제·이름 변경) 은 스펙을 명시한다
- @DeleteColumn · @RenameColumn 을 AutoMigrationSpec 에 적는다 데이터 변환이 필요하면 수동 Migration 이어야 한다
fallbackToDestructiveMigration은 데이터를 지운다
-
마이그레이션이 없으면 테이블을 전부 지우고 새로 만든다
-
허용되는 경우 — DB 가 순수 캐시라 다시 받으면 되는 경우
-
금지 — 사용자가 만든 데이터(메모·즐겨찾기·오프라인 초안) 가 있는 경우
면접 함정
- ❌ "version만 올리면 Room이 알아서 한다" → 자동 마이그레이션을 명시하지 않으면 크래시다.
- ❌ "마이그레이션은 한 단계씩만 지원하면 된다" → 1→3 경로도 필요하다. Room이 1→2, 2→3을 이어 붙일 수 있으면 되지만, 건너뛰는 사용자를 반드시 고려한다.