안드로이드/개발관련(Kotlin)

안드로이드 데이터베이스(Database) Room 사용하기

닉네임못짓는사람 2023. 4. 2. 02:38
반응형

이번 글에선 안드로이드 로컬 DB인 Room을 사용하는 방법에 대해서 알아보도록 하자.

 

ROOM


안드로이드를 사용하다보면 데이터를 로컬에 저장해서 사용해야 할 때가 있다.

이런 때를 위해서 안드로이드에서는 로컬에 데이터를 저장할 수 있는 DB를 지원하고 있다.

예전에는 SQLite를 사용했었는데, 최근에는 안드로이드 문서에서도 Room사용을 적극 권장하고 있다.

 

Room을 사용하기 위해선 기본적으로 세 가지가 필요한데,

  1. Entity
  2. Dao
  3. Database

위 세 가지를 각각 정의해주어야 Room사용이 가능하다.

 

일단 Room을 사용하기 위해서 dependencies를 추가해주어야 하는데,

안드로이드 스튜디오 Gradle에서 아래 코드를 넣어주자

    def room_version = "2.5.0"

    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"

다음으로 위에서 말한 세 가지 요소들을 각각 만들어보도록 하자.

 

Entity


먼저 Entity는 우리가 mysql등에서 익히 알고있는 Table과 동일하다고 생각하면 된다.

Room에서는 이러한 Entity에 Column을 각각 지정해주어서 data class로 정의해주어야 한다.

@Entity("user")
data class UserEntity (
    val name: String,
    val age: String,
    val sex: String,
    val address: String,
    val phoneNumber: String
) {
    @PrimaryKey(true)
    var id: Int = 0
}

이 글에선 유저의 정보를 기록하는 UserEntity를 만들어보도록 하자.

여기서 @Entity에 들어가는 argument는 Table의 이름이라고 생각하면 된다.

 

또한 Entity정의시 PrimaryKey를 지정해주어야 하는데, 여기선 id를 PrimaryKey로 지정해주도록 하자.

@PrimaryKey에 argument를 true로 지정해줌으로써 autoIncrement가 활성화 된다.

 

DAO


다음으로 Dao는 해당 테이블에 사용할 수 있는 명령어(Query)들을 함수로 가지는 interface이다.

우리가 주로 사용할 Query들을 이곳에 미리 정의해서 편리하게 사용할 수 있다.

@Dao
interface UserDao {
    @Insert
    fun insertUser(user: UserEntity)

    @Query("SELECT * FROM user")
    fun getAllUser(): List<UserEntity>

    @Query("SELECT * FROM user WHERE name = :name")
    fun getUserByName(name: String): List<UserEntity>

    @Update
    fun updateUser(user: UserEntity)

    @Delete
    fun deleteUser(user: UserEntity)

    @Query("DELETE FROM user")
    fun deleteAllUser()
}

Insert, Select, Update, Delete명령들을 함수로 정의해놓았다.

또한 Query는 argment로 Query명령어를 String으로 주어야 하는데, 이때 parameter를 사용하고 싶을 경우 앞에 콜론(:)을 붙인다.

 

Database


마지막으로 Database를 정의해주도록 하자.

@Database(entities = [UserEntity::class], version = 1)
abstract class LocalDatabase: RoomDatabase() {
    abstract fun getUserDao(): UserDao
}

@Database의 argument로는 이 DB에서 사용할 Entity(Table)의 목록과 version을 넣어주면 된다.

또한 abstract fun으로 해당 Entity들의 Dao를 반환해주는 함수들을 추가해주도록 한다.

 

이렇게 세 가지 요소들을 모두 정의해주었으면 사전 준비는 모두 끝났다고 볼 수 있다.

여기서 Room사용시 편리성을 위해서 DB와 Dao를 Hilt를 사용해서 받아오도록 해보자.

 

Inject


DB와 Dao를 Inject를 사용해 쉽게 사용하기 위해서 RoomModule을 정의해주도록 하자.

@Module
@InstallIn(SingletonComponent::class)
class RoomModule {
    @Provides
    @Singleton
    fun provideLocalDatabase(@ApplicationContext context: Context): LocalDatabase =
        Room.databaseBuilder(context, LocalDatabase::class.java, "local_database").build()

    @Provides
    @Singleton
    fun provideUserDao(userDatabase: LocalDatabase): UserDao = userDatabase.getUserDao()
}

 

여기까지 했으면 실제로 사용해보도록 하자.

 

MainActivity


이 글에선 간단하게 EditText들로부터 Text를 받아와서 DB에 Insert해준 후,

이 데이터들을 RecyclerView에서 보여주는 작업을 수행하도록 해보겠다.

 

