안드로이드 DB로 Realm 사용하기

Realm을 쓰게 된 배경은 간단하다. 저번 프로젝트는 SQLite를 사용했으니 이번에는 새로운 데이터베이스를 사용해보자는 취지였다. 새로운 데이터베이스로는 무엇을 써볼까 고민하던 중, 개발자 레퍼런스를 돌아다니면서 꽤 자주 들어본 이름이 떠올랐다. 그게 바로 Realm 이었다.

물론 Realm이 개발자에게 있어 최상의 데이터베이스라고 말할 수 있는 건 아니다. 하지만 커뮤니티가 활발하고, 자료가 풍부하며, 본인에게 있어 새로운 형식의 데이터베이스라는 점이 마음에 들어 사용하게 되었다. 그러면 Realm이 무엇이고 안드로이드에 이를 어떻게 적용하는지 알아보자.

Realm이란?

Realm 데이터베이스는 모바일에 최적화된 로컬 데이터베이스 라이브러리이다. 쿼리문을 사용해 테이블의 컬럼에 값을 저장하는 SQLite와 달리, 데이터를 객체의 형태로 저장한다는 특징을 가지고 있다.

오해하지 말아야 할 점은 Realm이 ORM(Object-relational mapping)과 다르다는 사실이다. 본인은 오해했었지만 그렇다면 그 둘은 어떻게 다를까?

ORM은 객체와 데이터베이스의 테이블 사이에서 중간다리로 데이터를 매핑하는 역할을 한다. 본래 객체의 데이터를 데이터베이스에 저장하려면 쿼리문을 사용해야 하는데, 네이티브 코드에서 이를 사용하기란 번거롭기 때문이다.

하지만 Realm은 데이터 컨테이너 모델을 사용해 객체를 직접 데이터베이스에 저장한다. 데이터를 중간에 변환할 필요가 없기 때문에 ORM을 사용할 필요 또한 없다는 뜻이다. 그리고 이러한 특징 덕분에 빠른 저장 및 불러오기가 가능하다.

Realm의 장점

Realm의 단점

Realm access from incorrect thread.
Realm objects can only be accessed on the thread they were created.

본인은 안드로이드에서 멀티 스레딩 개념을 알게 된지 얼마 되지 않았기 때문에 이 부분이 조금 까다로웠다. 덕분에 AsynchTask를 사용해 백그라운드에서 데이터베이스와 관련한 작업을 할 때 위와 같은 문장을 많이 만났다.

Realm 오브젝트는 생성된 스레드에서만 접근 가능하다는 뜻이다. 이 부분의 문제는 다음의 링크를 통해 해결했다.

Android how can i use Realm on Thread

요약하자면, Realm 오브젝트를 사용하려는 클래스에 Realm.getDefaultInstance를 생성자로 넘겨서 스레드 문제를 해결한다는 이야기이다. 하지만 Realm 스레드 깊게 들여다보기에 있는 내용을 적용해봐도 좋을 듯 하다.

안드로이드에서 Realm 사용하기

앞선 항목에서 Realm에 대해 알아보았으니 이제 적용해볼 차례이다. 아래의 코드에서는 코틀린을 사용해 기본적인 사용법을 다룬다.

  1. Project build.gradledependencies에 아래의 classpath를 추가한다.
dependencies {
 classpath "io.realm:realm-gradle-plugin:4.3.1"
 }
  1. Module ‘build.gradle’에 apply plugin: 'realm-android'를 추가하고, 아래의 항목을 추가한다.
realm {
 syncEnabled = true
 //아래의 코드는 kotlin extension 사용 시 추가
 kotlinExtensionsEnabled = true
}
  1. 모델 클래스를 생성한다.
1
2
3
4
5
6
open class Test : RealmObject() {
 lateinit var title:String
 lateinit var body: String

 ...
}
  1. 객체 데이터를 저장하고 불러온다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 데이터베이스에 저장할 값들
val title = "title"
val body = "body"

// Realm 초기화
Realm.init(context)

// 현재 스레드에서 Realm의 인스턴스 가져오기
val realm = Realm.getDefaultInstance()

// 트랜젝션을 시작해 데이터 저장
realm.beginTransaction()
val test:Test = realm.createObject(Test::class.java)//데이터베이스에 저장할 객체 생성
test.apply {
 this.title = title
 this.body = body
}
realm.commitTransaction()

//데이터베이스에 있는 객체 전부 불러오기
//하나의 객체만 저장되어 있으므로 savedTest는 test가 가진 값을 갖게된다.
val savedTest = realm.where(Test::class.java).findAll()

더 많은 예제를 보고 싶다면, 한국어 Realm 문서를 찾아보시길 바란다.