[Android / Kotlin] Room

Subeen·2024년 1월 23일
0

Android

목록 보기
47/73

Room

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

Room 주요 3요소

  • Room 주요 3요소
    • @Database : 클래스를 데이터베이스로 지정하는 annotation, RoomDatabase를 상속 받은 클래스여야 함
      • Room.databaseBuilder를 이용하면 인스턴스를 생성함
    • @Entity : 클래스를 테이블 스키마로 지정하는 annotation
    • @Dao : 클래스를 DAO(Data Access Object)로 지정하는 annotation
      • 기본적인 intert, delete, update SQL은 자동으로 만들어주며, 복잡함 SQL은 직접 만들 수 있음

gradle 파일 설정

  • Room은 안드로이드 아키텍처에 포함되어 있음
  • 사용하기 위해 build.gradle 파일의 dependencies에 아래 내용을 추가해야 함
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 생성

Room은 데이터베이스 내부의 각 항목을 프로그램에서 사용하기 위해 Object Relational Mapping 이라는 과정을 통해서 객체로 변환을 하는데, 이 변환을 위해 Entity를 정의해서 사용하게 된다.

  • Entity는 테이블 스키마 정의
  • CREATE TABLE student_table (student_id INTEGER PRIMARY KEY, name TEXT NOT NULL);
  • @Entity data class Student
  • PRIMARY KEY : 중복 되지 않는 유니크한 값
@Entity(tableName = "student_table") // 테이블 이름
data class Student (
	@PrimaryKey
    @ColumnInfo(name = "student_id")
    val id: Int,
    val name: String
)

DAO 생성

데이터베이스는 쿼리를 가능한 사용하지 않고, DAO를 써서 간접적으로 조작하게 된다.

  • DAO는 interface나 abstract class로 정의 되어야 함
  • Annotation에 SQL 쿼리를 정의하고 그 쿼리를 위한 메소드를 선언
  • 가능한 annotation으로 @Insert, @Update, @Delete, @Query가 있음
@Query("SELECT * from table") fun getAllData() : List<Data>
  • @Insert, @Update, @Delete는 SQL 쿼리를 작성하지 않아도 컴파일러가 자동으로 생성함
    • @Insert나 @Update는 key가 중복 되는 경우 처리를 위해 onConflict를 지정할 수 있음
      • OnConflictStrategy.ABORT : key 충돌시 종료
      • OnConflictStrategy.IGNORE : key 충돌 무시
      • OnConflictStrategy.REPLACE : key 충돌시 새로운 데이터로 변경
    • @Update나 @Delete는 Primary key에 해당되는 튜플을 찾아서 변경/삭제 함
  • @Query로 리턴되는 데이터 타입을 LiveData<>로 하면, 나중에 이 데이터가 업데이트 될 때 Observer를 통해 할 수 있음
@Query("SELECT * from table") fun getAllData() : LiveData<List<Data>>
  • @Query에 SQL을 정의할 때 메소드의 인자를 사용할 수 있음
@Query("SELECT * FROM student_table WHERE name = :sname")
suspend fun getStudentByName(sname: String): List<Studnet>
  • suspend : Kotlin coroutine을 사용하는 것이며 나중에 이 메소드를 호출할 때는 runBlocking {} 내에서 호출해야 함
  • LiveData는 비동기적으로 동작하기 때문에 coroutine으로 할 필요가 없음

Database 생성

데이터베이스 객체를 만들어서 엔티티에 대한 다오를 이 객체가 주관하도록 한다. 이런 구조를 구축함으로써 사용자가 데이터베이스 파일에 직접 접근하지 않고 SQL Query 사용을 최소화 하면서 조작 그자체를 추상화 할 수 있따.

  • RoomDatabase를 상속하여 자신의 Room 클래스를 만들어야 함
  • 포함되는 Entity들과 데이터베이스 버전을 @Database annotation에 지정함
  • DAO를 가져올 수 있는 getter 메소드를 만듬
  • Room 클래스의 인스턴스는 하나만 있으면 되므로 Singleton 패턴을 사용
  • Room 클래스의 객체 생성은 Room.databaseBuilder()를 이용함

UI와 연결

  • 안드로이드 아키텍쳐에 따라 Repository와 ViewModel을 사용하는 것을 권장한다.
  • RoomDatabase 객체에게 DAO 객체를 받아오고, 이 DAO 객체의 메소드를 호출하여 데이터베이스를 접근함
myDao = MyDatabase.getDatabase(this).getMyDao()
runBlocking {
	myDao.insertStudent(Student(1, "james"))
}
val allStudents = myDao.getAllStudents()    
  

UI와 연결 - LiveData

  • LiveData<> 타입으로 리턴되는 DAO 메소드의 경우
    • observe() 메소드를 이용하여 Observer를 지정
    • 데이터가 변경될 때마다 자동으로 Observer의 onChanged()가 호출됨
val allStudents = myDao.getAllStudents()
allStudents.observe(this) {   // Observer::onChanged() 는 SAM 이기 때문에 lambda로 대체
    val str = StringBuilder().apply {
            for ((id, name) in it) {
                append(id)
                append("-")
                append(name)
                append("\n")
            }
        }.toString()
    binding.textStudentList.text = str
}
profile
개발 공부 기록 🌱

0개의 댓글