안드로이드 시스템·Compose 용어 사전
데이터Migration · fallbackToDestructiveMigration

Room 마이그레이션

스키마 버전을 올릴 때 기존 사용자 데이터를 옮기는 절차. 빠뜨리면 앱이 죽는다.

스키마를 바꿀 때 기존 사용자 데이터를 옮기는 절차.

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을 이어 붙일 수 있으면 되지만, 건너뛰는 사용자를 반드시 고려한다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — Room — 로컬 DB·DAO·관계·마이그레이션