This is not the current version. View the latest documentation

Realm Xamarin을 사용하면 안전하고 영속적이며 빠른 여러분의 앱 모델 레이어를 효과적으로 작성할 수 있습니다. 다음을 보세요.

// 모델을 일반적인 C# 클래스로 작성합니다
public class Dog : RealmObject
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Person Owner { get; set; }
}

public class Person : RealmObject
{
    public string Name { get; set; }
    public IList<Dog> Dogs { get; }
}

var realm = Realm.GetInstance();

// 질의에 LINQ를 사용합니다
var puppies = realm.All<Dog>().Where(d => d.Age < 2);

puppies.Count(); // => 아직 개가 추가되지 않았기 때문에 0

// 스레드 안전한 트랜잭션에서 객체를 갱신하고 영속화하기
realm.Write(() =>
{
    var myDog = new Dog { Name = "Rex", Age = 1 };
    realm.Manage(myDog);
});

// 질의는 실시간으로 갱신
puppies.Count(); // => 1

// LINQ 질의 문장도 잘 작동합니다
var oldDogs = from d in realm.All<Dog>() where d.Age > 8 select d;

// 어떤 스레드에서나 질의하고 갱신할 수 있습니다
new Thread(() =>
{
    var realm2 = Realm.GetInstance();

    var theDog = realm2.All<Dog>().Where(d => d.Age == 1).First();
    realm2.Write(() => theDog.Age = 3);
}).Start();

시작하기

Realm은 NuGet을 통해 설치하거나 GitHub에서 소스 코드를 볼 수 있습니다.

나이틀리 빌드도 MyGet에서 받을 수 있습니다. 자세한 것은 아래를 보세요.

사전요구

아래 환경을 지원합니다.

  • Visual Studio 2015 업데이트 2 이상, Xamarin Studio 버전 6.1.1 이상.
  • 네이티브 UI나 Xamarin Forms을 iOS 7 이상 버전에서 사용하기 위해서 Xamarin iOS 버전 10.0.1.10.
  • 네이티브 UI나 Xamarin Forms을 API 레벨 10이상에서 사용하기 위해서 Xamarin Android 버전 7.0.1.3.
  • Xamarin.Mac는 지원하지 않습니다.

Xamarin 개발의 속도를 올리기 위해 Realm을 출시하는 시점의 스테이블 업데이트 채널의 지원 버전을 기초로 합니다. 만약에 몇단계 마이너 버전이 뒤쳐진 Xamarin 버전을 사용한다면 Realm이 Xamarin의 기능에 밀접하게 기반하지 않기 때문에 문제는 되지 않겠지만 베타나 알파 채널의 Xamarin을 쓰는 경우에는 문제가 될 수 있습니다.

PCL 사용자를 위한 중요한 안내 - NuGet 유인 상술을 쓸 수 있습니다. Realm에서 사용하는 플랫폼 특화 프로젝트의 모든 PCL마다 Realm Nuget 패키지를 설치해야합니다. 예: 여러분의 앱이 iOS와 안드로이드를 지원하고 공유된 프로젝트를 사용한다면 각 플랫폼 특화 프로젝트마다 NuGet을 설치해야 합니다.

안드로이드 ABI 지원

몇몇 인스트럭션 집합의 제한 때문에 armeabi ABI 설정을 지원하지 않습니다.

새 Xamarin 프로젝트를 만드는 기본 템플릿이 현재 디버그 빌드에서 모든 ABI 설정이 체크되어 있지만 릴리즈 빌드에서는 armeabi만 체크되어 있습니다. 반드시 여러분의 릴리즈 빌드를 하기 전에 설정을 수정하셔야 합니다.

만약 다른 ABI 체크가 없다면 다른 장비에서 수행할 때 System.TypeInitializationException가 발생할 수 있습니다. 예를 들어 갤럭시 탭 S2와 같은 64비트 장비에서 armeabiarmeabi-v7a가 체크되어 있고 arm64-v8a체크되지 않다면 에러를 발생시킨다.

다른 ABI들의 링크를 막을 좋은 이유가 없다면 armeabi을 제외한 모든 ABI들을 체크하는 것이 최선일 겁니다. 그래서 이렇게 설정할 수 있습니다.

  • armeabi-v7a
  • arm64-v8a
  • x86
  • x86_64

Xamarin 스튜디오에서 이 설정들은 마우스 오른쪽 클릭으로 Options - Build - Android Build - Advanced Tab입니다.

비주얼 스튜디오에서 이 설정들은 마우스 오른쪽 클륵으로 Properties - Android Options - Advanced Tab입니다.

설치

나이틀리 피드를 사용하려면 NuGet 소스를 대신 https://www.myget.org/F/realm-nightly/로 설정합니다. VS2015혹은 Xamarin 스튜디오 6.1 이상에서 NuGet v3를 쓰려면 https://www.myget.org/F/realm-nightly/api/v3/index.json를 사용합니다.

  1. Solution 페인의 여러분의 프로젝트에 “Packeses” 노드에서 기어 버튼을 클릭하고 “Add Packages…“를 클릭합니다.
  2. 검색 창에 “Realm”을 입력합니다.
  3. Realm을 선택하여 추가합니다.
  4. 의존성에서 Fody가 추가된 것을 볼 수 있습니다.

Realm 패키지는 Realm을 사용하기 위해 필요한 모든 것을 가지고 있습니다. 이는 Fody 위버에 의존합니다. 이는 여러분의 RealmObject 서브클래스를 영속적인 객체로 만드는 책임을 집니다.

이 시점에서 패키지를 설치해야 합니다. 프로젝트가 이미 Fody를 사용하고 있다면 기존의 FodyWeavers.xml가 갱신된 것을 볼 수 있습니다. 중요한 점은 FodyWeavers.xml 파일이 RealmWeaver를 포함한 당신이 필요한 모든 위버를 가지고 있다는 것입니다.

이미 추가한 다른 위버가 없었고 Realm을 추가했다면 FodyWeavers.xml 파일의 모양은 다음 예제와 같습니다.

<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
    <RealmWeaver />
</Weavers>
  1. 프로젝트에서 Tools - NuGet Package Manager - Manage Packages for Soultion을 선택합니다.
  2. 가능한 패키지에서 Realm 패키지를 선택합니다.
  3. 오른쪽에 여러분의 프로젝트가 선택되고 Install 버튼이 활성화된 것을 확인합시다.
  4. Install을 누릅니다.
  5. Realm과 Fody가 설치되었다는 다이얼로그가 뜨면 OK를 누릅니다.

Realm 패키지는 Realm을 사용하기 위해 필요한 모든 것을 가지고 있습니다. 이는 Fody 위버에 의존합니다. 이는 여러분의 RealmObject 서브클래스를 영속적인 객체로 만드는 책임을 집니다.

이 시점에서 패키지를 설치해야 합니다. 프로젝트가 이미 Fody를 사용하고 있다면 기존의 FodyWeavers.xml가 갱신된 것을 볼 수 있습니다. 중요한 점은 FodyWeavers.xml 파일이 RealmWeaver를 포함한 당신이 필요한 모든 위버를 가지고 있다는 것입니다.

이미 추가한 다른 위버가 없었고 Realm을 추가했다면 FodyWeavers.xml 파일의 모양은 다음 예제와 같습니다.

<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
    <RealmWeaver />
</Weavers>

Realm 브라우저

우리는 .realm 파일을 읽고 편집하기 위해 독립적인 맥 앱 Realm 브라우저를 제공합니다.

Realm Xamarin이 지원하는 파일 포맷은 현재 브라우저보다 낮습니다. 이런 이유로 새 버전의 브라우저로 realm 파일을 열게 되면 Realm Xamarin에서 동작하지 않게 됩니다. 위의 링크는 호환성을 위한 버전입니다.

Realm Browser

메뉴 항목 Tools > Generate demo database.을 이용하면 샘플 데이터로 테스트 데이터베이스를 생성할 수 있습니다.

