💡 Kotlin

[Kotlin/Android] Room 사용하기

콩드로이드 2022. 6. 30. 14:29

 

로컬 데이터베이스인 Room을 사용해보다가 꾸준히 사용하게 될 DB일 것 같아 정리해보려고 합니다

Room은 기본 개념을 이해하는 것이 중요하기 때문에, 아주 간단한 예제로 진행하겠습니다.


[2022.06.30] 업데이트

Room이란?

Jectpack 라이브러리의 일부로 내부 저장소이며, ORM 라이브러리(DB데이터를 JAVA/Kotlin으로 변환) 입니다

 

Room 사용이 권장되는 이유

SQLite의 단점

1. SQLite를 활용하여, 데이터 접근이 편리하고 유지보수가 용이하며, 마이그레이션도 간단합니다 

2. SQLite와 달리, 컴파일 타임에 쿼리의 적합성을 확인할 수 있습니다 

즉, SQLite보다 편리한 DB라고 생각하시면 될 것 같습니다 

 

우선, Room을 사용하기 전 구성 요소에 대해 간단히 알아보고, 

간단한 로그인, 회원가입 예제를 통해 소스로 구현해보겠습니다 :) 

 

📌 Room 라이브러리 적용하기
      def room_version = "2.3.0"
      implementation "androidx.room:room-runtime:$room_version"
     annotationProcessor "androidx.room:room-compiler:$room_version
"

 


1. Room의 3가지 구성 요소

 

1 . Entity 

  - DB에서 Table을 나타냅니다

   - @Entity로 선언합니다

 

2 . Dao(Data Access Objects)

  - 데이터에 Access 할 수 있는 Interface

   - @Dao로 선언합니다

 

3 . Room DataBase

   - abstract class로 선언하고 Room Database를 상속받습니다

   - @Database로 선언합니다

   - DB에서 사용될 테이블의 정보(entity)가 필요하고, version 정보가 필요합니다

    @Database(entities = [UserInfo::class], version = 1)

   - App이 업데이트 되어서 DB가 변경될 경우 이전 DB와 구분이 필요하므로 version에 int값을 넣어 관리합니다

 


 

2. 각 구성 요소 구현 

 

위의 각 3가지 구성 요소를 소스로 구현해보겠습니다.

 

1 ) Entity

Table에선 이메일, 비밀번호를 저장하고 , 구분하기 위한 값으로 pkId를 정합니다

PrimaryKey도 Annotation(@PrimaryKey)으로 선언합니다

각 컬럼으로 선언해주기 위해 @ColumInfo를 사용합니다

@Entity
data class UserInfo(
    @PrimaryKey val pkId: Int?,
    @ColumnInfo val email:String?,
    @ColumnInfo val pw:String
)

 

2) Dao

Room을 사용하면서 가장 생소한 개념인데,

말 그대로 Data 데이터를 Access 가공할 수 있는 Interface라 생각하면 용이합니다.

마찬가지로 Annotation으로 Dao를 선언해주고 예제에서 필요한 삽입(@Insert), 쿼리 부분(@Query)을 함수로 만들어줍니다

예제에선 회원가입, 비밀번호 체크, 가입 여부 판단이 필요하므로 아래의 3가지 함수를 선언합니다.

 

paramater를 가진 함수를 사용해서 Query문에 인자로 넣으려면  :param명 을 사용하시면 됩니다

@Dao
interface UserDao {
    @Insert
    fun insertUser(userInfo: UserInfo)

    @Query("select pw from UserInfo where email = :email")
    fun getPwByEmail(email: String):String

    @Query("select email from UserInfo")
    fun getEmail(): List<String>
}

 

 

3) Database

RoomDatabase를 사용하려면 아래와 같이 충족해야 하는 조건들이 있습니다

📌 @Database로 주석이 지정된 클래스는 다음 조건을 충족해야 합니다.

  • RoomDatabase를 확장하는 추상 클래스여야 합니다
  • entities= 데이터베이스와 연결된 항목의 목록을 포함해야 합니다.
  • Dao클래스를 반환하는 추상 메서드를 포함해야 합니다.

 

아래의 소스를 보며 충족되는 조건을 확인해보겠습니다

@Database(entities = [UserInfo::class], version = 1)
abstract class UserDB: RoomDatabase() {
    abstract fun getDao() : UserDao
}

 

1) RoomDatabase를 확장하는 추상 클래스 

 : abstract class 선언, :RoomDatabase() 

 

