[Android 앱 개발 심화] 2. Room

Room 개요

  • SQLite를 쉽게 사용할 수 있는 데이터베이스 객체 매핑 라이브러리
  • 쉽게 Query를 사용할 수 있는 API 제공
  • Query를 컴파일 시간에 검증
  • Query 결과를 LiveData로 하여 데이터베이스가 변경될 때마다 쉽게 UI 변경 가능

Room의 주요 3요소

  • (1) Database
    • RoomDatabase를 상속하는 클래스
    • @Database: 클래스를 데이터베이스로 지정하는 애노테이션
    • Room.databaseBuilder를 이용하여 인스턴스 생성
  • (2) Entity
    • @Entity: 클래스를 테이블 스키마로 지장하는 애노테이션
  • (3) Dao
    • @Dao: 클래스를 DAO(Data Access Object)로 지정하는 애노테이션
    • 기본적인 insert, delete, update SQL query 자동으로 만들어줌
    • 복잡한 SQL query 직접 만들 수 있음

gradle 파일 설정

  • Room은 안드로이드 아키텍쳐에 포함되어 있음
plugins {
	//...
    id 'kotlin-kapt'
}
//...
dependencies {
	//...
    def room_version = "2.5.1"
    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    // optional - Kotlin Extensions and Coroutines support for Room
    implementation "androidx.room:room-ktx:$room_version"
    // optional - Test helpers
    testImplementation "androidx.room:room-testing:$room_version"
}

Entity 생성

  • 테이블 스키마 정의
//CREATE TABLE student_table (student_id INTEGER PRIMARY KEY, name TEXT NOT NULL);
@Entity(tableName = "student_table")    student_table로 지정함
data class Student (
    @PrimaryKey 
	@ColumnInfo(name = "student_id") 
    val id: Int,
    val name: String
)

DAO 생성

  • interface나 abstract class로 정의
  • 애노테이션에 SQL query를 정의하고, 그 query를 위한 메소드 선언
    • 사용 가능한 애노테이션: @Insert, @Update, @Delete, @Query
    • @Insert, @Update, @Delete는 SQL 쿼리를 작성하지 않아도 컴파일러가 자동 생성
  • @Insert나 @Update는 key가 중복되는 경우 처리를 위해 onConflict 지정 가능
    • OnConflictStrategy.ABORT: key 충돌시 종료
    • OnConflictStrategy.IGNORE: key 충돌 무시
    • OnConflictStrategy.REPLACE: key 충돌시 새로운 데이터로 변경
  • @Query에 메소드 인자 사용 가능
  • 리턴되는 데이터 타입을 LiveData로 하여, 데이터가 업데이트될 때 Observer로 관찰
    (LiveData는 비동기적으로 동작 -> Kotlin coroutine으로 할 필요 X)
@Dao
interface MyDAO {
    @Insert(onConflict = OnConflictStrategy.REPLACE)  
    suspend fun insertStudent(student: Student)

    @Query("SELECT * FROM student_table")
    fun getAllStudents(): LiveData<List<Student>>     

    @Query("SELECT * FROM student_table WHERE name = :sname")   
    suspend fun getStudentByName(sname: String): List<Student>

    @Delete
    suspend fun deleteStudent(student: Student)
    // ...
}

Database 생성

  • RoomDatabase를 상속하는 클래스 생성

    • DAO를 가져올 수 있는 getter 메소드 만들기
      (실제 메소드 정의는 자동으로 생성됨)
    • SingleTon 패턴 사용
      (Room 클래스의 객체 생성은 Room.databaseBuilder() 호출)
  • @Database 애노테이션에 포함되는 Entity들데이터베이스의 version 저장

    • 애노테이션의 데이터베이스 version이 기존에 저장되어있는 데이터베이스의 version보다 높은 경우, 데이터베이스를 open할 때, migration 수행
      • addMigrations() 메소드를 호출하여 Migration 방법 지정
      • 여러 개의 Migration 지정 가능
@Database(entities = [
					Student::class, 
					ClassInfo::class, 
                    Enrollment::class, 
                    Teacher::class
                    ], 
          version = 1)
abstract class MyDatabase : RoomDatabase() {
    //DAO를 가져올 수 있는 getter 메소드
    //(실제 메소드 정의는 자동으로 생성됨)
    abstract fun getMyDao() : MyDAO 

    companion object {
        private var INSTANCE: MyDatabase? = null
        
        private val MIGRATION_1_2 = object : Migration(1, 2) {
            override fun migrate(database: SupportSQLiteDatabase) {
        		database.execSQL("ALTER TABLE student_table ADD COLUMN last_update INTEGER")
    		}
        }

        private val MIGRATION_2_3 = object : Migration(2, 3) {
            override fun migrate(database: SupportSQLiteDatabase) {
        		database.execSQL("ALTER TABLE class_table ADD COLUMN last_update INTEGER")
    		}
        }
        
        //SingleTon 패턴 사용
  		//(Room 클래스의 객체 생성은 Room.databaseBuilder() 호출)
        fun getDatabase(context: Context) : MyDatabase {
            if (INSTANCE == null) {
                INSTANCE = Room.databaseBuilder(
                    context, MyDatabase::class.java, "school_database")
                    .addMigrations(MIGRATION_1_2, MIGRATION_2_3)
                    .build()
            }
            return INSTANCE as MyDatabase
        }
    }
}

UI와 연결

  • 안드로이드 아키텍쳐에 따라 Repository와 ViewModel 사용 권장
profile
Be able to be vulnerable, in search of truth

0개의 댓글