앱의 Realm 파일에 대한 도움을 찾기를 원한다면 StackOverflow 답변에서 자세한 설명을 확인하세요.

Realm 브라우저는 맥 앱 스토어에서 사용할 수 있습니다.

API 문서

전체 클래스와 메서드는 전체 API 문서에서 살펴보세요.

예제

저장소의 예제 폴더에서 여러 예제를 찾을 수 있습니다.

도움을 구하려면

모델

Realm 데이터 모델은 프로퍼티를 포함한 전통적인 C# 클래스를 통해 정의합니다. 단순히 RealmObject를 상속받아 Realm 데이터 모델 객체를 생성합니다.

Realm 모델 객체는 대부분의 경우 다른 C# 객체들처럼 동작합니다. 자신만의 메서드 이벤트를 추가할 수 있고 다른 객체들처럼 사용할 수 있습니다. 주요 제약은 생성된 스레드에서만 객체를 사용해야 하고 영속적인 속성은 게터와 세터를 생성해야 한다는 점입니다.

또한 클래스가 퍼블릭이며 파라미터가 없는 생성자를 가져야한다는 점을 주의하세요. 만약 어떤 생성자도 추가하지 않았다면 컴파일러는 생성자를 추가합니다. 만약 최소 하나의 생성자를 추가했다면 공개된 파라미터 없는 생성자가 있는지 확인하세요.

관계와 내부 데이터 구조는 단순히 대상 타입이나 객체들의 리스트를 위한 타입 IList을 포함합니다.

// 개 모델
public class Dog : RealmObject
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Person Owner { get; set; }
}

public class Person : RealmObject
{
    public string Name { get; set; }
    public IList<Dog> Dogs { get; }
}

지원 타입

Realm은 unsigned를 제외한 타입(bool, char, byte, short, int, long, float, double)과 string, DateTimeOffset를 지원합니다. 널도 마찬가지로 지원합니다. 더 이상의 내용은 선택적 속성을 참고하세요.

날짜 타입

우리는 DateTime 대신 표준적인 DateTimeOffset형을 날짜를 표현하는 주 자료형으로 사용하고 있습니다.

이는 DateTime의 모호함 떄문에 마이크로소프트가 한 권고를 따른 것입니다.

이는 100 나노세컨드 단위의 틱의 정밀도로 저장됩니다.

타임존을 붙여 DateTimeOffset을 지정할 수 있지만 Realm은 이를 UTC 값으로 저장합니다. 이는 다른 언어에서 읽을 때 같은 의미를 가지는 인스턴트를 가질 수 있는 모호하지 않은 표현입니다. UtcTicks 대신에 Ticks을 사용하여 값을 비교할 때 혼란의 원인이 됩니다.

관계

RealmObjectRealmObjectIList 속성을 이용해서 서로 연결할 수 있습니다.

RealmList는 .NET의 표준 IList 제너릭 인터페이스를 구현합니다. 클래스에 IList 프로퍼티를 정의하면 get이 사용될 때 RealmList가 생성됩니다. get; 자동 메서드만 지정할 수 있고 이런 리스트에 대해 set할수는 없습니다.

대상이 하나인 관계

N대 1이나 1대 1 관계를 위해 단순히 속성을 RealmObject의 서브클래스에 선언하세요.

public class Dog : RealmObject
{
    // ... 다른 속성 선언
    public Person Owner { get; set; };
}

public class Person : RealmObject
{
    public string Name { get; set; }
}

이 속성을 다음처럼 사용할 수 있습니다.

realm.Write(() =>
{
    var jim = new Person { Name = "Jim" };
    realm.Manage(jim);

    var rex = new Dog { Owner = jim };
    realm.Manage(rex);
});

관계를 끊기 위해서는 단순히 null을 대입합니다.

rex.Owner = null;

RealmObject 속성을 이용할 때 중첩된 속성을 일반적인 속성 문법을 통해 접근할 수 있습니다. 예를 들어 rex.Owner.Address.Country는 객체 그래프를 순회하고 필요시 Realm으로 부터 각 객체를 자동으로 가져오게 됩니다.

대상이 여럿인 관계

대상이 여럿인 관계는 IList 속성을 통해 만들 수 있다. 이런 속성을 이용할 때 RealmList은 비어있을 수도 있고 관계된 RealmObject의 타입을 받을 수도 있습니다.

“개” 속성들을 여러 개와 연결될 수 있는 Person 모델에 추가하기 위해서 IList<Dog> 속성을 단순히 선언합시다. RealmList를 초기화할 필요는 없습니다. – Realm.CreateObject가 이런 작업을 대신 처리합니다. 주어진 PersonDog 사이의 관계 정립을 위해 목록으로 부터 Dog 객체들을 추가하거나 삭제하기만 하세요.

public class Dog : RealmObject
{
    public string Name { get; set; }
}

public class Person : RealmObject
{
    // ... 다른 속성 선언
    public IList<Dog> Dogs { get; }
}
jim.Dogs.Add(rex);
jim.Dogs.Count();  // => 1 -- rex외에 없음

선택적 속성

string, byte[]와 같은 참조 타입과 RealmObject의 값은 null이 될 수 있습니다. 우리는 각 속성 형의 선택적인 범위 전체를 가지기 위해 선택적인 DateTimeOffset? 형도 지원합니다.

int?와 같이 널 값이 허용되는 타입도 전부 지원합니다.

속성 영속성 제어하기

RealmObject을 상속받은 클래스들은 Fody weaver에 의해 컴파일 타임에 처리됩니다. 자동 세터와 게터가 추정될 수 있는 모든 속성은 영구적으로 저장되며 내부의 Realm 저장소와 연결되는 생성된 세터와 게터를 가지게 됩니다.

우리는 몇몇 C# 특성을 영속 제어를 위한 메타데이터로 제공합니다.

속성의 영속화를 막으려면 단순히 [Ignored] 특성을 추가하면 됩니다. 영속화를 막아야 하는 일반적인 예로는 외부 미디어를 사용하기 때문에 바이너리 이미지 대신에 파일의 경로를 저장해야하는 상황등을 영속화를 막아야 하는 상황등이 있습니다.

public string HeadshotPath { get; set; }

// 실행 중 메모리 이미지
[Ignored]
public Image Headshot { get; set; }

커스텀 세터

그들 자체의 세터와 게터 구현을 가진 속성들은 자동으로 무시됩니다. 검증을 위해서는 아래를 사용할 수 있습니다.

private string Email_ { get; set; }

// 영속 Email_ 속성의 검증
public string Email
{
    get { return Email_; }
    set
    {
        if (!value.Contains("@")) throw new Exception("Invalid email address");
        Email_ = value;
    }
}

인덱스가 추가된 속성

현재 스트링, 정수, 불린, DateTimeOffset은 인덱스를 추가할 수 있습니다.

속성을 인덱스하는 것은 속성은 (예를 들어 ==Contains 연산을 사용하여) 동등을 비교하는 것에 비해 많은 성능을 향상시킵니다. 다만 삽입 성능은 조금 느려집니다.

속성을 인덱스화하기 위해 단순히 [Indexed] 특성을 속성 선언에 추가합니다.

public class Person : RealmObject
{
    [Indexed]
    public string Name { get; set; }
    public IList<Dog> Dogs { get; }
}

자동 갱신되는 객체

RealmObject 인스턴스가 관리되는 상태로 바뀌자마자 이는 라이브가 되고 내부의 데이터로 자동 갱신됩니다. 이 말은 객체를 리프레쉬할 필요가 없다는 뜻입니다. 객체의 속성을 수정하면 즉각 다른 인스턴스가 참조하는 같은 객체에 반영됩니다.

public class Dog : RealmObject
{
    public string Name { get; set; }
    public int Age { get; set; }
}

var realm = Realm.GetInstance();
// 설정 끝

var myDog = new Dog { Name = "Fido", Age = 1 };
realm.Write(() =>
{
    realm.Manage(myDog);
});

