[Android, Room] Room Database 스터디 기록 (1)

 

https://developer.android.com/training/data-storage/room?hl=ko#components 

 

Room을 사용하여 로컬 데이터베이스에 데이터 저장  |  Android 개발자  |  Android Developers

Room 라이브러리를 사용하여 더 쉽게 데이터를 유지하는 방법 알아보기

developer.android.com

 

Room

Room 지속성 라이브러리를 활용해서 SQLite를 통해 원활한 데이터베이스 엑세스 방법을 배운다.

큰 규모의 구조화 된 데이터를 처리하는 앱을 room을 활용해서 데이터를 로컬에 유지하는 방법으로 큰 이점을 얻을 수 있다.

 

가장 큰 이점은 기기가 네트워크에 엑세스할 수 없을 때도 사용자가 오프라인 상태로

계속 콘텐츠를 탐색할 수 있도록 관련 데이터를 캐시하는 것이다.

 

정식 명칭은 Room Persistence library이며, ORM(Object Relational Mapping)으로서,

SQLite 데이터베이스를 사용하기 쉽도록 데이터베이스를 객체로 매핑해 주는 역할을 한다.

Gradle

앱에서 Room을 사용하기 위해서 build.gradle 파일에 아래와 같은 종속 항목을 추가한다.

    // room
    def room_version = "2.5.1"
    implementation "androidx.room:room-runtime:$room_version"
    implementation "androidx.room:room-ktx:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    implementation "androidx.room:room-paging:$room_version"
    testImplementation "androidx.room:room-testing:$room_version"

 

 

Room의 Components

 

Room에는 3가지 주요 컴포넌트가 존재한다.

Room 라이브러리 아키텍처 다이어그램

데이터베이스 클래스는 데이터베이스와 연결된 DAO 인스턴스를 앱에 제공한다.

이 과정으로 앱은 DAO를 사용하여 데이터베이스의 데이터를 연결된 데이터 항목 객체의 인스턴스로 검색할 수 있게 된다.

앱은 정의된 데이터 항목을 사용하여 상응하는 테이블의 행을 업데이트하거나 삽입할 새 행을 만들 수도 있다.

 

이러한 과정에서 3가지 컴포넌트가 상호작용 한다.

 

1. Entity

Room에서의 Entity 컴포넌트는 관계형 데이터베이스에서의 Table 역할을 한다.

데이터가 들어있는 각 한 줄을 Row로 정의하며, 형태를 가지는 데이터이다.

 

2. Database

DBMS의 엑세스 포인트이며, 데이터베이스 홀더이다.

위의 Entity들이 묶인 것이 Database이며 학교 전산 시스템의 Entity로 예를 들면,

teacher, student, parent, admin 등의 Entity들을 통틀어서 Database로 정의한다.

 

Room에서는 Database 클래스를 상속받는 abstract Class로 정의한다.

이는 Entity의 리스트를 가지게 된다.

 

3. Dao

Database 내의 Entity 들의 Row 목록에 저장 된 데이터를 접근하기 위한 컴포넌트이다.

Data Access Objects의 줄임말이며, data에 엑세스하는 객체이다.

엑세스에 필요로 하는 메소드들을 가지고 있게 구성해서 사용하며,

앱에서 데이터를 얻어오거나 쓰기 위해서 이 클래스의 함수들을 사용하게 된다.

 

 

Sample 구현하기

데이터 항목과 DAO를 활용한 Room 데이터베이스의 구현을 통해서 학습한다.

우선 data 항목으로 Student Class를 생성한다.

 

Data Class

Room에서 사용하는 Data클래스는 반드시 @Entity annotation을 붙여 주어야 한다.

이는 data 클래스를 이용해서 테이블과 매핑하는 과정이며, 관계형 DB의 핵심인 @PrimaryKey 어노테이션도 반드시 활용해야 한다.

 

@Entity(tableName = "students")
class Student (
    @PrimaryKey(autoGenerate = true) val studentId : Int,
    @ColumnInfo(name = "first_name") val firstName : String?,
    @ColumnInfo(name = "last_name") val lastName : String?,
    @ColumnInfo(name = "isTrue") val isTrue : Boolean
)

 

여기서 주의할 점은 primary key의 auto-generate는 Room Entity의 데이터클래스로 객체를 생성할 때 설정된 값이,

이 객체에 설정 된 id값으로 DB에 바로 반영되지 않는다.

 

즉 Insert 메소드를 실행할 때 auto - generate되며,

id 부분을 바로 사용해야 한다면 Insert 되고 난 시점에 사용해 주어야 한다.

 

Room은 관계형 데이터베이스와 같은 방식을 사용하므로 foreign Key를 활용한 방법도 사용할 수 있다.

 

// foreignKey 활용
class Teacher()

@Entity(
        foreignKeys = [ForeignKey(entity = Teacher::class,
        parentColumns = arrayOf("teacherId"),
            childColumns = arrayOf("teacherId"))]
)
class Student2(
    @PrimaryKey(autoGenerate = true) val studentId : Int,
    @ColumnInfo(name = "first_name") val firstName : String?,
    @ColumnInfo(name = "last_name") val lastName : String?,
    @ColumnInfo(name = "teacherId") val teacherId : Int
)

 

 

Dao

데이터 클래스를 생성하였으니, 이제 DB에 엑세스하는 메소드를 선언해야 한다.

SQL 쿼리를 데이터에 접근할 때마다 수정하기는 번거롭기 때문에, 미리 함수에 저장해 두었다가 사용하는 방식이다.

 

Query 어노테이션에 괄호를 열고 SQL문을 작성하는 형식을 사용한다.

Insert, Update, Delete, Query 어노테이션으로 CRUD 방식을 구현하며, vararg를 활용해서 kotlin의 가변인자를 다룰 수 있다.

 

주의할 점은 Room은 SQLite 기반으로 다루기 때문에, true, false를 1,0으로 구분한다.

 

// true : 1 , false : 0으로 표현
const val SQL_TRUE = 1
const val SQL_FALSE = 0

 

정렬 순서를 결정한다면, ACS와 DESC를 활용할 수 있다.

 

// 정렬 순서 설정
// SELECT * FROM chat_messages ORDER BY createdAt DESC

 

 

student 데이터를 처리하기 위한 다양한 Dao 쿼리를 작성했다.

 

@Dao
interface StudentDao{
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertAll(vararg students : Student)

    @Query(value = "SELECT * FROM students")
    fun getAll() : List<Student>

    @Query(value = "SELECT * FROM students WHERE studentId IN (:studentIds)")
    fun loadAllByIds(studentIds: IntArray) : List<Student>

    @Query(value = "SELECT * FROM students WHERE first_name LIKE :first And " +
            "last_name LIKE : last LIMIT 1")
    fun findByName(first : String, last : String) : Student

    @Query(value = "SELECT * FROM students WHERE score BETWEEN :min AND :max")
    fun findStudentsBetweenScores(min:Int, max:Int)

    @Update
    fun update(student:Student)

    @Delete
    fun delete(student:Student)

}

 

이제 Room을 사용하기 위한 2가지 컴포넌트의 준비를 마쳤다.

다음 포스팅을 통해서 데이터베이스를 구현하기 위한 AppDatabase 클래스 정의와 사용 방법을 공부해야 겠다.