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

Realm 리액트 네이티브는 안전하고, 영속적, 빠른 앱 모델 레이어를 효율적으로 작성하게 해줍니다. 모습은 아래와 같습니다.

// 모델과 프로퍼티를 정의합니다.
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}, // 선택적인 프로퍼티
  }
};

// 객체들을 지원하는 기본 Realm 가져오기
let realm = new Realm({schema: [Car, Person]});

// Realm 오브젝트를 만들어 로컬 저장소에 저장하기
realm.write(() => {
  let myCar = realm.create('Car', {
    make: 'Honda',
    model: 'Civic',
    miles: 1000,
  });
  myCar.miles += 20; // 프로퍼티 값을 갱신
});

// 많이 주행한 모든 차들을 질의
let cars = realm.objects('Car').filtered('miles > 1000');

// 한개의 자동차 Results 객체가 반환됩니다
cars.length // => 1

// 다른 차를 추가합니다
realm.write(() => {
  let myCar = realm.create('Car', {
    make: 'Ford',
    model: 'Focus',
    miles: 2000,
  });

// 질의 결과는 실시간으로 갱신됩니다
cars.length // => 2

시작하기

아래의 설치 설명을 따라 Realm 리액트 네이티브를 npm으로 설치하거나 깃헙의 소스를 보세요.

요구사항

  • 리액트 네이티브 어플리케이션을 실행할 수 있게 환경 설정을 합니다. 리액트 네이티브 설명을 따라가세요.
  • 리액트 네이티브 0.20.0 이상을 지원합니다.
  • Realm을 사용하는 앱은 iOS와 안드로이드에서 사용할 수 있습니다.

설치

  • 새 리액트 네이티브 프로젝트를 생성합니다:

    react-native init <project-name>

  • 새 프로젝트 디렉터리로 이동하고 (cd <project-name>) realm 의존성을 추가합시다.

    npm install --save realm

  • 생성된 Xcode 프로젝트 (ios/<project-name>.xcodeproj)를 엽시다.
  • 사이드바에 top-level project가 선택된지 확인하고, 프로젝트 설정에서 iOS 배포 타깃을 최소한 8.0으로 변경하세요.
  • 사이드바의 Libraries 그룹을 우클릭하고 Add Files to “<project-name>”를 클릭하고 다이얼로그에서 ../node_modules/realm/RealmJS.xcodeproj를 선택하세요.
  • 앱 타깃 설정의 General 탭을 엽니다. 왼쪽 컬럼에서 Libraries > RealmJS > Products를 확장하고 앱 타깃 설정을 위해 RealmReact.framework을 긁어서 General 탭의 Embedded Binaries 섹션으로 옮깁니다. 스크린샷을 보세요
  • Build Phrases 탭에서 앱 타깃 설정을 위해 RealmReact.frameworkLink Binary with Libraries 빌드 프레이즈에 있는지 확인합니다.
  • 새 리액트 네이티브 프로젝트를 생성합니다.

    react-native init <project-name>

  • 새 프로젝트 디렉터리로 이동하고 (cd <project-name>) realm 의존성을 추가합시다.

    npm install --save realm

  • 프로젝트 디렉터리에서 이 커맨드를 수행합니다.

    react-native link realm

  • android/app/src/main/java/com/<project-name>/MainActivity.java를 열어 아래를 합니다.

    • 다른 imports 아래에 import io.realm.react.RealmReactPackage;를 추가합니다.
    • getPackages() 메서드에 추가된 리스트에 new RealmReactPackage()를 추가합니다.


이제 준비가되었습니다. Realm을 동작하기 위해 index.ios.jsindex.android.js에 있는 여러 분의 class <project-name>의 정의에 아래를 추가합니다.

const Realm = require('realm');

class <project-name> extends Component {
 render() {
   var realm = new Realm({schema:[{name:'Dog', properties:{name: 'string'}}]});
   realm.write(()=>{
     realm.create('Dog', ['Rex']);
   })
   return (
     <View style={styles.container}>
       <Text style={styles.welcome}>
         Count of Dogs in Realm: {realm.objects('Dog').length}
       </Text>
     </View>
   );
 }
}

앱을 기계나 시뮬레이터에서 수행할 수 있습니다!

API 문서

전체 API 문서에서 모든 클래스, 메서드 등을 확인할 수 있습니다.

예제

깃헙의 프로젝트에서 클론을 받는 것으로 시작합니다.

git clone https://github.com/realm/realm-js.git

리액트 네이티브 예제 프로젝트는 examples/ReactExample 디렉터리에 있습니다. 처음 예제를 수행하기 앞서 지금 디렉터리에서 npm install을 실행해야 합니다.

도움을 구하려면

만약 (Crashlytics나 HockeyApp 같은) 크래쉬 리포터를 사용한다면 로그 수집 활성화를 잊지 마세요. Realm은 예외가 발생하거나 회복할 수 없는 상황이 있을 때 (사용자 데이터가 아닌) 메타데이터 정보를 로그로 남깁니다. 이 메시지들은 무언가 잘못되었을 때 디버깅하는 것을 돕습니다.

모델

Realm 데이타 모델은 스키마 정보를 정의해 초기화 과정에 Realm에 전달합니다. 객체의 스키마는 객체의 namenametype을 가진 속성으로의 집합이다. 객체와 리스트 속성을 위해 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}, // 선택적인 프로퍼티
  }
};

