Join us Sept 17 at .local NYC! Use code WEB50 to save 50% on tickets. Learn more >
MongoDB Event
Docs Menu
Docs Home
/ /
Atlas App Services
/ /

튜토리얼: React Native 애플리케이션을 PowerSync로 마이그레이션

2024 9월부터 Atlas Device SDK(Realm), Device Sync 및 App Services 더 이상 사용되지 않습니다. 즉, 이러한 서비스의 사용자는 2025 9월까지 다른 솔루션으로 마이그레이션 해야 합니다. 시간이 더 필요하면 지원 에 문의하세요.

PowerSync는 Atlas Device Sync 의 최고의 대안입니다. SQLite 기반 솔루션이며 Device Sync 사용하는 모바일 애플리케이션 있는 경우 마이그레이션 하기에 적합한 솔루션이 될 수 있습니다.

이 튜토리얼에서는 React Native 로 작성된 Device Sync 모바일 애플리케이션 PowerSync로 마이그레이션 데 필요한 단계를 가이드 합니다. 백엔드 데이터는 Atlas 에 유지되므로 PowerSync 서비스를 구성하고, 로컬 데이터베이스 스키마 및 바인딩을 업데이트 , Atlas 에 쓰기 위한 백엔드 서비스를 설정하다 .

이 튜토리얼에서는 영역 Powersync 예시 리포지토리 에서 제공되는 React Native 용 Realm 할 일 목록 애플리케이션 사용합니다.2

먼저 Atlas 클러스터를 배포 하고 몇 가지 테스트 데이터를 입력해야 합니다. 이렇게 하면 Atlas 처음 설정하는 것처럼 가이드 할 수 있습니다. 이미 클러스터 배포한 경우 언제든지 건너뛰어도 됩니다.

  1. MongoDB Atlas 로 이동하여 Atlas 계정을 등록하거나 이미 계정이 있는 경우 로그인합니다.

  2. 다음으로 클러스터 만듭니다.

    UI 스크린샷

    테스트 목적으로 기본값 설정으로 M0 (무료) 클러스터 선택합니다. 필요에 맞게 자유롭게 추가로 변경할 수 있습니다.

  3. 배포 만들기 를 클릭합니다.

    UI 스크린샷

    대시 대시보드 로 돌아갑니다. Connect to Cluster(클러스터에 연결) 모달이 자동으로 표시됩니다.

  4. 연결 방법 선택 을 클릭한 다음 드라이버 를 선택합니다.

    UI 스크린샷

    이 화면에서 3단계에 표시된 URL 복사합니다.

    UI 스크린샷

    애플리케이션 코드에 연결 문자열 추가합니다. 다음은 연결 문자열 입니다. MongoDB 인스턴스 액세스 데 필요합니다. 나중에 참조할 수 있도록 연결 문자열 저장합니다.

    다음 단계에서는 PowerSync 인스턴스 데이터베이스 에 연결하는 데 사용할 사용자 이름 과 비밀번호를 생성합니다.

  5. Done(완료)을 클릭하여 모달을 닫습니다.

    클러스터 배포가 완료되면 대시 대시보드 다음과 유사하게 표시됩니다.

  6. Add data(데이터 추가)를 클릭하여 새 데이터베이스 만듭니다.

    UI 스크린샷

    Atlas 에서 데이터베이스 만들기 카드에서 시작 을 클릭합니다.

    UI 스크린샷

    PowerSync라는 데이터베이스 와 Item이라는 컬렉션 생성한 다음 Create Database(데이터베이스 생성)를 클릭합니다.

    UI 스크린샷

    대시 대시보드 로 돌아가서 새로 생성된 데이터베이스 와 컬렉션 볼 수 있습니다.

    UI 스크린샷

    마지막으로 PowerSync가 이 데이터베이스 에 연결하는 데 사용할 새 사용자를 생성해야 합니다.

    왼쪽 사이드바의 보안 제목 아래에서 데이터베이스 액세스를 클릭합니다.

    UI 스크린샷

    Add New Database User(새 데이터베이스 사용자 추가)를 클릭하고(이)라는 새 사용자를 powersync 만들고 비밀번호를 입력합니다. 이전에 복사한 연결 문자열 에 사용할 사용자 이름 과 비밀번호를 기록해 둡니다.

    참고

    사용자 이름 또는 비밀번호에 다음과 같은 특수 문자가 포함되어 있는 경우 연결 문자열 에 대해 URL 안전 형식으로 변환해야 합니다: $, :, /, ?, !, #, [, ], @. 이 작업을 수동으로 수행하거나 urlencoder.org와 같은 URL 인코딩 애플리케이션 사용할 수 있습니다.

    데이터베이스 사용자 권한 섹션에서 특정 권한 추가를 클릭하고 PowerSync 데이터베이스 에 대한 및 역할 에 대한 권한 을 추가합니다. readWrite dbAdmin

    UI 스크린샷

    Add User(사용자 추가)를 클릭합니다.

    필요한 데이터베이스 권한을 가진 새로 생성된 사용자가 표시되어야 합니다.

    UI 스크린샷

사용자 권한에 대한 자세한 내용은 PowerSync 소스 데이터베이스 설정 가이드 의 MongoDB 섹션을 참조하세요.

PowerSync가 Atlas 에서 실행 데이터베이스 액세스 하려면 IP 액세스 목록 에 서비스 IP 주소를 추가해야 합니다. 이러한 IP 주소는 PowerSync 보안 & IP 필터링 설명서에 나열되어 있습니다.

왼쪽 사이드바의 보안 제목 아래에서 네트워크 액세스를 클릭합니다.

+ IP 주소 추가를 클릭하고 IP 주소 입력합니다. 향후 이 목록을 관리하는 사람을 더 잘 지원할 수 있도록 선택적 설명으로 PowerSync를 입력하는 것도 좋습니다.

Confirm(확인)을 클릭하고 각 IP 에 대해 이 과정을 반복합니다.

아직 업데이트하지 않았다면 이전에 복사한 연결 문자열 의 자리 표시자를 데이터베이스 사용자의 사용자 이름 과 비밀번호로 업데이트 .

이 단계에서는 이후 단계에서 데이터를 동기화 데 사용할 몇 가지 샘플 데이터를 가져옵니다.

먼저 MongoDB 데이터베이스 도구 설치하여 mongoimport에 대한 액세스 얻습니다. 운영 체제의 설치 가이드 지침을 참조하세요.

database-tools를 설치한 후 터미널에 다음을 입력하여 mongoimport 액세스 할 수 있는지 확인합니다.

mongoimport --version

그러면 도구 버전이 반환됩니다. 문제가 있는 경우 위의 설치 가이드 참조하세요.

다음으로, 다음 내용이 포함된 sample.json 라는 JSON 파일 만듭니다.

