[번역] Room auto-migrations

2021. 6. 2. 10:08

Room 2.4.0-alpha01 버전부터 지원하는 오토 마이그레이션 기능을 이용해 Room DB 마이그레이션이 좀 더 간편해졌다. 지금까지는 DB 스키마가 변경될 때마다 Migration 클래스를 구현하고 변경 사항을 Room에 정확히 명시해주어야 했다. 대부분의 경우에 이는 복잡한 SQL 쿼리의 작성과 실행에 연관되어 있었다.

 

오토 마이그레이션 기능을 이용하면, 어떤 버전에서 어떤 버전으로 마이그레이션을 원하는지 명시해주기만 하면 Room이 자동으로 컬럼 추가/삭제, 테이블 추가와 같은 간단한 마이그레이션을 진행한다. 좀 더 애매하고 복잡한 경우는 사용자가 구체적인 사항을 명시해주어 마이그레이션을 진행한다. (ex. 컬럼/테이블 이름 변경)

간단한 변경사항 마이그레이션

테이블에 새로운 컬럼을 추가하는 등의 간단한 변경사항으로 인해 버전이 변경되는 경우, 아래와 같이 @Database 어노테이션의 버전을 증가시키고 auto-migrations에 마이그레이션 될 버전 정보를 입력해준다.

@Database(
    version = 2,
    entities = [ Doggos.class ],
    autoMigrations = [
        AutoMigration (from = 1, to = 2)
    ]
)
abstract class DoggosDatabase : RoomDatabase { }

 

DB 버전이 업데이트 될 때마다 AutoMigration을 추가해주면 간단하게 마이그레이션이 가능하다.

@Database(
    version = 3,
    entities = [ Doggos.class ],
    autoMigrations = [
       AutoMigration (from = 1, to = 2),
       AutoMigration (from = 2, to = 3)
    ]
)
abstract class DoggosDatabase : RoomDatabase { }

 

복잡한 변경사항 마이그레이션

테이블 또는 컬럼의 이름이 변경되는 경우 Room은 이름이 변경된건지 삭제된건지 판단하지 못한다. 이러한 경우가 발생했을 때 Room은 컴파일 에러를 발생시키며 사용자에게 AutoMigrationSpec의 구현을 요구한다. AutoMigrationSpec 클래스는 아래 어노테이션 중 하나 이상을 사용하여 구현할 수 있다.

  • @DeleteTable(tableName)
  • @RenameTable(fromTableName, toTableName)
  • @DeleteColumn(tableName, columnName)
  • @RenameColumn(tableName, fromColumnName, toColumnName)

예를 들어, Doggos 테이블을 GoodDoggos로 변경하는 경우 아래와 같이 명시할 수 있다.

@Database(
    version = 2,
    entities = [ GoodDoggos.class ],
    autoMigrations = [
        AutoMigration (
            from = 1, 
            to = 2,
            spec = DoggosDatabase.DoggosAutoMigration::class
        )
    ]
)
abstract class DoggosDatabase : RoomDatabase {   
    @RenameTable(fromTableName = "Doggos", toTableName = "GoodDoggos")
    class DoggosAutoMigration: AutoMigrationSpec {   }
}

 

Migrations vs. Auto-migrations

수동적으로 DB를 마이그레이션 하는 경우, DB 스키마의 변경이 있을 때마다 Room에서 제공하는 Migration 클래스를 이용했었다. 한 테이블을 2개의 다른 테이블로 나눈다고 했을 때, 아래와 같이 테이블을 어떻게 나눌지, 어떤 데이터가 옮겨질지를 정의한 뒤 addMigrations() 함수를 통해 databaseBuilder()에 추가해야 한다.

val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(database: SupportSQLiteDatabase) {
        ...
    }
}

Room.databaseBuilder(applicationContext, DoggosDatabase::class.java, "doggos-database")
    .addMigrations(MIGRATION_1_2,)
    .build()

 

Migrations와 Auto-migrations의 조합

