최신 영문 문서를 보려면 이곳을 참고하세요.

소개

Realm JavaScript을 사용하면 앱의 모델 레이어를 안전하고 지속적이며 빠르고 효율적으로 작성할 수 있습니다. React NativeNode.js와 함께 사용할 수 있습니다.

React Native를 사용하는 예제입니다.

// Define your models and their properties
class Car {}
Car.schema = {
  name: 'Car',
  properties: {
    make:  'string',
    model: 'string',
    miles: 'int',
  }
};
class Person {}
Person.schema = {
  name: 'Person',
  properties: {
    name:    {type: 'string'},
    cars:    {type: 'list', objectType: 'Car'},
    picture: {type: 'data', optional: true}, // optional property
  }
};

// Get the default Realm with support for our objects
let realm = new Realm({schema: [Car, Person]});

// Create Realm objects and write to local storage
realm.write(() => {
  let myCar = realm.create('Car', {
    make: 'Honda',
    model: 'Civic',
    miles: 1000,
  });
  myCar.miles += 20; // Update a property value
});

// Query Realm for all cars with a high mileage
let cars = realm.objects('Car').filtered('miles > 1000');

// Will return a Results object with our 1 car
cars.length // => 1

// Add another car
realm.write(() => {
  let myCar = realm.create('Car', {
    make: 'Ford',
    model: 'Focus',
    miles: 2000,
  });

// Query results are updated in realtime
cars.length // => 2

시작하기

설치

npm으로 Realm JavaScript를 설치하려면 아래 설명을 따르면 됩니다. GitHub에서 소스를 볼 수도 있습니다.

준비 사항

  • React Native 애플리케이션을 실행할 수 있는 환경이 설정돼야 합니다. React Native instructions를 참고하세요.
  • iOS와 안드로이드 앱 모두 Realm을 사용할 수 있습니다.
  • React Native 0.20.0 상위 버전을 지원합니다.
  • 최신 React Native 패키지 매니저 (rnpm)가 전역으로 설치돼 있는지 확인하세요.

    npm install -g rnpm

설치

  • 새 React Native 프로젝트를 생성합니다.

    react-native init <project-name>
  • (cd <project-name>) 명령어로 새 프로젝트 디렉토리로 이동한 후 realm 디펜던시를 추가합니다.

    npm install --save realm
  • 다음으로 프로젝트를 realm native 모듈에 연결합니다.

    • React Native >= 0.31.0

      react-native link realm
    • React Native < 0.31.0

      rnpm link realm

Android 개발 시 경고: 버전에 따라 rnpm이 잘못된 설정을 생성할 수 있습니다. android/settings.gradleandroid/app/build.gradle와 같은 Gradle이 제대로 업데이트되지만 Realm 모듈을 추가하지 못할 수 있습니다. 이 경우 라이브러리를 다음 단계에 따라 수동으로 연결하세요.

  1. android/settings.gradle에 다음 코드를 추가합니다.

    gradle include ':realm' project(':realm').projectDir = new File(rootProject.projectDir, '../node_modules/realm/android')

  2. android/app/build.gradle 내의 디펜던시에 다음 컴파일 코드를 추가하세요.

    gradle dependencies { compile project(':realm') }

  3. MainApplication.java에서 패키지를 import하고 연결합니다.

    import io.realm.react.RealmReactPackage; // add this import
    
    public class MainApplication extends Application implements ReactApplication {
        @Override
        protected List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
                new MainReactPackage(),
                new RealmReactPackage() // add this line
            );
        }
    }

모든 준비를 마쳤습니다. 실제 Realm을 확인하려면 index.ios.jsindex.android.js 내의 class <project-name>을 아래와 같이 정의합니다.

const Realm = require('realm');

class <project-name> extends Component {
 render() {
   let realm = new Realm({
     schema: [{name: 'Dog', properties: {name: 'string'}}]
   });

   realm.write(() => {
     realm.create('Dog', {name: 'Rex'});
   });

   return (
     <View style={styles.container}>
       <Text style={styles.welcome}>
         Count of Dogs in Realm: {realm.objects('Dog').length}
       </Text>
     </View>
   );
 }
}

이제 앱을 디바이스나 시뮬레이터에서 실행할 수 있습니다.

Realm Node.js SDK 개발자 에디션에 대한 설명입니다. 프로페셔널이나 엔터프라이즈 에디션을 다운로드했다면 이메일로 전달받은 설명을 참고해 주세요.

Node 패키지 매니저를 사용해서 Realm Node.js를 설치합니다.

npm install --save realm

SDK를 사용하려면 애플리케이션 내에서 require('realm')을 적용합니다.

'use strict';

var Realm = require('realm');

var realm = new Realm({
  schema: [{name: 'Dog', properties: {name: 'string'}}]
});

realm.write(() => {
  realm.create('Dog', {name: 'Rex'});
});

예제 설치

예제를 위한 부가 저장소를 Github에서 클론할 수 있습니다. git clone https://github.com/realm/realm-js.git

클론된 디렉토리로 이동한 후 서브모듈을 업데이트합니다. cd realm-js git submodule update --init --recursive

안드로이드라면 NDK 설치가 필요하며 ANDROID_NDK 환경 변수를 설정해야 합니다. export ANDROID_NDK=/usr/local/Cellar/android-ndk/r10e

React Native 예제는 examples 디렉토리에 있습니다. 각 예제에서 npm install을 실행해야 합니다.

Getting Help

Crashlytics나 HockeyApp와 같은 크래시 리포터를 사용하고 있다면 로그 컬렉션을 활성화하세요. Realm은 예외가 발생하거나 회복불가한 상황이 생기면 사용자 정보를 제외한 메타데이터 정보를 로그로 남기므로 이상이 생긴 경우의 디버깅에 활용할 수 있습니다.

모델

Realm 데이터 모델은 초기화하는 동안 Realm으로 전달된 스키마 정보로 정의됩니다. 객체의 스키마는 객체의 name과 속성 집합으로 구성되며, 속성 집합은 name타입, 객체와 리스트 속성을 위한 objectType을 각각 가집니다. optional이나 default 값으로 각 속성을 지정할 수도 있습니다.

var Realm = require('realm');

const CarSchema = {
  name: 'Car',
  properties: {
    make:  'string',
    model: 'string',
    miles: {type: 'int', default: 0},
  }
};
const PersonSchema = {
  name: 'Person',
  properties: {
    name:     'string',
    birthday: 'date',
    cars:     {type: 'list', objectType: 'Car'},
    picture:  {type: 'data', optional: true}, // optional property
  }
};

// Initialize a Realm with Car and Person models
let realm = new Realm({schema: [CarSchema, PersonSchema]});

객체를 기존 클래스에서 상속받기 원한다면 객체 생성자에서 스키마를 정의하고 Realm을 만들때 이를 전달합니다.