[
{
"isComplete": false,
"summary": "Complete project documentation",
"owner_id": "mockUserId"
},
{
"isComplete": true,
"summary": "Buy groceries",
"owner_id": "mockUserId"
},
{
"isComplete": false,
"summary": "Schedule dentist appointment",
"owner_id": "mockUserId"
},
{
"isComplete": false,
"summary": "Prepare presentation for next week",
"owner_id": "mockUserId"
},
{
"isComplete": true,
"summary": "Pay utility bills",
"owner_id": "mockUserId"
},
{
"isComplete": false,
"summary": "Fix bug in login system",
"owner_id": "mockUserId2"
},
{
"isComplete": false,
"summary": "Call mom",
"owner_id": "mockUserId"
},
{
"isComplete": true,
"summary": "Submit expense reports",
"owner_id": "mockUserId2"
},
{
"isComplete": false,
"summary": "Plan team building event",
"owner_id": "mockUserId2"
},
{
"isComplete": false,
"summary": "Review pull requests",
"owner_id": "mockUserId2"
}
]

이 샘플 데이터에는 몇 가지 할 일 목록 항목이 포함되어 있습니다. owner_id 는 이 튜토리얼의 뒷부분에서 필터링 예시에 사용됩니다.

이 JSON 가져오려면 다음 명령을 입력하여 <connection-string> 자리 표시자를 연결 문자열 로 바꿉니다.

mongoimport --uri="<connection-string>" --db=PowerSync --collection=Item
--file=sample.json --jsonArray

다음 메시지가 표시됩니다.

10 document(s) imported successfully. 0 document(s) failed to import.

그렇지 않은 경우 명령 매개변수( 연결 문자열 포함)가 올바른지, Atlas user 올바른 데이터베이스 액세스 있는지 확인하세요.

Atlas UI 에서 컬렉션 으로 이동하거나 MongoDB Compass 시각적 데스크톱 애플리케이션 사용하여 삽입된 문서를 보고 관리 할 수 있습니다. MongoDB Compass 통해 데이터베이스 와 컬렉션을 보고 관리 하려면 동일한 연결 문자열 사용하여 연결해야 합니다.

UI 스크린샷

이제 PowerSync로 이동하여 등록하거나 로그인합니다.

처음으로 로그인하는 경우 시작하려면 새 인스턴스 만들어야 합니다.

TodoList라는 새 인스턴스 만듭니다.

UI 스크린샷

연결 데이터베이스 로 MongoDB 선택합니다.

UI 스크린샷

Atlas 연결 문자열 사용하여 연결 설정을 채웁니다.

중요

사용자 이름, 비밀번호 또는 기타 URL 매개변수를 포함하지 않는 축약된 버전의 연결 문자열 사용하세요. 예시 들어 연결은 mongodb+srv://m0cluster.h6folge.mongodb.net/와(과) 같이 표시됩니다.

데이터베이스 이름("PowerSync"), 사용자 이름 ("Powersync"), 이전 단계에서 이 계정에 할당한 비밀번호를 입력합니다.

UI 스크린샷

Test connection(연결 테스트)을 클릭하여 성공적으로 연결할 수 있는지 확인합니다.

다음 오류가 표시되면 필요한 모든 PowerSync 서비스 IP가 Atlas IP 액세스 목록 에 있는지 확인합니다.

UI 스크린샷

그래도 문제가 지속되면 MongoDB 연결에 대한 PowerSync 데이터베이스 연결 가이드를 참조하세요.

Next(다음)를 클릭하여 새 PowerSync 인스턴스 배포 . 이 작업을 완료하는 데 몇 분 정도 걸릴 수 있습니다.

인스턴스 배포된 후에는 몇 가지 기본 동기화 규칙을 만들어 마이그레이션된 데이터를 볼 수 있는지 확인할 수 있습니다.

먼저 기본값 동기화 규칙을 제거 하고 다음으로 바꿉니다.

bucket_definitions:
user_buckets:
parameters: SELECT request.user_id() as user_id
data:
- SELECT _id as id, * FROM "Item" WHERE bucket.user_id = 'global'
OR owner_id = bucket.user_id

항목이 PowerSync 서비스에 올바르게 동기화 되도록 하려면 다음 사항에 유의하세요.

  • _idid에 매핑되어야 합니다.

  • 컬렉션 이름 'Item'은 따옴표로 묶어야 합니다. 이는 컬렉션 이름이 대문자로 시작하기 때문입니다.

  • 사용자별 버킷은 전체 데이터베이스 에 대한 액세스 제공하는 globaluser_id 와 일치해야 합니다. 그렇지 않으면 제공된 user_id에서 일치하게 되며, 이는 인증 토큰에서 검색됩니다.

PowerSync 동기화 규칙은 매우 심층적 주제 입니다. 자세한 학습 은 이 동기화 규칙 블로그 포스트 또는 PowerSync 동기화 규칙 문서를 참조하세요.

저장 및 배포 를 클릭합니다. 배포서버 완료되는 데 몇 분 정도 걸릴 수 있습니다.

배포서버 완료되면 다음이 표시됩니다.

UI 스크린샷

배포서버 완료되면 적절한 상태가 표시됩니다.

오류가 발생하면 사용자가 PowerSync PowerSync 소스 데이터베이스 설정 문서에 나열된 권한으로 설정하다 있는지 확인하세요.

인스턴스 관리를 클릭하여 동기화 규칙 및 배포서버 상태를 검토 .

이 설정 완료하려면 PowerSync 진단 앱을 사용하여 방금 생성하고 동기화 규칙에 추가한 할 일 목록 항목을 확인합니다. 이 도구를 사용하려면 먼저 개발 토큰을 만들어야 합니다.

  • PowerSync 페이지 상단에서 Manage Instances를 클릭합니다.

  • 왼쪽 사이드바에서 TodoList 옆에 있는 줄임표(...)를 클릭하여 이 인스턴스에 대한 컨텍스트 메뉴를 연 다음 인스턴스 편집을 선택합니다.

  • 클라이언트 인증 탭 선택하고 개발 토큰 활성화를 클릭합니다.

  • 저장 및 배포 클릭합니다.

UI 스크린샷

TodoList 옆에 있는 줄임표(...)를 클릭하여 이 인스턴스 에 대한 컨텍스트 메뉴를 다시 열고 개발 토큰 생성 을 선택합니다.

토큰 주체/user_id를 제공하라는 메시지가 표시됩니다. 이는 user_id 역할을 하며 이에 따라 작동하도록 동기화 규칙을 설정하다 수 있습니다.

앞에서 정의한 동기화 규칙을 사용하여 subject/user_id를 global 로 설정하다 전체 데이터 세트에 액세스 할 수 있는 토큰을 생성할 수 있습니다. 이 값을 mockUserId 또는 mockUserId2 로 설정하다 특정 owner_id에서 동기화 할 수도 있습니다.

생성된 토큰을 복사한 다음 진단 앱을 열고 개발 토큰을 붙여넣습니다.

참고

개발 토큰은 12 시간 후에 만료됩니다. 진단 도구는 만료 후 Atlas 와의 동기화를 중지하므로 동기화를 다시 시작하려면 새 토큰을 생성해야 합니다.

이와 유사한 페이지가 표시됩니다.