Room은 Migrations와 Auto-migrations의 혼용을 허용한다. 예를 들어, 버전 1에서 2는 Migration을 이용하고 버전 2에서 3은 Auto-migration을 이용할 수 있다. 만약 동일한 버전에 대해 Migration과 Auto-migration이 정의되어 있으면 Migration이 수행된다.

 

내부적으로 Auto-migration은 Migration 클래스를 구성하고 있으므로, 동일한 마이그레이션 로직이 적용된다. DB에 최초 접근 시, Room은 현재 DB 버전과 @Database에 명시된 버전이 동일한지 확인한다. 버전이 다른 경우 해당 버전으로의 마이그레이션 경로를 찾아 연속적으로 마이그레이션이 수행된다.

테스트

Migrations 또는 Auto-migrations를 테스트하기 위해서는 MigrationTestHelper 테스트 룰을 사용하여 helper.runMigrationsAndValidate() 함수를 호출한다. (자세히 보기)

결론

Auto-migration 기능은 DB 스키마 변경에 쉽게 대응할 수 있도록 도와준다. @Database 내부에 autoMigration 파라미터를 추가하여 대부분의 기본적인 변경사항을 Room 자체적으로 핸들링할 수 있다. 테이블, 컬럼의 삭제나 리네임의 경우 AutoMigrationSpec을 구현해야 하며 나머지 경우는 기존의 Migrations을 사용해야 한다.

 

 

원문보기: Room auto-migrations

안드로이드 시스템 아키텍처

Android Open Source Project(AOSP)의 시스템 아키텍처는 다음과 같은 요소들의 계층 구조로 이루어져 있다.

   

어플리케이션 프레임워크

API를 통해 제공되는 안드로이드의 전체 feature-set이다. 어플리케이션 개발자가 주로 사용하는 API를 말한다.

ActivityManager, WindowManager, ContentProvider 등의 시스템 앱 및 서비스들이 여기에 포함된다.

 

사용자 어플리케이션 수준에서는 단순 API 호출로 보이지만, HAL 인터페이스에 직접 매핑되는 API들이 다수 존재한다.

바인더 IPC

바인더 IPC(Inter Process Communication)는 어플리케이션 프레임워크와 시스템 서비스를 연결해주는 인터페이스로, IPC를 통해 어플리케이션 레벨에서 시스템 서비스가 제공하는 기능을 사용할 수 있게 해준다.

하드웨어 추상화 계층(HAL: Hardware Abstraction Layer)

HAL은 하드웨어들을 연동하기 위한 표준 인터페이스이다. 하드웨어 공급업체들에게 이 인터페이스의 구현을 강제함으로써 모델별로 달라질 수 있는 제조업체의 하위 수준 소프트웨어를 안드로이드 프레임워크로부터 분리할 수 있다.

 

HAL은 여러 개의 라이브러리 모듈로 구성되어 있다. 각 모듈은 하드웨어 타입(ex. camera, bluetooth)에 맞는 인터페이스를 구현하고 있으며, 프레임워크 API가 호출되면 안드로이드 시스템은 해당 모듈을 로드하여 기능을 사용할 수 있게 해준다.

 

리눅스 커널

안드로이드 런타임(ART)은 리눅스 커널 기반으로 동작한다. 안드로이드에서 사용하는 커널에는 모바일 환경을 위한 LMK(Low Memory Killer), Wake lock 등의 몇몇 추가 기능이 포함되어 있다.

 

리눅스 커널을 사용하는 이점으로는 우선 안정성이 보장되고, 잘 알려진 커널이기 때문에 하드웨어 공급업체들이 하드웨어 드라이버를 개발하기 용이하다는 점을 들 수 있다.

참고

  1. Android Architecture
  2. Platform Architecture

안드로이드의 화면은 아래와 같은 단위로 구성된다. 화면을 구성하는 최소 단위는 View이며 최대 단위는 Window이다.

Window > Surface > Canvas > View

Window

Window는 화면 구성의 가장 상위 요소로 무언가를 그릴 수 있는 화면상의 사각 영역을 말한다.

하나의 화면 안에는 여러 개의 Window가 존재할 수 있고, 각각의 Window는 용도에 따라 고유한 타입을 가진다. 이들은 WindowManager에 의해 관리된다.