// Car와 Person 모델로 Realm 초기화하기
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;

// `Person` 생성자를 전달한 것에 주의하라
let realm = new Realm({schema: [CarSchema, Person]});

모델 클래스의 정의가되어 있으면 객체를 Realm에 저장하거나 검색할 수 있습니다.

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

  // 모델에 정의 된 모든 속성을 읽고 쓸 수 있습니다
  console.log('Car type is ' + car.make + ' ' + car.model);
  car.miles = 1500;
});

기본 프로퍼티 타입

Realm는 다음과 같은 데이터 유형을 기본 데이터 형으로 지원하고 있습니다: bool , int , float , double , string , data, date.

  • bool 속성은 JavaScript의 Boolean타입으로 연결됩니다.
  • int , floatdouble 속성은 JavaScript의 Number 형에 연결됩니다. 내부적으로 ‘int’ 와 ‘double’ 형은 64 비트로 저장됩니다. 반면 float 형은 32 비트로 저장됩니다.
  • string 속성은 String 형으로 연결됩니다.
  • data 속성은 ArrayBuffer 형으로 연결됩니다.
  • date 속성은 Date 형으로 연결됩니다.

기본 속성을 간단히 지정하는 경우 단 하나의 속성을 지정하게 됩니다. 그렇지 않은 경우에는 하나의 입구를 가지는 사전형을 정의해야 합니다.

const CarSchema = {
  name: 'Car',
  properties: {
    // 아래의 type 속성의 정의는 모두 같은 의미입니다
    make:   {type: 'string'},
    model: 'string',
  }
}

객체 속성

객체 스키마에 name 속성을 지정하여 객체 형을 지정합니다.

const PersonSchema = {
  name: 'Person',
  properties: {
    // 아래의 type 속성의 정의는 모두 같은 의미입니다
    car: {type: 'Car'},
    van: 'Car',
  }
};

객체 속성을 사용하는 경우 Realm을 열 때 지정한 스키마에 모든 참조 형이 정의되어 있어야 합니다.

// PersonSchema에서 'Car'형의 속성을 사용되고 있기 때문에 CarSchema도 전달해야 합니다.
let realm = new Realm({schema: [CarSchema, PersonSchema]});

객체 속성에 접근하려면 일반적인 속성 문법을 사용하여 중첩 된 속성에 접근합니다.

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

  // 올바른 JSON을 속성으로 설정하여 새 Car를 생성합니다
  person.van = {make: 'Ford', model: 'Transit'};

  // 같은 자동차 인스턴스에 두 속성을 설정합니다
  person.car = person.van;
});

리스트 속성

리스트 속성을 위해 속성 형 listobjectType을 지정합니다.

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