UI 스크린샷

왼쪽 사이드바에서 SQL 콘솔 을 클릭합니다.

모든 항목을 보려면 SELECT 쿼리 만듭니다.

SELECT * FROM Item
UI 스크린샷

이제 MongoDB database 모바일 애플리케이션 에 동기화 데 필요한 모든 서비스를 사용할 수 있습니다.

이 단계에서는 React Native 용 Realm 할 일 목록 애플리케이션 복제합니다. main 예시 리포지토리 의 브랜치에는 마이그레이션 의 최종 결과가 포함되어 있습니다.

예시 리포지토리를 사용하여 이 가이드 따르려면 00-Start-Here 브랜치를 확인하세요.

git clone https://github.com/takameyer/realm2powersync
cd realm2powersync
git checkout 00-Start-Here

다음으로, 편집기가 가져오기를 선택할 수 있도록 종속성을 설치하고 이 프로젝트 편집할 때 오류가 없는지 확인합니다.

중요

이 튜토리얼에서는 최신 버전의 Node.js가 설치되어 있다고 가정합니다.

npm install

이 애플리케이션 활성 Device Sync 서비스가 있는 Atlas cluster 가 있다고 가정하기 때문에 아직 실행할 수 없습니다. 다음 단계에서는 프로젝트 로컬 전용 애플리케이션 으로 실행 데 필요한 수정 작업을 수행합니다.

애플리케이션 이 로컬 전용 데이터로 실행 되도록 Atlas Device Sync 부분을 제거 해야 합니다.

먼저 source/AppWrapper.txs를 열고 AppProvider, UserProvidersync 구성을 제거 .

업데이트된 AppWrapper.txs 파일 다음과 유사해야 합니다.

import React from 'react';
import { StyleSheet, View, ActivityIndicator } from 'react-native';
import { RealmProvider } from '@realm/react';
import { App } from './App';
import { Item } from './ItemSchema';
const LoadingIndicator = () => {
return (
<View style={styles.activityContainer}>
<ActivityIndicator size="large" />
</View>
);
};
export const AppWrapper = () => {
return (
<RealmProvider schema={[Item]} fallback={LoadingIndicator}>
<App />
</RealmProvider>
);
};
const styles = StyleSheet.create({
activityContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-around',
padding: 10,
},
});

다음으로 source/App.tsx를 열고 dataExplorerLink 관련 부분과 OfflineModeLogout의 헤더 버튼을 제거 (이는 나중에 구현됨).

업데이트된 App.tsx 파일 다음과 유사해야 합니다.

import React from 'react';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { StyleSheet, Text, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { LogoutButton } from './LogoutButton';
import { ItemListView } from './ItemListView';
import { OfflineModeButton } from './OfflineModeButton';
const Stack = createStackNavigator();
const headerRight = () => {
return <OfflineModeButton />;
};
const headerLeft = () => {
return <LogoutButton />;
};
export const App = () => {
return (
<>
{/* All screens nested in RealmProvider have access
to the configured realm's hooks. */}
<SafeAreaProvider>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Your To-Do List"
component={ItemListView}
options={{
headerTitleAlign: 'center',
//headerLeft,
//headerRight,
}}
/>
</Stack.Navigator>
</NavigationContainer>
<View style={styles.footer}>
<Text style={styles.footerText}>
Log in with the same account on another device or simulator to see
your list sync in real time.
</Text>
</View>
</SafeAreaProvider>
</>
);
};
const styles = StyleSheet.create({
footerText: {
fontSize: 12,
textAlign: 'center',
marginVertical: 4,
},
hyperlink: {
color: 'blue',
},
footer: {
paddingHorizontal: 24,
paddingVertical: 12,
},
});

마지막으로 source/ItemListView.tsx를 열고 다음과 같이 업데이트합니다.

  • Flexible 동기화 구독 코드 제거

  • 사용자를 모의 사용자로 바꿉니다: - const user={ id: 'mockUserId' };

  • 모든 dataExplorerer 참조 제거

  • Show All Tasks 스위치의 기능 제거(나중에 구현됨)

업데이트된 ItemListView.tsx 파일 다음과 유사해야 합니다.

import React, { useCallback, useState, useEffect } from 'react';
import { BSON } from 'realm';
import { useRealm, useQuery } from '@realm/react';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { Alert, FlatList, StyleSheet, Switch, Text, View } from 'react-native';
import { Button, Overlay, ListItem } from '@rneui/base';
import { CreateToDoPrompt } from './CreateToDoPrompt';
import { Item } from './ItemSchema';
import { colors } from './Colors';
export function ItemListView() {
const realm = useRealm();
const items = useQuery(Item).sorted('_id');
const user = { id: 'mockUserId' };
const [showNewItemOverlay, setShowNewItemOverlay] = useState(false);
const [showAllItems, setShowAllItems] = useState(true);
// createItem() takes in a summary and then creates an Item object with that summary
const createItem = useCallback(
({ summary }: { summary: string }) => {
// if the realm exists, create an Item
realm.write(() => {
return new Item(realm, {
summary,
owner_id: user?.id,
});
});
},
[realm, user],
);
// deleteItem() deletes an Item with a particular _id
const deleteItem = useCallback(
(id: BSON.ObjectId) => {
// if the realm exists, get the Item with a particular _id and delete it
const item = realm.objectForPrimaryKey(Item, id); // search for a realm object with a primary key that is an objectId
if (item) {
if (item.owner_id !== user?.id) {
Alert.alert("You can't delete someone else's task!");
} else {
realm.write(() => {
realm.delete(item);
});
}
}
},
[realm, user],
);
// toggleItemIsComplete() updates an Item with a particular _id to be 'completed'
const toggleItemIsComplete = useCallback(
(id: BSON.ObjectId) => {
// if the realm exists, get the Item with a particular _id and update it's 'isCompleted' field
const item = realm.objectForPrimaryKey(Item, id); // search for a realm object with a primary key that is an objectId
if (item) {
if (item.owner_id !== user?.id) {
Alert.alert("You can't modify someone else's task!");
} else {
realm.write(() => {
item.isComplete = !item.isComplete;
});
}
}
},
[realm, user],
);
return (
<SafeAreaProvider>
<View style={styles.viewWrapper}>
<View style={styles.toggleRow}>
<Text style={styles.toggleText}>Show All Tasks</Text>
<Switch
trackColor={{ true: '#00ED64' }}
onValueChange={() => {
setShowAllItems(!showAllItems);
}}
value={showAllItems}
/>
</View>
<Overlay
isVisible={showNewItemOverlay}
overlayStyle={styles.overlay}
onBackdropPress={() => setShowNewItemOverlay(false)}>
<CreateToDoPrompt
onSubmit={({ summary }) => {
setShowNewItemOverlay(false);
createItem({ summary });
}}
/>
</Overlay>
<FlatList
keyExtractor={item => item._id.toString()}
data={items}
renderItem={({ item }) => (
<ListItem key={`${item._id}`} bottomDivider topDivider>
<ListItem.Title style={styles.itemTitle}>
{item.summary}
</ListItem.Title>
<ListItem.Subtitle style={styles.itemSubtitle}>
<Text>{item.owner_id === user?.id ? '(mine)' : ''}</Text>
</ListItem.Subtitle>
<ListItem.Content>
{!item.isComplete && (
<Button
title="Mark done"
type="clear"
onPress={() => toggleItemIsComplete(item._id)}
/>
)}
<Button
title="Delete"
type="clear"
onPress={() => deleteItem(item._id)}
/>
</ListItem.Content>
</ListItem>
)}
/>
<Button
title="Add To-Do"
buttonStyle={styles.addToDoButton}
onPress={() => setShowNewItemOverlay(true)}
/>
</View>
</SafeAreaProvider>
);
}
const styles = StyleSheet.create({
viewWrapper: {
flex: 1,
},
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
addToDoButton: {
backgroundColor: colors.primary,
borderRadius: 4,
margin: 5,
},
completeButton: {
backgroundColor: colors.primary,
borderRadius: 4,
margin: 5,
},
showCompletedButton: {
borderRadius: 4,
margin: 5,
},
showCompletedIcon: {
marginRight: 5,
},
itemTitle: {
flex: 1,
},
itemSubtitle: {
color: '#979797',
flex: 1,
},
toggleRow: {
flexDirection: 'row',
alignItems: 'center',
padding: 12,
},
toggleText: {
flex: 1,
fontSize: 16,
},
overlay: {
backgroundColor: 'white',
},
status: {
width: 40,
height: 40,
justifyContent: 'center',
borderRadius: 5,
borderWidth: 1,
borderColor: '#d3d3d3',
backgroundColor: '#ffffff',
alignSelf: 'flex-end',
},
delete: {
alignSelf: 'flex-end',
width: 65,
marginHorizontal: 12,
},
statusCompleted: {
borderColor: colors.purple,
},
statusIcon: {
textAlign: 'center',
fontSize: 17,
color: colors.purple,
},
});

이러한 변경으로 인해 앱 로컬 데이터베이스 에 대해 작동해야 합니다.

마이그레이션 시작하기 전에 업데이트된 애플리케이션 빌드 하고 실행 의도한 대로 작동하는지 확인해야 합니다.

iOS 의 경우 다음 명령을 실행 .

npx pod-install
npm run ios

Android의 경우 다음 명령을 실행 .

npm run android

모든 빌드 오류는 이 문서의 범위를 벗어납니다. 빌드 관련 문제가 발생하는 경우 React Native 설명서를 참조하여 환경이 올바르게 설정하다 되었는지 확인하세요.

앱 실행 되는 동안 기본 기능을 확인할 수 있습니다. 다음을 수행할 수 있어야 합니다.

  • 새 항목 만들기

  • 항목을 완료로 표시

  • 항목 삭제

UI 스크린샷

이제 로컬 전용 Realm 애플리케이션 실행 이 애플리케이션 로컬 전용 버전의 PowerSync 클라이언트 사용하도록 변환할 수 있습니다.

PowerSync는 SQLite 기반 데이터베이스 사용하므로 스키마 를 일부 수정해야 합니다.

이렇게 하려면 PowerSync 클라이언트 를 설정하다 해야 합니다. 자세한 지침은 @Powersync/react-native npm 리포지토리 또는 PowerSync React Native 설정 문서를 참조하세요.

먼저 다음 명령을 실행 PowerSync React Native Client, 지원 SQLite 데이터베이스, 비동기 반복자 폴리필(지침에 따라 필요) 및 bson 종속성(삽입을 위한 ObjectId를 생성하는 데 사용됨)에 대한 종속성을 추가합니다. 문서를 MongoDB 에 저장):