어플리케이션은 WindowManager와 상호작용하여 Window를 만들 수 있다. WindowManager는 각각의 Window에 Surface를 만들어 어플리케이션에 전달하고, 어플리케이션은 이를 통해 화면을 렌더링한다.

Window는 터치 이벤트, 키 이벤트 등 사용자 이벤트를 받아서 처리할 수 있다.

Surface

Surface는 화면에 합성되는 픽셀을 보유한 객체이다.

화면에 보여지는 모든 윈도우(스테이터스 바, 다이얼로그, 액티비티 등)는 자신만의 Surface를 가지고 있으며, Surface Flinger가 각 Surface의 픽셀들을 Z-order에 따라 합성하여 실제 화면에 렌더링하는 역할을 맡고 있다.

Surface는 렌더링시에 더블 버퍼링 방식을 이용하기 위해 일반적으로 두개의 버퍼를 가지고 있다.

Canvas

Canvas는 모든 드로잉 메서드를 포함하고 있는 클래스이다. 각종 도형, 선 등을 그리기 위한 모든 로직이 Canvas 내에 포함되어 있다. Canvas는 Bitmap 또는 OpenGL 컨테이너 위에 그려진다.

View

View는 Window 내에 존재하는 인터랙티브한 UI 요소를 말한다. (Button, TextView 등)

화면이 그려지는 방식

Surface가 잠겨있는 상태

Surface의 Canvas를 가져와 그리기에 사용한다. 계층 구조에 따라 View로 Canvas를 전달(onDraw)하여 각 View에 해당하는 UI를 그려나간다.

Canvas 잠금 해제 및 Post

모든 View가 버퍼에 그려지면 Surface가 잠금 해제되면서 현재 버퍼와 포그라운드 버퍼가 스왑되고 Surface Flinger에 의해 화면에 합성된다.

View가 그려지는 방식

View는 포커스를 얻으면 레이아웃을 그리도록 요청한다. 이 때, 레이아웃의 루트 노드를 제공해야 한다. 그리기는 루트 노드로부터 자식 노드 방향으로 트리를 따라 전위 순회 방식으로 그려진다. 레이아웃을 그리는 과정은 아래와 같다.

measure() -> onMeasure() -> layout() -> onLayout() -> draw() -> onDraw()

measure, onMeasure

View의 크기를 측정하기 위해 호출된다. 측정 과정에서 부모와 자식 View 간의 크기 정보를 정의하기 위해 아래 두 가지의 클래스를 사용한다.

  1. ViewGroup.LayoutParams
  2. 자식 View가 어떻게 측정될지를 요청하는데 사용된다.
    ViewGroup의 하위 클래스에 따라 다른 속성의 LayoutParams가 존재할 수 있다. (ex. LinearLayout.LayoutParams)
  3. ViewGroup.MeasureSpec
    • UNSPECIFIED: 자식 View가 원하는 대로 크기를 결정
    • EXACTLY: 부모 View가 자식 View의 크기를 결정
    • AT_MOST: 지정된 크기까지 자식 View가 원하는 대로 크기를 결정
  4. 부모 View가 자식 View에게 요구사항을 전달하는데 사용된다.

layout, onLayout

View의 크기와 위치를 할당하기 위해 호출된다.

draw, onDraw

실제로 View를 그리는 단계이다. 전달받은 Canvas 객체와 Paint 객체를 이용하여 필요한 모양과 색을 그린다.

참고

  1. Android - Window, View, Surface, Canvas, Bitmap
  2. [안드로이드] Window, Surface, Canvas, View
  3. [Android] View 함수

'개발 > Android' 카테고리의 다른 글

[번역] Room auto-migrations  (0) 2021.06.02
안드로이드 시스템 아키텍처  (0) 2021.04.24
안드로이드 아키텍처: MVVM  (0) 2021.04.05
안드로이드 아키텍처: MVP  (0) 2021.03.03
안드로이드 아키텍처: MVC  (0) 2021.02.24

+ Recent posts