[Kotlin]Room Database 개념 및 사용법 - entity, DAO, database, Typeconverter 생성 - MVVM 뉴스앱 만들기 3편
저번 포스트에서는 레트로핏을 사용하여 API통신을 구현해보았는데요 이번 편에서는 database객체를 접근하는데 도움을주는 RoomDatabase의 사용법을 알아보겠습니다.
레트로핏 사용법 - Interface, retrofitInstance, constant만들기, GET의미, JSON To Kotlin 플러그인 - MVVM 뉴스앱 만들기 2편
Room 개념
Room은 database의 객체를 JAVA 나 Kotlin객첼 매핑해주는 것입니다. sqlite 모든 기능을 제공하면서 좀 더 간편하게 데이터 베이스에 접근할 수 있습니다.
적은 양의 데이터를 로컬에 저장할 때 유용합니다. 가장 자주 쓰이는 케이스는, 우리가 네트워크가 없을 때 유저가 오프라인 이어도 콘텐츠들을 접근 가능하게 해 줄 때도 씁니다. 예를 들면 우리가 즐겨 찾기 한 항목들을 보여 줄 때 도 room을 사용하기 적합한 예입니다.
Room의 장점
annotiation을 이용해서 코드를 더 간편하게 쓸 수 있습니다.
sqlite에서는 compile에러인지 확인할 수 없지만 room에서는 compile도중에 sql에 대한 유효성 검사가 가능합니다.
schema가 변경될 때 room은 자동으로 sql query를 업데이트해줍니다.
room은 livedata와 rxjava를 위한 observation으로 생성하여 동작할 수 있습니다.
Room의 구성
1. Database: 데이터베이스를 생성하고 관리하는 abstract class입니다.
2. DAO: 데이터 베이스에 접근하는 함수를 제공합니다. (insert, update, delete...) 클래스가 아닌 interface입니다.
3. entity:
primary key | 영화 제목 | 장르 | 플레이 타임 |
1 | 안녕 | 로맨스 | 00:00:32 |
2 | 하세 | 호러 | 00:31:54 |
3 | 요 | 액션 | 01:50:32 |
데이터베이스 내의 테이블을 나타냅니다. 데이터 테이블로 나타내기 위해서는 한 데이터가 위에 예시와 같이 장르 영화 이름 플레이 시간으로 속성이 모여 하나의 정보 단위를 이루어야 되는데 이것을 entity라고 부릅니다.
Room 사용법/예제
Room을위해 필요한 entity, DAO, Database, TypeConverter를 구현해보겠습니다.
Entity 생성
먼저 entity를 구현해보겠습니다. entity는 data class파일에서 구현하여야 합니다. 이번 예제에서는 entity와 primary key까지 구현해보겠습니다.
**이번 예제에서 사용되는 코드들은 뉴스앱을 만드는 과정에서 만들었던 코드입니다.**
우선 primary key는 위에 예시 테이블에서 썼듯이 각 열의 데이터마다 가지고 있는 key값입니다.
각각 정해주기 번거롭기때문에 룸이 자동으로 만들어 주도록 auto generate을 true로 하겠습니다.
- Article
//추가 시작
@Entity(
tableName = "articles"
)
data class Article(
@PrimaryKey(autoGenerate = true)//primary key 자동생성
var id: Int? = null, //모든 기사가 id가 있지않으므로 null표시
//추가끝
val author: String,
val content: String,
val description: String,
val publishedAt: String,
val source: Source,
val title: String,
val url: String,
val urlToImage: String
)
DAO 생성
새로운 패키지를 만들어 주겠습니다. 이름은 db로 만들어주었습니다.
안에는 ArticleDao라는 이름의 인터페이스를 만들어 주었습니다.
- db/ArticleDao
@Dao
interface ArticleDao {
@Insert(onConflict = OnConflictStrategy.REPLACE) //기사가 추가나 업데이트되는 기능을 넣어주겠습니다.
suspend fun upsert(article: Article):Long //만약 기사가 업데이트되거나 생기면 바꾸게 됩니다.
//반환값이 long인 이유는 대체될 article의 id가 들어오기때문입니다.
@Query("SELECT * FROM articles")
fun getAllArticle():LiveData<List<Article>>
//라이브데이터를 반환 하기때문에 기사가 추가되거나 바뀔때 UI에 업데이트됩니다.
@Delete
suspend fun deleteArticle(article:Article)
}
이렇게 하면 room생성을 위한 기본 설정이 완료됩니다.
Database 설정
db폴더 아래에 articleDatabase 를 만들어줍니다.
@Database(
entities = [Article::class],//만들어 주었던 entity 클래스 를 연결해줍니다.
version = 1
)
@TypeConverters(Converters::class)//typeconverter을 아직 만들지 않았지만, 먼저 typeconverter class를 연결해 주겠습니다.
abstract class ArticleDatabase:RoomDatabase() {
abstract fun getArticleDao():ArticleDao
companion object{
@Volatile// 다른 쓰레드들이 인스턴스에 변화가있을때 바로 확인할수있습니다.
private var instance:ArticleDatabase? = null//생성한 database를 인스턴스 합니다.
private val LOCK = Any()
operator fun invoke(context: Context) = instance?: synchronized(LOCK) { //database가 instance될때 invoke가 실행됩니다.
instance ?: createDatabase(context).also{ //만약 인스턴스가없으면 synchronized안에있는 코드를 실행합니다.
instance = it
}
}
private fun createDatabase(context: Context) =
Room.databaseBuilder(
context.applicationContext,
ArticleDatabase::class.java, //Database class를 연결해줍니다.
"article_db.db" //article db의 이름을 정합니다.
).build()
}
}
TypeConverters 생성
우선 typeConverter가 필요한 이유는 아래 article data class를 보면 Source를 타입을 가진 source가 있습니다. 이 것을 String타입으로 바꿔 주기 위해 typeconverter가 필요합니다.
아래와 같이 db폴더안에 converters클래스를 만들어줍니다.
아래와같이 Source를 위한 converter를 만들어줍니다.
class Converters {
//source를 위한 type converter를 만들어줍니다.
@TypeConverter
fun fromSource(source: Source):String{
return source.name
}
@TypeConverter
fun toSource(name:String):Source{
return Source(name,name)
}
}
다음편에서는 DiffUtil을 활용한 RecyclerView만드는 법을 다뤄보겠습니다.
참고자료