Room, SQLite 쉽게 접근하기

dongbin is free·2023년 2월 22일
0

Android

목록 보기
3/6

Room 너 뭐야?

안드로이드 로컬에 데이터를 저장하는 방식

안드로이드에서 로컬에 데이터를 저장하는 방식은 여러가지가 있다. 데이터의 크기, 용도 등을 따져 알맞게 골라서 사용하면 될 것 같다.

1. SharedPreferences

  • key-value(키-값) 컬렉션의 양이 비교적 적은 경우 사용
  • 앱 안에 xml 형식으로 저장
  • 자동로그인 기능 구현할 때 유용하게 써본 경험 有

2. Room

  • Android Architecture Component
  • SQLite의 추상화 계층을 제공하여 SQLite의 기능을 사용하면서 원활한 액세스가 가능
  • 객체 매핑을 통해 DB에 접근하는 ORM(Object Relational Mapping) 방식

3. Realm

  • 데이터를 객체 형태로 저장
  • 큰 용량의 데이터를 저장해야할 경우 사용
  • 크로스 플랫폼 DB로 AOS, IOS간 공유가 가능

👉 Query문을 작성하는게 익숙한 나.. App의 용량이 너무 커지고 싶지 않은 나.. Room과 잘 맞을지도.. 그럼 Room의 사용방법을 알아보자

Room 종속성 추가

def room_version = "2.5.0"
implementation("androidx.room:room-runtime:$room_version")
kapt("androidx.room:room-compiler:$room_version")

앱 수준의 build.gradle의 dependency안에 위와 같이 추가하였다.
또한 kapt를 사용하기 위해 plugin에 id 'kotlin-kapt' 추가하였다.

Room 기본 구성요소

Room에는 3가지 주요 구성요소가 존재한다. ORM 방식이라 그런지 JPA를 썼을 때와 유사한 느낌이다.

1. Database

  • Database holder를 포함, 앱의 영구 데이터와의 기본 연결을 위한 주 액세스 지점

2. Entity

  • Database 안의 테이블, 데이터 모델 클래스

3. DAO (Data Access Object)

  • Database에 접근하는데 사용되는 메서드를 제공 (CRUD..)

아래는 Room 구성요소 간 관계를 나타낸다.

Database 클래스는 DB와 연결된 DAO 인스턴스를 앱에 제공한다. 그러면 앱은 DAO를 사용하여 DB의 데이터를 연결된 데이터 항목 객체의 인스턴스로 검색할 수 있게 된다. 이를 코드로 구현해보자

Entity 생성

@Entity(tableName = "word")
data class Word(
    val text: String,
    val mean: String,
    val type: String,
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
  • @Entity을 통해 엔티티임을 명시하고 tableName을 직접 지정해줄 수 있다.
    • tableName을 따로 지정하지 않으면 알아서 클래스 명으로 테이블 명이 정해진다.
  • @PrimaryKey을 통해 주 키를 지정하고, 다른 DB와 마찬가지로 autoGenerate 또는 autoIncrease 기능이 존재한다.
  • @ColumnInfo(name = "컬럼 명")을 통해 해당 변수에 매칭되는 컬럼 명을 지정할 수 있다.

👉 더 상세한 데이터 정의는 레퍼런스를 참고하자

DAO 생성

@Dao
interface WordDao {
    @Query("SELECT * from word ORDER BY id DESC")
    fun getAll(): List<Word>

    @Query("SELECT * from word ORDER BY id DESC LIMIT 1")
    fun getLatestWord(): Word

    @Insert
    fun insert(word: Word)

    @Delete
    fun delete(word: Word)

    @Update
    fun update(word: Word)
}
  • @Dao을 통해 DAO를 명시
  • 편의 메서드(@Insert, @Update, @Delete)를 통해 간단한 삽입, 삭제, 업데이트 메서드 정의 가능
  • 쿼리 메서드(@Query)를 통해 SQL문을 작성하여 보다 복잡한 CRUD 메서드 정의 가능

👉 더 상세한 데이터 액세스 정의는 레퍼런스를 참고하자

Database 생성

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

    abstract fun wordDao(): WordDao
}
  • @Database을 통해 DB와 연결된 데이터 항목을 모두 나열하는 entities 배열이 포함된 DB임을 명시
  • 해당 클래스는 RoomDatabase를 확장하는 abstract class여야 함
  • DB와 연결된 각 DAO를 해당 클래스에서 인수가 0개인 DAO 인스턴스를 반환하는 abstract method를 정의해야 함

사용

// Database Instance 생성
val db = Room.databaseBuilder(
            applicationContext,
            AppDatabase::class.java,
            "db-name.db"
        ).build()
 /* ... */
 val wordDao = db.wordDao()
 val words: List<Word> = wordDao.getAll()
  • 빌더 패턴을 통해 데이터베이스 인스턴스를 생성
  • 인스턴스를 통해 DAO 인스턴스를 가져옴
  • 해당 DAO 인스턴스를 통해 쿼리 작업

이렇게도 사용하더라 (SingleTon)

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

    abstract fun wordDao(): WordDao
    
    companion object {
        private var INSTANCE: AppDatabase? = null
        fun getInstance(context: Context): AppDatabase? {
            if(INSTANCE == null) {
                synchronized(AppDatabase::class.java) {
                    INSTANCE = Room.databaseBuilder(
                        context.applicationContext,
                        AppDatabase::class.java,
                        "app-database.db"
                    ).build()
                }
            }
            return INSTANCE
        }
    }
}

/* 사용 */
val words: List<Word> = AppDatabase.getInstance(this)?.wordDao()?.getAll()
  • Database 인스턴스를 싱글톤 패턴을 통해 하나임을 보장하여 전역에서 접근하도록 사용
  • 여러 액티비티에서 생성될 인스턴스라면 차라리 싱글톤으로 메모리 공간을 효율적으로 사용

결론

생으로 SQLite에 접근해서 해봤던 경험이 전부였었는데, 이렇게 만들어주니까 너무 편하다.
하다가 모르는건 Android Developers에서 먼저 찾아보는 습관을 들이자

profile
배운 것을 적어나가는 그런 공간.. 적다 보면 또 까먹는 그런 사람..

0개의 댓글