Sam Story

룸 (Room) 본문

Android

룸 (Room)

Sam H 2024. 4. 16. 12:24

 

1. 룸 (Room) 이란 ?

Room은 SQLite의 DB에 편하게 접근할 수 있도록 해주는 AAC에 속한 라이브러리다.

 

SQLite에 비해 Room을 사용했을 때 얻을 수 있는 이점이 많다.

 

불필요한 코드들을 줄일 수 있다는 점 그리고

 

LiveData 등과 함께 사용해 데이터를 Observation 할 수 있다는 점 등 장점이 많다.

 

현재 Google은 SQLite 대신 Room 사용을 권장하고 있다.

 

※ SQLite 란 ?

 

간단하게 말하면 클라이언트 내부에서 사용하는 데이터베이스다.

 

 

 

2. 룸 (Room)의 구성요소

 

● 데이터베이스 클래스 (AppDatabase)

 

데이터 베이스를 보유하고 앱의 데이터와 연결해주는 클래스

 

 

● 데이터 항목 (Entities)

 

앱 데이터베이스의 테이블을 나타냄

 

 

● 데이터 액세스 객체 (DAO)

 

앱이 데이터베이스의 데이터를 query , Update , Insert , Delete 를 할 수 있는 메서드를 제공

 

 

공식문서에 있는 도식화된 그림으로 살펴보면 아래와 같다.

 

출처 : 안드로이드 공식

 

 

 

3. 예제

 

오늘의 예제는 이름,나이,취미를 리사이클러뷰에 추가해주는 간단한 예제다.

 

기본적인 SQL 문에 대한 이해도가 필요한 예제이다.

 

 

먼저 공식문서에서 가져온 Room 관련 dependency이다.

 

앱에서 Room을 사용하려면 앱의 build.gradle 파일에 다음 종속 항목을 추가해준다.

dependencies {
    def room_version = "2.6.1"

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

    // To use Kotlin annotation processing tool (kapt)
    kapt "androidx.room:room-compiler:$room_version"
    // To use Kotlin Symbol Processing (KSP)
    ksp "androidx.room:room-compiler:$room_version"

    // optional - RxJava2 support for Room
    implementation "androidx.room:room-rxjava2:$room_version"

    // optional - RxJava3 support for Room
    implementation "androidx.room:room-rxjava3:$room_version"

    // optional - Guava support for Room, including Optional and ListenableFuture
    implementation "androidx.room:room-guava:$room_version"

    // optional - Test helpers
    testImplementation "androidx.room:room-testing:$room_version"

    // optional - Paging 3 Integration
    implementation "androidx.room:room-paging:$room_version"
}

 

 

 

User 클래스 ( 데이터 항목 (Entities) )

package com.example.room_aac

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity
data class User(

    @PrimaryKey val num: Int,
    @ColumnInfo(name = "name") val name: String?,
    @ColumnInfo(name = "age") val age: String?,
    @ColumnInfo(name = "hobby") val hobby: String?

)

 

데이터 클래스로 사용되며 각 칼럼들에 대한 값들이 할당되어 있는 클래스다.

 

 

 

DAO interface

package com.example.room_aac

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query

@Dao
interface UserDao {

    @Query("SELECT * FROM user")
    fun getAll(): List<User>

    @Query("SELECT * FROM user WHERE num IN (:userIds)")
    fun loadAllByIds(userIds: IntArray): List<User>

    @Query("SELECT * FROM user WHERE name LIKE :name AND " + "age LIKE :age AND " + "hobby Like :hobby LIMIT 1")
    fun findByName(name: String, age: String, hobby: String): User

    @Insert
    fun insertAll(vararg users: User)

    @Delete
    fun delete(user: User)

}

 

DAO 인터페이스에 있는 메서드들에 각각 어노테이션으로 SQL 문을 사용할 수 있다.

 

 

 

데이터 베이스 클래스 (AppDatabase)

package com.example.room_aac

import androidx.room.Database
import androidx.room.RoomDatabase

@Database(entities = [User::class], version = 1)
abstract class AppDatabase: RoomDatabase() {

    abstract fun userDao(): UserDao

}

 

데이터베이스를 보유하고 있는 클래스다

 

 

 

MainActivity

package com.example.room_aac

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import androidx.room.Room
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch


class MainActivity : AppCompatActivity() {

    val TAG = "메인 액티비티"

    lateinit var button: Button
    lateinit var recyclerView: RecyclerView
    lateinit var editTextName: EditText
    lateinit var editTextAge: EditText
    lateinit var editTextHobby: EditText