var myPuppy = realm.All<Dog>().First(d => d.Age == 1);
realm.Write(() =>
{
    myPuppy.age = 2;
}

myDog.Age; // => 2

RealmObject의 이런 특성은 Realm을 빠르고 효과적으로 할 뿐 아니라 여러분의 코드를 단순하고 더 반응적이게 합니다. 예를 들어 UI 코드가 특정 Realm 객체에 기반한다면 UI를 다시 그리기 전에 객체를 리프레쉬하거나 다시 가져올 필요가 없습니다.

Realm 알림을 구독하여 객체의 Realm 데이터가 갱신되는 것을 알 수 있고 이를 이용하여 앱의 UI를 갱신할 수 있습니다.

기본키 특성

[PrimaryKey] 특성은 RealmObject 클래스의 객체 id를 설정하는 하나의 속성에 정의할 수 있다. PrimaryKey를 정의하면 객체들의 참조하고 갱신하는 것이 효과적으로 되고 각 값마다 유일함이 강제된다.

오직 char, 정수형, 문자열만이 PrimaryKey이 될 수 있다. char나 작은 정수형 형을 사용할 때 사용되는 특별한 저장소는 없고 성능상의 이점도 없습니다. 그 형으로 된 속성을 이미가지고 있을 때만 이점이 있습니다.

여러 속성에 [PrimaryKey]을 붙이면 컴파일은 되지만 실행 시에 검증되며 Realm을 열자마자 _Schema validation failed_를 보고하는 예외를 던진다.

한번 PrimaryKey의 객체가 Realm에 추가되면 PrimaryKey는 변경할 수 없다.

같은 키를 가진 다른 객체를 만들면 RealmDuplicatePrimaryKeyValueException을 발생시킨다.

Realm.ObjectForPrimaryKey을 사용하면 LINQ를 사용하는 것 보다 더 날씬한 질의 생성으로 인덱스를 사용해서 빠르게 객체를 질의할 수 있습니다. 문자열, 문자, 정수 키 등으로 오버로드된다.

public class Person : RealmObject
{
    [PrimaryKey]
    public string SSN { get; set; }
    public string Name { get; set; }
    public IList<Dog> Dogs { get; }
}

var objByPK = _realm.ObjectForPrimaryKey<Person>("457-55-5462");

속성 무시

RealmObject 클래스의 속성에 자동생성되지 않은 게터와 세터를 정의하면 이 속성은 영속적이지 않습니다. 명시적으로 속성을 무시하고 싶으면 속성이 무시될 수 있도록 [Ignored] 특성을 적용시킵니다.

모델 상속

Realm Xamarin은 어떤 식으로든 추가로 상속되는 것을 허용하지 않는다. RealmObject 클래스로 부터 간접적으로 상속받은 객체가 발견되면 위버가 실패한다.

컬렉션

Realm은 객체의 그룹을 표현하기 위해 “Realm 컬렉션”이라 부르는 두가지 타입을 가지고 있다.

  1. RealmResults, 질의로부터 가져온 객체들을 표현하는 클래스.
  2. RealmList, 모델에서 대상이 여럿인 관계를 표현하는 클래스.

둘다 IEnumerable interface이기 때문에 지연 열거를 지원합니다. 컬렉션의 크기만 알려지고 객체는 컬렉션을 순회할 때만 메모리에 적재됩니다.

쓰기

객체에 대한 모든 변경(추가, 수정, 삭제)은 쓰기 트랜잭션 내에서 이루어져야 합니다.

스레드 간에 객체를 공유하거나 앱이 실행된 후 재사용하기 위해 그들을 쓰기 트랜잭션내에 연산을 처리하여 Realm으로 영속화를 해야합니다.

쓰기 트랜잭션은 무시못할 오버헤드를 초래하기 때문에 코드에서 쓰기 트랜잭션의 수를 줄이도록 궁리할 필요가 있습니다.

쓰기 트랜잭션은 잠재적으로 다른 디스크 IO 연산처럼 디스크 용량 부족등의 잠재적인 실패 가능성이 있기 때문에 쓰기로 부터 예외를 대비하고 실패를 다루거나 복원하여야 합니다. 다른 복구가능한 에러는 없습니다. 간결함을 위해 우리의 코드 샘플은 이런 에러들을 다루지 않습니다만 제품 어플리케이션에는 포함되어야 합니다.

쓰기 트랜잭션을 작성하는 쉬운 방법 두가지가 있습니다. Realm.BeginWrite()Realm.Write()입니다. 첫번째 Realm.BeginWrite()Dispose 패턴을 구현한 Transaction을 반환합니다. using과 함께 사용할 수 있습니다.

using(var transaction = realm.BeginWrite())
{
    person.FirstName = "John";
    person.Age = 56;
    transaction.Commit();
}

트랜잭션을 명시적으로 Commit해야하는 점을 유의하세요. 그렇지 않으면 자동으로 복구됩니다. 다른 방법은 변경 코드를 Realm.Write()로 감싸는 것입니다.

realm.Write(() =>
{
    person.FirstName = "John";
    person.Age = 56;
});

예외가 발생되지 않으면 기본으로 암시적인 트랜잭션이 커밋됩니다.

객체 만들기

모델을 정의할 때 RealmObject의 서브클래스를 실체화하고 새로운 인스턴스를 Realm에 추가할 수 있습니다. 간단한 모델을 고려합시다.

public class Dog : RealmObject
{
    public string Name { get; set; }
    public int Age { get; set; }
}

이 클래스의 인스턴스를 Realm.Manage() 메서드를 이용하여 영속적인 라이브 객체로 변환할 수 있습니다.

realm.Write(() =>
{
    var myDog = new Dog();
    myDog.Name = "Rex";
    myDog.Age = 10;
    realm.Manage(myDog);
});

또는 `Realm.CreateObject<>()` 메소드를 호출하여 인스턴스를 만들  있습니다.

```csharp
realm.Write(() =>
{
    var myDog = realm.CreateObject<Dog>();  // Realm에 의해 즉시 관리됩니다
    myDog.Name = "Rex";
    myDog.Age = 10;
});

객체를 만든 이후 실시한 모든 변경은 영속적이게 됩니다. (이 변경들은 쓰기 트랜잭션에서 이루어져야 합니다.) 쓰기 트랜잭션이 완료되면 같은 Realm을 사용해서 다른 스레드에서 변경을 하는 것이 가능합니다.

쓰기가 다른 쓰기를 블록하고 만약 진행중인 여러 쓰기가 있다면 다른 스레드를 블록할 것이라는 점을 유의하세요. 이것은 다른 영속적인 도구에서도 비슷합니다. 우리는 이런 상황에서 일반으로 통용되는 최선의 방법인 별도의 스레드에서 쓰기를 해 부담을 감하는 것을 추천합니다.

Realm이 MVCC 아키텍쳐를 사용하기 때문에 읽기는 트랜잭션이 열려 있어도 방해받지 않습니다. 한번에 여러 스레드에서 쓰기를 같이 하지 않는 한 여러분은 잘게 나눈 많은 쓰기 트랜잭션보다 큰 쓰기 트랜잭션을 선호할 것입니다.

객체 갱신하기

쓰기 트랜잭션에서 속성들을 설정하는 방법으로 객체를 갱신할 수 있습니다.

// 트랜잭션에서 객체를 갱신하기
using (var trans = realm.BeginWrite())
{
    author.Name = "Thomas Pynchon";
    trans.Commit();
}

객체 삭제하기

쓰기 트랜잭션에서 Realm.Remove 메서드에 삭제할 객체를 전달합니다.

var cheeseBook = realm.All<Book>().First(b => b.Name == "Cheese");

// 트랜잭션에서 객체를 삭제합니다
using (var trans = realm.BeginWrite())
{
    realm.Remove(cheeseBook);
    trans.Commit();
}

Realm에 저장된 모든 객체들을 저장할 수 있습니다. Realm 파일은 삭제된 용량을 유지해서 앞으로 쓰일 객체에서 재사용을 효과적으로 만들 것입니다.

질의 {#queries}

질의는 표준 LINQ 구문을 구현했습니다.

All 메서드를 사용해서 주어진 타입의 모든 객체를 담은 기본적인 타입 클래스를 얻고 Where를 적용하거나 다른 LINQ 연산을 컬렉션 필터링을 위해 사용할 수 있습니다.

LINQ 구현된 구체적 범위를 보고싶으면 LINQ 서포트 페이지을 보세요.

능숙한 혹은 확장된 구문

var oldDogs = realm.All<Dog>().Where(d => d.Age > 8);

질의 표현 구문

var oldDogs = from d in realm.All<Dog>() where  d.Age > 8 select d;

어떤 문구를 선택하든 반환된 RealmResults 컬렉션은 IQueryable 인터페이스입니다. 그래서 그걸로 반복하거나 다른 처리를 할 수 있습니다.

foreach (var d in oldDogs)
{
    Debug.WriteLine(d.Name);
}

더 많은 질의 예제

LINQ 질의에 익숙하지 않은 경우를 위해 여기 기본적인 질의 문구 예제를 여기에 나열합니다. 확장 문구 와 함께 기본적인 질의를 살펴봅시다.

John 혹은 Peter란 이름을 가진 모든 사용자의 리스트를 출력하려면 다음과 같이 합니다.

var johnsAndPeters = realm.All<Person>().Where(p =>
    p.FirstName == "John" ||
    p.FirstName == "Peter");
var peopleList = johnsAndPeters.ToList();

첫번째 문장은 [`RealmResults`](api/class_realms_1_1_realm_results.html) 클래스의 `johnsAndPeters` 인스턴스를 돌려줍니다. 이것은 "John" "Peter" 이름인 사용자를 찾는 `IQueryable`를 생성합니다. 질의는 반복이나 결과의 수를 세는 추가적인 호출이 있을 때까지 어떤 일도 하지 않습니다.

 예제에서 `ToList` 호출은 Realm 코어에 직접 연결된 질의를 _수행시킵니다_

객체는 **복사되지 않습니다**. 조건에 맞는 객체들의 레퍼런스 리스트를 받아 질의에 맞는 원본 객체를 직접 다룹니다.

결과를 모두 리스트로 받아오는 대신 질의 결과를 표준 C#의 `foreach` 문으로 타고   있습니다.

```csharp
foreach (var person in johnsAndPeters)        // 질의를 탐
{
    Debug.WriteLine(person.Name);
}

논리 연산자

LINQ 표현의 표준 C# 논리 연산자로 질의를 작성할 수 있습니다.

if 문에 기대하는 전제 조건들을 괄호에 묶인 중첩된 표현식으로도 사용할 수 있습니다.

var complexPeople = realm.All<Person>().Where(p =>
    p.LastName == "Doe" &&
    (p.FirstName == "Fred" || p.Score > 35));

타입으로 객체를 가져오기

주어진 타입에 대해 모든 객체를 Realm에서 가져오려면 단순히 realm.All<T>()를 쓰면 된다. 이것은 RealmResults을 반환하는데 질의된 모델 클래스의 전체 인스턴스를 가진다. 위의 예제에서는 Person이다. 집합에 대해 추가적인 제약을 적용하기 위해 LINQ 질의를 사용할 수 있다. RealmResults에 대해 순환을 하기 전까지는 어떠한 오버헤드도 없습니다.

var ap = realm.All<Person>();  // 타입에 관한 전체 아이템
var scorers = ap.Where(p => p.Score > 35);  // 첫번째 검색 절로 제한 두기
var scorerDoe = scorers.Where(p => p.LastName == "Doe");  // 효과적인 AND 

정렬

RealmResults에 의해 지원되는 객체들의 순서에 영향을 미치는 표준 LINQ 절 OrderBy, OrderByDescending, ThenBy, ThenByDescending은 여러 단계의 검색에 사용될 수 있습니다.

그들은 결과에 대한 전체 객체를 읽지 않으며 효과적인 검색 정렬을 지원하는 내부의 쿼리 엔진을 통해 이루어집니다. 정렬을 하기 위해 Where 질의를 실행할 필요가 없습니다. 단지 All으로 부터 질의를 정렬하세요.

주의: 만약 ToList 절을 객체 리스트를 추출하기 위해 사용하고 객체를 위한 LINQ를 이용해서 메모리 상에서 정렬하기 위해 OrderBy를 사용할 수 있습니다. ToList를 표현의 마지막에 사용했는지 확인해주세요.

var highestScore = realm.All<Person>().OrderByDescending(p => p.Score).First();

var sortedPeople = realm.All<Person>()
    .OrderBy(p => p.LastName)
    .ThenBy(p => p.FirstName);

연결 질의

결과는 복제되지 않고 요청 시점에 계산되기 때문에 데이터를 단계적으로 필터링하기 위해 효과적으로 연결 질의를 할 수 있습니다.

var teenagers = realm.All<Person>().Where(p => p.Age >= 13 && p.Age <= 20);
var firstJohn = teenagers.Where(p => p.FirstName == "John").First();

결과 제한

대부분의 다른 데이타베이스 기술들은 질의 결과들을 (SQLite의 LIMIT 키워드와 같이) 페이지를 바꾸는 기능을 제공합니다. 이는 종종 디스크로 부터 너무 많이 읽는 것을 막거나 한번에 메모리로 많은 결과를 가져오는 것을 막기 위해 필요합니다.

Realm의 질의는 지연되고 질의의 결과로 부터 객체들을 명시적으로 접근될 때만 가져오기 때문에 페이지를 바꾸는 행위같은 것은 전적으로 불필요합니다.

만약 UI 관련이나 다른 구현 이유로 질의로 부터 특정한 객체의 부분집합을 원한다면 RealmResults 객체를 가져오고 필요한 객체만 읽으면 됩니다.

만약 LINQ Take를 사용하길 원하더라도 아직 쓸 수 없습니다. 이것이 추가되면 한 명령에 요청한 객체 전부를 열거하고 인스턴스화할 수 있을 것입니다.

Realms

Realm은 우리의 실제 데이터베이스와 동등합니다. 여러 종류의 객체를 가지고 있고 디스크 상 하나의 파일에 대응합니다.

기본 Realm

realm 변수를 항상 optionalPath 지정없이 Realm.GetInstance(string optionalPath)으로 초기화한 것을 보았을 것입니다. 정적 메서드는 스레드에 맞는 Realm 인스턴스를 반환합니다. 이 인스턴스는 Environment.SpecialFolder.Personal에 저장되는 default.realm 파일에 연결됩니다.

Realm 설정하기

Realm.getInstance()을 호출하면 Realm을 손쉽게 시작할 수 있습니다. Realm이 생성되는 여러 측면을 제어할 수 있는RealmConfiguration 객체를 생성해 보다 세밀한 제어를 할 수 있습니다.

RealmConfiguration은 다양한 데이터 베이스 경로를 허용합니다. (한번 설정이 생성되면 경로를 바꿀 수 없는 점을 유의하세요.)

아래와 같이 할 수 있습니다.

  1. 새로운 절대 경로를 전달하여 전체 경로를 변경하기.
  2. 표준 경로의 서브 디렉토리에 Realm 파일들을 두고 상대 경로를 전달하기.
  3. 새로운 파일 이름을 전달하여 Realm 파일 이름을 변경하기.

설정에서 스키마 버전을 지정할 수 있습니다. 여기에 대한 자세한 것은 마이그레이션 섹션을 참조하세요. 개발중에 ShouldDeleteIfMigrationNeeded 속성을 true로 설정할 수 있습니다. 이 설정은 열려있는 파일이 여러분의 스키마와 맞지 않다면 Realm.GetInstance()가 기존의 데이터베이스를 삭제하게 됩니다. 이렇게 설정하면 릴리즈 전에 모델을 다루는게 편해집니다. 하지만 이 설정으로 릴리즈하지 마세요. 사고를 막기 위해서는 #if DEBUG 섹션안에 설정할 수 있습니다.

같은 구성의 Realm을 열기 위해 어떤 설정의 인스턴스를 여기 저기에 쓸 수 있습니다. 다른 스레드에서 같은 Realm을 열기 위해 사용하는 일반적인 사용례입니다.

물론 어떤 객체를 전달하지 않고 기본 값을 바꾸기 위해 기본 설정을 재정의할 수 있습니다.

설정 마다 스레드마다 싱글턴으로 Realm 인스턴스가 유지된다는 것이 중요합니다. 같은 스레드에서 같은 설정이라면 매번 같은 인스턴스를 반환합니다.

Realm 인스턴스 닫기

Realm은 네이티브 메모리 해제와 파일 설명자(descriptors)를 다루기 위해 IDisposable을 구현합니다. 이렇게 하면 인스턴스는 변수가 스코프에서 사라질 때 자동으로 닫힐 수 있습니다.

realm을 닫기 위해 Realm.Close()를 호출할 수 있습니다. 같은 파일을 참조하는 다른 인스턴스들을 닫는다는 것을 유의하세요.

Realm 파일 찾기

앱의 Realm 파일을 찾는 것의 도움이 필요하면 자세한 설명을 StackOverflow의 답변에서 확인하세요.

외부 Realm 파일

표준 .realm 파일을 따라 Realm은 내부적인 자체 연산을 위해 추가적인 파일을 생성하고 관리합니다.

  • .realm.lock - 리소스 락을 의한 락 파일.
  • .realm.note - 노티를 위한 명명된 파이프(named pipe).

이 파일들은 .realm 데이터베이스 파일에 어떤 영향도 미치지 않습니다. 그들의 부모 데이터베이스 파일이 삭제되거나 대체되어도 어떤 에러를 유발하지 않습니다.

Realm 이슈를 보고할 때는 디버깅을 위해 유용한 정보를 포함한 외부 파일들을 .realm 파일과 함께 포함해주세요.

Realm 파일 포함하기

앱에 Realm 파일을 포함하길 원한다면 스택오버플로우의 대단한 답변을 보세요. 어떻게 Xamarin 프로젝트에 리소스로 포함하고 그것을 사용하기 위해 복사하는지 보여줍니다.

클래스 부분집합

어떤 시나리오에서 특정 Realm에 저장될 클래스를 한정짓고 싶을 수 있습니다.

이를 RealmConfigurationObjectClass 속성을 설정해서 할 수 있습니다.

class LoneClass : RealmObject
{
    public string Name { get; set;}
}

var config = new RealmConfiguration("RealmWithOneClass.realm");
config.ObjectClasses = new Type[] {typeof(LoneClass)};

// 또는 Realm에 두개의 클래스를 지정하기
config.ObjectClasses = new Type[] {typeof(Dog), typeof(Cat)};

Realm 파일을 삭제하기

캐시를 삭제하거나 전체 데이터세트를 리셋하는 등의 경우에 디스크로부터 Realm 파일을 전체적으로 삭제하는게 적합할 수 있습니다.

다른 파일들과 달리 Realm은 메모리 맵되어 있고 Realm 인스턴스는 인스턴스의 생애 주기 동안 해당 파일들이 존재하기를 기대합니다.

애플리케이션코드가 Realm에 관련한 전체 파일을 아는 것을 피하기 위해 편의를 위한 메서드 Realm.DeleteRealm(RealmConfiguration)를 제공합니다.

var config = new RealmConfiguration("FileWeThrowAway.realm");
Realm.DeleteRealm(config);
var freshRealm = Realm.GetInstance(config);

스레드

개별적인 스레드에서 모든 것을 동시성과 멀티스레드에 대한 걱정없이 일반 객체처럼 다룰 수 있습니다. (다른 스레드에서 동시에 수정되더라도) 데이터에 접근하기 위해 락을 쓰거나 리소스를 조율할 필요가 없습니다. 단 수정을 위한 연산만 트랜잭션으로 감싸야 합니다.

Realm은 개별 스레드가 항상 Realm의 일관된 뷰를 보는 걸 보장함으로서 동시적인 사용이 쉽도록 합니다. 개별 스레드가 자신만의 스냅샷을 가지기 때문에 같은 Realm을 병렬적으로 여러 스레드에서 수행할 수 있습니다. 그들은 서로에게 일관성이 없는 상태를 보게끔 영향을 주지 않습니다.

알아야 할 유일한 부분은 Realm 객체의 같은 인스턴스들은 여러 스레드에서 공유될 수 없다는 점입니다. 만약 여러 스레드에서 같은 객체에 대한 접근이 필요하다면 그들은 각자의 인스턴스들이 필요합니다. (그렇지 못하면 하나의 스레드에서 변경점은 다른 스레드에 불완전하게 보게되거나 불일관된 데이터를 보게 됩니다.)

다른 스레드에서 변경 바라보기

메인 UI 스레드(혹은 runloop이나 looper의 어떤 스레드)에서 객체는 매 runloop의 단계마다 다른 스레드의 변경은 자동으로 객체에 갱신됩니다. 어떤 시점이든 스냅샷을 다루게 되고 개별 메서드는 언제나 일관성있는 뷰를 보게 되고 다른 스레드에서 무엇이 일어나고 있는지 염려할 필요가 없습니다.

스레드에서 Realm을 초기화했다면 이것의 상태는 최근에 성공한 쓰기 커밋에 기반을 하게 되고 갱신되기 전까지는 그 버전을 유지하게 됩니다. Realm은 매 runloop의 매 단계의 시작마다 자동으로 갱신됩니다. (일반적으로 백그라운드 스레드인 경우에) 만약 스레드에 runloop이 없다면 가장 최근 상태의 트랜잭션으로 전진하기 위해 Realm.Refresh()은 수동으로 호출 해야합니다.

Realm은 쓰기 트랜잭션이 Transaction.Commit()으로 커밋되었을 때도 갱신합니다.

일반적인 이유로 Realm 갱신이 실패하면 그 버전에 사용되었던 디스크 공간이 재사용되는 것을 막기 위해서 어떤 트랜잭션 버전이 “고정”될 수 있고 파일 사이즈가 커질 수 있습니다. 자세한 내용은 우리의 현재 제한사항을 참고하세요.

스레드간 인스턴스 전달하기

Realm, RealmObject, RealmResults, RealmList의 영속적인 인스턴스는 그것이 생성된 스레드에서만 사용할 수 있고 그렇지 않으면 예외를 발생합니다. 이는 Realm이 트랜잭션 버전을 고립시키는 한가지 방식입니다. 그렇지 않으면 잠재적으로 확장된 관계 그래프를 가진 다른 트랜잭션 버전의 스레드들 사이에 객체를 전달할 때 무엇을 해야할지 결정할 수 없습니다.

대신에 스레드 사이에 데이터를 전달할 안전한 방법들이 있습니다. 예를 들어 PrimaryKey의 객체는 그 객체의 PrimaryKey 값으로 표현될 수 있습니다. Realm은 그것의 RealmConfiguration으로 표현 수 있습니다. 대상 스레드는 RealmRealmObject를 그들의 스레드 안전 표현을 이용하여 다시 가져올 수 있습니다. 다시 가져오기는 대상 스레드에 그 버전의 인스턴스를 가져오게 됩니다. 이때 대상 스레드는 원 스레드와 다를 수 있습니다.

스레드 사이에 Realm을 사용하기

같은 Realm 파일을 다른 스레드에서 접근하려면 앱의 다른 스래드마다 다른 인스턴스를 얻기 위해 새로운 Realm을 초기화해야합니다. 같은 설정을 지정하는 한 모든 Realm 인스턴스는 디스크의 같은 파일에 연결됩니다.

여러 스레드에서 Realm 인스턴스를 공유하는 것은 지원되지 않습니다. 같은 realm 파일을 접근하는 Realm 인스턴스는 같은 RealmConfiguration을 사용해야 합니다.

Realm은 많은 양의 데이터를 하나의 트랜잭션에 여러 쓰기를 같이 일괄적으로 기록하는 것이 효과적입니다. Realm 객체는 스레드 안전하지 않고 스레드 사이에 공유될 수 없습니다. 읽기나 쓰기가 필요할 때 각 스레드나 dispatch_queue 마다 Realm 인스턴스를 얻으세요.

알림

안드로이드에서 변경 리스너는 오로지 Looper 스레드에서 작동합니다. 비 Lopper 스레드에서는 대신에 Realm.Refresh()을 수동으로 호출해야 합니다.

Realm 알림

Realm에 데이터를 추가하는 백그라운드 스레드가 있고 여러분의 UI나 다른 스레드에서 리스너를 추가하여 Realm의 변경을 통보받을 수 있습니다. 변경 리스너는 (다른 스레드나 프로세스에서도 물론) Realm이 변경될 때 마다 수행됩니다.

realm.RealmChanged += () =>
{
    // UI를 갱신한다
}

Object 알림

인터페이스 INotifyPropertyChanged를 여러분의 클래스 RealmObject에 붙여 알림을 구독할 수 있습니다. 이렇게 하기 하면 속성이 변경될 떄마다 PropertyChangedEvent가 자동으로 요청됩니다.

public class Account : RealmObject, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public long Balance { get; set; }
}

