본문 바로가기
안드로이드

[Android] RoomDB

by 베어 그릴스 2021. 11. 7.
320x100

 

//ROOMDB

이전에 RoomDB를 사용한 적이 있었지만 다시 한번 정리해보기로 한다

 

우선, 데이터 베이스란?

데이터를 어딘가에 쌓아두고 보관하며 필요할 때마다 추가하거나 조회, 수정, 삭제할 수 있는 장소가 필요
이것을 위한 요소가 데이터베이스다.

데이터 베이스를 깊게 들어갈 수야 있겠지만, 안드로이드 쪽에서 간단히 RoomDB를 정리하기로 한만큼 간단히 정리해보고자한다. 이미 안드로이드에서 RoomDB를 사용해서 간편하게 테이블을 구조화할 수 있고, 저장할 수 있으므로 자세한 데이터 베이스 설명은 생략!

 

vs SharedPreference

우리는 이미 sharedPreference 라는 것을 사용해서 key value 형식으로 데이터를 저장해왔다. 이것은 데이터 베이스가 아닌가? 답은 '아니다'이다. 

sharedPreference는 Key/Value 형태로 데이타를 저장할 수 있는 데이타 구조고, 내부적으로는 XML 파일로 저장이 된다

즉, 데이터가 파일에 저장되고, 이 말은 데이터의 보안이 확보되지 않으며 메모리 상 손해가 있다! 즉, 크고 중요한 데이터들은 앱 내부 데이터 베이스 혹은 서버 데이터 베이스에 저장하는게 알맞다.

 

vs Server

백엔드 즉, 서버에서 이미 데이터베이스를 운영하는데 굳이 데이터베이스를 하나더 운영해야할까?

카카오 앱을 예를 들어서, 생각해보자.

우린 데이터가 없이도 이전에 대화했던 대화목록 친구목록을 가져올 수 있고, 심지어 속도도 매우 빠르다 이걸 가능하게 하는게 바로 RoomDB이고 속도도 매우 빠르다.

또, 보통 데이터가 필요없는 게임들도 앱 내부 데이터 베이스를 사용할 것이다.

 

사용법!

우선 RoomDB는 다음 세가지로 구성되어 있다.

1. Entity

2. DAO

3. Database

 

Entity

데이터베이스에 만들어줄 테이블

어노테이션과 함께 데이터클래스를 다음과 같이 만들어주면 된다. 구체적인 설명은 다음에..!

@Entity
data class Profile(
    var name: String,
    var age: String,
    var phone: String
){
    @PrimaryKey(autoGenerate = true) var id: Int = 0
}

이와 같이 코드를 짜면

다음과 같은 테이블이 생성된다.

코드를 짜며 느꼈지만 역시,, 데이터 베이스를 만들땐, 안드로이드도 마찬가지로 ERD설계가 필요한 것 같다

 

DAO

Data Access Object

어노테이션으로 얘가 무슨 역할을 하는 메소드야~ 하고 데이터베이스에 전달을 해주면, 데이터 베이스에서 알아서 해당하는 기능을 실행

@Dao
interface ProfileDao {
    @Insert
    fun insert(profile: Profile)

    @Update
    fun update(profile: Profile)

    @Delete
    fun delete(profile: Profile)

    @Query("SELECT * FROM Profile") // 테이블의 모든 값을 가져와라
    fun getAll(): List<Profile>

    @Query("DELETE FROM Profile WHERE name = :name") // 'name'에 해당하는 유저를 삭제해라
    fun deleteUserByName(name: String)
}

DataBase

실제 데이터 베이스 싱글톤으로 짜기를 구글에서 강추! 자칫하면 여러 인스턴스를 만들어서 메모리 손해가 올수도 있다.

sycronized 즉, 동기화 블럭안에 짜야하는 이유는 데이터 베이스에 접근할 때 메인스레드에서 접근해주면 시간이 너무 오래걸리기 때문에 워커스레드에 작업을 요청해주어야 한다. 강제로 메인스레드에 할당해줄 수 있지만 여기선 다루지 않겠다.

@Database(entities = [Profile::class], version = 1)
abstract class ProfileDatabase: RoomDatabase() {
    abstract fun profileDao(): ProfileDao

    companion object {
        //내부 디비 접근하는 인스턴스가 여러 개일 필요가 있을까?? 굳이 자원을 써가면서?!?
        // 인스턴스를 최초 한번만 생성 즉, 한번만 생성되기 때문에 고정된 메모리 영역을 사용, 하나의 데이터 베이스를 사용할 것이기 때문에 다른 클래스 간에 데이터 공유가 쉽다
        //이것은 class를 만들때 여러 인스턴스들이 공용으로 사용할 것들을 companion object(동반객체)로 만들어 준다
        private var instance: ProfileDatabase? = null


        //자바에서는 스레드를 동기화 하기 위해서 synchronized를 제공한다.
        //(예를 들면, 자칫 여러 인스턴스를 생성될 수 있는 경우에 하나만 생성하도록 하기 위해 이 노테이션을 통해 방지한다.)
        //여러 스레드가 동시에 하나의 데이터베이스 인스턴스를 요청할 수 있으므로 하나가 아닌 두 개의 데이터베이스가 생성될 수도 있다. 이 문제는 sample app에서는 발생하지 않지만 더 복잡한 앱에서는 발생할 수도 있다
        //데이터베이스를 가져오는 코드를 synchronized 블럭에 wrapping 하면 한 번에 하나의 실행 스레드만이 코드 블럭에 들어갈 수 있으므로 데이터베이스가 한번만 초기화 된다.
        @Synchronized
        fun getInstance(context: Context): ProfileDatabase? {
            if (instance == null) {
                synchronized(ProfileDatabase::class){
                    instance = Room.databaseBuilder(
                        context.applicationContext,
                        ProfileDatabase::class.java,
                        "user-database"//다른 데이터 베이스랑 이름겹치면 꼬임
                    ).build()
                }
            }
            return instance
        }
    }
}

자세한 내용은 주석에!

 

실제 사용

var db: ProfileDatabase = ProfileDatabase.getInstance(this)!!

Thread{
            val savedContacts = db.profileDao().getAll()
            if(savedContacts.isNotEmpty()){
                list.addAll(savedContacts)
            }
        }.start()

실제론 더 효율적인 코루틴에 써주기도 하지만 난 가볍게 thread에 작업을 요청해주었다.

 

sql 쿼리문을 공부하면 더 효율적으로 또 더 다양한 메소드를 쓸 수 있을 것 같다!!

728x90