npm install @powersync/react-native @journeyapps/react-native-quick-sqlite @azure/core-asynciterator-polyfill bson

폴리필을 설정 하려면 index.js를 열고 파일 맨 위에 import '@azure/core-asynciterator-polyfill'; 를 추가합니다.

업데이트된 index.js 파일 다음과 유사해야 합니다.

import '@azure/core-asynciterator-polyfill';
import 'react-native-get-random-values';
import {AppRegistry} from 'react-native';
import {AppWrapper} from './source/AppWrapper';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => AppWrapper);

이제 종속성이 추가되었으므로 애플리케이션 다시 빌드해야 합니다.

  • iOS 의 경우 pod-install를 실행 .

  • Android의 경우 react-native-quick-sqlite과 호환되도록 최소 필수 SDK를 24(으)로 업데이트 . 이렇게 하려면 android/build.gradle 를 열고 minSdkVersion 을 21 에서 24로 변경합니다.

이제 로컬 데이터베이스 의 데이터 유형과 스키마를 설정하다 .

특정 스키마 설정하다 방법을 결정하려면 PowerSync MongoDB 유형 매핑 설명서를 참조하세요. 다음은 사용 가능한 유형에 대한 빠른 참조입니다.

유형
설명

null

정의되지 않았거나 설정되지 않은 값

integer

64비트 부호 있는 정수

real

64비트 부동 점 숫자

text

UTF-8 텍스트 문자열

blob

이진 데이터

이 튜토리얼에서는 다음과 같이 source/ItemSchema.tsx 를 수정합니다.

import {column, Schema, Table} from '@powersync/react-native';
export const ItemSchema = new Table({
isComplete: column.integer,
summary: column.text,
owner_id: column.text,
});
export const AppSchema = new Schema({
Item: ItemSchema,
});
export type Database = (typeof AppSchema)['types'];
export type Item = Database['Item'];

중요

Schema 에 전달된 속성 이름은 로컬 테이블과 MongoDB 컬렉션 의 이름을 나타냅니다. 이 경우 이름이 Item인지 확인합니다.

이 코드는 유형을 수동으로 정의하는 대신 AppSchema에서 직접 내보냅니다.

PowerSync에 액세스 하고 데이터를 바인딩하려면 PowerSync 클라이언트 의 후크 및 제공자에 액세스 해야 합니다. 이 기능은 PowerSyncContext 구성 요소를 통해 제공됩니다.