class Person {
  get ageSeconds() {
    return Math.floor((Date.now() - this.birthday.getTime()));
  }
  get age() {
    return ageSeconds() / 31557600000;
  }
}

Person.schema = PersonSchema;

// Note here we are passing in the `Person` constructor
let realm = new Realm({schema: [CarSchema, Person]});

객체 모델을 정의했으면 객체를 만들거나 Realm으로부터 가져올 수 있습니다.

realm.write(() => {
  let car = realm.create('Car', {
    make: 'Honda',
    model: 'Civic',
    miles: 750,
  });

  // you can access and set all properties defined in your model
  console.log('Car type is ' + car.make + ' ' + car.model);
  car.miles = 1500;
});

기본 속성 타입

Realm은 bool, int, float, double, string, data, date와 같은 기본 타입을 지원합니다.

  • bool 속성은 JavaScript의 Boolean 객체에 매핑됩니다.
  • int, float, double 속성은 JavaScript의 Number 객체에 매핑됩니다. 내부적으로 ‘int’와 ‘double’은 64 비트로, float은 32 비트로 저장됩니다.
  • string 속성은 String으로 매핑됩니다.
  • data 속성은 ArrayBuffer로 매핑됩니다.
  • date 속성은 Date로 매핑됩니다.

단일 항목을 가진 딕셔너리로 지정하는 대신, 타입만 지정해서 간단하게 기본 속성을 지정할 수도 있습니다.

const CarSchema = {
  name: 'Car',
  properties: {
    // The following property types are equivalent
    make:   {type: 'string'},
    model: 'string',
  }
}

객체 속성

객체 타입의 경우 참조하는 객체 스키마의 name 속성을 지정합니다.

const PersonSchema = {
  name: 'Person',
  properties: {
    // All of the following property definitions are equivalent
    car: {type: 'Car'},
    van: 'Car',
  }
};

객체 속성을 사용할 때 참조한 모든 타입이 Realm을 열 때 사용한 스키마에 있는지 확인해야 합니다.

// CarSchema is needed since PersonSchema contains properties of type 'Car'
let realm = new Realm({schema: [CarSchema, PersonSchema]});

객체 속성에 접근할 경우, 일반적인 속성 구문을 사용해서 중첩된 속성에 접근할 수 있습니다.

realm.write(() => {
  var nameString = person.car.name;
  person.car.miles = 1100;

  // create a new Car by setting the property to valid JSON
  person.van = {make: 'Ford', model: 'Transit'};

  // set both properties to the same car instance
  person.car = person.van;
});

리스트 속성

리스트 속성을 사용할 경우 list라고 속성 타입을 특정해야 하며, objectType도 마찬가지입니다.

const PersonSchema = {
  name: 'Person',
  properties: {
    cars: {type: 'list', objectType: 'Car'},
  }
}

리스트 속성에 접근할 때 List 객체가 반환됩니다. List는 일반 JavaScript 배열과 유사한 메서드를 지닙니다. 가장 큰 차이점은 List의 변경 사항이 자동으로 기본 Realm에 유지된다는 점입니다. 또한 List는 자신을 만든 기본 객체에 속합니다. List 인스턴스는 소유 객체의 속성에 접근해서 가져올 수 있으며, 수동으로 만들 수 없습니다.

let carList = person.cars;

// Add new cars to the list
realm.write(() => {
  carList.push({make: 'Honda', model: 'Accord', miles: 100});
  carList.push({make: 'Toyota', model: 'Prius', miles: 200});
});

let secondCar = carList[1].model;  // access using an array index

선택 속성

속성 정의에서 optional 지정자를 사용해서 선택 혹은 필수 속성을 선언할 수 있습니다.

const PersonSchema = {
  name: 'Person',
  properties: {
    name:     {type: 'string'},               // 필수 속성
    birthday: {type: 'date', optional: true}, // 선택 속성

    // 객체 속성은 언제나 optional입니다.
    car:      {type: 'Car'},
  }
};

let realm = new Realm({schema: [PersonSchema, CarSchema]});

realm.write(() => {
  // 선택 속성은 생성할때 `null` 또는 `undefined`로 설정할 수 있습니다
  let charlie = realm.create('Person', {
    name: 'Charlie',
    birthday: new Date(1995, 11, 25),
    car: null,
  });

  // 선택 속성은 null`, `undefined`, 혹은 null이 아닌 새 값으로
  // 설정될 수 있습니다
  charlie.birthday = undefined;
  charlie.car = {make: 'Honda', model: 'Accord', miles: 10000};
});

위 예제처럼 객체 속성은 항상 선택 속성이며, optional을 지정하지 않아도 됩니다. 리스트 속성은 선택 속성으로 지정하거나 null로 설정할 수 없습니다. 리스트를 초기화해서 지우려면 빈 배열로 설정합니다.

기본 속성값

속성 정의을 정의할때 default 지정자를 설정해서 기본 속성값을 설정할 수 있습니다. 객체를 만들 때 속성을 지정하지 않으면 기본값을 사용합니다.

const CarSchema = {
  name: 'Car',
  properties: {
    make:  {type: 'string'},
    model: {type: 'string'},
    drive: {type: 'string', default: 'fwd'},
    miles: {type: 'int',    default: 0}
  }
};

realm.write(() => {
  // 지정하지 않은 `miles` 속성에는 기본값 `0`이 설정됩니다.
  // 지정한 `drive` 속성은 기본 값이 새로운 값으로 대체됩니다.
  realm.create('Car', {make: 'Honda', model: 'Accord', drive: 'awd'});
});

인덱스 속성

속성 정의시 indexed 지정자를 사용해서 속성이 인덱싱되도록 할 수 있습니다. 인덱싱은 int, string, bool 타입에서 지원됩니다.

var BookSchema = {
  name: 'Book',
  properties: {
    name: { type: 'string', indexed: true },
    price: 'float'
  }
};

속성을 인덱싱하면 데이터를 추가할 때의 속도가 느려지지만, 속성이 일치하는지 비교하는 쿼리 속도가 빨라집니다.

기본 키

