Room Database를 활용해서 내장DB를 자유자재로
👌 SQLite와 Room의 차이점
- SQLite는 저수준 데이터베이스 솔루션으로, 직접적인 데이터 베이스 작업과 SQL쿼리를 통해 작동한다.
- Room은 SQLite의 상위 레벨 추상화를 제공하며, SQL쿼리를 직접 작성하는 대신 애노테이션과 DAO를 통해 데이터베이스와 상호작용 할 수 있게 해준다.
👌 Room의 장점
- 컴파일 타임 검증 : Room은 SQL쿼리를 컴파일 시점에 검증하여 실행 시간 오류를 줄여준다.
- 간결한 데이터베이스 작업 : Room은 데이터베이스 작업을 더 간결하고 직관적으로 만들어준다.
- LiveData와의 통합 : Room은 LiveData와 같은 다른 Android Architecture Components와 쉽게 통합되어 UI와 데이트베이스 상태의 동기화를 간소화한다.
- 자동 마이그레이션 도구 : Room은 데이트베이스 스키마 변경 시 자동으로 마이그레이션을 지원하는 도구를 제공한다.
👌 Room의 기본 구조
1. Entity
- Entity는 Room에서 데이터베이스의 테이블을 나타내는 클래스이다. 각 Entity 클래스는 데이터베이스 내의 테이블과 매핑되며, 클래스 내의 필드는 테이블의 컬럼에 해당한다.
2. DAO
- DAO는 데이터베이스에 접근하기 위한 메서드들을 포함하는 인터페이스 또는 추상 클래스이다. 이를 통해 애플리케이션이 데이터베이스에 저장된 데이터를 쿼리, 업데이트, 삭제, 삽입할 수 있다.
3. Database
- Database는 데이터베이스 홀더를 포함하며, 앱의 지속적인 데이터와의 주 연결점이다. 이 클래스는 데이터베이스를 구성하고, 버전 관리를 제공한다.
📜 Room 사용 방법
매우 간단한 예제로 Room의 사용법을 살짝 알아볼까요?
EditText에 입력한 값을 저장, 가져오기, 삭제하는 예제를 만들어 봅시다.
build.gradle (:app)
plugins {
id "org.jetbrains.kotlin.kapt"
}
implementation("androidx.room:room-runtime:2.5.1")
annotationProcessor("androidx.room:room-compiler:2.5.1")
implementation "androidx.room:room-ktx:2.5.1"
kapt "androidx.room:room-compiler:2.5.1"
물론 위 코드에서 Room버전은 최신버전이 아닐 수 있으므로 자세한건 공식 문서를 참고바랍니다.
MyEntity.kt
@Entity
data class MyEntity (
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val text: String
)
MyDao.kt
@Dao
interface MyDao {
@Insert
suspend fun insert(myEntity: MyEntity)
@Query("SELECT * FROM MyEntity")
suspend fun getAll(): List<MyEntity>
@Query("DELETE FROM MyEntity")
suspend fun deleteAll()
}
MyAppDatabase.kt
@Database(entities = [MyEntity::class], version = 1)
abstract class MyAppDatabase :RoomDatabase(){
abstract fun myDao(): MyDao
companion object {
@Volatile
private var INSTANCE: MyAppDatabase? = null
fun getDatabase(context: Context): MyAppDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
MyAppDatabase::class.java,
"my-database"
).build()
INSTANCE = instance
instance
}
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:id="@+id/edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<Button
android:id="@+id/saveButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:text="저장하기"/>
<TextView
android:id="@+id/view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/edit"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:id="@+id/getButon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/saveButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="10dp"
android:text="가져오기"/>
<Button
android:id="@+id/deleteButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/getButon"
app:layout_constraintStart_toStartOf="@id/getButon"
app:layout_constraintEnd_toEndOf="@id/getButon"
android:layout_marginTop="10dp"
android:text="삭제하기"/>
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
private lateinit var database: MyAppDatabase
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
database = MyAppDatabase.getDatabase(this)
binding.saveButton.setOnClickListener {
val text = binding.edit.text.toString()
if (text.isNotEmpty()) {
saveText(text)
}
}
binding.getButon.setOnClickListener {
loadTexts()
}
binding.deleteButton.setOnClickListener {
deleteTexts()
}
}
private fun saveText(text:String){
CoroutineScope(Dispatchers.IO).launch {
database.myDao().insert(MyEntity(text=text))
binding.edit.text = null
}
}
private fun loadTexts() {
CoroutineScope(Dispatchers.IO).launch {
val texts = database.myDao().getAll().joinToString("\n") { it.text }
withContext(Dispatchers.Main) {
binding.view.text = texts
}
}
}
private fun deleteTexts(){
CoroutineScope(Dispatchers.IO).launch {
database.myDao().deleteAll()
loadTexts()
}
}
}