리스트 속성에 접근하면 List 객체가 반환됩니다. List 객체는 일반적인 JavaScript 배열 객체와 거의 흡사한 메서드를 가집니다. 크게 다른 점은 List 객체에 대한 변경 사항이 자동으로 하부의 Realm에 보관된다는 점입니다. 또 List 객체는 리스트에 의해 획득된 하부의 객체를 가지고 있습니다. List 인스턴스를 소유한 객체의 속성으로부터만 접근할 수 있고 수동으로 생성할 수 없습니다.

let carList = person.cars;

// 리스트에 새 차들을 추가하기
realm.write(() => {
  carList.push({make: 'Honda', model: 'Accord', miles: 100});
  carList.push({make: 'Toyota', model: 'Prius', miles: 200});
});

let secondCar = carList[1].model;  // 배열 인덱스를 통해 접근합니다

선택적 속성

각 속성은 optional 지사자를 이용하여 선택적인지 비 선택적인지 정의할 수 있습니다.

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

    // 객체 속성은 언제나 선택적입니다
    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};
});

기본 프로퍼티 값

속성 정의에 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'});
});

기본 키

객체 모델의 stringint 속성을 위한 객체 모델에 primaryKey를 지정할 수 있다. 기본 키를 정의하면 객체의 검색, 업데이트를 효과적으로 할 수 있고 각 값들이 중복되지 않은 것을 강요할 수 있습니다. 기본 키가 설정된 객체는 Realm에 저장 한 후 기본 키를 변경할 수 없습니다.

const PersonSchema = {
  name: 'Person',
  primaryKey: 'id',
  properties: {
    id:   'int',    // 기본 키
    name: 'string'
  }
};

쓰기

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

쓰기 트랜잭션는 무시할 수 없는 오버헤드를 가져옵니다. 쓰기 트랜잭션이 최소가 되도록 설계하십시오.

객체의 생성

지금까지 설명한 바와 같이, 객체의 생성은 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', {primaryId: 1, title: 'Recipes', price: 35});

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

위의 예제에서 먼저 저장된 객체는 기본 키로 primaryId 속성이 기본 키는 1 인 객체가 있고 세 번째 인자를 true 로 지정하였습니다. 그렇기 때문에 새로운 객체가 생성되는 것이 아니라 기존 객체의 가격 속성이 업데이트됩니다. name 속성은 전달하는 객체에 포함되어 있지 않기 때문에 갱신되지 않고 원래 값이 유지됩니다.

객체 삭제

객체를 삭제하려면 쓰기 트랜잭션에서 delete 메소드를 사용합니다.