객체 모델의 stringint 속성에 primaryKey` 속성을 지정할 수 있습니다. 기본 키를 지정하면 객체를 빠르게 찾고 효율적으로 업데이트할 수 있으며, 고유한 값을 지니도록 강제할 수 있습니다. 기본 키를 가진 객체를 Realm에 추가한 다음에는 기본 키를 변경할 수 없습니다.

const BookSchema = {
  name: 'Book',
  primaryKey: 'id',
  properties: {
    id:    'int',    // 기본 키
    title: 'string',
    price: 'float'
  }
};

기본 키 속성은 자동으로 인덱싱됩니다.

쓰기

추가, 수정, 삭제 등 객체의 모든 변화는 쓰기 트랜잭션 내에서 이뤄져야 합니다.

쓰기 트랜잭션은 무시할 수 없는 오버 헤드가 발생하므로 사용을 최소화하도록 코드를 작성하는 것이 좋습니다.

객체 생성

앞서 설명한대로 create 메서드로 객체를 생성합니다.

let realm = new Realm({schema: [CarSchema]);

realm.write(() => {
  realm.create('Car', {make: 'Honda', model: 'Accord', drive: 'awd'});
});

중첩 객체

객체 속성을 가지고 있는 객체는 자식 속성 각각의 JSON 값을 특정하여 속성의 값을 재귀적으로 생성할 수 있습니다.

let realm = new Realm({schema: [PersonSchema, CarSchema]);

realm.write(() => {
  realm.create('Person', {
    name: 'Joe',
    // 중첩된 객체를 한 번에 재귀적으로 생성할 수 있습니다
    car: {make: 'Honda', model: 'Accord', drive: 'awd'},
  });
});

객체 업데이트

속성 업데이트

쓰기 트랜잭션 내에서 객체의 속성 값을 설정하고 객체를 업데이트할 수 있습니다.

realm.write(() => {
  car.miles = 1100;
});

기본 키를 사용한 객체 생성 혹은 업데이트

모델 클래스에 기본 키를 설정했다면 Realm이 기본 키를 바탕으로 새 객체를 생성하거나 기존 객체를 업데이트하도록 할 수 있습니다. create 메서드의 세 번째 인자를 true로 설정하면 됩니다.

realm.write(() => {
  // 책 객체를 생성하고 저장합니다
  realm.create('Book', {id: 1, title: 'Recipes', price: 35});

  // 본키에 맞는 book을 새 가격으로 갱신합니다
  realm.create('Book', {id: 1, price: 55}, true);
});

위 예제에서는 id 값이 1인 객체가 이미 있고, 세 번째 인자를 true로 넘겼기 때문에, 새 객체가 만들어지는 대신 기존 객체의 price 속성이 업데이트됩니다. 또한 name 속성이 생략되었으므로 원래 값이 유지됩니다. 기본 키 속성으로 객체를 만들거나 업데이트하려면 기본 키를 지정해야 합니다.

객체 삭제

쓰기 트랜잭션 안에서 delete 메서드를 호출하면 객체를 삭제할 수 있습니다.

realm.write(() => {
  // 책을 생성합니다
  let book = realm.create('Book', {id: 1, title: 'Recipes', price: 35});

  // 을 삭제합니다
  realm.delete(book);

  // `Results`, `List`나 자바스크립트 `Array`를 전달하여
  // 여러권의 책을 삭제할 수 있습니다
  let allBooks = realm.objects('Book');
  realm.delete(allBooks); // Deletes all books
});

쿼리

쿼리를 사용하면 Realm에서 한 가지 타입의 객체를 가져올 수 있으며, 그 결과를 필터링하거나 정렬할 수도 있습니다. 쿼리와 속성 접근을 포함한 Realm의 모든 쿼리는 지연 형태입니다. 객체와 속성에 접근할 때만 데이터를 읽습니다. 이 방식으로 많은 양의 데이터를 효율적으로 처리할 수 있습니다.

쿼리를 수행하면 Results 객체가 반환됩니다. Results는 단순한 데이터 뷰로 변경할 수 없습니다.

Realm에서 객체를 검색하는 가장 기본적인 방법은 Realmobjects 메서드를 사용해서 인자로 지정한 타입의 객체를 모두 가져오는 것입니다.

let dogs = realm.objects('Dog'); // Realm에서 개를 전부 가져옵니다

필터링

쿼리 문자열에 filtered 메서드를 호출해서 Results를 필터링할 수 있습니다.

예를 들어 다음 코드는 색상이 tan이고 이름이 B로 시작하는 모든 개를 가져옵니다.

let dogs = realm.objects('Dog');
let tanDogs = dogs.filtered('color = "tan" AND name BEGINSWITH "B"');

현재 쿼리 언어로는 NSPredicate 구문의 하위 집합만 지원됩니다. 숫자 속성에는 ==, !=, >, >=, <, <= 등 기본적인 비교 연산자가 지원됩니다. 문자열 속성에는 ==, BEGINSWITH, ENDSWITH, CONTAINS가 지원됩니다. 대소문자를 구별하는 문자열 비교는 연산자에 [c]를 붙여서 ==[c], BEGINSWITH[c]와 같은 형태로 사용합니다. 연결된 객체나 자식 객체의 속성을 필터링하려면 car.color == 'blue'와 같이 키패스를 지정합니다.

정렬

Results를 사용하면 하나 혹은 여러 속성을 기반으로 정렬 기준이나 순서를 지정할 수 있습니다. 예를 들어 다음 예제에서는 윗 줄에서 반환된 자동차를 주행거리 수대로 정렬합니다.

let hondas = realm.objects('Car').filtered('make = "Honda"');

// 주행거리 순으로 혼다 자동차를 정렬합니다
let sortedHondas = hondas.sorted('miles');

Results의 순서는 쿼리가 정렬될때만 일관적으로 유지됩니다. 성능을 유지하기 위해 삽입 순서는 보장되지 않습니다.

Results 자동 업데이트

Results 인스턴스는 실시간 데이터로 자동 업데이트되는 뷰이므로 결과를 다시 가져올 필요가 없습니다. 쿼리에 영향을 주는 객체 수정이 일어나는 즉시 Results에 변화가 반영됩니다.

let hondas = realm.objects('Car').filtered('make = "Honda"');
// hondas.length == 0

realm.write(() => {
  realm.create('Car', {make: 'Honda', model: 'RSX'});
});
// hondas.length == 1

이 변화는 objects, filtered, sorted 등에 의해 반환된 모든 Results 인스턴스에 적용됩니다.

이런 Results의 속성은 Realm을 빠르고 효율적으로 유지할 뿐만 아니라 더 간단하고 반응성이 좋은 코드를 만들 수 있게 합니다. 예를 들어 뷰가 어떤 쿼리의 결과를 반영한다면 Results를 속성에 저장하고, 사용 전에 데이터를 새로 고칠 필요가 없습니다.

알림을 구독하면 Results를 다시 가져올 필요없이 Realm 데이터가 업데이트되는 시점을 알고, 앱의 UI를 새로 고쳐야하는 시간을 알려줄 수 있습니다.

결과 제한

대부분의 다른 데이터베이스는 SQLite의 ‘LIMIT’ 키워드와 같이 쿼리의 결과를 ‘페이지 매김(paginate)’하는 기능을 제공합니다. 이는 종종 디스크에서 너무 많이 읽어 오거나, 너무 많은 결과를 한 번에 메모리로 가져오는 것을 피하기 위해서입니다.

반면 Realm의 쿼리는 지연식이므로 쿼리에서 객체를 명시적으로 접근할 때만 객체를 불러오며, 이런 페이지 매김 동작이 필요하지 않습니다.

UI과 관련되거나 다른 구현 상의 이유로 객체의 특정 하위집합이 필요하다면 간단히 Results 객체를 가져와서 필요한 객체만 읽으면 됩니다.

let cars = realm.objects('Car');

// 처음 5개의 차만 가지고 옵니다
let firstCars = cars.slice(0, 5);

Realms

다중 Realm

다중 Realm을 여러 위치에 보관하는 것이 유용할 경우가 있습니다. 예를 들어 Realm 파일에 기존의 메인 Realm과 함께 Realm 파일에 앱과 일부 데이터를 묶을 수도 있습니다. Realm 객체를 초기화할 때 path 인자를 지정하면 Realm 파일의 위치를 지정할 수 있습니다. 모든 경로는 애플리케이션의 쓰기 가능한 문서 디렉토리부터의 상대 경로입니다.

// Open a realm at another path
let realmAtAnotherPath = new Realm({
  path: 'anotherRealm.realm',
  schema: [CarSchema]
});

기본 Realm 경로

앞서 본 모든 예에서는 경로 인자가 생략돼 있으며, 이런 경우 기본 Realm 경로가 사용됩니다. Realm.defaultPath 전역 속성을 사용해서 기본 Realm 경로에 액세스하거나 변경할 수 있습니다.

스키마 버전

Realm을 열 때 사용하는 마지막 옵션은 schemaVersion 속성이며, 생략할 경우 기본 값은 0 입니다. 이전 데이터에서 스키마를 변경한 경우에는 반드시 Realm을 초기화하는 시점에서 schemaVersion을 지정해야 합니다. 스키마를 업데이트하고 schemaVersion을 업데이트하지 않으면 예외가 발생합니다.

const PersonSchema = {
  name: 'Person',
  properties: {
    name: 'string'
  }
};

// schemaVersion의 기본 값은 0
let realm = new Realm({schema: [PersonSchema]});

const UpdatedPersonSchema = {
  // 스키마 이름이 같기 때문에 Realm의
  // 이전 `Person` 객체가 갱신됩니다
  name: 'Person',
  properties: {
    name: 'string',
    dog:  'Dog'     // 새 속성
  }
};

// 스키마가 업데이트됐지만 `schemaVersion`을 지정하지 않아서 예외가 발생합니다.
let realm = new Realm({schema: [UpdatedPersonSchema]});

// 새로운 스키마로 Realm을 가져오는데 성공하는 코드입니다.
let realm = new Realm({schema: [UpdatedPersonSchema], schemaVersion: 1});

Realm.schemaVersion를 사용하면 Realm의 현재 스키마 버전을 알 수 있습니다.

let currentVersion = Realm.schemaVersion(Realm.defaultPath);

마이그레이션

시간이 지남에 따라 데이터 모델을 변경하는 경우가 많습니다. 예를 들어 다음과 같은 Person 모델을 가정해 보겠습니다.

var PersonSchema = {
  name: 'Person',
  properties: {
    firstName: 'string',
    lastName: 'string',
    age: 'int'
  }
}

이름과 성을 구분하기보다 name 속성을 지니도록 데이터 모델을 업데이트하려면 스키마를 다음과 같이 변경합니다.

var PersonSchema = {
  name: 'Person',
  properties: {
    name: 'string',
    age: 'int'
  }
}

이 시점에서 이전 모델 버전으로 데이터를 저장한 경우 새 코드와 디스크에 저장된 Realm의 예전 데이터 사이에 불일치가 발생합니다. 이런 경우 마이그레이션을 실행하지 않는다면 새 Realm으로 기존 Realm을 열 때 예외가 발생합니다.

마이그레이션 수행

스키마 버전을 업데이트하고 선택적 migration 기능을 정의하면 마이그레이션과 관련 스키마 버전을 정의할 수 있습니다.

마이그레이션 함수는 이전 스키마의 데이터 모델을 새 스키마로 변환하는데 필요한 모든 로직을 제공합니다. 마이그레이션이 필요한 경우에만 Realm을 열 때 마이그레이션 함수가 적용돼서 Realm을 주어진 스키마 버전으로 업데이트합니다.

마이그레이션 함수가 제공되지 않으면 새 속성이 자동으로 추가되고, 새 schemaVersion으로 업데이트할 때 이전 속성이 데이터베이스에서 제거됩니다. 버전을 업그레이드할 때 예전 속성을 업데이트하거나 새 속성을 만들어야 한다면 마이그레이션 기능에서 해당 작업을 수행할 수 있습니다. 예를 들어 앞서 선언한 Person 모델을 마이그레이션하는 경우, 아래 코드처럼 기존의 firstNamelastName 속성을 사용해서 새 스키마의 name 속성을 만들 수 있습니다.

var realm = new Realm({
  schema: [PersonSchema],
  schemaVersion: 1,
  migration: function(oldRealm, newRealm) {
    // schemaVersion을 1로 업데이트하는 경우만 이 변경을 적용합니다
    if (oldRealm.schemaVersion < 1) {
      var oldObjects = oldRealm.objects('Person');
      var newObjects = newRealm.objects('Person');

      // 모든 객체를 순환하며 새 스키마의 name 속성을 설정합니다
      for (var i = 0; i < oldObjects.length; i++) {
        newObjects[i].name = oldObjects[i].firstName + ' ' + oldObjects[i].lastName;
      }
    }
  }
});

var fullName = realm.objects('Person')[0].name;

마이그레이션이 성공적으로 완료된 이후에는 Realm과 모든 객체들을 일반적인 경우와 동일하게 앱에서 접근할 수 있습니다.

선형 마이그레이션

앞서 설명한 마이그레이션 패턴으로 여러 버전을 마이그레이션할 때 문제가 발생할 수 있습니다. 사용자가 앱 업데이트를 건너뛰고, 건너뛴 버전에서 속성이 여러 번 변경될 가능성도 있습니다. 이 경우 이전 스키마를 최신 스키마로 업데이트하려면 이전 마이그레이션 코드를 수정해야 합니다.

이 이슈를 예방하기 위해 여러 마이그레이션을 선형적으로 진행하면서 이전 버전으로부터 확실히 데이터베이스가 업데이트되도록 해서 관련 마이그레이션 코드를 수행할 수 있습니다. 이 패턴을 따르면 이젠 마이그레이션 코드를 수정하지 말아야 하며, 나중에 사용할 수 있도록 모든 이전 스키마와 코드 블럭을 유지해야 합니다. 다음 예제를 참고하세요.

var schemas = [
  { schema: schema1, schemaVersion: 1, migration: migrationFunction1 },
  { schema: schema2, schemaVersion: 2, migration: migrationFunction2 },
  ...
]

// 배열에 있는 첫 번째 스키마기 때문에
// 현재 스키마 버전에 적용되는 첫 번째 스키마
var nextSchemaIndex = Realm.schemaVersion(Realm.defaultPath);
while (nextSchemaIndex < schemas.length) {
  var migratedRealm = new Realm(schemas[nextSchemaIndex++]);
  migratedRealm.close();
}

// 마지막 스키마로 Realm을 엽니다
var realm = new Realm(schemas[schemas.length-1]);

알림

Realm,Results, List 객체는 알림 콜백을 등록할 수 있는 addListener 메소드를 제공합니다. 객체가 업데이트될 때마다 변경 알림 콜백이 호출됩니다.

알림에는 쓰기 트랜잭션이 커밋될 때마다 알려주는 간단한 콜백인 “Realm 알림”과, 삽입, 삭제, 업데이트시 변경 메타 데이터를 수신하는 보다 정교한 콜백인 “컬렉션 알림”의 두 가지 종류가 있습니다.

또한 프로페셔널과 엔터프라이즈 에디션은 이벤트 처리 알림을 제공합니다. 자세한 내용은 “Realm 모바일 플랫폼” 섹션을 참고하세요.

Realm 알림

Realm 인스턴스는 쓰기 트랜잭션이 커밋될 때마다 다른 인스턴스에게 알림을 보냅니다. 알림은 다음 예제처럼 등록합니다.

// Observe Realm Notifications
realm.addListener('change', () => {
  // Update UI
  ...
});

// Unregister all listeners
realm.removeAllListeners();

컬렉션 알림

컬렉션 알림은 상세한 변경 내용을 설명하는 정보를 포함합니다. 이 정보에는 마지막 알림 이후 삽입, 삭제, 수정된 객체의 인덱스가 포함됩니다. 컬렉션 알림은 비동기적으로 전달되는데, 먼저 초기 결과에 전달되며, 이후 컬렉션의 오브젝트를 수정, 삭제하거나 추가하는 각 쓰기 트랜잭션이 발생할 때마다 전달됩니다.

addListener의 알림 콜백 함수는 이러한 변경이 발생할 때 두 개의 매개 변수를 받습니다. 첫 번째는 변경된 콜렉션이며 두 번째는 삭제, 삽입 및 수정의 영향을받는 컬렉션의 인덱스에 대한 정보를 담은 changes 객체입니다.

삭제삽입 은 객체가 시작하는 시점마다 인덱스를 기록하고 컬렉션에 포함되지 않도록 하며, Realm에 객체를 추가하거나 삭제할 때 반영됩니다. Results에도 적용되는데, 특정 값으로 필터링하고 객체가 변경돼서 쿼리와 일치하거나 더 이상 일치하지 않을 때 적용됩니다. List 기반 컬렉션에는 객체가 관계에 추가되거나 삭제될 때 적용됩니다.

이전부터 컬렉션에 있었고 계속 컬렉션에 존재하는 객체라면 객체의 속성이 변경될 때마다 응용 프로그램에 수정 알림이 전송됩니다. 일대일 관계나 일대다 관계의 변경에도 적용되지만 관계의 역방향으로는 변경이 전달되지 않습니다.

class Dog {}
Dog.schema = {
  name: 'Dog',
  properties: {
    name:  'string',
    age: 'int',
  }
};
class Person {}
Person.schema = {
  name: 'Person',
  properties: {
    name:    {type: 'string'},
    dogs:    {type: 'list', objectType: 'Dog'},
  }
};

위와 같은 모델 코드에서 개의 주인 목록을 관찰한다고 가정해 보겠습니다. 아래와 같은 경우 Person 객체의 수정 사항에 대한 알림이 전달됩니다.

  • Personname 속성을 변경합니다.
  • Persondogs 속성에 Dog를 추가하거나 삭제합니다.
  • Person에 딸린 Dogage 속성을 변경합니다.

이렇게 하면 알림이 발생할 때마다 모든 내용을 다시 불러오지 않고, UI 내부의 컨텐츠에 대한 애니메이션이나 시각적 업데이트를 개별적으로 관리할 수 있습니다.

// 컬렉션 알림을 관찰합니다.
realm.objects('Dog').filtered('age < 2').addListener((puppies, changes) => {
  
  // 객체가 삽입되면 UI를 갱신합니다.
  changes.insertions.forEach((index) => {
    let insertedDog = puppies[index];
    ...
  });
  
  // 객체가 수정되면 UI를 갱신합니다.
  changes.modifications.forEach((index) => {
    let modifiedDog = puppies[index];
    ...
  });
  
  // 객체가 삭제되면 UI를 갱신합니다.
  changes.deletions.forEach((index) => {
    // 현재 삭제된 객체에 직접 접근이 불가능합니다.
    // 곧 이 기능을 지원할 예정입니다.
    ...
  });
  
  
});

// 모든 리스너를 해제합니다.
realm.removeAllListeners();

동기화

Realm 모바일 플랫폼(RMP)은 Realm 모바일 데이터베이스를 네트워크 전체로 확장해서 여러 기기에서 데이터를 자동으로 동기화하도록 합니다. 이런 동기화된 Realm을 지원하기 위해 새로운 타입 및 클래스 집합이 제공되며, 기존 Realm 보다일 데이터베이스에 추가됩니다.

사용자

Realm 오브젝트 서버의 중심 객체는 동기화된 Realm에 사용되는 Realm 사용자(Realm.Sync.User)입니다. 사용자는 사용자이름/비밀번호 체계 혹은 서드파티 인증 방법을 통해 공유 Realm을 인증할 수 있습니다.

사용자를 생성하고 로그인하려면 다음 두 가지가 필요합니다.

  • 연결할 Realm 오브젝트 서버 URL
  • 인증 방법에 맞는 사용자 자격 증명이나 인증서 (사용자이름/암호, 엑세스 키 등)

인증

서드파티 엑세스 자격 증명은 Realm 오브젝트 서버 인증에서 만들 수 있습니다.

사용자이름/비밀번호
Realm.Sync.User.login('http://my.realm-auth-server.com:9080', 'username', '[email protected]$w0rd', (error, user) => { /* ... */ });

이를 수행하기 전에 관리자 대시 보드를 사용해서 서버에서 이를 수행하거나 register 메서드를 사용해서 사용자를 만들어야 합니다.

Realm.Sync.User.register('http://my.realm-auth-server.com:9080', 'username', '[email protected]$w0rd', (error, user) => { /* ... */ });
Google
const googleAccessToken = 'acc3ssT0ken...';
Realm.Sync.User.registerWithProvider('http://my.realm-auth-server.com:9080', 'google', googleAccessToken, (error, user) => { /* ... */ });
Facebook
const fbAccessToken = 'acc3ssT0ken...';
Realm.Sync.User.registerWithProvider('http://my.realm-auth-server.com:9080', 'facebook', fbAccessToken, (error, user) => { /* ... */ });
로그 아웃

간단한 코드로 동기화된 Realm에서 로그 아웃할 수 있습니다.

user.logout();

사용자가 로그 아웃한 경우 동기화는 중단되며, 사용자는 더 이상 동기화된 Realm을 열 수 없습니다.

사용자로 작업하기

동기화 서버 URL은 사용자의 고유 식별자인 물결 문자(“~”)를 포함할 수 있습니다. 이 스키마를 사용해서 개별 사용자를 수용하는 앱을 쉽게 만들 수 있습니다. 공유된 Realm이 디스크에 저장되는 위치는 프레임워크가 관리하지만 필요시 오버라이드할 수 있습니다.

Realm.Sync.User.login(/* ... */, (error, user) => {
  if (!error) {
    var realm = new Realm({
      sync: {
        user: user,
        url: 'realm://object-server-url:9080/~/my-realm',
      },
      schema: [/* ... */]
    });

    realm.write(() => { 
      /* ... */
    })
  }
})

Realm.Sync.User.current를 사용해서 현재 로그인한 사용자를 얻을 수 있습니다. 로그인한 사용자가 없거나 모두 로그아웃한 경우 undefined가 반환되며, 두 명 이상의 사용자가 로그인한 경우에는 오류가 발생합니다.

let user = Realm.Sync.User.current;

여러 명의 사용자가 로그인한 경우라면 Realm.Sync.User.all를 호출해서 해당 사용자의 컬렉션을 가져올 수 있습니다. 로그인한 사용자가 없다면 빈 상태입니다.

let users = Realm.Sync.User.all;

for(const key in users) {
  const user = users[key];

  // do something with the user.  
})

동기화된 Realm 사용

Realm 오브젝트 서버와 User 객체의 URL을 사용해서 Realm을 열면 JavaScript 내의 다른 Realm과 마찬가지로 사용할 수 있습니다.

realm.write(() => {
  realm.create('MyObject', jsonData);
});

var objects = realm.object('MyObject');

동기화 세션

Realm 오브젝트 서버에 동기화된 Realm의 연결은 Session 객체로 표현됩니다. realm.syncSession를 호출해서 전체 세션을 검색할 수 있습니다.

state 속성을 사용하면 기본 세션의 상태가 활성 상태인지, 서버에 연결되지 않은 상태인지, 혹은 오류 상태인지를 파악할 수 있습니다.

접근 제어

Realm 모바일 플랫폼은 어떤 사용자가 어떤 Realm 파일과 동기화할 수 있는지 제한할 수 있도록 유연한 접근 제어 방법을 제공합니다. 예를 들어 여러 사용자가 동일한 Realm을 쓸 수 있는 협업 앱을 만들 수 있습니다. 한 쓰기 사용자가 읽기 권한이 있는 많은 사용자와 데이터를 공유하는 게시자/구독자 시나리오에서 데이터를 공유할 수도 있습니다.

지정 Realm에의 사용자 접근 레벨은 세 가지 권한으로 분류됩니다.

  • mayRead는 사용자가 Realm을 읽을 수 있는 권한입니다.
  • mayWrite는 사용자가 Realm에 쓸 수 있는 권한입니다.
  • mayManage는 사용자가 Realm의 권한을 변경할 수 있는 권한입니다.

권한을 명시적으로 수정하지 않으면 Realm의 소유자, 즉 작성자만 접근할 수 있으며, 유일한 예외는 관리 사용자입니다. 관리 사용자는 항상 서버의 모든 Realm에 대한 모든 권한을 가집니다.

일반 Realm 오브젝트 서버 문서의 Access Control 섹션에서 자세한 컨셉을 알아보세요.

Realm 관리

모든 접근 레벨 관리 작업은 관리 Realm에 기록돼서 수행됩니다. 관리 Realm은 일반적인 동기화된 Realm과 같지만, Realm 오브젝트 서버는 기본적으로 반응하도록 특별히 설계됐습니다. 권한 수정된 객체를 Realm에 추가해서 Realm 파일의 접근 제어 설정을 변경할 수 있습니다.

user.openManagementRealm() 메서드를 호출하면 주어진 사용자에 대한 관리 Realm으르 얻을 수 있습니다.

const managementRealm = user.openManagementRealm();

권한 수정

Realm 파일에 대한 접근 제어 설정을 수정하려면 권한 객체 인스턴스를 관리 Realm에 추가합니다. 현재 두 가지 작업방법이 지원됩니다.

PermissionChange

PermissionChange 객체를 사용하면 Realm의 접근 제어 설정을 직접 바꿀 수 있습니다. 사용 권한을 부여할 사용자의 ID를 이미 알고 있거나, 모든 사용자에게 사용 권한을 부여하려고 할 때 사용합니다.

managementRealm.write(() => {
  managementRealm.create('PermissionChange', {
    id: generateUniqueId(),   // implement something that creates a unique id.
    createdAt: Date.now(),
    createdAt: Date.now(),
    userId: '...',
    realmUrl: '...',
  });
});

사용자가 관리하는 모든 Realm에 대한 권한 변경 사항을 적용하려면 realmURL의 값을 *로 변경합니다.

오브젝트 서버에 허가된 모든 사용자에 대한 권한 변경 사항을 적용하려면 userId 값을 *로 지정합니다.

mayRead, mayWrite, mayManage 값을 지정하지 않거나 null을 지정하면, read, write, manage 권한은 변경되지 않습니다. 예를 들어 사용자에게 read 권한을 부여하면서 이미 그 권한이 있는 사용자에게서 write 권한을 뺏지 않으려는 경우에 사용합니다.

managementRealm.write(() => {
  for(const userId in users) {
    managementRealm.create('PermissionChange', {
      id: generateUniqueId(),
      createdAt: Date.now(),
      createdAt: Date.now(),
      userId: userId,
      realmUrl: realmUrl,
      mayRead: true
    });
  });
});

이 경우 목록 내의 모든 사용자에게 read 권한이 부여되면서, writemanage 권한을 가진 사용자들의 권한은 유지됩니다.

PermissionOffer/PermissionResponse

PermissionOfferPermissionOfferResponse 클래스는 초대한 사람이 한 명 이상의 사용자에 의해 소비될 수 있는 토큰을 생성하는 경우에 사용합니다.

managementRealm.write(() => {
  let expirationDate = Date.now();
  expirationDate.setDate(expirationDate.getDate() + 7); // Expires in a week.

  managementRealm.create('PermissionOffer', {
    id: generateUniqueId(),
    createdAt: Date.now(),
    createdAt: Date.now(),
    userId: userId,
    realmUrl: realmUrl,
    mayRead: true,
    mayWrite: true,
    mayManage: false,
    expiresAt: expirationDate
  });
});

/* Wait for the offer to be processed */

var token = permissionOffer.token;

/* Send token to the other user */

PermissionChange와 마찬가지로, 제공된 realmUrl에서 Realm의 read, write, manage 접근을 제어하는 인자가 있습니다. 또한 expiresAt이라는 추가적인 인자를 제공해서 소비가 불가능한 토큰을 제어할 수 있습니다. 값을 넘기지 않거나 null을 넘기면, 오퍼는 만료되지 않습니다. 이 오퍼를 사용한 사용자는 만료 시점 이후에도 액세스 권한을 잃지 않습니다. 다른 사용자가 token을 얻으면 이를 소비할 수 있습니다.

const managementRealm = anotherUser.openManagementRealm();
let offerResponse;

managementRealm.write(() =>
{
    offerResponse = managementRealm.create('PermissionOfferResponse', {
      id: generateUniqueId(),
      createdAt: Date.now(),
      createdAt: Date.now(),
      token: token
    });
});

/* Wait for the offer to be processed */

const realmUrl = offerResponse.realmUrl;

// Now we can open the shared realm:

var realm = new Realm({
  sync: {
    user: anotherUser,
    url: realmUrl,
  },
  schema: [/* ... */]
});

PermissionOffer를 소비해서 받은 권한은 부가적입니다. 예를 들어 이미 write 접근을 가는 사용자가 read 권한을 주는 PermissionOffer를 받아도 read 권한은 없어지지 않습니다.

관리 Realm에서 삭제하거나 expiresAt을 과거의 날짜로 설정해서 PermissionOffer를 취소할 수 있습니다. 이 경우 새 사용자는 오퍼를 받지 못하게 되지만, 이전에 소비한 사용자의 권한은 취소되지 않습니다.

오브젝트 서버가 권한 객체로 인코딩한 작업을 처리하면 객체의 statusCodestatusMessage 속성이 설정됩니다.

이벤트 핸들링

이 기능은 프로페셔널과 엔터프라이드 에디션에서만 가능합니다. 이 곳에서 더 많은 기능을 알아보고 무료 시험버전을 시작해보세요.

이벤트 핸들링은 Realm 오브젝트 서버의 엔터프라이즈와 프로페셔널 에디션에서만 제공되는 기능입니다. 이 기능은 Realm 오브젝트 서버로 연결되는 전역 이벤트 리스너 API를 통해 서버측의 Node.js SDK에서 지원되며, Realm 전체의 변경 사항이나 특정 패턴에 맞는 Realm을 관찰할 수 있습니다. 예를 들어 사용자가 설정 데이터를 가상 경로가 /~/settings인 사용자마다 고유한 Realm으로 분리한 앱 구조를 가진 경우 모든 사용자의 settings 영역에 대한 변화에 반응하는 리스너를 설정할 수 있습니다.

변경 사항이 서버로 동기화될 때마다, 이에 대한 응답으로 사용자가 정의한 서버측 로직을 실행할 수 있는 알림이 트리거됩니다. 이 알림은 업데이트된 Realm의 가상 경로를 알려주고 Realm 객체와 변화된 객체에 대한 상세한 정보를 제공합니다. 변경된 집합은 마지막으로 동기화된 트랜잭션에서 삽입, 삭제, 수정된 객체의 클래스 이름별로 세분화된 객체 인덱스를 제공합니다.

이벤트 핸들러 생성

Realm 이벤트 핸들링을 사용하려면 간단한 Node.js 애플리케이션을 생성해야 합니다.

서버 파일을 저장할 디렉토리를 만든 다음 package.json을 생성합니다. 이 JSON 파일은 Node.js와 패키지 관리자인 npm에서 애플리케이션을 설명하고 외부 디펜던시를 지정하는데 사용합니다.

npm init 명령어를 사용해서 이 파일을 만들거나, 텍스트 에디터를 사용해서 간단한 구조의 파일을 직접 만들 수도 있습니다.

{
    "name": "MyApp",
    "version": "0.0.1",
    "main": "index.js",
    "author": "Your Name",
    "description": "My Cool Realm App",
    "dependencies": {
        "realm": "file:realm-1.0.0-professional.tgz"
    }
}

file: 디펜던시로 Realm 모바일 플랫폼을 다운로드한 아카이브를 지정합니다. 이는 package.json 파일과 같은 디렉토리에 있어야 합니다. 실제로 디스크에 있는 파일을 지정했는지 확인해 보세요.

또다른 디펜던시를 애플리케이션에 사용한다면 파일 내 dependencies 섹션에 지정합니다.

package.json 파일의 설정을 마쳤으면 아래 명령어를 입력하세요.

npm install

모든 모듈과 디펜던시를 다운로드해서 압축을 풀고 구성하는 명령어입니다.

예제 index.js 파일은 다음과 같습니다. 이 예제는 /~/private라는 가상 경로에 있는 사용자 특정 개인 Realm의 변경 사항을 수신합니다. 이 Realm에서 업데이트된 Coupon 객체를 찾아서, 아직 검증되지 않았다면 코드를 검증한 뒤, 검증 결과를 Coupon 객체의 isValid 속성에 씁니다.

var Realm = require('realm');

// Insert the Realm admin token here
//   Linux:  cat /etc/realm/admin_token.base64
//   macOS:  cat realm-object-server/admin_token.base64
var ADMIN_TOKEN = 'ADMIN_TOKEN';

// the URL to the Realm Object Server
var SERVER_URL = 'realm://127.0.0.1:9080';

// The regular expression you provide restricts the observed Realm files to only the subset you
// are actually interested in. This is done in a separate step to avoid the cost
// of computing the fine-grained change set if it's not necessary.
var NOTIFIER_PATH = '/^\/([0-9a-f]+)\/private$/';

// The handleChange callback is called for every observed Realm file whenever it
// has changes. It is called with a change event which contains the path, the Realm,
// a version of the Realm from before the change, and indexes indication all objects
// which were added, deleted, or modified in this change
function handleChange(changeEvent) {
  // Extract the user ID from the virtual path, assuming that we're using
  // a filter which only subscribes us to updates of user-scoped Realms.
  var matches = changeEvent.path.match(/^\/([0-9a-f]+)\/private$/);
  var userId = matches[1];

  var realm = changeEvent.realm;
  var coupons = realm.objects('Coupon');
  var couponIndexes = changeEvent.changes.Coupon.insertions;

  for (var couponIndex in couponIndexes) {
    var coupon = coupons[couponIndex];
    if (coupon.isValid !== undefined) {
      var isValid = verifyCouponForUser(coupon, userId);
      // Attention: Writes here will trigger a subsequent notification.
      // Take care that this doesn't cause infinite changes!
      realm.write(function() {
        coupon.isValid = isValid;
      });
    }
  }
}

// create the admin user
var adminUser = Realm.Sync.User.adminUser(adminToken);

// register the event handler callback
Realm.Sync.addListener(SERVER_URL, adminUser, NOTIFIER_PATH, 'change', handleChange);

console.log('Listening for Realm changes');

다른 서비스와 통합

이벤트 핸들링 프레임워크를 다른 서비스와 통합하는 온전한 예제는 IBM Watson의 Bluemix와의 통합 예제인 Scanner 앱에서 볼 수 있습니다.

Realm은 관리자 Realm에서 추적할 때만 관찰할 수 있습니다. 개별 사용자에 대한 인증을 항상 생성하면 됩니다.

데이터 접근

이 기능은 프로페셔널과 엔터프라이드 에디션에서만 가능합니다. 이 곳에서 더 많은 기능을 알아보고 무료 시험버전을 시작해보세요.

Realm 오브젝트 서버의 프로페셔널과 엔터프라이즈 에디션을 사용하면 서버를 시작할 때 생성한 관리 토큰을 사용해서 모든 공유 Realm 서버에 있는 해당 Realm의 모든 데이터에 접근하고 변경할 수 있습니다.

Linux에서 관리자 토큰을 검색하려면 아래 명령어를 사용하세요.

cat /etc/realm/admin_token.base64

macOS에서는 토큰이 realm-object-server 폴더의 zip 내부에 저장됩니다.

cat realm-object-server/admin_token.base64

이를 사용해서 Realm.Sync.User 객체를 동기적으로 생성할 수 있습니다. 이 객체는 Realm 생성자에서 전달돼서 서버 측 Realm에 연결합니다.

// Open a Realm using the admin user
var adminToken = '3x4mpl3T0k3n…';
var adminUser = Realm.Sync.User.adminUser(adminToken);
var realm = new Realm({
  sync: {
    user: admin_user,
    url: 'realm://object-server-url:9080/~/my-realm',
  },
  schema: [{...}
  }]
});

이 작업은 Realm 오브젝트 서버의 인증 서비스를 우회하므로, 이렇게 작성된 새 Realm은 내부적으로 추적되지 않고 전역 리스너 API를 사용한 알림이 불가능합니다. 기존의 동기화 Realm에 쿼리나 쓰기 작업을 하거나 이벤트 핸들링을 하는 것과 같이 서버측 사용을 위해서는 관리자 토큰을 사용하기를 권장합니다. 다른 모든 유스케이스의 경우 개별 사용자 인증을 사용하세요.

관리자가 아닌 사용자 기반의 인증은 상위의 사용자 섹션을 참고하세요.

React Native ListView

ListResults 인스턴스를 ListView의 데이터로 사용하려면 realm/react-native 모듈에서 제공하는 ListViewListView.DataSource를 이용하는 것을 권장합니다.

import { ListView } from 'realm/react-native';

API는 React.ListView와 거의 동일하므로 사용 정보는 ListView 문서를 참고하세요.

암호화

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

Realm 생성시 64 바이트 암호 키를 제공하는 경우 Realm 데이터베이스 파일을 디스크에 AES-256+SHA2로 저장할 수 있습니다.

var key = new Int8Array(64);  /// 암호키 생성
var realm = new Realm({schema: [CarObject], encryptionKey: key});

// Realm을 일반적인 방법으로 쓰기
var dogs = realm.objects('Car');

필요시 디스크에 저장되는 모든 데이터가 AES-256 암호화, 복호화 과정을 겪고 SHA-2 HMAC으로 검증됩니다. Realm 인스턴스를 얻을 때마다 같은 암호키가 필요합니다.

암호화된 Realm을 사용하면 일반적으로 10% 내로 느려지는 작은 성능 지연이 발생할 수 있습니다.

문제 해결

Realm 생성자가 없는 경우

앱이 충돌하면서 Realm 생성자가 발견되지 않았다고 보고하는 경우 몇 가지 해결 방안이 있습니다.

먼저 react-native link realm를 실행합니다.

그래도 문제가 해결되지 않고 Android라면 다음 단계를 시도해 보세요.

MainApplication.java 파일에 다음과 같이 추가합니다.

import io.realm.react.RealmReactPackage;

패키지 리스트에 RealmReactPackage를 추가합니다.

protected List getPackages() {
    return Arrays.asList(
        new MainReactPackage(),
        new RealmReactPackage() // add this line
    );
}

settings.gradle에 다음 두 줄을 추가합니다.

include ':realm'
project(':realm').projectDir = new File(settingsDir, '../node_modules/realm/android')

iOS에서 문제가 발생한 경우 다음 단계를 따르세요. 1. 모든 시뮬레이터/기기 빌드를 닫습니다. 2. 터미널에서 실행 중인 패키지 관리자를 중지합니다. 터미널을 다시 시작하는 것이 확실합니다. 3. 파인더에서 앱 루트의 iOS 폴더를 엽니다. 4. 빌드 폴더로 이동합니다. (atom을 쓰는 경우 마우스 오른쪽 버튼을 클릭하고 파인더에서 열기를 클릭합니다.) 5. 빌드 폴더 안의 모든 것을 삭제합니다. (휴지통에서 완전히 삭제하지 않아도 됩니다.) 6. 전체 재빌드를 위해 react-native run-ios를 실행합니다.

충돌 보고

충돌 보고 도구를 사용을 권장합니다. 다른 디스크 I/O 작업과 마찬가지로 Realm 작업은 런타임에 잠재적으로 실패할 수 있습니다. 애플리케이션에서 오류 보고서를 수집하면 문제 발생의 원인을 확인할 수 있고 오류 처리 개선 및 버그 수정에 도움이 됩니다.

대부분의 상용 충돌 보고 도구는 로그 수집 옵션을 제공하며, 이 기능의 사용을 권장합니다. Realm은 사용자 데이터를 제외한 메타 데이터의 로그를 기록하므로, 예외가 발생하고 복구할 수 없는 상황이 생기면 이런 메시지를 디버그에 유용하게 활용할 수 있습니다.

Realm 이슈 보고

Realm과 관련된 이슈를 발견하면 GitHub 이슈를 발행하거나 kr@realm.io로 메일을 주세요. 아래 정보를 제공해 주시면 이슈를 이해하고 재현하는데 큰 도움이 됩니다.

  1. 목적
  2. 기대한 결과
  3. 실제 결과
  4. 재현에 필요한 단계
  5. 이슈가 포함된 코드 샘플 (컴파일이 가능한 전체 프로젝트가 가장 이상적입니다).
  6. Realm 버전
  7. 충돌 로그와 스택 트레이스. 자세한 사항은 충돌 보고를 확인하세요.