Room ๊ฒ์๊ธ ์๋ฆฌ์ฆ
#1 ์ด์ ๊ธ
[Android] Room - ๊ธฐ์ด, INSERT์ DELETE ์ฐ์ต
#1 Room ์๊ฐ Room์ ์ฌ์ฉํ์ฌ ๋ก์ปฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ดํฐ ์ ์ฅ | Android Developers Room ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋ ์ฝ๊ฒ ๋ฐ์ดํฐ๋ฅผ ์ ์งํ๋ ๋ฐฉ๋ฒ ์์๋ณด๊ธฐ developer.android.com SQLite๋ ๋ชจ๋ฐ์ผ ๊ธฐ๊ธฐ๋ฅผ ์ํ
kenel.tistory.com
๋ณธ ๊ฒ์๊ธ์ Room์ ๊ธฐ๋ณธ์ ๋ด์ ์ฑ์ ๋ง๋ค๊ธฐ ์ํ ์ด์ ๊ฒ์๊ธ์์ ์ด์ด์ง๋ค. ์ฌ๊ธฐ์ Room์ 3๊ฐ์ง ํต์ฌ ํด๋์ค์ธ Entity ํด๋์ค, DAO ํด๋์ค, Database ํด๋์ค์ ๊ตฌํ์ ๋ค๋ฃฌ๋ค. ์ด์ ๊ฒ์๊ธ์์ ๋ชจ๋ ์์ค build.gradle.kts์์ ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ค์ด๋ก๋ํด์ผ ๋ณธ ๊ฒ์๊ธ์ ์งํํ ์ ์๋ค.
#2 Entity, DAO, (Room) Database์ ๊ด๊ณ