이제 PropertyChangedEvent 이벤트를 클래스에서 구독하면 Balance 속성이 바뀔때 마다 핸들러는 요청됩니다.

var realm = Realm.GetInstance ();
realm.Write(() => {
    var acc = realm.CreateObject<Account> ();
    acc.PropertyChanged += (sender, e) => { Debug.WriteLine("New value set for " + e.PropertyName); };
    acc.Balance = 10; // => "Balance에 새 값을 설정합니다."
});

이렇게 하면 알림에도 딱 맞지만 Xamarin.Forms에도 데이타바인드를 허용할 수 있다. 더 많은 정보는 Xamarin 문서의 데이터 바인딩에서 MVVM까지를 참고하라.

RealmResult 알림

RealmResult는 라이브 쿼리입니다. 이는 항상 최신의 데이터로 갱신된다는 것입니다. 언제 순회하든 마지막 변경이 적용된 결과를 받습니다.

RealmResult 변경에 대한 알림을 받고 싶다면 RealmResult.SubscribeForNotifications()를 사용하세요. 변경 집합에 대해 호출되어, 무엇이 추가되고 지워지고 변경되었는지 알려줄 델리게이트를 메서드에 전달할 수 있습니다. 이는 전형적인 GUI 목록 갱신에서의 쓰임새입니다.

