React native @realm/react 패키지 적용하기

시작하기

기존에 relam 관련 포스팅을 남겼었는데 시간이 지나 realm업데이트와 realm이 mongodb로 인수되는 과정을 통해서 많은 부분이 바뀐 것이 있습니다. 그리고 추가로 @realm/react 패키지를 사용해서 더 간편하게 realm을 사용하는 방법과 주의 사항등을 알아보도록 하겠습니다.

설치하기

react-native v0.70.6에서 작성하였습니다.

1
npm install --save realm @realm/react @types/realm react-native-get-random-values
  • react-native-get-random-values: BSON랜덤 데이터 생성시 필요
  • @types/realm: realm type정의된 패키지
  • realm: realm 패키지
  • @realm/react: react에서 사용하기 편하게, hooks로 몇가지 메소드가 정의 되어있음

패키지들을 설치해줍니다.

1
cd ios && pod install && cd ..

ios에 cocoapod을 설치합니다.
realm studio설치는 relam 포스팅을 참고해주세요.

코드 작성

realm을 이용해서 todo를 만들어보도록 하겠습니다.
조건은 다음과 같습니다.

1
2
3
todo 리스트를 만든다.
생성과 삭제가 가능하다.
주소들과 이름을 작성할 수 있어야 한다.

먼저 Todo스키마를 만들어줍니다.

src/db/Todo.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import 'react-native-get-random-values';
import {Realm} from '@realm/react';

export interface ITodo {
_id?: string;
text: string;
addresses: IAddress[];
createdAt?: string;
updatedAt?: string;
}

export interface IAddress {
name: string;
}

export const AddressSchema = {
name: 'Address',
embedded: true,
properties: {
name: 'string',
},
};

export const TodoSchema = {
name: 'Todo',
properties: {
_id: {type: 'string', default: () => `${new Realm.BSON.ObjectId()}`},
text: 'string',
addresses: {type: 'list', objectType: 'Address'},
createdAt: {type: 'string', default: () => new Date().toISOString()},
updatedAt: {type: 'string', default: () => new Date().toISOString()},
},
primaryKey: '_id',
};

class TodoModel extends Realm.Object {
static schema = TodoSchema;
}

export default TodoModel;

스키마를 만들고나면 realm 초기설정과 함께 스키마를 불러오는 코드를 작성합니다.

src/db/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import {createRealmContext} from '@realm/react';
import TodoModel from './Todo';

export const SCHEMA_VERSION = 1;

export const config = {
schema: [TodoModel],
schemaVersion: SCHEMA_VERSION,
};

const realmContext = createRealmContext(config);

export default realmContext;

초기설정이 되었다면 다음으로 react에 연동해보도록 하겠습니다.
AppWrapper를 만들어서 App을 RealmProvider로 감싸줍니다.

AppWrapper.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React from 'react';
import App from './App';
import realmContext from './src/db';

const AppWrapper = () => {
const {RealmProvider} = realmContext;
return (
<RealmProvider>
<App />
</RealmProvider>
);
};

export default AppWrapper;

index.js파일을 수정해줍니다.

index.js
1
2
3
4
5
6
7
8
9
/**
* @format
*/

import {AppRegistry} from 'react-native';
import {name as appName} from './app.json';
import AppWrapper from './AppWrapper';

AppRegistry.registerComponent(appName, () => AppWrapper);

작성하고 실행을 해보면 다음과 같은 에러가 나옵니다.

에러

주의 이때는 embedded가 true로 되어있는 스키마도 config에 넣어주어야 합니다.
TodoSchema에 연결되어 있더라도 따로 불러와 주어야 합니다.

src/db/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import {createRealmContext} from '@realm/react';
import TodoModel, {AddressSchema} from './Todo';

export const SCHEMA_VERSION = 2; // 디비구조가 바뀌면 버전을 바꿔야합니다.

export const config = {
schema: [TodoModel, AddressSchema], // AddressSchema 추가
schemaVersion: SCHEMA_VERSION,
};

