Realm은 최고의 Android 데이터베이스 솔루션입니다.

게시 됨: 2022-03-11

Android가 만들어진 이후로 우리 앱 개발자는 SQLite를 사용하여 로컬 데이터를 저장해 왔습니다. 때로는 SQL 문을 사용하여 직접, 때로는 ORM(Object-Relational Mapper)을 추상화 계층으로 사용하지만 어느 쪽이든 결국에는 SQLite를 사용했습니다.

그러나 SQLite의 모든 장점에도 불구하고 관계형 모델에 대한 대안이 있기를 원했던 때가 있었습니다. 클래스와 테이블, 필드와 열, 외래 키 등

다시 말해, 애플리케이션 수준에서 실제로 사용하는 것과 더 유사한 데이터 구조를 가진 데이터베이스입니다. 더군다나 메모리가 효율적으로 설계되어 리소스가 제한된 장치에서 더 나은 경험을 할 수 있다면 정말 좋을 것입니다.

사실 이것은 SQLite의 새로운 대안으로 부상한 독특한 아키텍처를 가진 데이터베이스 플랫폼인 Realm을 통해 얻을 수 있는 바로 사용할 수 있는 이점 중 일부입니다.

이 기사에서는 Realm이 많은 관심을 받은 몇 가지 주요 이유와 사용을 고려하고 싶은 이유를 설명합니다. SQLite에 비해 Realm이 Android 개발자에게 제공하는 몇 가지 주요 이점에 대해 설명합니다.

Realm은 여러 플랫폼에서 사용할 수 있으므로 이 기사에서 다룰 내용 중 일부는 iOS, Xamarin, React Native와 같은 다른 모바일 플랫폼과도 관련이 있습니다.

SQLite: 작동하지만 대부분의 시간에 필요한 것은 아닙니다.

대부분의 모바일 개발자는 SQLite에 익숙할 것입니다. 2000년부터 사용되었으며, 틀림없이 세계에서 가장 많이 사용되는 관계형 데이터베이스 엔진입니다.

SQLite에는 우리 모두가 인정하는 여러 가지 이점이 있으며 그 중 하나는 Android에서 기본적으로 지원된다는 것입니다.

표준 SQL 관계형 데이터베이스라는 사실은 또한 관계형 데이터베이스 배경에서 온 사람들을 위한 학습 곡선을 최소화합니다. 또한 잠재력을 최대한 활용하면(준비된 명령문, 트랜잭션을 사용한 대량 작업 등의 기능 활용) 상당히 좋은 성능을 제공합니다. SQLite는 모든 요구 사항에 맞게 확장되지 않을 수 있습니다.

그러나 SQL 문을 직접 처리하면 여러 가지 단점이 있습니다.

공식 Android 문서에 따르면 SQLite 읽기/쓰기를 시작하는 데 필요한 단계는 다음과 같습니다.

  1. 계약 클래스 측면에서 스키마를 설명합니다.
  2. 문자열에서 테이블 생성/삭제 명령을 정의합니다.
  3. SQLiteOpenHelper 를 확장하여 생성 명령을 실행하고 업그레이드/다운그레이드를 관리합니다.

이 작업을 완료하면 데이터베이스를 읽고 쓸 준비가 된 것입니다. 그러나 응용 프로그램의 개체와 데이터베이스의 값 사이를 앞뒤로 변환해야 합니다. 긴 이야기 요약: 그것은 많은 상용구 코드입니다!

또 다른 문제는 유지 관리 가능성입니다. 프로젝트가 커지고 더 복잡한 쿼리를 작성해야 할 필요성이 발생함에 따라 문자열에 원시 SQL 쿼리의 큰 덩어리가 생깁니다. 나중에 이러한 쿼리의 논리를 변경해야 하는 경우 상당히 번거로울 수 있습니다.

단점에도 불구하고 원시 SQL을 사용하는 것이 최선의 선택인 경우가 있습니다. 한 가지 예는 성능과 크기가 중요한 요소인 라이브러리를 개발할 때 가능하면 타사 라이브러리를 추가하는 것을 피해야 하는 경우입니다.