realm.All<Person>().SubscribeForNotifications ((sender, changes, error) =>
{
    // Access changes.InsertedIndices, changes.DeletedIndices and changes.ModifiedIndices
});

RealmResult을 XAML 데이터바인딩 시나리오에서 사용할 때는 RealmResult.ToNotifyCollectionChanged() 확장 메서드를 사용할 수 있다. 이는 INotifyCollectionChanged가 구현된 버전의 결과를 주고 Xamarin 폼에 적절히 결합될 버전의 결과를 줍니다. 어떻게 사용할 수 있는지는 Xamarin.Forms과 Realm을 이용하여 크로스플랫폼 개발하기 블로그 글을 보세요.

마이그레이션

어떤 데이터베이스를 쓰더라도 데이터 모델은 시간에 따라 바뀌곤 합니다. Realm의 데이터 모델이 표준 C# 클래스로 정의되었기 때문에 모델의 변경은 다른 클래스를 수정하는 것 만큼 쉽습니다. 예를 들어 아래의 Person 모델을 가지고 있다고 가정해보자.

public class Person : RealmObject
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

성과 이름 대신에 FullName 속성을 요구하게 데이터 모델을 갱신하고 싶을 수 있습니다. 그럴 경우 단순히 객체 인터페이스를 아래와 같이 변경합니다.