2) 주석 내에 데이터베이스와 연결된 항목의 목록을 포함 

 : entities = [UserInfo::class] 

 : 선언된 형식을 보면 배열을 받고 있으므로, 여러 개의 Entity가 포함될 수 있습니다.

 

3) Dao 클래스를 반환하는 추상 메서드를 포함 

 :  abstract fun getDao() : UserDao

 


3. 메인 코드에서 Room 가져오기

 

Room에 관련된 소스들을 MainActivity에서 사용해보도록 하겠습니다 :)

 

기본적인 레이아웃은  아래와 같이 구성되어 있습니다.

간단한 예제이므로 사용법 정도만 참고하시면 될 것 같습니다 

Room을 테스트하는 예제이므로 각각 입력값의 정규식은 구현되지 않았습니다 

 

 

👀 onCreate

 

lateinit으로 DB를 선언한 뒤, 

Room.databaseBuilder로 DB를 생성하는데 builder의 원형은 다음과 같습니다

public static Builder<T> databaseBuilder (Context context, 
                Class<T> klass, 
                String name)

 

Class<T> klass에는 RoomDatabase를 확장하는 추상 클래스가 들어가면 됩니다 

name엔 DB file의 이름을 지정해줍니다

 

   lateinit var db: UserDB

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

        //DB 생성
        db = Room.databaseBuilder(this, UserDB::class.java, "UserDB").allowMainThreadQueries().build()

        initView()
    }

initView에서는 버튼에 클릭 리스너만 연동해주므로 따로 적지않겠습니다 

 

👀 회원가입

pkId에는 구분값이 필요하므로 이번 예제에선 각 회원마다 현재 회원수로 Int값을 설정하겠습니다

Dao에 선언된 insertUser를 사용해 입력된 값으로 회원가입을 합니다

fun joinUser() {
        val pkId = db.getDao().getEmail().size + 1
        db.getDao().insertUser(UserInfo(pkId, etMail.text.toString(), etPw.text.toString()))
}

 

👀 가입여부 체크 및 로그인

첫 번째 함수를 통해 입력된 이메일이 가입된 이메일인지 확인합니다

두 번째 함수를 통해 입력된 이메일로 비밀번호를 가져오고, 사용자가 입력된 비밀번호와 DB에 저장된 비밀번호가 같은지 확인합니다 

    fun checkValidUser(): Boolean {
        return db.getDao().getEmail().isNotEmpty()
    }

    fun checkValidPw(email: String): String {
        return db.getDao().getPwByEmail(email)
    }

 

두 입력 칸 모두 채워져있는 지 확인하고, 

로그인 시도 시 가입된 이메일인지 확인하고, 저장된 비밀번호와 입력된 비밀번호가 같은지 체크합니다

결과에 따라 Toast 메시지를 표시합니다

    override fun onClick(v: View?) {
        when (v) {
            btnLogin -> {
                if (etMail.text.isNotEmpty() && etPw.text.isNotEmpty()) {
                    if (checkValidUser()) {
                        if (checkValidPw(etMail.text.toString()) == etPw.text.toString()) {
                            Toast.makeText(this, "로그인에 성공했습니다", Toast.LENGTH_SHORT).show()
                        } else
                            Toast.makeText(this, "아이디 또는 비밀번호가 일치하지 않습니다.", Toast.LENGTH_SHORT)
                                .show()
                    } else {
                        Toast.makeText(this, "가입된 계정이 없습니다.", Toast.LENGTH_SHORT).show()
                    }
                } else {
                    Toast.makeText(this, "아이디와 비밀번호는 필수 입력 사항입니다.", Toast.LENGTH_SHORT).show()
                }
            }

            btnRegister -> {
                if (etMail.text.isNotEmpty() && etPw.text.isNotEmpty()) {
                    joinUser()
                } else {
                    Toast.makeText(this, "아이디와 비밀번호는 필수 입력 사항입니다.", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

 

 

👀 실행화면

 


짧은 예제로 알아봤지만, Room DB는 임시 저장이나 검색어 등을 관리할 때 아주 유용하게 쓰일 것 같습니다 :) 

궁금하신 점이나 의견이 있으시면 댓글 부탁드립니다 감사합니다 😊

'💡 Kotlin' 카테고리의 다른 글

[Kotlin] Collection (List, Set, Map)  (0) 2023.01.03
[kotlin] Pair  (0) 2023.01.02
[Kotlin] apply, let, with, also, run 비교 (Scope Function)  (0) 2022.07.14
[Kotlin] 배열  (0) 2022.07.02
[Kotlin] 동적 View 생성  (0) 2021.06.13