    lateinit var mAdapter: MAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        button = findViewById(R.id.button)
        recyclerView = findViewById(R.id.recyclerView)
        editTextName = findViewById(R.id.editTextName)
        editTextAge = findViewById(R.id.editTextAge)
        editTextHobby = findViewById(R.id.editTextHobby)

        mAdapter = MAdapter()

        // Database 초기화
        val db = Room.databaseBuilder(
            applicationContext,
            AppDatabase::class.java, "database-name"
        ).fallbackToDestructiveMigration()
            .build()

        // userDao 초기화
        val userDao = db.userDao()


        // Database 데이터 불러오기
        Thread {

            var userList = userDao.getAll()

            mAdapter.userList.addAll(userList)
            recyclerView.adapter = mAdapter

        }.start()

        button.setOnClickListener {

            Thread{

                val user = User(mAdapter.userList.size,editTextName.text.toString(),editTextAge.text.toString(),editTextHobby.text.toString())
                userDao.insertAll(user)

                Handler(Looper.getMainLooper()).post {

                    mAdapter.userList.add(user)
                    mAdapter.notifyDataSetChanged()
                    Toast.makeText(this,"성공적으로 추가했습니다",Toast.LENGTH_SHORT).show()

                }

            }.start()

        }

    }

}

 

메인 액티비티 코드쪽에서 한가지 특이한 점을 볼 수 있는데

 

데이터베이스 관련 코드들을 메인스레드가 아닌

 

별도의 스레드를 사용해 코드를 작성하였는데 그이유는

 

안드로이드에서는 메인 스레드(UI 스레드)에서는

 

오랜 시간이 걸리는 작업을 수행하지 않도록 권장하고 있다.


따라서 데이터베이스 액세스와 같이 오랜 시간이 걸리는 작업은

 

백그라운드 스레드에서 처리해야 하기 때문이다.

 

 

 

MainActivity 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:gravity="center"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_marginHorizontal="10dp">

        <EditText
            android:id="@+id/editTextName"
            android:hint="이름"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

        <EditText
            android:id="@+id/editTextAge"
            android:hint="나이"
            android:inputType="number"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

        <EditText
            android:id="@+id/editTextHobby"
            android:hint="취미"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>


    </LinearLayout>

    <Button
        android:id="@+id/button"
        android:text="추가"
        android:layout_margin="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>

 

 

※ 어댑터 클래스와 아이템뷰 레이아웃은 생략하겠다.

- 깃허브에 어댑터 클래스 코드와 아이템뷰 레이아웃 코드 참고

 

리사이클러뷰에 대한 내용이 부족하다면 

 

 

리사이클러뷰(RecyclerView)

1. 안드로이드 리사이클러뷰 "큰 데이터 집합에 대한 제한된 창을 제공하기 위한 유연한 보기" 안드로이드 공식문서에 있는 리사이클러뷰에 대한 정의를 번역한 내용이다. 간단한 설명을 덧붙이

samtistory.tistory.com

 

위에 포스팅을 통해 공부해보도록 하자.

 

 

위 순서대로 작성하고 코드를 실행해보게 되면

 

 

 

실행결과

실행결과

 

최초 실행할 때에는 데이터베이스에 데이터가 없기 때문에

 

리사이클러뷰에 아무것도 표시되지 않는다.

 

 

이제 EditText에 값을 추가한 후 결과를 보자.

 

추가버튼을 누르게되면 간단한 토스트 메시지와 함께

 

리사이클러뷰에 아이템이 추가되는걸 볼 수 있다.

 

이제 데이터가 데이터베이스에 잘 들어갔는지 확인을 위해

 

앱을 다시 실행시켜보자.

 

재실행 결과

 

이렇게 내부 데이터베이스에 데이터가 성공적으로 저장되었고

 

불러올 때도 문제가 없는걸 볼 수 있다.

 

이러한 룸(Room) 을 사용하게 되면

 

서버를 거치지 않고도 내부에서도 query문을 사용한 데이터 처리가 가능하고

 

SharedPreference 보다 좀 더 디테일하게

 

데이터베이스 테이블을 짜서 데이터를 관리할 수도 있다.

 

 

오늘의 예제 github 주소

nam-su/RoomExample: AAC RoomExample (github.com)

 

GitHub - nam-su/RoomExample: AAC RoomExample

AAC RoomExample. Contribute to nam-su/RoomExample development by creating an account on GitHub.

github.com

 

'Android' 카테고리의 다른 글

안드로이드 ConstraintLayout  (2) 2024.04.25
제트팩 컴포즈 (Jetpack Compose)  (1) 2024.04.21
라이브 데이터 (LiveData)  (0) 2024.04.13
뷰모델 (ViewModel)  (0) 2024.04.11
라이프 사이클 (Lifecycle)  (1) 2024.04.02