public class Person : RealmObject
{
    public string FullName { get; set; }
    public int Age { get; set; }
}

이 시점에서 이전 모델 버전에서 어떤 데이터를 저장한다면, 코드 상에 정의된 Realm과 디스크 보이는 Realm의 불일치가 발생합니다. 불일치가 있고 마이그레이션이 수행되지 않으면 기존 파일을 열 때 예외를 발생시킨다.

마이그레이션 수행하기

최소한의 해법은 스키마 버전을 올리는 것입니다. 지정하지 않았다면 realm은 스키마 버전을 0으로 설정합니다. 스키마를 변경하면 설정에서 스키마 버전을 올려야 합니다. 이렇게 하면 우연한 스키마 변경으로 잠재적인 데이터 파괴를 막습니다.

var config = new RealmConfiguration() { SchemaVersion = 1 };

var realm = Realm.GetInstance(config);

만약에 이렇게 해두고 기존의 스키마로 생성된 데이터베이스 파일을 실행하면 전체 FirstNameLastName 속성이 삭제되고 모든 Person은 비어있는 FullName 속성을 가지게 됩니다. Age 속성은 양쪽 버전에 있고 같은 형이라 그대로 남게 됩니다. 여기에 있는 것은 우리가 원하는 것이 아닐 수 있습니다. 우리가 원하는 것은 마이그레이션을 다루는 방법입니다. MigrationCallback 함수를 정의해서 할 수 있습니다. 이 함수는 Migration 객체를 받고 이는 두개의 Realm 속성 OldRealmNewRealm을 가집니다. 이제 예전 스키마에서 새 스키마로 데이터를 복사하고 받아들일 수 있습니다. 단순하게 클래스와 속성을 바꾸면 Realm은 이를 감지하지 못하고 마이그레이션에서 데이터를 복사하여야 한다는 것을 유의하세요.

이 콜백 함수는 낮은 버전의 realm이 열릴 때 정확히 한번 호출됩니다. 이 호출 동안 갱신된 클래스 전체를 마이그레이션 해야합니다.

Person 모델은 더 이상 FirstNameLastName 속성을 포함하지 않으니 형에 관한 API를 통해 접근할 수 없습니다. 하지만 다이나믹 API를 통해 이용하고 같은 것을 얻을 수 있다.

var config = new RealmConfiguration()
    {
        SchemaVersion = 1,
        MigrationCallback = (migration, oldSchemaVersion) =>
        {
            var newPeople = migration.NewRealm.All<Person>();

            // Use the dynamic api for oldPeople so we can access
            // .FirstName and .LastName even though they no longer
            // exist in the class definition.
            var oldPeople = migration.OldRealm.All("Person");

            for (var i = 0; i < newPeople.Count(); i++)
            {
                var oldPerson = oldPeople.ElementAt(i);
                var newPerson = newPeople.ElementAt(i);

                newPerson.FullName = oldPerson.FirstName + " " + oldPerson.LastName;
            }
        }
    };

var realm = Realm.GetInstance(config);

앱이 오래되어 가며 다른 버전들에서 여러 변경점이 생기면 콜백에서 oldSchemaVersion 인자를 점검하여 마이그레이션의 일련을 만듭니다. Person 모델에서 더 나아가 Age 필드에서 Birthday 필드로 변경해봅시다.

public class Person : RealmObject
{
    public string FullName { get; set; }
    public int Age { get; set; }
    public DateTimeOFfset Birthday { get; set; }
}

스키마 버전 2입니다. 마이그레이션 설정은 아래와 같습니다.

var config = new RealmConfiguration()
    {
        SchemaVersion = 2,
        MigrationCallback = (migration, oldSchemaVersion) =>
        {
            var newPeople = migration.NewRealm.All<Person>();
            var oldPeople = migration.OldRealm.All("Person");

            for (var i = 0; i < newPeople.Count(); i++)
            {
                var oldPerson = oldPeople.ElementAt(i);
                var newPerson = newPeople.ElementAt(i);

                // Migrate Person from version 0 to 1: replace FirstName and LastName with FullName
                if (oldSchemaVersion < 1)
                {
                    newPerson.FullName = oldPerson.FirstName + " " + oldPerson.LastName;
                }

                // Migrate Person from version 1 to 2: replace Age with Birthday
                if (oldSchemaVersion <2)
                {
                    newPerson.Birthday = DateTimeOffset.Now.AddYears(-(int)oldPerson.Age);
                }
            }
        }
    };

