로컬 디바이스에 데이터를 저장하는 방식을 SQLite를 사용하고 있다가 요즘 유명한 AAC인 Room
을 공부해보려고 합니다.
Room
은 SQLite를 내부적으로 사용하고 있어 기능적으로 동일하지만 데이터베이스를 구조적으로 분리하여 편리한 데이터 접근과 유지 보수의 유연성을 높여줍니다. 하지만 어노테이션
을 사용하여 관계를 정의하므로 안드로이드에 익숙하지 않다면 복잡하게 느껴질 수도 있습니다.
들어가며
이 포스팅은로 필자의 개인 프로젝트에서 사용자의 계정을 저장하는 Account Database를 Room으로 구현하는 방법에 대해 필요한 코드만 간추려서 작성한 글입니다. 구현하고자 하는 기능, 요구에 따라 구조가 달라 질 수 있으니 참고정도로만 생각해주시면 감사하겠습니다.
build gradle
dependencies {
def room_version = "2.2.5"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor
// Test helpers
testImplementation "androidx.room:room-testing:$room_version"
}
관계형 Table 정의
@Entity(tableName = "account_table")
data class Account(
@PrimaryKey(autoGenerate = true) val id : Int,
@ColumnInfo(name = "site") val site : String,
@ColumnInfo(name = "user_id") var userId : String,
@ColumnInfo(name = "user_pwd") var userPwd : String
)
Room
에서는 class로 table을 명시할 수 있습니다. 기본적으로 SQLite에서는 Create Table Sql 문법으로 Table을 생성하고 데이터를 목록으로 보여주기 위해서 별도의 Data Class를 생성했었는 데, Room
에서는 두 가지 기능을 모두 제공해주는 것이죠.
@Entity
를 사용하여 이 클래스가 table이라는 것을 명시한다. 기본적으로 클래스 이름으로 table 이름이 결정되고 다르게 하고 싶다면tableName
속성으로 변경이 가능하다.@PrimaryKey
속성으로 기본 키를 지정한다. Table에 값이 저장될 때마다 자동으로 증가되는 Id 값이라면autoGenerate
속성을 사용한다.- 클래스의 멤버 변수들은 모두 Table의
colum
이 되고 이름도 변수의 이름과 동일하게 결정된다. 다르게 하고 싶다면@ColumInfo
의name
속성으로 변경하면 된다. Room
에서 멤버 변수에 접근할 수 있게public
으로 접근지정자가 구성되어야 합니다. private일 경우, getter와 setter가 정의되어야 합니다.
composite primary key 구성
@Entity(primaryKeys = arrayOf("firstName", "lastName"))
data class User(
val firstName: String?,
val lastName: String?
)
@Entity
의 primaryKeys
속성을 사용해서 복합 키도 정의할 수 있습니다.
실제로 데이터베이스를 접근하는 DAO 클래스 정의
@Dao
interface AccountDao {
@Query("SELECT * from account_table")
fun getAlphabetizedWords(): LiveData<List<Account>>
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(account: Account)
@Query("DELETE FROM account_table")
suspend fun deleteAll()
}
@Dao
를 사용하여 데이터 베이스의 접근하는 메소드로 구성되어 있는 DAO
(Data Access Object) 클래스를 정의합니다. 사용자가 Database
에 직접 접근하는 것이 아니기 때문에 안전하고, 분리되어 있어 앱 테스트에도 좋습니다.
@Insert
,@Update
,@Delete
를 사용하여 sql query문을 사용하지 않아도 간편하게 데이터를 처리할 수 있습니다. 대신 함수의 매개변수는Entity
로 구성되어 있어야 합니다.@Insert
할 때, 중복된 값이 있을 때 처리하는 방법을 정의하는 것이onConflict
속성입니다. 코드에서는 중복된 값이 존재하면 무시하라고 명시합니다.- 복잡한 query가 필요하다면
@Query
를 사용하여 정의하면 됩니다.
@Delete와 @Update
@Update
fun updateUsers(vararg users: User)
@Delete
fun deleteUsers(vararg users: User)
vararg
키워드를 이용해 여러개의 Entity
을 처리할 수 있습니다.
데이터베이스 정의
@Database(entities = arrayOf(Account::class), version = 1, exportSchema = false)
public abstract class AccountDB : RoomDatabase(){
abstract fun accountDao(): AccountDao
companion object {
// Singleton prevents multiple instances of database opening at the
// same time.
@Volatile
private var INSTANCE: AccountDB? = null
fun getDatabase(
context: Context
): AccountDB {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
AccountDB::class.java,
"word_database"
).build()
INSTANCE = instance
return instance
}
}
}
}
데이터베이스의 정의는 거의 동일합니다. @Database
로 Database 클래스임을 명시하며,RoomDatabase을 상속받아 abstract
로 선언되어야 하며 abstract
의 Dao를 가지고 있습니다.
Room 객체는 많은 리소스를 소모하기 때문에 Singleton
패턴으로 정의합니다.
DB에 접근하는 방법
val accountsDao = AccountDB.getDatabase(application).accountDao()
val allAccounts: LiveData<List<Account>> = accountDao.getAlphabetizedWords()
참고
'Android > Common' 카테고리의 다른 글
[Android] Executor - 기능 별 Thread 분리 (0) | 2020.07.17 |
---|---|
[Android] MVC 적용하기 (0) | 2020.07.07 |
[Android] Google Calendar API 사용법 (4) | 2020.05.02 |
[Android] 바인딩 서비스로 음악 재생 (0) | 2020.04.29 |
[Android] DataBinding 정리 (0) | 2020.04.25 |