객체 관계형 매퍼: SQL 과제에 대한 반창고

원시 SQL을 다루지 않도록 ORM이 구출되었습니다.

가장 유명한 Android ORM 중 일부는 DBFlow, greenDAO 및 OrmLite입니다.

그들이 가져오는 가장 큰 가치는 데이터베이스 엔티티를 Java 객체에 비교적 쉽게 매핑할 수 있게 해주는 SQLite 추상화입니다.

다른 이점 중에서 응용 프로그램 개발자는 훨씬 더 친숙한 데이터 구조인 개체로 작업할 수 있습니다. 또한 더 강력한 타이핑으로 고수준 개체를 처리하고 라이브러리에 더러운 작업을 맡기므로 유지 관리 용이성에도 도움이 됩니다. 문자열을 연결하거나 데이터베이스와의 연결을 수동으로 처리하여 쿼리를 작성하는 데 어려움을 덜 겪습니다. 오타가 적습니다.

이러한 ORM이 Android 데이터베이스의 기준을 높인 것은 사실이지만 단점도 있습니다. 많은 경우 불필요한 데이터를 로드하게 됩니다.

다음은 예입니다.

15개의 열이 있는 테이블이 있고 앱의 특정 화면에 이 테이블의 개체 목록이 표시된다고 가정해 보겠습니다. 이 목록에는 세 열의 값만 표시됩니다. 따라서 테이블 행에서 모든 데이터를 로드하면 해당 화면에 실제로 필요한 것보다 5배 더 많은 데이터를 가져오게 됩니다.

사실 이러한 라이브러리 중 일부에서는 검색할 열을 미리 지정할 수 있지만 이를 위해서는 추가 코드를 추가해야 하며, 그렇다 하더라도 검색할 열을 정확히 알 수만 있는 경우에는 충분하지 않습니다. 데이터 자체를 살펴본 후 사용: 일부 데이터는 어쨌든 불필요하게 로드될 수 있습니다.

또한 복잡한 쿼리를 작성해야 하고 ORM 라이브러리가 API로 이러한 쿼리를 설명하는 방법을 제공하지 않는 시나리오가 종종 있습니다. 예를 들어, 필요한 것보다 더 많은 계산을 수행하는 비효율적인 쿼리를 작성할 수 있습니다.

그 결과 성능이 저하되어 원시 SQL에 의존하게 됩니다. 이것이 우리 중 많은 사람들에게 거래 차단기는 아니지만 객체 관계형 매핑의 주요 목적을 해치고 SQLite와 관련하여 앞서 언급한 몇 가지 문제로 돌아가게 합니다.

Realm: 완벽한 대안

Realm 모바일 데이터베이스는 처음부터 모바일 장치용으로 설계된 데이터베이스입니다.

Realm과 ORM의 주요 차이점은 Realm이 SQLite 위에 구축된 추상화가 아니라 완전히 새로운 데이터베이스 엔진이라는 것입니다. 관계형 모델이 아니라 개체 저장소를 기반으로 합니다. 핵심은 자체 포함된 C++ 라이브러리로 구성됩니다. 현재 Android, iOS(Objective-C 및 Swift), Xamarin 및 React Native를 지원합니다.

Realm은 2014년 6월에 출시되었으므로 현재 2년 반이 되었습니다(아주 새롭습니다!).

서버 데이터베이스 기술이 2007년 이후로 많은 새로운 기술이 등장하면서 혁명을 겪고 있는 동안 모바일 장치용 데이터베이스 기술은 SQLite와 그 래퍼에 고정되어 있었습니다. 이것은 처음부터 무언가를 만드는 주요 동기 중 하나였습니다. 게다가, 우리가 보게 되겠지만, Realm의 일부 기능은 낮은 수준에서 데이터베이스가 작동하는 방식에 대한 근본적인 변경을 요구했으며, 이는 SQLite 위에 무언가를 구축하는 것이 불가능했습니다.

그러나 Realm이 정말 가치가 있습니까? 다음은 툴 벨트에 Realm을 추가해야 하는 주요 이유입니다.

쉬운 모델링