const realmContext = createRealmContext(config);

export default realmContext;

스키마에 프로퍼티가 하나라도 바뀔시에는 SCHEMA_VERSION을 바꾸어야합니다. properties의 타입이 추가되거나 바뀌어도 버전을 바꾸어줘야합니다.

App.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import React from 'react';
import {Button, SafeAreaView, TextInput, View} from 'react-native';
import realmContext from './src/db';

const App = () => {
const {useRealm} = realmContext;
const realm = useRealm();
return (
<SafeAreaView>
<View>
<TextInput
style={{
borderWidth: 1,
height: 40,
}}
/>
<Button
title="생성"
onPress={() => {
console.log(realm.path);
}}
/>
</View>
</SafeAreaView>
);
};

export default App;

앱 컴포넌트를 작성합니다.

realm studio 실행

버튼을 누르고 콘솔을 확인해보면 다음과 같은 path가 나옵니다.
path경로

터미널을 새로 열고, open 명령어를 이용해서 폴더를 열어줍니다.

1
open /Users/<사용자이름>/Library/Developer/CoreSimulator/Devices/<값>/data/Containers/Data/Application/<값>/Documents/default.realm

realm studio

생성 및 삭제 구현

useTodo를 생성합니다.
realm이 수정되면 자동으로 리렌터링이 되기 때문에 state관리를 따로 하지 않아도 됩니다. state을 수동으로 관리를 하려면 realm을 useRealm이 아닌 직접 realm을 open시켜주어야 합니다.

src/hooks/useTodo.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import {useCallback} from 'react';
import realmContext from '../db';
import {ITodo} from '../db/Todo';

const useTodo = () => {
const {useQuery, useRealm} = realmContext;
const todos: Realm.Results<ITodo> = useQuery<ITodo>('Todo');
const realm = useRealm();

const createTodo = useCallback(
(todo: ITodo) => {
if (!todo.text) {
return;
}
try {
realm.write(() => {
let t: ITodo = realm.create('Todo', {
text: todo.text,
addresses: todo.addresses,
});
console.log(`created one todo: ${t.text}`);
});
} catch (error) {
console.log('Fail create', error);
}
},
[realm],
);

const deleteTodo = useCallback(
(_id: string) => {
try {
const todo = realm.objectForPrimaryKey<ITodo>('Todo', _id);
if (todo) {
realm.write(() => {
realm.delete(todo);
});
}
} catch (error) {
console.log('Not found id', error);
}
},
[realm],
);
return {createTodo, deleteTodo, todos};
};

export default useTodo;

그리고 App을 수정해줍니다.

App.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import React, {useState} from 'react';
import {
Button,
FlatList,
SafeAreaView,
Text,
TextInput,
View,
} from 'react-native';
import useTodo from './src/hooks/useTodo';

const App = () => {
const [text, setText] = useState('');
const {createTodo, deleteTodo, todos} = useTodo();

return (
<SafeAreaView>
<View>
<TextInput
style={{
borderWidth: 1,
height: 40,
}}
onChangeText={setText}
value={text}
/>
<Button
title="생성"
onPress={() => {
createTodo({
text,
addresses: [],
});
setText('');
}}
/>
<FlatList
data={todos}
extraData={todos}
keyExtractor={item => `${item._id}${Math.random()}`}
renderItem={({item}) => (
<View>
<Text>{item.text}</Text>
<Button
title="삭제"
onPress={() => {
if (item._id) {
deleteTodo(item._id);
}
}}
/>
</View>
)}
/>
</View>
</SafeAreaView>
);
};

export default App;

앱을 실행시켜줍니다.

결과

결과

마무리

react-native 와 realm 그리고 @realm/react를 같이 사용해보았습니다.
다른 자세한 내용은 공식문서에서 찾아보시면 됩니다.

공식문서의 예시가 적고 구글에서도 React native에서 realm을 사용하는 사례가 많지 않아서 구현하기 번거로움이 많았습니다.

Share