먼저 source/AppWrapper.tsx 을 (업데이트 PowerSyncContext 을(를) 사용하고 PowerSync 클라이언트 초기화합니다.

import React from 'react';
import {App} from './App';
import {AppSchema} from './ItemSchema';
import {PowerSyncContext, PowerSyncDatabase} from '@powersync/react-native';
const powerSync = new PowerSyncDatabase({
schema: AppSchema,
database: {
dbFilename: 'powersync.db',
},
});
powerSync.init();
export const AppWrapper = () => {
return (
<PowerSyncContext.Provider value={powerSync}>
<App />
</PowerSyncContext.Provider>
);
};

그런 다음 PowerSync 클라이언트 사용하도록 ItemListView.tsx 을 (업데이트 . 이렇게 하려면 이 구성 요소의 상단에 사용되는 후크를 업데이트 해야 합니다.

  • 쓰기 및 업데이트를 위해 로컬 데이터베이스 에 액세스 하려면 usePowerSync 후크를 사용합니다.

  • 업데이트 시 자동으로 다시 렌더링되는 할 일 목록 항목의 목록을 가져오려면 useQuery 후크를 사용하세요.

다음과 같이 변경합니다.

  • 제거 import { BSON } from 'realm';

  • 추가 import { ObjectId } from 'bson';

  • ItemListView 함수의 처음 두 줄을 다음과 일치하도록 변경합니다.

    export function ItemListView() {
    const db = usePowerSync();
    const {data: items} = useQuery<Item>('SELECT * FROM Item');

다음으로 createItem, deleteItemtoggleItemIsComplete 메서드를 업데이트 해야 합니다.

이러한 각 메서드에 대해 usePowerSync에서 반환된 db 객체 사용하게 됩니다. Realm 과 마찬가지로 로컬 데이터베이스 트랜잭션 열어 삽입, 업데이트 또는 삭제 와 같은 변경 가능한 작업을 수행합니다. 또한 오류를 애플리케이션 의 프론트엔드 에 전파하기 위해 try/catch 블록을 추가합니다.

이 코드는 bson 에서 ObjectId 를 가져와서 각 항목에 대한 고유 ID를 생성한다는 점에 유의하세요. PowerSync는 기본 키 항목의 이름이 id로 지정될 것으로 예상합니다.

생성 코드는 또한 이 로직에서 직접 항목의 기본값 을 구현합니다. 이 경우 isComplete 는 false로 초기화되고, id 는 새로 생성된 ObjectId의 문자열 결과로 초기화됩니다.

createItem 메서드는 다음과 같이 구현할 수 있습니다.

// createItem() takes in a summary and then creates an Item object with that summary
const createItem = useCallback(
async ({summary}: {summary: string}) => {
try {
// start a write transaction to insert the new Item
db.writeTransaction(async tx => {
await tx.execute(
'INSERT INTO Item (id, summary, owner_id, isComplete) VALUES (?, ?, ?, ?)',
[new ObjectId().toHexString(), summary, user?.id, false],
);
});
} catch (ex: any) {
Alert.alert('Error', ex?.message);
}
},
[db],
);

deleteItemtoggleItemIsComplete 메서드와 유사하므로 다음과 같이 구현 .

// deleteItem() deletes an Item with a particular _id
const deleteItem = useCallback(
async (id: String) => {
// start a write transaction to delete the Item
try {
db.writeTransaction(async tx => {
await tx.execute('DELETE FROM Item WHERE id = ?', [id]);
});
} catch (ex: any) {
Alert.alert('Error', ex?.message);
}
},
[db],
);
// toggleItemIsComplete() updates an Item with a particular _id to be 'completed'
const toggleItemIsComplete = useCallback(
async (id: String) => {
// start a write transaction to update the Item
try {
db.writeTransaction(async tx => {
await tx.execute(
'UPDATE Item SET isComplete = NOT isComplete WHERE id = ?',
[id],
);
});
} catch (ex: any) {
Alert.alert('Error', ex?.message);
}
},
[db],
);

마지막으로 렌더링된 FlatList를 업데이트 . 다음을 수행할 수 있습니다.

  • _id 의 인스턴스를 id로 바꾸기

  • id 문자열을 직접 사용하도록 FlatListkeyExtractor 를 업데이트합니다.

  • 이전에는 데이터베이스 ObjectId을(를) 반환했습니다. 이를 문자열로 변환해야 합니다.

업데이트된 FlatList 은(는) 이제 다음과 유사합니다.

<FlatList
keyExtractor={item => item.id}
data={items}
renderItem={({item}) => (
<ListItem key={`${item.id}`} bottomDivider topDivider>
<ListItem.Title style={styles.itemTitle}>
{item.summary}
</ListItem.Title>
<ListItem.Subtitle style={styles.itemSubtitle}>
<Text>{item.owner_id === user?.id ? '(mine)' : ''}</Text>
</ListItem.Subtitle>
<ListItem.Content>
<Pressable
accessibilityLabel={`Mark task as ${
item.isComplete ? 'not done' : 'done'
}`}
onPress={() => toggleItemIsComplete(item.id)}
style={[
styles.status,
item.isComplete && styles.statusCompleted,
]}>
<Text style={styles.statusIcon}>
{item.isComplete ? '✓' : '○'}
</Text>
</Pressable>
</ListItem.Content>
<ListItem.Content>
<Pressable
accessibilityLabel={'Remove Item'}
onPress={() => deleteItem(item.id)}
style={styles.delete}>
<Text style={[styles.statusIcon, {color: 'blue'}]}>
DELETE
</Text>
</Pressable>
</ListItem.Content>
</ListItem>
)}
/>

코드 업데이트를 완료하면 로컬 PowerSync 클라이언트 사용할 수 있습니다.

확인하려면 애플리케이션 다시 빌드합니다. iOS 사용하는 경우 npx pod-install(으)로 파드를 업데이트 것을 잊지 마세요.

UI 스크린샷

이제 PowerSync를 사용하여 할 일 목록 항목을 추가, 업데이트 및 삭제 수 있는 애플리케이션 작동합니다.

문제가 발생하면 예시 리포지토리 의 02-Migrate-Local-Client 브랜치에서 이 점 까지 변경된 내용을 확인할 수 있습니다.

이제 모바일 애플리케이션 MongoDB 에서 실시간 으로 데이터를 동기화 할 준비가 되었습니다.

참고

Realm 데이터가 아직 마이그레이션되지 않은 것을 보았을 것입니다. 이 가이드 Atlas 에서 호스팅되는 MongoDB cluster 데이터의 진실 소스라고 가정하고 이를 애플리케이션 에 동기화 . 로컬 데이터 마이그레이션은 이 튜토리얼의 범위를 벗어나지만 향후 문서에서 해결될 수 있습니다.

이제 PowerSync 진단 도구를 사용하여 확인된 Atlas 의 동기화된 데이터가 포함된 PowerSync 서비스가 실행 중이어야 합니다.

이 단계에서는 이 데이터를 React Native 애플리케이션 에 동기화 합니다.

시작하려면 토큰 및 엔드포인트에 대한 일부 환경 변수를 설정하다 방법을 만들어야 합니다.

먼저, 개발자 종속성에 react-native-dotenv 을(를) 설치합니다. 이 바벨 플러그인은 프로젝트 의 루트에서 .env 파일 가져와 환경 변수를 애플리케이션 으로 직접 가져올 수 있습니다.

npm install -D react-native-dotenv

그런 다음 babel.config.js 파일 에 다음 줄을 추가합니다.

module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: ['module:react-native-dotenv'],
};

types라는 새 디렉토리 만들고 그 안에 가져오려는 다음 변수를 포함하는 env.d.ts 라는 새 파일 만듭니다.

declare module '@env' {
export const AUTH_TOKEN: string;
export const POWERSYNC_ENDPOINT: string;
}

PowerSync에서 환경 변수에 필요한 값을 조회 해야 합니다.

  • PowerSync 콘솔의 왼쪽 사이드바에서 TodoList 옆에 있는 ...을 클릭하여 컨텍스트 메뉴를 엽니다.

  • 인스턴스 편집 을 선택합니다.

  • URL 복사하여 저장합니다.

UI 스크린샷

다음으로, 주체/사용자 ID가 인 인스턴스 에 대한 새 개발 토큰을 mockUserId 생성합니다. 생성된 토큰을 복사하여 저장합니다.

애플리케이션 프로젝트 에서 루트 디렉토리 .env 파일 만들고 방금 생성한 PowerSync 엔드포인트와 토큰을 붙여넣습니다.

POWERSYNC_ENDPOINT=<endpoint>
AUTH_TOKEN=<dev-token>

PowerSync 인스턴스 에 연결할 수 있도록 애플리케이션 약간 리팩터링해야 합니다.

먼저 sourcePowerSync.ts 라는 새 파일 만들고 다음을 붙여넣습니다.

import { AppSchema } from './ItemSchema';
import {
AbstractPowerSyncDatabase,
PowerSyncDatabase,
} from '@powersync/react-native';
import { AUTH_TOKEN, POWERSYNC_ENDPOINT } from '@env';
const powerSync = new PowerSyncDatabase({
schema: AppSchema,
database: {
dbFilename: 'powersync.db',
},
});
powerSync.init();
class Connector {
async fetchCredentials() {
return {
endpoint: POWERSYNC_ENDPOINT,
token: AUTH_TOKEN,
};
}
async uploadData(database: AbstractPowerSyncDatabase) {
console.log('Uploading data');
}
}
export const setupPowerSync = (): PowerSyncDatabase => {
const connector = new Connector();
powerSync.connect(connector);
return powerSync;
};
export const resetPowerSync = async () => {
await powerSync.disconnectAndClear();
setupPowerSync();
};

이 파일 다음을 수행합니다.

  • PowerSync 클라이언트 에서 개발자 토큰 및 PowerSync 엔드포인트를 설정하다 데 사용할 새 Connector 클래스를 생성합니다.

  • 다음 단계에서 Atlas 에 변경 사항을 푸시하는 데 사용할 모의 uploadData 함수를 정의합니다.

  • PowerSync 클라이언트 설정하다 하고 재설정하는 메서드를 정의합니다. 클라이언트 재설정하면 변경 사항이 대기열에 배치되므로 지금 개발하는 데 유용합니다. 이러한 변경 사항이 처리될 때까지는 새 업데이트를 받을 수 없습니다.

다음으로, 새로운 setupPowerSync 메서드를 사용하도록 AppWrapper.tsx 업데이트 .

import { PowerSyncContext } from '@powersync/react-native';
import React from 'react';
import { App } from './App';
import { setupPowerSync } from './PowerSync';
const powerSync = setupPowerSync();
export const AppWrapper = () => {
return (
<PowerSyncContext.Provider value={powerSync}>
<App />
</PowerSyncContext.Provider>
);
};

그런 다음 LogoutButton.tsx 을 리팩터링하여 resetPowerSync 메서드를 구현 . 이름을 ResetButton.tsx(으)로 바꾸고 다음과 같이 내용을 업데이트 .

import React, { useCallback } from 'react';
import { Pressable, Alert, View, Text, StyleSheet } from 'react-native';
import { colors } from './Colors';
import { resetPowerSync } from './PowerSync';
export function ResetButton() {
const signOut = useCallback(() => {
resetPowerSync();
}, []);
return (
<Pressable
onPress={() => {
Alert.alert('Reset Database?', '', [
{
text: 'Yes, Reset Database',
style: 'destructive',
onPress: () => signOut(),
},
{ text: 'Cancel', style: 'cancel' },
]);
}}>
<View style={styles.buttonContainer}>
<Text style={styles.buttonText}>Reset</Text>
</View>
</Pressable>
);
}
const styles = StyleSheet.create({
buttonContainer: {
paddingHorizontal: 12,
},
buttonText: {
fontSize: 16,
color: colors.primary,
},
});

그런 다음 헤더 왼쪽에 Reset 버튼이 표시되도록 App.tsx 을 수정합니다.

  • import { LogoutButton } from './LogoutButton'; 을(를) import { ResetButton } from './ResetButton';(으)로 바꿉니다.

  • headerLeft에서 기존 줄을 return <ResetButton />;로 바꿉니다.

  • 재설정 버튼이 표시되도록 //headerLeft 줄의 주석 처리를 제거합니다.

변경 사항은 다음과 같습니다.

import React from 'react';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { StyleSheet, Text, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { ResetButton } from './ResetButton';
import { ItemListView } from './ItemListView';
import { OfflineModeButton } from './OfflineModeButton';
const Stack = createStackNavigator();
const headerRight = () => {
return <OfflineModeButton />;
};
const headerLeft = () => {
return <ResetButton />;
};
export const App = () => {
return (
<>
{/* All screens nested in RealmProvider have access
to the configured realm's hooks. */}
<SafeAreaProvider>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Your To-Do List"
component={ItemListView}
options={{
headerTitleAlign: 'center',
headerLeft,
//headerRight,
}}
/>
</Stack.Navigator>
</NavigationContainer>
<View style={styles.footer}>
<Text style={styles.footerText}>
Log in with the same account on another device or simulator to see
your list sync in real time.
</Text>
</View>
</SafeAreaProvider>
</>
);
};
const styles = StyleSheet.create({
footerText: {
fontSize: 12,
textAlign: 'center',
marginVertical: 4,
},
hyperlink: {
color: 'blue',
},
footer: {
paddingHorizontal: 24,
paddingVertical: 12,
},
});

마지막으로 react-native-dotenv 라이브러리를 사용하려면 React Native 서버 캐시 비운 상태로 재설정해야 하며, 이는 바벨에 기능을 추가할 때 정상적인 현상입니다.

이렇게 하려면 ctrl-c를 사용하여 현재 실행 React Native 인스턴스를 모두 종료한 후 다음을 입력하여 캐시 비운 상태에서 인스턴스 실행 .

npm start -- --reset-cache

이제 Atlas 데이터를 React Native 애플리케이션 에 동기화 있는 모든 설정하다 이 완료되었습니다.

이제 애플리케이션 재설정합니다. 이전에 애플리케이션의 로컬 데이터베이스 수정한 경우, 새 Reset 버튼을 클릭하여 Atlas 에 저장된 내용으로 로컬 데이터베이스 재설정해야 합니다.

이제 mockUserId에 대한 모든 할 일 목록 항목이 표시됩니다.

UI 스크린샷

문제가 발생하면 에뮬레이터/시뮬레이터에서 애플리케이션 삭제 하고 처음부터 다시 시작하도록 합니다.

여전히 문제가 발생하면 예시 리포지토리 의 03-Sync-Data-From-Atlas 브랜치에서 이 점 까지 변경된 내용을 볼 수 있습니다.

이제 데이터가 모바일 애플리케이션 에 동기화되었으므로, 다음 단계는 로컬 변경 사항을 Atlas 에 전파하는 방법을 만드는 것입니다.

이 단계에서는 다음을 수행합니다.

  • Connector에서 uploadData 메서드를 구현합니다.

  • 모바일 장치에서 작업을 처리하다 하는 간단한 백엔드 서버 만들기

간단하게 설명하기 위해 이 가이드 서버 로컬에서 실행 . 프로덕션 사용 사례의 경우 이러한 요청을 처리하다 위해 cloud 서비스를 사용하는 것을 고려해야 합니다(예: 저니앱스는 이를 돕기 위해 서버리스 cloud 기능을 제공합니다).

먼저 모바일 애플리케이션 에서 로컬 변경이 있을 때 uploadData 메서드로 전송되는 작업을 살펴봅니다.

source/PowerSync.ts을 다음과 같이 변경합니다.

async uploadData(database: AbstractPowerSyncDatabase) {
const batch = await database.getCrudBatch();
console.log('batch', JSON.stringify(batch, null, 2));
}

다음으로 모바일 애플리케이션 에서 다음을 포함하여 변경합니다.

  • 항목 삭제

  • 항목을 완료 또는 미완료로 전환하기

  • 새 항목 추가

가져오기 요청 에서 이 정보를 전송하는 uploadData 메서드 구현을 완료합니다.

먼저 .env에 새 값을 추가합니다.

BACKEND_ENDPOINT=http://localhost:8000

types/env.d.ts:

declare module '@env' {
export const AUTH_TOKEN: string;
export const POWERSYNC_ENDPOINT: string;
export const BACKEND_ENDPOINT: string;
}

Android 에뮬레이터를 사용하는 경우 포트 8000localhost 에 대한 요청이 에뮬레이터에서 로컬 시스템으로 전달되고 있는지 확인해야 합니다. 이를 활성화 하려면 다음 명령을 실행 .

adb reverse tcp:8000 tcp:8000

다음으로 source/PowerSync.ts의 가져오기 성명서 에 BACKEND_ENDPOINT 를 추가합니다.

import { AUTH_TOKEN, POWERSYNC_ENDPOINT, BACKEND_ENDPOINT } from '@env';

그런 다음 uploadData 메서드를 업데이트 .

async uploadData(database: AbstractPowerSyncDatabase) {
const batch = await database.getCrudBatch();
if (batch === null) {
return;
}
const result = await fetch(`${BACKEND_ENDPOINT}/update`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(batch.crud),
});
if (!result.ok) {
throw new Error('Failed to upload data');
}
batch.complete();
}

업데이트된 메서드는 이제 CRUD 작업 배열 백엔드 엔드포인트로 보냅니다.

  • 애플리케이션 이 오프라인 상태이면 실패합니다.

  • 애플리케이션 긍정적인 응답을 받으면 작업을 완료된 것으로 표시하고 작업 배치 모바일 애플리케이션 에서 제거됩니다.

이제 프로젝트 에 backend라는 새 폴더를 만듭니다.

mkdir backend

그런 다음 package.json 파일 만듭니다.

{
"main": "index.js",
"scripts": {
"start": "node --env-file=.env index.js"
},
"dependencies": {
"express": "^4.21.2",
"mongodb": "^6.12.0"
}
}

package.json 에는 .env 의 변수를 서비스에 추가하는 start 스크립트 포함되어 있습니다.

이전에 만든 Atlas connection string 을 사용하여 새 .env 를 만듭니다.

MONGODB_URI=<connection_string>

이제 종속성을 설치합니다.

npm install

이 가이드 에는 이 서비스에 타입스크립트 (Typescript) 및 기타 도구를 추가하는 방법이 포함되어 있지 않지만, 자유롭게 추가할 수 있습니다. 또한 이 가이드 유효성 검사 최소한으로 유지하고 모바일 애플리케이션 에서 들어오는 데이터를 MongoDB 에 삽입하기 위해 준비하는 데 필요한 변경 사항만 구현합니다.

먼저 다음 콘텐츠로 index.js 를 만듭니다.

const express = require("express");
const { MongoClient, ObjectId } = require("mongodb");
const app = express();
app.use(express.json());
// MongoDB setup
const client = new MongoClient(
process.env.MONGODB_URI || "mongodb://localhost:27017",
);
// Helper function to coerce isComplete to boolean
function coerceItemData(data) {
if (data && "isComplete" in data) {
data.isComplete = !!Number(data.isComplete);
}
return data;
}
async function start() {
await client.connect();
const db = client.db("PowerSync");
const items = db.collection("Item");
app.post("/update", async (req, res) => {
const operations = req.body;
try {
for (const op of operations) {
console.log(JSON.stringify(op, null, 2));
switch (op.op) {
case "PUT":
await items.insertOne({
...coerceItemData(op.data),
_id: new ObjectId(op.id),
});
break;
case "PATCH":
await items.updateOne(
{ _id: new ObjectId(op.id) },
{ $set: coerceItemData(op.data) },
);
break;
case "DELETE":
await items.deleteOne({
_id: new ObjectId(op.id),
});
break;
}
}
res.json({ success: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(8000, () => {
console.log("Server running on port 8000");
});
}
start().catch(console.error);

위 서비스에서 isCompleteboolean 값으로 강제로 적용된다는 점에 유의하세요. 이렇게 하면 새 todolist 항목이 1 또는 0 대신 true 또는 false 을(를) 사용하여 MongoDB 에 도착합니다. ObjectId 인스턴스 도 op.id에서 생성 중입니다. 이 값을 _id 속성 으로 설정하면 MongoDB 요구 사항 및 권장사항 에 맞게 데이터를 구성할 수 있습니다.

이제 서버 가동할 수 있습니다.

npm start

모바일 애플리케이션 이미 이 엔드포인트로 작업을 전송하려고 시도하고 있어야 합니다. console.log 성명서 요청이 전송될 때 표시되어야 하며 변경 사항이 Atlas 로 전파되어야 합니다.

Atlas UI 또는 MongoDB Compass 에서 MongoDB 컬렉션 확인하여 이를 확인할 수 있습니다.

UI 스크린샷

이제 Atlas 와 데이터를 동기화하는 모바일 애플리케이션 이 완벽하게 작동합니다. Wi-Fi를 꺼서 앱이 오프라인 상태일 때 앱 어떻게 작동하는지 테스트할 수도 있습니다.

문제가 발생하면 예시 리포지토리 의 04-Write-To-Backend 브랜치에서 이 점 까지 변경된 내용을 확인할 수 있습니다.

이 마지막 단계에서는 두 가지 선택적 앱 테스트 기능을 구현 방법과 프로젝트 불필요한 코드 및 종속성을 정리하는 방법을 다룹니다.

이 애플리케이션 만드는 프로세스 에서 모든작업 표시 및 오프라인 모드 전환 기능이 생략되었습니다. 이러한 기능은 앱 기능을 테스트하는 데 유용하며 프로덕션 애플리케이션 에서는 사용하기 위한 것이 아닙니다.

참고

이러한 기능과 관련된 단계는 선택 사항으로 표시됩니다. 이 단계가 관심이 없다면 이 선택적 단계를 건너뛰어도 좋습니다.

선택 사항인 모두 표시 토글을 구현 위해 클라이언트 매개변수에 따라 활성화되는 두 번째 버킷이 생성됩니다. 현재 동기화 세션 연결을 끊고 새 값 설정하다 로 다시 연결하여 이를 적용 . 이 값은 view_all라는 부울이며, 클러스터 에서 생성된 모든 할 일 목록 항목을 표시하는 안전하지 않은 백도어로 사용됩니다. 이 기능은 특정 매개변수를 기반으로 버킷을 동적으로 생성할 수 있음을 보여주는 데 도움이 됩니다.

참고

여기에 사용된 방법론은 안전하지 않으므로 이 작업을 수행하려면 버킷에서 accept_potentially_dangerous_queries 플래그를 활성화해야 합니다. 이를 달성하는 안전한 방법은 사용자 역할 기반으로 백업 데이터베이스 에서 사용자 권한 부여를 업데이트 것인데, 이는 이 가이드 의 범위를 벗어납니다.

시작하려면 PowerSync 대시보드 로 이동하여 설정하다 중인 view_all 매개변수에 따라 버킷을 포함하도록 동기화 규칙을 업데이트 .

bucket_definitions:
user_buckets:
parameters:
- SELECT request.user_id() as user_id
data:
- SELECT _id as id FROM "Item" WHERE bucket.user_id = 'global'
OR owner_id = bucket.user_id
view_all_bucket:
accept_potentially_dangerous_queries: true
parameters:
- SELECT (request.parameters() ->> 'view_all') as view_all
data:
- SELECT _id as id FROM "Item" WHERE bucket.view_all = true

버킷 정의는 함께 결합되므로 view_all_bucket 가 활성화되면 user_buckets 데이터에 추가됩니다.

그런 다음 프로젝트 에서 source/PowerSync.ts 를 업데이트 view_all 플래그 상태 결정하는 로컬 변수를 포함하고 이를 연결 인스턴스 의 매개변수에 적용 .

먼저 viewAll 매개변수를 추가하고 setupPowerSync 함수를 업데이트 .

let viewAll = false;
export const setupPowerSync = (): PowerSyncDatabase => {
const connector = new Connector();
powerSync.connect(connector, {params: {view_all: viewAll}});
return powerSync;
};

그런 다음 다음 두 함수를 추가합니다.

export const resetPowerSync = async () => {
await powerSync.disconnectAndClear();
setupPowerSync();
};
export const toggleViewAll = () => {
viewAll = !viewAll;
resetPowerSync();
};

마지막으로 source/ItemListView.tsx을(를 ) 업데이트 .

먼저 PowerSync에서 toggleViewAll 를 가져옵니다.

import { toggleViewAll } from './PowerSync';

그런 다음 'Show All Tasks(모든 작업 표시)' 스위치의 onValueChange 속성을 수정하여 toggleViewAll 메서드를 호출합니다. 다음 코드를 사용하여 TextSwitch 구성 요소를 바꿉니다.

<Text style={styles.toggleText}>Show All Tasks</Text>
<Switch
trackColor={{true: '#00ED64'}}
onValueChange={() => {
setShowAllItems(!showAllItems);
toggleViewAll();
}}
value={showAllItems}
/>

이제 애플리케이션 다시 시작하고 앱 의도한 대로 작동하는지 확인합니다.

UI 스크린샷

선택 사항인 오프라인 모드 토글을 구현 하려면 동기화 세션 연결을 끊었다가 다시 연결해야 합니다. 이렇게 하면 동기화 에 연결되어 있지 않은 동안 로컬 변경을 수행하고 동기화 세션 다시 설정될 때 변경 사항이 전송되는지 확인할 수 있습니다.

연결 상태 에 대한 변수를 추가한 다음 이를 토글하는 메서드를 만들고 PowerSync 클라이언트 에서 connectdisconnect 메서드를 호출합니다.

먼저 source/PowerSync.ts에 다음을 추가합니다.

let connection = true;
export const toggleConnection = () => {
if (connection) {
powerSync.disconnect();
} else {
setupPowerSync();
}
connection = !connection;
};

다음으로 source/OfflineModeButton.tsx 을 리팩터링하여 Realm 기능을 제거 하고 새 toggleConnection 메서드를 호출하여 대체합니다. 또한 몇 가지 가져오기를 추가해야 합니다.

import { useState } from 'react';
import { Pressable, Text, StyleSheet } from 'react-native';
import { colors } from './Colors';
import {toggleConnection} from './PowerSync';
export function OfflineModeButton() {
const [pauseSync, togglePauseSync] = useState(false);
return (
<Pressable
onPress={() => {
toggleConnection();
togglePauseSync(!pauseSync);
}}>
<Text style={styles.buttonText}>
{pauseSync ? 'Enable Sync' : 'Disable Sync'}
</Text>
</Pressable>
);
}
const styles = StyleSheet.create({
buttonText: {
padding: 12,
color: colors.primary,
},
});

마지막으로 source/App.tsx 을 열고 애플리케이션 의 Stack.Screen 에서 headerRight 구성 요소의 주석 처리를 제거합니다.

<Stack.Screen
name="Your To-Do List"
component={ItemListView}
options={{
headerTitleAlign: 'center',
headerLeft,
headerRight,
}}
/>

이제 앱 의 두 번째 인스턴스 열어 업데이트를 확인한 다음 몇 가지를 변경합니다.

UI 스크린샷

마지막으로 프로젝트 정리할 수 있습니다.

다음 파일은 안전하게 삭제할 수 있습니다.

  • atlasConfig.json

  • source/WelcomeView.tsx

package.json에서 다음 종속성을 제거 할 수도 있습니다.

  • @realm/react

  • realm

이 가이드 PowerSync로의 마이그레이션 과정을 시작하는 데 필요한 빌딩 블록을 제공해야 합니다.

요약하자면, 이 가이드 따르면 다음을 달성했을 것입니다.

  • 샘플 데이터가 포함된 MongoDB database 배포

  • 샘플 데이터를 동기화하는 PowerSync 서비스 배포

  • 진단 도구를 사용하여 이 데이터를 보고 쿼리 방법 알아보기

  • Device Sync 모바일 애플리케이션 로컬 전용으로 변환

  • 로컬 전용 Realm 데이터베이스 에서 PowerSync로 마이그레이션

  • PowerSync에서 모바일 데이터베이스 로 동기화 설정

  • PowerSync 클라이언트 에서 MongoDB 로 변경 사항을 푸시하는 백엔드 생성

다음 단계에서는 모바일 애플리케이션 의 일부를 가져와서 PowerSync를 사용하도록 변환해 보세요. 그리고 고급 사용 사례에 대한 향후 문서를 주시하세요.

돌아가기

Amazon Web Services AppSync로 마이그레이션