다음은 Realm으로 만든 몇 가지 모델의 예입니다.

 public class Contact extends RealmObject { @PrimaryKey String id; protected String name; String email; @Ignore public int sessionId; //Relationships private Address address; private RealmList<Contact> friends; //getters & setter left out for brevity }
 public class Address extends RealmObject { @PrimaryKey public Long id; public String name; public String address; public String city; public String state; public long phone; }

모델은 RealmObject에서 확장됩니다. Realm은 모든 기본 유형과 boxed 유형( char 제외), String , Datebyte[] 를 허용합니다. RealmObjectRealmList<? extends RealmObject> 의 하위 클래스도 지원합니다. RealmList<? extends RealmObject> 를 모델 관계로 확장합니다.

필드는 모든 액세스 수준(비공개, 공개, 보호 등)을 가질 수 있습니다. 모든 필드는 기본적으로 유지되며 "특수" 필드에 주석만 추가하면 됩니다(예: 기본 키 필드의 경우 @PrimaryKey , 비지속 필드를 설정하는 @Ignore 등).

이 접근 방식의 흥미로운 점은 ORM과 비교하여 클래스를 덜 "오염된 주석"으로 유지한다는 것입니다. 대부분의 경우 클래스를 테이블에 매핑하고 일반 필드를 데이터베이스 열에 매핑하고 외래 키 필드를 다른 테이블에 매핑하는 주석이 필요하기 때문입니다. 켜짐.

관계

관계에 관해서는 두 가지 옵션이 있습니다.

  • 다른 모델의 필드로 모델을 추가합니다. 이 예에서 Contact 클래스는 Address 필드를 포함하고 이 필드는 관계를 정의합니다. 연락처에 주소가 있을 수 있지만 동일한 주소가 다른 연락처에 추가되는 것을 막을 수는 없습니다. 이는 일대일 및 일대다 관계를 허용합니다.

  • 참조되는 모델의 RealmList 를 추가합니다. RealmLists 는 Realm 객체의 컨테이너 역할을 하는 오래된 Java Lists 처럼 동작합니다. 이 예제에서 그녀의 친구인 연락처의 RealmListContact 모델에 있음을 알 수 있습니다. 이 접근 방식으로 일대다 및 다대다 관계를 모델링할 수 있습니다.

나는 우리 Java 개발자들에게 매우 자연스럽게 느껴지기 때문에 관계를 표현하는 이러한 방식을 좋아합니다. 이러한 객체(또는 이러한 객체 목록)를 클래스의 필드로 직접 추가함으로써 다른 비모델 클래스에 대해 수행하는 것처럼 외래 키에 대한 SQLite 설정을 처리할 필요가 없습니다.

주의 사항: 모델 상속에 대한 지원이 없습니다. 현재 해결 방법은 구성을 사용하는 것입니다. 따라서 예를 들어 Animal 모델이 있고 Animal 에서 확장되는 Dog 모델을 생성하려는 경우 대신 Animal 인스턴스를 Dog 필드로 추가해야 합니다. 구성 대 상속에 대한 큰 논쟁이 있습니다. 상속을 사용하려는 경우 Realm에 대해 알아야 할 사항입니다. SQLite를 사용하면 외래 키로 연결된 두 개의 테이블(부모용 테이블 하나와 자식 테이블 하나)을 사용하여 구현할 수 있습니다. DBFlow와 같은 일부 ORM도 이 제한을 부과하지 않습니다.

필요한 데이터만 검색하세요! 제로 카피 디자인

이것은 킬러 기능입니다.

Realm은 데이터가 메모리에 복사되지 않는다는 제로 카피 설계 개념을 적용합니다. 쿼리에서 얻은 결과는 실제로 실제 데이터에 대한 포인터일 뿐입니다. 데이터 자체는 액세스할 때 느리게 로드됩니다.

예를 들어, 10개의 필드(SQL의 열)가 있는 모델이 있습니다. 이 모델의 개체를 쿼리하여 화면에 나열된 개체를 표시하고 목록 항목을 채우는 데 10개 필드 중 3개만 필요한 경우 해당 필드만 검색됩니다.

결과적으로 쿼리는 엄청나게 빠릅니다(일부 벤치마크 결과는 여기 및 여기 참조).

이것은 일반적으로 선택한 SQL 행의 모든 ​​데이터를 미리 로드하는 ORM에 비해 큰 이점입니다.

결과적으로 개발자의 추가 노력 없이 화면 로딩이 훨씬 더 효율적이 됩니다. 이는 Realm의 기본 동작일 뿐입니다.

또한 이는 앱이 더 적은 메모리를 사용한다는 것을 의미하며 모바일 장치와 같이 리소스가 제한된 환경에 대해 이야기하는 것을 고려할 때 큰 차이를 만들 수 있습니다.

무복사 방식의 또 다른 결과는 Realm에서 관리하는 객체가 자동으로 업데이트된다는 것입니다.

데이터는 메모리에 복사되지 않습니다. 쿼리 결과가 있고 쿼리 이후에 다른 스레드가 데이터베이스에서 이 데이터를 업데이트한 경우 보유한 결과에는 이러한 변경 사항이 이미 반영됩니다. 결과는 실제 데이터에 대한 포인터일 뿐입니다. 따라서 필드의 값에 액세스하면 가장 최신 데이터가 반환됩니다.

그림: 여러 개체와 스레드에서 Realm 데이터에 액세스합니다.

예를 들어 Realm 객체에서 이미 데이터를 읽고 이를 화면에 표시했으며 기본 데이터가 변경될 때 업데이트를 수신하려면 리스너를 추가할 수 있습니다.

 final RealmResults<Contact> johns = realm.where(Contact.class).beginsWith("name", "John ").findAll(); johns.addChangeListener(new RealmChangeListener<RealmResults<Contact>>() { @Override public void onChange(RealmResults<Contact> results) { // UPDATE UI } });

단순한 래퍼가 아닙니다.

ORM에 대한 수십 가지 옵션이 있지만 이들은 래퍼이며 모두 SQLite 아래에 있으므로 얻을 수 있는 범위가 제한됩니다. 이와 대조적으로 Realm은 또 다른 SQLite 래퍼가 아닙니다. ORM이 제공할 수 없는 기능을 자유롭게 제공할 수 있습니다.

Realm의 근본적인 변화 중 하나는 데이터를 객체 그래프 저장소로 저장할 수 있다는 것입니다.

이것은 Realm이 프로그래밍 언어 수준에서 데이터베이스에 이르기까지 모든 단계에서 객체라는 것을 의미합니다. 결과적으로 관계형 데이터베이스와 비교하여 값을 쓰고 읽을 때 앞뒤로 변환하는 것이 훨씬 적습니다.

데이터베이스 구조는 애플리케이션 개발자가 사용하는 데이터 구조를 보다 밀접하게 반영합니다. 사실, 이것이 관계형 모델링에서 서버 측 개발에서 집계 모델로 이동하는 주요 이유 중 하나입니다. Realm은 마침내 이러한 아이디어 중 일부를 모바일 개발 세계에 도입했습니다.

Realm 아키텍처의 구성 요소를 생각하면 맨 아래에는 플랫폼의 가장 기본적인 구현을 포함하는 핵심이 있습니다. 그 위에 지원되는 각 플랫폼에 대한 바인딩 라이브러리가 있습니다.

그림: 코어에서 다양한 라이브러리로의 영역 아키텍처.

제어할 수 없는 일부 기술에 대해 래퍼를 사용할 때 결국 주변에 일종의 추상화 계층을 제공해야 합니다.

Realm 바인딩 라이브러리는 추상화 복잡성을 줄이기 위해 최대한 얇게 설계되었습니다. 그들은 주로 Core에서 디자인 아이디어를 전파합니다. 전체 아키텍처를 제어함으로써 이러한 구성 요소는 서로 더 잘 동기화됩니다.

한 가지 실용적인 예는 다른 참조 객체(SQL의 외래 키)에 대한 액세스입니다. Realm의 파일 구조는 기본 링크를 기반으로 하므로 관계를 쿼리할 때 ORM 추상화를 관계형으로 변환하거나 여러 테이블을 조인하는 대신 파일 형식의 파일 시스템 수준에서 객체에 대한 원시 링크를 얻습니다.

그것은 다른 개체를 직접 가리키는 개체입니다. 따라서 관계를 쿼리하는 것은 예를 들어 정수 열을 쿼리하는 것과 같습니다. 외래 키를 순회하는 값비싼 작업이 필요하지 않습니다. 포인터를 따라가는 것이 전부입니다.

커뮤니티 및 지원

Realm은 현재 개발 중이며 업데이트된 버전을 꽤 자주 출시하고 있습니다.

Realm 모바일 데이터베이스의 모든 구성 요소는 오픈 소스입니다. 문제 추적기 및 스택 오버플로에 대한 응답이 매우 빠르기 때문에 이러한 채널에서 빠르고 좋은 지원을 기대할 수 있습니다.

또한 문제(버그, 개선 사항, 기능 요청 등)의 우선 순위를 지정할 때 커뮤니티의 피드백을 고려합니다. 당신이 사용하는 도구의 개발에 당신이 발언권을 가질 수 있다는 것을 아는 것은 항상 좋은 일입니다.

저는 2015년부터 Realm을 사용하기 시작했고, 그 이후로 웹상에서 Realm에 대한 다양한 의견을 담은 여러 게시물을 접하게 되었습니다. 우리는 곧 그 한계에 대해 이야기할 것이지만, 내가 알아차린 한 가지는 게시 당시에 제기된 많은 불만 사항이 이후 수정되었다는 것입니다.

예를 들어 내가 Realm에 대해 알게 되었을 때 모델에 대한 사용자 정의 메소드와 비동기식 호출에 대한 지원은 아직 없었습니다. 이들은 당시 많은 거래를 차단했지만 현재는 둘 다 지원됩니다.

이러한 개발 속도와 응답성은 중요한 기능을 오래 기다리지 않을 것이라는 확신을 갖게 합니다.

제한 사항

삶의 모든 것과 마찬가지로 Realm은 장미만 있는 것은 아닙니다. 앞서 언급한 상속 제한 외에도 염두에 두어야 할 다른 단점이 있습니다.

  • 여러 스레드가 동시에 데이터베이스에서 읽고 쓸 수 있지만 Realm 객체는 스레드 간에 이동할 수 없습니다 . 따라서 예를 들어 백그라운드 스레드에서 실행되는 AsyncTask의 doInBackground() 를 사용하여 영역 객체를 검색하는 경우 이 인스턴스는 기본 스레드에서 실행되기 때문에 onPostExecute() 메서드에 전달할 수 없습니다. 이 상황에 대한 가능한 해결 방법은 개체의 복사본을 만들어 함께 전달하거나 개체의 ID를 전달하고 onPostExecute() 에서 개체를 다시 검색하는 것입니다. Realm은 읽기/쓰기를 위한 동기 및 비동기 방법을 제공합니다.

  • 자동 증가 기본 키에 대한 지원이 없으므로 이러한 기본 키 생성을 직접 처리해야 합니다.

  • 별개의 프로세스에서 동시에 데이터베이스에 액세스할 수 없습니다 . 문서에 따르면 다중 프로세스 지원이 곧 제공될 예정입니다.

Realm은 모바일 데이터베이스 솔루션의 미래입니다

SQLite는 견고하고 강력하며 입증된 데이터베이스 엔진이며 관계형 데이터베이스는 곧 사라지지 않을 것입니다. 많은 시나리오에서도 트릭을 수행할 수 있는 좋은 ORM이 많이 있습니다.

그러나 최신 동향을 파악하는 것이 중요합니다.

그런 면에서 Realm은 모바일 데이터베이스 개발에 있어 최근 몇 년간 가장 큰 트렌드 중 하나라고 생각합니다.

Realm은 기존 솔루션보다 더 나은 옵션이 될 수 있을 뿐만 아니라 새로운 가능성 측면에서 시야를 넓히고 모바일 데이터베이스 기술의 기준을 높인다는 점에서 개발자에게 가치 있는 데이터를 처리하는 고유한 접근 방식을 제공합니다.

이미 Realm을 사용한 경험이 있습니까? 자유롭게 생각을 공유해 주세요.