var realm = Realm.GetInstance(config);

암호화

미국으로부터의 수출 제한이나 금수 조치가 있는 국가에 거주하는 경우 Realm 사용에 대한 제한이 있을 수 있으므로, 저희 라이센스의 수출 규정 준수 조항을 참고하십시오.

Realm이 생성될 때 64바이트 암호화 키를 제공해 AES-256+SHA2를 이용하여 디스크의 데이터베이스 파일을 암호화하는 것을 제공합니다.

var config = new RealmConfiguration("Mine.realm");
config.EncryptionKey = new byte[64] // 키는 반드시 이 사이즈여야 합니다
{
  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
  0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
  0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
  0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
  0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
  0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
  0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
  0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78
};

var realm = Realm.GetInstance(config);  // 암호화된 "Mine.realm" 생성하거나 읽을 것입니다

필요에 따라 디스크에 저장되는 모든 데이터에 AES-256와 SHA-2 HMAC에 의해 검증되는 암호화와 복호화를 투명하게 적용할 수 있습니다. 이를 적용하면, Realm 인스턴스를 얻을 때 마다 같은 암호키를 전달해야 합니다.

앱에 대해 유일한 키를 사용해야 합니다. 위의 예제에 사용된 키는 절대 안됩니다. 더 나아가 사용자마다 개인화된 키를 원한다면 Xamarin.Auth API를 살펴보세요.

암호화된 Realm에 대해 잘못된 키를 지정하거나 키를 지정하는데 실패하면 GetInstance를 호출 할 때 RealmFileAccessErrorException을 얻게 됩니다.

Realm 암호화를 사용할 때 작은 성능 하락이 있습니다. (일반적으로 10% 이하 느려집니다.)

현재 제한사항

Realm은 현재 베타이고 지속적으로 여러 기능을 추가하고 이슈를 해결하며 1.0 릴리스를 향하고 있습니다. 그때까지 우리의 가장 널리 요청 받은 제약리스트를 엮어 두었습니다.

알려진 이슈에 대한 상세한 목록을 얻기 위해서는 우리의 GitHub issues을 참고하세요.

베타 버전에서 빠졌지만 1.0 이전에 포함될 기능입니다.

  • 비동기 질의
  • 종속적 삭제 (이 StackOverflow 답변에 우회법이 있습니다)
  • 컬렉션 알림
  • 기본 값 - 정의를 통하는 일반적인 방법은 위빙 과정에서 지원되지 않습니다
  • 더 많은 LINQ 연산 - 자세한 것은 LINQ 서포트 페이지를 참고하세요.
  • 관계 데이터에 대한 검색
  • 그들의 PrimaryKey를 이용하여 RealmObject를 갱신하기

일반적인 제한

