애플리케이션을 개발하다 보면 프로젝트 요구사항이 발전하는 것은 지극히 정상적인 일입니다. 기능을 추가함에 따라 Room 엔티티 클래스 와 해당 테이블도 이러한 새로운 요구사항을 수용하기 위해 변경해야 할 필요가 생깁니다 . 여기서 가장 큰 과제는 앱이 업데이트되고 데이터베이스 스키마가 변경될 때 사용자의 데이터가 손실되지 않도록 하는 것입니다.
Room은 이러한 문제를 해결하기 위해 가장 간단한 구현 방식부터 더욱 복잡한 구현 방식까지 다양한 옵션을 제공합니다. 단순한 열 추가부터 완전한 구조 개편에 이르기까지 변경 사항에 따라 자동 또는 수동 마이그레이션을 선택할 수 있으며 , 이를 통해 애플리케이션이 예기치 않게 다운되는 현상 없이 원활한 전환을 보장합니다.
가장 빠른 방법: 자동 마이그레이션
Room은 버전 2.4.0-alpha01부터 자동 마이그레이션 기능을 통해 작업을 간소화합니다. 기본적으로 프레임워크가 이전 버전과 새 버전 간의 차이점을 분석하여 마이그레이션 계획을 자동으로 생성해 줍니다. 이 기능을 활성화하려면 `@Database` 데코레이터의 `autoMigrations` 속성에 `@ AutoMigration` 어노테이션을 추가하기만 하면 됩니다.
중요한 세부 사항이 있습니다. 이 기능이 제대로 작동하려면 `exportSchema`를 `true`로 설정해야 합니다 . 스키마를 내보내지 않았거나 새 버전 번호로 데이터베이스를 컴파일하지 않은 경우 Room은 변경 사항을 인식하지 못하여 마이그레이션이 완전히 실패합니다.
복잡한 사례 자동 처리
Room은 때때로 테이블을 삭제하거나 열 이름을 변경하는 경우처럼 모호한 변경 사항을 감지하는 데 실패할 수 있습니다. 이러한 경우 컴파일러는 오류를 발생시키고 AutoMigrationSpec 을 구현하도록 요청합니다 . 이 정적 클래스는 Room이 오류를 감지하는 데 필요한 추가 정보를 제공하는 곳입니다.
이 스펙 내에서는 `@RenameTable` 과 같은 어노테이션을 사용하여 시스템을 제어할 수 있습니다. 또한 자동 마이그레이션이 완료된 후 추가 코드를 실행해야 하는 경우, 최종 데이터 조정을 수행하기에 적합한 `onPostMigrate()` 메서드를 사용할 수 있습니다.
완벽한 제어: 수동 마이그레이션
자동화로는 충분하지 않은 상황이 있는데, 예를 들어 테이블의 데이터를 두 개의 별개 엔티티로 분할해야 하는 경우가 그렇습니다. 이럴 때는 수동 마이그레이션을 사용하는데, Migration 클래스를 상속받는 클래스를 만들고 migrate() 메서드를 재정의하면 됩니다.
이 방법에서는 SQL 문을 직접 실행할 수 있는 SupportSQLiteDatabase 객체를 사용합니다. 예를 들어 열을 추가하려면 ALTER TABLE 문을 사용합니다 . 데이터베이스 빌더에서 이러한 경로를 등록할 때는 addMigrations() 메서드 를 사용하는 것이 중요합니다.
중요한 팁: SQL 마이그레이션 쿼리 내에서 Kotlin 상수를 사용하지 마세요. 대신 완전한 SQL 쿼리를 작성하세요 . 이렇게 하면 나중에 상수의 값을 변경했는데 이전 마이그레이션에서 여전히 원래 값을 요구하는 경우 발생하는 치명적인 오류를 방지할 수 있습니다.
고급 전략 및 오류 관리
마이그레이션 경로를 정의할 수 없고 데이터 삭제를 감수할 수 있는 상황(캐시 데이터일 가능성이 높음)이라면 `fallbackToDestructiveMigration()`을 사용할 수 있습니다 . 이 메서드를 사용하면 Room이 모든 데이터를 삭제하고 테이블을 처음부터 다시 생성하여 `IllegalStateException` 오류로 앱이 충돌하는 것을 방지합니다.
보다 세밀한 제어를 위해서는 사용자가 특정 버전에서 마이그레이션하는 경우에만 데이터를 삭제하는 fallbackToDestructiveMigrationFrom() 또는 사용자가 새 버전 위에 이전 버전의 앱을 설치할 때 유용한 fallbackToDestructiveMigrationOnDowngrade() 와 같은 대안이 있습니다 .
사전 패키지 데이터베이스
때때로 앱에 미리 채워진 데이터가 필요한 경우가 있습니다. Room은 `createFromAsset()` 또는 `createFromFile()` 함수 를 사용하여 이를 수행할 수 있도록 지원합니다. 흥미로운 점은 이 기능이 파괴적 마이그레이션과 어떻게 상호 작용하는지입니다. 대상 버전과 일치하는 미리 패키징된 파일이 있는 경우, Room은 파괴적 정리 작업을 수행한 후 해당 파일을 사용하여 데이터베이스를 채웁니다.
NOT NULL을 이름 변경하고 처리하는 기술
SQLite에서 열 이름을 바꾸는 것은 특히 API 29 이전 버전에서는 골칫거리일 수 있습니다. 여기서 핵심적인 방법은 생성-복사-삭제 패턴을 사용하는 것입니다 . 올바른 이름으로 새 테이블을 생성하고, INSERT INTO … SELECT 문을 사용하여 데이터를 삽입한 다음, DROP TABLE 문으로 기존 테이블을 삭제하는 것입니다.
또 다른 중요한 시나리오는 이미 레코드가 있는 테이블에 기본값이 없는 NOT NULL 열을 추가하는 경우입니다 . SQLite는 이를 직접 허용하지 않으므로 임시 테이블을 생성하고 임시 기본값을 설정한 다음 데이터를 이동하고 원래 테이블을 삭제한 후 임시 테이블의 이름을 최종 테이블 이름으로 변경해야 합니다.
안정성 확보: 테스트 및 회로도
마이그레이션을 프로덕션 환경에 배포하기 전에 반드시 테스트하십시오. Room은 ` room-testing` 아티팩트 와 `MigrationTestHelper` 클래스를 제공합니다. 이를 사용하면 이전 버전에서 데이터베이스를 생성하고 실제 데이터를 삽입한 다음 `runMigrationsAndValidate()`를 실행하여 최종 스키마가 예상대로인지, 데이터가 여전히 존재하는지 확인할 수 있습니다.
이 모든 것이 제대로 작동하려면 스키마의 JSON 파일을 관리하는 것이 필수적입니다. Gradle 파일에서 ` room.schemaLocation`을 구성하면 Room이 버전 기록을 생성합니다. 이 파일은 절대적인 진실이며, JSON 파일의 `createSql` 필드는 수동 마이그레이션에서 오류를 방지하기 위해 정확히 복제해야 하는 내용입니다.
순수 SQLite에서 Room으로의 전환
기존 방식대로 SQLite를 사용하다가 Room으로 전환하는 것은 매우 유익합니다. 전환 과정은 모델을 `@Entity` 로 변경하고 , 수동 쿼리를 대체할 DAO를 정의하고, RoomDatabase를 상속하는 클래스를 생성하는 것으로 구성됩니다. Room이 제어권을 가져올 때 기존 데이터를 삭제하지 않도록 데이터베이스 버전을 높이고 마이그레이션 경로를 정의하는 것이 중요합니다. 마이그레이션 경로가 비어 있더라도 반드시 정의해야 합니다.
JSON 내보내기 및 정확한 SQL 작성부터 계측 테스트를 통한 유효성 검사에 이르기까지 스키마 수명 주기를 완벽하게 이해하면 사용자 경험을 저해하거나 기기에서 중요한 데이터를 손실할 염려 없이 애플리케이션을 발전시킬 수 있습니다. 더 많은 사람들이 이 정보를 활용할 수 있도록 공유해 주세요.