RecyclerView의 Adapter와 ViewHolder에 대한 설명은 생략하도록 하겠다.

궁금하신 분들은 아래 링크에서 Adapter와 ViewHolder를 만드는 법에 대해 확인해보길 바란다.

https://angangmoddi.tistory.com/320

 

안드로이드 리사이클러뷰(RecyclerView) 사용하기

영어 한국어 일본어 중국어 (간체) 중국어 (번체) 베트남어 인도네시아어 태국어 독일어 러시아어 스페인어 이탈리아어 프강스어 복사하기 이 확장을 지원합니다 ㅇ 이번 글에서는 RecyclerView를

angangmoddi.tistory.com

 

Adpater

class UserAdapter(
    private var userList: ArrayList<UserEntity>
): Adapter<UserViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
        val binding = ItemUserBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return UserViewHolder(binding)
    }

    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
        val item = userList[position]

        holder.binding.run {
            name.text = item.name
            age.text = item.age
            sex.text = item.sex
            address.text = item.address
            phoneNumber.text = item.phoneNumber
        }
    }

    override fun getItemCount(): Int = userList.count()
}

ViewHolder

class UserViewHolder(val binding: ItemUserBinding): ViewHolder(binding.root) 

item_user.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:orientation="horizontal"
    tools:background="@color/cardview_dark_background">

    <TextView
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="10dp"
        android:layout_marginVertical="20dp"
        android:layout_weight="1"
        android:textColor="@color/white"
        android:textSize="15dp"
        tools:text="name" />

    <TextView
        android:id="@+id/age"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="10dp"
        android:layout_weight="1"
        android:textColor="@color/white"
        android:textSize="15dp"
        tools:text="age" />

    <TextView
        android:id="@+id/sex"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="10dp"
        android:layout_weight="1"
        android:textColor="@color/white"
        android:textSize="15dp"
        tools:text="sex" />

    <TextView
        android:id="@+id/address"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="10dp"
        android:layout_weight="1"
        android:textColor="@color/white"
        android:textSize="15dp"
        tools:text="add" />

    <TextView
        android:id="@+id/phone_number"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="10dp"
        android:layout_weight="1"
        android:textColor="@color/white"
        android:textSize="15dp"
        tools:text="phone" />

</LinearLayout>

MainActivity.kt

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private val userList = ArrayList<UserEntity>()
    private lateinit var adapter: UserAdapter
    @Inject lateinit var userDao: UserDao

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        initList()

        binding.addButton.setOnClickListener {
            insertUser()
        }
    }

    private fun initList() {
        CoroutineScope(Dispatchers.Main).launch {
            val list = withContext(Dispatchers.IO) {
                userDao.getAllUser()
            }
            userList.addAll(list)
            adapter = UserAdapter(userList)
            binding.recyclerView.adapter = adapter
        }
    }

    private fun insertUser() {
        CoroutineScope(Dispatchers.Main).launch {
            if (binding.name.text.isNullOrEmpty()) return@launch
            val user = UserEntity(
                binding.name.text.toString(),
                binding.age.text.toString(),
                binding.sex.text.toString(),
                binding.address.text.toString(),
                binding.phoneNumber.text.toString()
            )
            withContext(Dispatchers.IO) { userDao.insertUser(user) }
            userList.add(user)
            adapter.notifyItemInserted(userList.lastIndex)
        }
    }
}

main_activity.xml

<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="10dp"
            android:layout_weight="1"
            android:hint="name"
            android:textSize="16dp" />

        <EditText
            android:id="@+id/age"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="10dp"
            android:layout_weight="1"
            android:hint="age"
            android:inputType="number"
            android:textSize="16dp" />

        <EditText
            android:id="@+id/sex"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="10dp"
            android:layout_weight="1"
            android:hint="sex"
            android:textSize="16dp" />

        <EditText
            android:id="@+id/address"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="10dp"
            android:layout_weight="1"
            android:hint="address"
            android:textSize="16dp" />

        <EditText
            android:id="@+id/phone_number"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="10dp"
            android:layout_weight="1"
            android:hint="phone_number"
            android:textSize="16dp" />

    </LinearLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:background="@color/cardview_dark_background"
        android:orientation="vertical"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:listitem="@layout/item_user" />

    <Button
        android:id="@+id/add_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="30dp"
        android:text="add" />

</LinearLayout>

 

이 예제에선 Dao에 정의한 Insert, Select, Update, Delete중 Insert와 Select만 사용했다.

나머지 Update와 Delete는 직접 해보시는걸 추천한다.

 

DB확인


또한 안드로이드 스튜디오에서 DB를 확인해 볼 수도 있다.

View -> Tool Windows -> App Inspection-> Database Inspecor

반응형