realm.write(() => {
  // 책을 생성합니다
  let book = realm.create('Book', {primaryId: 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는 단순한 뷰나 데이터이고, 변경할 수 없습니다.

Realmd에서 객체를 가져오는 가장 기본적인 방법은 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"');

정렬

Results는 정렬 기준을 설정할 수 있고 하나 이상의 속성에 기반해서 정렬할 수 있습니다. 아래는 위의 주행거리를 숫자를 표시하는 예제에서 반환받은 자동차들을 정렬하는 예입니다.

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

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

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 를 보유하고 직접 데이터의 표시에 사용하면 재검색 할 필요없이 항상 최신 데이터를 표시 할 수 있습니다.

변경 이벤트를 구독하면 Realm 데이터의 갱신을 알 수 있어 Results를 다시가져올 필요가 없고 앱의 UI가 갱신되어야 할 때를 알 수 있습니다.

결과 제한

대부분의 다른 데이터베이스 기술은 (SQLite의 ‘LIMIT’ 키워드같은) 질의의 결과에 대한 ‘페이지옮기기’를 제공합니다. 이 방법은 디스크의 과도한 읽어들이거나 한 번에 대량의 데이터를 메모리에로드 하는 것을 피하기 위해 사용됩니다.

Realm 질의는 지연 실행되므로 이러한 페이지 옮기기는 전혀 필요 없습니다. 왜냐하면 Realm은 질의로부터 객체를 실제로 명시적인 접근을 할 때만​ 불러오기 때문입니다.

UI관련 혹 다른 구현에서 질의된 객체의 특정 소집합이 필요하다면 단순히 Results를 가져오고 필요한 객체들만 읽으면 됩니다.

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

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

Realms

다중 Realm

어떨 경우에는 여러 Realm 파일을 다른 위치에 저장하는 것이 유용합니다.

예를 들어 기존의 메인 Realm 파일 대신에 애플리케이션의 Realm 파일로 어떤 데이터를 저장하고 싶을 수 있습니다. Realm 객체의 초기화시에 path 를 지정하여 Realm 파일의 저장 위치를 지정할 수 있습니다. 모든 경로는 애플리케이션의 쓰기가능한 문서 디렉터리로 부터 상대적입니다.

// 다른 경로의 realm을 열기
let realmAtAnotherPath = new Realm({
  path: 'anotherRealm.realm',
  schema: [CarSchema]
});

기본 Realm 경로

이 문서에서 지금까지의 코드 예제에서는 path 인자를 지정하지 않았습니다. path 인자를 지정하지 않으면 기본 경로가 사용됩니다. 기본 저장 경로에 접근하거나 변경하려면 전역 속성에 정의 된 Realm.defaultPath을 사용합니다.

스키마 버전

Realm의 초기화시에 지정할 수있는 마지막 선택은 schemaVersion 속성입니다. 지정하지 않으면 기본 값으로 0 이 사용됩니다. 기존 데이터에서 스키마를 변경 한 경우에는 반드시 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을 가져오는데 성공합니다
// this will succeed and update the Realm to the new schema
let realm = new Realm({schema: [UpdatedPersonSchema], schemaVersion: 1});

마이그레이션

마이그레이션 기능은 현재 위에 언급된 Realm을 열때 schemaschemaVersion가 갱신되는 것으로 제한되어 있습니다. 데이터의 마이그레이션은 지원되지 않으며 향수 지원할 예정입니다.

변경 이벤트

변경 이벤트는 쓰기 트랜잭션이 완료되면 발송됩니다. 변경 이벤트는 이렇게 등록합니다.

// Realm 변경 이벤트를 관찰합니다
realm.addListener('change', () => {
  // Update UI
  ...
});

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

리액트 네이티브 ListView

ListResults 인스턴스를 ListView 의 데이터 소스로 이용하는 경우 realm/react-native 모듈에서 제공되는 ListViewListView.DataSource 을 이용하는 것을 강하게 권장합니다.

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

API는 거의 React.ListView와 같습니다. 자세한 정보는 ListView documentation를 참고하세요.

문제 해결

충돌 보고

우리는 개발자로 충돌 보고 도구를 사용하실 것을 권장하고 있습니다. (디스크 IO 등과 같은) 많은 Realm 작업 중에 실시간 오류가 발생할 수 있습니다. 그렇기에 충돌 보고서를 수집하는 것은 무엇으로 인해 문제가 발생했는지를 확인할 수 있고 오류 처리 개선 및 버그 수정에 도움이 됩니다.

많은 상용 충돌 보고 도구들은 로그를 수집하는 옵션을 제공합니다. 우리는 이 기능을 사용하는 것을 강하게 권유합니다. 예외가 발생하거나 회복 불가능상 상황에 빠졌을 때 Realm이 (사용자 데이터를 제외한) 메타데이터 정보를 기록할 수 있습니다. 이 메시지들은 이런 문제가 발생했을 때 디버깅에 유용합니다.

Realm 이슈 보고하기

Realm에 대한 어떤 이슈를 발견하면 우리가 충분히 이해할 수 있게 많은 정보를 포함하거나 재현할 수 있는 정보들을 포함해서 GitHub에 이슈를 발행하거나 kr@realm.io로 메일을 주세요.

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

  1. 목적
  2. 기대 결과
  3. 실제 결과
  4. 재현 단계
  5. 이슈에 집중한 코드 샘플 (컴파일 가능한 전체 작동 프로젝트가 이상적입니다).
  6. Realm의 버전
  7. 충돌 로그와 스택 트레이스 위에 자세히 나온 충돌 보고을 참고하세요