로컬 데이터베이스인 Room을 사용해보다가 꾸준히 사용하게 될 DB일 것 같아 정리해보려고 합니다
Room은 기본 개념을 이해하는 것이 중요하기 때문에, 아주 간단한 예제로 진행하겠습니다.
[2022.06.30] 업데이트
Room이란?
Jectpack 라이브러리의 일부로 내부 저장소이며, ORM 라이브러리(DB데이터를 JAVA/Kotlin으로 변환) 입니다
Room 사용이 권장되는 이유
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 |