Room Database๋ ์์ ๊ณผ ์ฐ๊ฒฐ๋ DAO ์ธ์คํด์ค๋ฅผ Application์ ์ ๊ณตํ๋ค. ๊ทธ๋ฌ๋ฉด Application์์ DAO๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ดํฐ์ ๊ตฌ๋ถ ๋จ์์ธ Entity์ ์ธ์คํด์ค๋ฅผ ์ฐธ์กฐํ ์ ์๊ฒ ๋๋ค. ์ฆ, INSERT, UPDATE, DELETE ๋ช
๋ น์ ์ํํ ์ ์๋ค๋ ๋ง์ด๋ค.
#3 Database์์ ์ฌ์ฉํ ํ ์ด๋ธ (Entity ๋์์ธํ๊ธฐ)
user_id | user_name | user_email |
... | ... | ... |
user_id๊ฐ ๊ธฐ๋ณธํค์ด๊ณ ์ด 3๊ฐ์ Column์ ๊ฐ์ง๋ ํ
์ด๋ธ์ด๋ค.
#4 Entity ํด๋์ค ์์ฑํ๊ธฐ (User.kt)
// package com.example.roombasics.db
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "user_data_table")
data class User(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "user_id")
val id: Int,
@ColumnInfo(name = "user_name")
var name: String,
@ColumnInfo(name = "user_email")
var email: String
)
#3์ ์ ๋ณด๋ฅผ ํ ๋๋ก Room Entity ํด๋์ค๋ฅผ ๋ง๋ค๋ฉด ์ ์ฝ๋์ ๊ฐ๋ค. ๊ธฐ๋ณธํค์ธ id๋ ํ๋ฒ ํ ๋น๋๋ฉด ์ดํ๋ก ๋ฐ๊ฟ ์ผ์ด ์์ผ๋ฏ๋ก val ๋ณ์๋ก ๋๋ค. ๋ฐ๋ฉด, name์ด๋ email์ ๊ฒฝ์ฐ๋ UPDATE ์์ ๋ณ๊ฒฝ๋ ์ฌ์ง๊ฐ ์๋ค. ๋ฐ๋ผ์ var ๋ณ์๋ก ์ค์ ํ๋ค.
@PrimaryKey์ autoGenerate ์์ฑ์ด true๋ก ์ค์ ๋์ด์์ผ๋ฉด, ํด๋น Entity์ ๊ธฐ๋ณธํค๋ก์์ id๊ฐ์ ๋ช
์ํ์ฌ INSERTํ์ง ์์๋ ์์์ id๊ฐ์ ์์ฑํ๋ค.
#5 DAO(Database Access Object Interface) ํด๋์ค (UserDAO.kt)
#5-1 ์ฝ๋
// package com.example.roombasics.db
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
@Dao
interface UserDAO {
@Insert
suspend fun insertUser(user: User): Long
@Insert(onConflict = OnConflictStrategy.REPLACE) // ์ถฉ๋ ์ ๋์ ์ ์
suspend fun insertUser2(user: User): Long
@Insert
suspend fun insertUser2(user: List<User>): List<Long> // ๋ฉ์๋ ์ค๋ฒ๋ก๋ฉ
@Insert
suspend fun insertUser3(user: List<User>): Array<Long>
@Update
suspend fun updateUser(user: User)
@Delete
suspend fun deleteUser(user: User)
@Query("INSERT INTO user_data_table (user_name, user_email) VALUES (:name, :email)")
suspend fun insertUser4(name: String, email: String): Long
@Query("DELETE FROM user_data_table")
suspend fun deleteAll()
@Query("SELECT * FROM user_data_table")
fun getAllUsers(): LiveData<List<User>>
}
#4์์ ๊ตฌ์ฑํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์ด๋ธ์ ์ ๊ทผํ๊ธฐ ์ํ ์ธํฐํ์ด์ค๋ค. Room์ Annotation์ ์๋ณํด ์๋ํ๊ธฐ ๋๋ฌธ์, ์ค์ ํจ์ ์ด๋ฆ์ Room์ ๋์์ ์ํฅ์ ๋ฏธ์น์ง ์๋๋ค. ์๋ฅผ ๋ค์ด, @Insert ์ด๋
ธํ
์ด์
์ด ๋ถ์ insertUser() ํจ์ ์ด๋ฆ์ abcdefg()์ ๊ฐ์ด ์๋ฌด๋ ๊ฒ๋ ๋ฐ๊ฟ๋ abcdefg() ํจ์๋ ๊ณ์ @Insert์ ๋์์ ์ํํ๋ค. ๋, ์ ์ฝ๋์์ ๋ณด๋ฏ @Insert๋ฅผ ๋ฉ์๋ ์ค๋ฒ๋ก๋ฉํ ์๋ ์๋ค. ์ค๋ฒ๋ก๋ฉ๋ ํจ์๋ List<User>๋ฅผ ๋งค๊ฐ๋ณ์๋ก ์ ๋ฌ๋ฐ๋๋ฐ, ์ด๋ฌํ ํํ๋ ์ฌ๋ฌ ๊ฐ์ Entity๋ฅผ Insertํ ๋ ์ฌ์ฉ๋๋ค.
๋, ํจ์๋ค์ suspend ํค์๋๊ฐ ๋ถ์ ์ด์ ๊ฐ ์๋ค (suspend ํค์๋๊ฐ ๋ถ์ง ์์ getAllUsers()๋ #5-6์์ ์ค๋ช
ํจ). Room์ Main ์ค๋ ๋์์ Database๋ก์ ์ ๊ทผ์ ์ง์ํ์ง ์๋๋ค. ํด๋น ์์
์ด Main ์ค๋ ๋์ ์ฃผ์ ์ญํ ์ธ UI ๊ฐฑ์ ์ Blockํ ์๋ ์๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋์ ์ฐ๋ฆฌ๋ ์ด ํจ์๋ฅผ Background์์ ๋๋ ค์ผ ํ๋ค ์ฆ, ์ฝ๋ฃจํด์ ์ด์ฉํด ๋ฉํฐ์ค๋ ๋ฉ์ ๊ตฌํํด์ผ ํ๋ค.
#5-2 @Insert ์ด๋ ธํ ์ด์
Insert๋ setter์ ๋น์ทํ ๋๋์ด์ง๋ง, return์ด ์กด์ฌํ ์ ์๋ค. ๊ฐ๋ น, Insertํ User์ id๊ฐ์ ๋ฐํํ๋ ์์ผ๋ก ๋ง์ด๋ค. ์ด ๊ฒฝ์ฐ ๋ฐํํ์ Room์ ์ค๊ณ ์ Long ๋๋ List<Long>์ด์ด์ผ ํ๋ค๊ณ ํ๋ค. List<Long>์ ๊ฒฝ์ฐ 2๊ฐ ์ด์์ Entity๋ฅผ ๋ฃ์ ๊ฒฝ์ฐ์ ๋ฐํํ์ด๋ค. return์ ์๋ต(Unitํ)ํด๋ ๋๋ค. ๋ฌผ๋ก ์ด๋ฌ๋ฉด Insertํ User์ id๊ฐ์ ์ฐธ์กฐํ ์ ์์ ๊ฒ์ด๋ค.
OnConflictStrategy | Android Developers
androidx.appsearch.builtintypes.properties
developer.android.com
@Insert์ @Update ์ด๋
ธํ
์ด์
์๋ onConfilct๋ผ๋ ์์ฑ์ด ์๋ค. ์ด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฌด๊ฒฐ์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํ ์์ฑ์ผ๋ก, ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ด๋ค ์ถฉ๋์ด ์ผ์ด๋ฌ์ ๋์ ๋์์ ์ ์ํ๋ค. ์ด ์์ฑ์ OnConflictStrategy.REPLACE๋ฅผ ํ ๋นํ๋ฉด, (๊ธฐ๋ณธํค ๋น๊ต๋ก) ์ด๋ฏธ ์กด์ฌํ๋ Entity๋ฅผ ๋ฃ์์ ๋, ์๋์ Entity๋ฅผ ์ญ์ ํ๊ณ ์๋ก ๋ฃ์ Entity๊ฐ ๋์ ๋ค์ด๊ฐ๋ค. OnConflictStrategy.IGNORE๋ ์ถฉ๋์ ๋ฌด์ํ๋ค. ์ฆ, ์๋ก ๋ฃ์ Entity๋ ๋ฌด์๋๊ณ ์๋ ์๋ Entity๋ ๊ณ์ ์ด์์๊ฒ ๋๋ค. ๊ทธ๋ฆฌ๊ณ -1์ด ๋ฐํ๋๋ค. OnConflictStrategy.ABORT๋ ์ถฉ๋์ด ์ผ์ด๋๋ฉด Transaction์ Rollbackํ๋ค.
#5-3 @Update ์ด๋ ธํ ์ด์
@Insert์ ์ค๋ช
์์์ ๋ง์ฐฌ๊ฐ์ง๋ก, List๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ 2๊ฐ ์ด์์ Entity๋ฅผ ํ ๋ฒ์ Updateํ ์๋ ์๊ณ Update๊ฐ ์ํ๋ Entity์ ๊ธฐ๋ณธํค ๊ฐ์ return๋ฐ์ ์ ์๋ค.
#5-4 @Delete ์ด๋ ธํ ์ด์
@Insert์ ์ค๋ช
์์์ ๋ง์ฐฌ๊ฐ์ง๋ก, List๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ 2๊ฐ ์ด์์ Entity๋ฅผ ํ ๋ฒ์ Deleteํ ์๋ ์๊ณ Delete๊ฐ ์ํ๋ Entity์ ๊ธฐ๋ณธํค ๊ฐ์ return๋ฐ์ ์ ์๋ค.
#5-5 @Query ์ด๋ ธํ ์ด์
#5-1 ~ #5-3์ SQLite์ Query๋ฌธ์ ๊ฐํธํ ์ธ ์ ์๊ฒ ๊ฐ๊ณต๋ ํจ์๋ค์ ๋์ด์ด์๋ค. ์๋ฅผ ๋ค์ด, @Insert ์ด๋
ธํ
์ด์
์ด ๋ฌ๋ฆฐ InsertUser()๋ "INSERT INTO user_data_table (user_name, user_email) VALUES (:name, :email)"๋ผ๋ Query๋ฌธ์ ์ํํ๋ InsertUser4()์ ๊ทธ ๋์์ด ๊ฐ๋ค. ์ฆ, ๋ณต์กํ๊ณ ๋ฒ๊ฑฐ๋ก์ธ ์ ์๋ ๊ธฐ๋ณธ ๋์์ @Insert๋ผ๋ ์ด๋
ธํ
์ด์
์ผ๋ก ํ์น ๊ฒ์ด๋ค.
@Query ์ด๋
ธํ
์ด์
์ ๊ฐ๊ณต๋์ง ์์ ๋ ๊ฒ์ Query๋ฌธ ์์ฒด๋ฅผ ์๋ฏธํ๋ค. ๋ฐ๋ผ์ ์กฐ๊ธ ๋ ์ง์ฝ์ ์ธ, ์กฐ๊ธ ๋ ๊ธฐ๋ณธ์ ์ด์ง ์์ ๋์์ @Query ์ด๋
ธํ
์ด์
์ ์ ์ํ๋ค. ์ ์ฝ๋์์๋ ํ
์ด๋ธ์ ์ญ์ ํจ์ผ๋ก์จ ๊ทธ ์์ ๋ด๊ธด ๋ชจ๋ Entity๋ฅผ Deleteํ๋ ๋์ ๋ฑ์ ์ ์ํ๋ค.
#5-6 LiveData์ suspend ํค์๋์ ์๋ต
getAllUsers()์ฒ๋ผ ๋ฐํํ์ด LiveData๋ผ๋ฉด, suspend ํค์๋๋ฅผ ์๋ตํ๋ค. ์๋ตํ์ง ์์ผ๋ฉด ์ปดํ์ผ ์๋ฌ๊ฐ ๋๋๋ฐ, ๊ทธ ๋ด์ฉ์ ์๋์ ๊ฐ๋ค.
Dao functions that have a suspend modifier must not return a deferred/async type (androidx.lifecycle.LiveData). Most probably this is an error. Consider changing the return type or removing the suspend modifier.
์๋ฌ ๋ฉ์์ง์ ๋ด์ฉ์ ํ ๋ง๋๋ก, LiveData๋ฅผ ๋ฐํํ๋ ํจ์์ suspend ํค์๋๋ฅผ ๋ฌ์ง ๋ง๋ผ๋ ๊ฒ์ด๋ค. ์๋ํ๋ฉด, LiveData๋ ๋ฐ์ดํฐ๊ฐ ์๋๋ผ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ด๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ๋ฐ์ดํฐ์ ์ฐ์์ ์ธ ํ๋ฆ์ ๋ํ๋ด๋ ๊ฐ๋
์ด๋ค. LiveData๋ผ๋ ๊ฐ์ฒด ์์ฒด๊ฐ ์ด๋ฏธ ๊ทธ ์์ฒด๋ก ๋น๋๊ธฐ์ (๋ณ๋ ฌ์ )์ธ ๋์์ ๋
๋ฆฝ์ ์ผ๋ก ๋ด์ฌํ๊ณ ์๊ธฐ ๋๋ฌธ์, ํจ์์ suspend๋ฅผ ๋ถ์ผ ํ์๊ฐ ์๋ ๊ฒ์ด๋ค.
#6 Room ๋ฐ์ดํฐ๋ฒ ์ด์ค ํด๋์ค (UserDatabse.kt)
// package com.example.roombasics.db
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(entities = [User::class], version = 1)
abstract class UserDatabase : RoomDatabase() {
abstract val userDAO: UserDAO
companion object {
/* @Volatile ์ด๋
ธํ
์ด์
์ ํด๋์ค์ ์ด๋ค ํ๋กํผํฐ(ํ๋)๊ฐ Update๋์ ๋,
* ๊ทธ ๊ฐฑ์ ๋ ๊ฐ์ ๋ค๋ฅธ ์ค๋ ๋์์ ๋ฐ๋ก ์ฝ๊ฒ(= ์บ์๊ฐ ์๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฝ๊ฒ) ํ๋ค.
* ์ด ์ด๋
ธํ
์ด์
์ด ์๋ค๋ฉด, ๋ค๋ฅธ ์ค๋ ๋์์ ๊ฐฑ์ ์ด์ ์ ๊ฐ์ผ๋ก ์๋ชป ์ฝ์ ์๋ ์๋ค.
* ์บ์์๋ ์์ฐจ๊ฐ ์กด์ฌํ๊ธฐ ๋๋ฌธ์ด๋ค.
*/
@Volatile
private var INSTANCE: UserDatabase? = null
fun getInstance(context: Context): UserDatabase {
synchronized(this) {
var instance = INSTANCE
if (instance == null) {
instance = Room.databaseBuilder(
context.applicationContext,
UserDatabase::class.java,
"user_data_database"
).build()
INSTANCE = instance
}
return instance
}
}
}
}
Entity ํด๋์ค์ ํด๋น Entity๋ฅผ ๋ค๋ฃจ๋ DAO ํด๋์ค๋ฅผ ๊ตฌํํ๋ค. ๋จ์ ๊ฒ์ Application์ ํ์ฌ๋ ์ค์ Database๋ฅผ ๋๋ณํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํด๋์ค๋ฅผ ๊ตฌํํด๋ณธ๋ค. ๋จผ์ @Database ์ด๋
ธํ
์ด์
๊ณผ ํจ๊ป, ํด๋น ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ฌ์ฉํ Entity ํด๋์ค ๊ทธ๋ฆฌ๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฒ์ ์ ์ ๋๋ค. ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฒ์ ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ ์ง๋ณด์ํ๋ฉด์ ํ๋์ฉ ์ฌ๋ ค๊ฐ๋ ์ซ์๋ค. ์ง๊ธ์ ์ฒ์ ๋ง๋ค์์ผ๋ฏ๋ก 1์ด๋ผ๊ณ ์ ์๋ค. ํ๋กํผํฐ๋ก DAO๋ ์ถ๊ฐํ๋ค. DAO๋ ์ด Database ํด๋์ค์ ์ ๊ทผํ๊ธฐ ์ํ ์ง์
๋ฌธ ์ญํ ์ ํ๋ค.
companion object { ... }๋ฅผ ๋ณด๋ฉด ์ด Database ํด๋์ค๋ฅผ Singleton ํจํด์ผ๋ก ๊ตฌํํ๊ณ ์์์ ์ ์ ์๋ค. Database ๊ฐ์ฒด๋ ๊ตณ์ด ํ๋ ์ด์ ๋ง๋ค์ด์ง ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ด์ ๋ฌธ์ฅ์ ์ํค๋ฐฑ๊ณผ ๋งํฌ์์, ์ฝํ๋ฆฐ์์์ ์ฑ๊ธํค ํจํด ์์๊ฐ object ํค์๋๋ฅผ ์ด์ฉํด ๊ตฌํํ ์ฝ๋๋ก ์ ํ์๋ค. ํ์ง๋ง, @Database๋ abstract class๋ก object๊ฐ ์๋๋ค. ๋ฐ๋ผ์ companion object { ... }๋ฅผ ์ด์ฉํด ์ฑ๊ธํค ํจํด์ ๊ตฌํํ๋ค. object ํค์๋ ๋ฒ์ ์ ๋นํด ๋ ๋ณต์กํ์ง๋ง, ์ด์ฉ ์ ์๋ค.
์ ์ฝ๋๋ Room์ @Database๋ฅผ ๊ตฌํํ๋ ๋ค๋ฅธ ๋ง์ ํ๋ก์ ํธ๋ค๋ ๋๊ฐ์ด ์ฌ์ฉ๋๋ค๊ณ ํ๋ค. ๋ณต์ฌ/๋ถ์ฌ๋ฃ๊ธฐ๋ฅผ ์์ฃผํ๊ฒ ๋ ์ฝ๋๋ก ๋ณด์ธ๋ค.
#7 ์์ฝ
Entity๋ ๋ฐ์ดํฐ, Database๋ ๋ง ๊ทธ๋๋ก Base, DAO๋ ๊ทธ ๋์ ์๋ ๊ด๋ฌธ์ด๋ค.
#8 ๋ค์ ์ด์ ๊ธ๋ก
[Android] Room - ๊ธฐ์ด, INSERT์ DELETE ์ฐ์ต
#1 Room ์๊ฐ Room์ ์ฌ์ฉํ์ฌ ๋ก์ปฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ดํฐ ์ ์ฅ | Android Developers Room ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋ ์ฝ๊ฒ ๋ฐ์ดํฐ๋ฅผ ์ ์งํ๋ ๋ฐฉ๋ฒ ์์๋ณด๊ธฐ developer.android.com SQLite๋ ๋ชจ๋ฐ์ผ ๊ธฐ๊ธฐ๋ฅผ ์ํ
kenel.tistory.com
๋ค์ ์ด์ ๊ธ๋ก ๋์๊ฐ ๋๋จธ์ง ๋ถ๋ถ์ ๊ตฌํํด๋๊ฐ๋ค.
'๊นจ์ ๊ฐ๋ ๐ > Android' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Android] ViewModel - View์ Event ๋ฐ์์ํค๊ธฐ (0) | 2024.02.27 |
---|---|
[Android] Room - UPDATE ์ฐ์ต (0) | 2024.02.26 |
์ฐธ[Android] Room - ๊ธฐ์ด, INSERT์ DELETE ์ฐ์ต (0) | 2024.02.23 |
[Kotlin] Coroutines - LiveData Builder (0) | 2024.02.22 |
[Kotlin] Coroutines - LifecycleScope (0) | 2024.02.21 |