Realm은 유연함과 성능 사이에 균형을 찾는 것을 목표로 합니다. 목적을 이루기 위해 Realm에 정보를 저장하는 여러 측면에 현실적인 제약을 시행하게 됩니다. 예를 들어서 다음과 같습니다.

  1. 클래스 이름은 0부터 57 바이트 사이의 길이여야 합니다. UTF-8 문자열이 지원됩니다. 앱의 초기화 때 제한을 넘게 되면 예외가 던져집니다.
  2. 프로퍼티 이름은 0부터 63 바이트 사이의 길이어야 합니다. UTF-8 문자열이 지원됩니다. 앱의 초기화 때 제한을 넘게 되면 예외가 던져집니다.
  3. iOS 제한: 열린 Realm 파일의 사이즈는 iOS에 연결되어 애플리케이션에 허용된 총 메모리 사이즈보다 클 수 없습니다. 이는 디바이스마다 다르고 메모리 공간이 실행 시점에 어떻게 파편화되었냐에 의존합니다. (여기에 대한 radar 이슈가 있습니다. rdar://17119975) 더 많은 데이터를 저장하길 원한다면 여러 개의 Realm 파일로 분할하고 그들을 필요에 따라 열고 닫을 수 있습니다.

파일 사이즈와 중간 버전의 추적

Realm 데이타베이스는 동등한 SQLite 데이터베이스에 비해 일반적으로 디스크 공간을 더 적게 사용합니다. 만약 기대보다 Realm 파일이 크다면 이것은 RealmObject가 데이터베이스 데이터의 오래된 버전을 참조하기 때문입니다.

데이터의 일관된 뷰를 제공하기 위해 Realm은 런 룹 단계의 시작에서만 접근된 활성 버전을 갱신합니다. 만약 Realm으로 부터 어떤 데이터를 읽었습니다. 그리고는 다른 스레드에서 Realm에 기록하는 동안 오래 수행되는 연산으로 스레드를 막았습니다. 버전은 갱신되지 못하고 Realm은 당신이 필요하지 않은 중간 버전 데이터를 붙잡고 있습니다. 그 결과로 매 쓰기마다 파일의 크기는 증가하게 되지요. 여분의 공간은 결국에는 다음 쓰기에 재사용이 됩니다.

질의응답

일반

제품 애플리케이션에 Realm을 써도 될까요?

Realm은 2012년 부터 상용 제품의 제작에 상요되었습니다. Xamarin C# 제품은 2015년 12월 부터 베타였지만 C++ 코어는 모든 다른 Realm 제품에서 사용되는 것과 동일합니다.

우리의 C# API가 커뮤니티 피드백에 따라 제품이 발전되어 변화하는 것을 기대할 수 있습니다. 더 많은 기능과 버그 수정도 따라오는 것을 보게 될 것입니다.

Realm은 오픈소스인가요?

맞습니다. Realm 내부의 C++ 저장소 엔진과 그 위의 언어 SDK들은 전적으로 오픈소스이고 Apache 2.0 라이선스입니다. 또 Realm은 선택적으로 사용하는 Realm 플랫폼 확장을 가지고 있고 이는 비공개 소스이자만 Realm을 임베디드 데이터베이스로 사용할 때 필수인 건 아닙니다.

돈을 어떻게 벌 생각이에요?

우리는 이미 매출을 올릴 엔터프라이즈 제품과 서비스를 우리 기술에 기반해서 만들었습니다. 만약 현재 릴리즈된 것이나 realm-dotnet보다 더 많은 것이 필요하면 이메일을 주세요. 우리는 언제나 환영하고 있습니다. 우리가 개발을 커밋하는 realm-dotnet은 공개되어 있고 이것은 무료로 유지될 것입니다. Apache 2.0 라이선스에 맞추어 오픈 소스로 유지될 것입니다.

코드에서 “코어”라고 참조하는 부분을 보았습니다. 그게 무엇이에요?

코어는 내부의 C++ 스토리지 엔진입니다. 이것은 현재 오픈소스가 아니지만 Apache 2.0 라이선스 개방될 계획이 있습니다. 코드를 정리하고 이름을 변경하고 내부의 주요 기능을 확정할 것입니다.

내 앱을 빌드할 때 Mixpanel에 네트워크 호출을 하는 것을 보았는데 이건 무엇인가요?

RealmObject를 포함하고 있는 여러분의 어셈블리에 대해 Realm 어셈블리 위버를 호출할 때 Realm은 익명의 통계를 수집합니다. 완전히 익명이고 어떤 Realm 버전을 쓰고 어떤 OS를 쓰는지 등을 파악해서 어떻게 제품을 향상하고 어떤 지원을 중단할 수 있는지 우리가 파악할 수 있게 합니다. 실제 앱이 수행될 때 혹은 여러분의 디바이스에서 수행될 때는 어떤 호출도 없습니다. - 빌드 과정에 Fody에 의해 여러분의 어셈블리를 위브할때만 호출이 됩니다. 정확히 어떻게 무엇을 수집하는지는 소스 코드에서 확인하실 수 있습니다.

포터블 (PCL) 라이브러리와 함께 쓸 수 있나요?

실행파일에 iOS 또는 안드로이드 버전의 Realm을 사용할 필요가 있지만 우리는 PCL을 제공하고 있고 Realm API를 컴파일할 때 사용할 수 있습니다. NuGet 유인 상술입니다. NuGet을 통해 Realm을 PCL 프로젝트에 추가할 때 이는 실행 파일에 추가되고 동작됩니다.

이 PCL이 Realm을 임의의 .NET 플랫폼에서 사용할 수 있다는 의미는 아닙니다. Realm은 각 플랫폼으로 포팅되어야 할 C++ 코어에 근간하고 있습니다.

Realm 모바일 플랫폼

Realm 모바일 플랫폼은 네트워크를 통해 Realm 모바일 데이터베이스를 확장해 여러 장비를 통해서 데이터의 자동 동기화를 활성화합니다.

Xamarin에서도 이를 위해 작업하고 있습니다!

트러블 슈팅

크래쉬 보고

우리는 애플리케이션에서 크래쉬 리포터를 사용하는 것을 장려합니다. 많은 Realm 연산은 (다른 디스크 IO와 같이) 잠재적으로 실시간에 실패할 수 있습니다. 애플리케이션으로 부터 수집된 크래쉬 보고는 문제가 여러분(이나 우리)에게 있는지 확인할 수 있고 에러 처리를 향상하고 크래쉬 버그를 잡게 합니다.

대부분의 상업 크래쉬 리포터들은 로그 수집 옵션이 있습니다. 우리는 강하게 이 기능을 활성화하는 것을 추천합니다. Realm은 예외가 발생하거나 복구불가능한 상황일 때 (사용자 데이터에 관련되지 않은) 메타데이터 정보를 로그로 남깁니다. 이 메시지들은 문제가 발생했을 때 디버그 할 때 도움이 됩니다.

Realm 이슈 보고하기

Realm에 이슈를 찾으면 우리가 이슈를 이해하거나 재연할 수 있도록 최대한 많은 정보를 담아 GitHub에 이슈를 발행하거나 help@realm.io로 메일을 주세요.

아래의 정보는 우리에게 매우 유용합니다.

  1. 목표.
  2. 기대 결과.
  3. 실제 결과.
  4. 재연할 수 있는 단계.
  5. 이슈를 특정짓는 코드 샘플 (빌드할 수 있는 전체 Xamarin 프로젝트가 우리에게 이상적입니다).
  6. Realm, OS X나 Windows, Xamarin/Visual Studio의 버전 정보들
  7. 대상이 iOS면 Xcode, 대상이 안드로이드인 경우 NDK 정보.
  8. 버그가 발생한 플랫폼, OS 버전, 아키텍쳐 (예: 64 비트 iOS 8.1).
  9. 크래쉬 로그, 스택 트레이스. 자세한 것은 크래쉬 보고를 살펴보세요.

클래스에 속성이 없다는 예외가 발생하면

System.InvalidOperationException를 “No properties in class, has linker stripped it?” 메시지와 함께 받을 수 있습니다.

알려진 두가지 원인이 있습니다.

  1. 거의 Fody가 잘못되어서 위브된 RealmObjects가 없는 경우이거나,
  2. RealmObjects가 스트립된 속성을 가지고 있어 Realm에 비어있게 전달된 경우.

첫번째 경우는 RealmSchema에서 예외가 발생합니다. 자세한 내용은 위브에 실패할 때를 보세요.

두번째 경우는 ObjectSchema로 부터 예외가 발생합니다. 우리는 Linker BeahviourLink All로 설정하고 그들의 클래스 정의에 [Preserve(AllMembers = true)]을 누락했을 때 몇몇 사용자들이 문제에 빠지는 것을 보았습니다. 링커들은 코드에서 명시적으로 참조하는 것만 보호합니다. 영속적으로 보관되어야 속성들이 있는데 어디에서도 참조되지 않아 제거되면 데이터베이스가 불일치에 빠질 수 있습니다.

기본적으로 Realm은 어셈블리에 있는 모든 RealmObject으로 표현되는 스키마를 빌드합니다. 이는 게으르게 이루어지고 GetInstance()가 처음 호출될 때 까지 호출되지 않습니다. 이는 최대 한번 이루어지며 메모리 상에 결과가 캐쉬됩니다.

주어진 어셈블리에서 클래스 중 어떤 것만 저장하고 싶다면 클래스 부분집합을 정의하고 GetInstance(myConf)의 설정을 이용하세요. 이는 전체 빌드를 막고 또 예외도 막습니다.

아니면 사용하지 않는 클래스에 Preserve 특성을 지정하세요.

Fody: 처리되지 않는 예외 발생

이런 기본적인 실패는 이미 빌드된 프로젝트에 새로운 RealmObject 서브클래스를 추가하는 것으로 간단히 발생시킬 수 있습니다.

Build or Run을 선택하면 Fody Weaver를 실행할만큼 충분히 프로젝트를 재빌드하지 않습니다.

단순히 프로젝트에 대해 리빌드를 선택하면 에러없이 빌드됩니다.

위브에 실패할 때

클래스가 위브되지 않았다는 클래스에 관한 빌드 로그에서 워닝을 볼 수 있습니다. 이는 Fody 에 패키지가 완전히 설치되지 않았다는 것을 의미합니다.

첫번째로 RealmWeaver에 대한 항목이 FodyWeavers.xml에 있는지 확인하세요.

설치는 RealmWeaver.Fody.dll를 여러분의 솔루션 디렉토리의 Tools에 직접적으로 복사합니다. (이것은 보통 프로젝트 디렉토리의 이웃입니다.) 이것은 프로젝트의 어디에도 연결되어 있지 않지만 Fody가 DLL을 찾기 위해 그 자리에 있어야 합니다.

Fody의 설치가 실패했을 가능성도 있습니다. Visual Studio 2015와 3.2 버전 이전의 NuGet 패키지 매니저를 쓸 때 경험할 수 있습니다. 이 창이 뜨면 .csproj 파일을 텍스트 에디터에서 열어서 Fody.targets를 포함한 라인이 다음과 같이 있는지 확인해 보세요.

<Import Project="..\packages\Fody.1.29.3\build\portable-net+sl+win+wpa+wp\Fody.targets"
    Condition="Exists('..\packages\Fody.1.29.3\build\portable-net+sl+win+wpa+wp\Fody.targets')" />

단순히 NuGet 패키지 매니저를 최신 버전으로 올리는 것 만으로 이 문제가 해결됩니다.

동작하지 않으면 Fody와 Microsoft.Bcl.Build.targets 사이에 문제일 수 있습니다. .csproj 파일로 부터 아래의 라인을 제거하는게 도움이 될 수 있습니다.

<Import Project="..\..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />

여기에 대해 자세한 내용은 이 StackOverflow 답변를 살펴보세요.

Realm Core Binary 다운로드 실패

Realm을 빌드할 때 처리의 한 부분으로 코어 라이브러리를 정적 라이브러리로 다운받고 Realm 프로젝트에 통합하는 과정이 있습니다. 이것은 wrappers의 한 부분으로 Makefile로 빌드됩니다.

어떤 경우에 아래의 에러와 함께 코어 바이너리 다운이 실패합니다.

Downloading core failed. Please try again once you have an Internet connection.

아래의 이유에 따라 에러가 발생합니다.

  1. 미국의 금수조치 목록에 포함된 지역의 IP 주소를 사용하는 경우. 미국 법률에 따라 Realm은 이 지역에서 사용할 수 없습니다. 자세한 정보를 알고 싶다면 라이선스를 살펴보세요.
  2. 중국 본토에 있다면 국가 수준의 방화벽 때문에 CloudFlare나 Amazon AWS S3 서비스를 상황에 따라 사용할 수 없을 수 있습니다. 자세한 내용은 우리 다른 제품의 해당 이슈를 참조하세요.
  3. Amazon AWS S3의 서비스에 이슈가 있을 수 있습니다. AWS Service 상태 대시보드를 참고하고 다시 시도하세요.