리액트 네이티브에서 Parse Cloud Functions 구현 가이드
리액트 네이티브 앱에서 클라우드 함수 사용하기
소개
이 가이드에서는 리액트 네이티브 앱에서 Parse 클라우드 코드 함수를 사용하는 방법을 배웁니다. 클라우드 함수를 사용하여 구현된 트리거의 예를 보고, 이러한 함수와 Back4App을 사용하여 리액트 네이티브 컴포넌트를 구현하는 방법도 확인할 수 있습니다.
전제 조건
이 튜토리얼을 완료하려면 다음이 필요합니다:
목표
리액트 네이티브 앱에서 Back4App의 Parse 클라우드 코드를 실행합니다.
1 - 클라우드 코드 함수 이해하기
애플리케이션에서는 특정 데이터 작업이나 모바일 장치에서 수행해서는 안 되는 무거운 처리를 수행해야 할 때가 있습니다. 이러한 경우에는 클라우드 코드 함수를 사용할 수 있으며, 이는 Parse 서버에서 직접 실행되고 Parse API를 사용하여 호출됩니다. 이는 또한 앱 스토어에서 새 버전을 출시할 필요 없이 앱의 일부 로직을 변경할 수 있게 해주므로 유용할 수 있습니다.
클라우드 코드 함수에는 두 가지 주요 유형이 있습니다. 일반 함수는 정의하다와 원하는 모든 유형의 코드를 보유하고 있으며, 트리거는 Parse 서버에서 특정 이벤트가 발생할 때 자동으로 실행됩니다.
다음 단계에서는 이러한 함수 유형의 대부분에 대한 예제가 제공되며, 이는 Back4App 대시보드 또는 CLI를 통해 직접 생성하고 배포할 수 있습니다. 클라우드 함수 시작 가이드를 참조하여 이 작업을 수행하는 방법을 확인할 수 있습니다.
2 - 클라우드 코드 참조
클라우드 함수
1 // This a generic cloud function that may contain any type of operation,
2 // these should be called via API in your app to be run
3 Parse.Cloud.define("getAverageMovieReviews", async (request) => {
4 const query = new Parse.Query("Review");
5 // Parameters can be passed in your request body and are accessed inside request.params
6 query.equalTo("movie", request.params.movie);
7 const results = await query.find();
8 let sum = 0;
9 for (let review of results) {
10 sum += review.get("stars");
11 }
12 return {
13 result: sum / results.length,
14 }
15 });응답:
저장 트리거
1 // This will run every time an object of this class will be saved on the server
2 Parse.Cloud.beforeSave("Product", (request) => {
3 // You can change any data before saving, can be useful for trimming text
4 // or validating unique fields
5 const name = request.object.get("description");
6 if (description.length > 140) {
7 // Truncate and add a ...
8 request.object.set("description", comment.substring(0, 137) + "...");
9 }
10 });삭제 트리거
1 // This will be called before deleting an object, useful for checking
2 // for existing dependent conditions
3 Parse.Cloud.beforeDelete("Album", async (request) => {
4 // This won't proceed deleting if the condition is not met, protection your
5 // data
6 const query = new Parse.Query("Photo");
7 query.equalTo("album", request.object);
8 const count = await query.count();
9 if (count > 0) {
10 throw "Can't delete album if it still has photos.";
11 }
12 });파일 트리거
1 // Can be called to change any file properties, like renaming it
2 Parse.Cloud.beforeSaveFile(async (request) => {
3 const { file } = request;
4 const fileData = await file.getData();
5 // Note that the new file will be saved instead of the user submitted one
6 const newFile = new Parse.File('a-new-file-name.txt', { base64: fileData });
7 return newFile;
8 });찾기 트리거
1 // This will be called before any queries using this class
2 Parse.Cloud.beforeFind('MyObject', (request) => {
3 // Useful for hard setting result limits or forcing
4 // certain conditions
5 let query = request.query;
6 query.limit(5);
7 });세션 트리거
1 // Can be used for checking for user permissions and state
2 Parse.Cloud.beforeLogin(async (request) => {
3 // Doesn't allow banned users to login
4 const { object: user } = request;
5 if (user.get('isBanned') === true) {
6 throw 'Access denied, you have been banned.';
7 }
8 });3 - React Native 컴포넌트에서 Cloud Code 사용하기
이제 예제 Parse Cloud Code 함수를 만들고 React Native의 컴포넌트 내에서 호출해 보겠습니다. 함수 결과를 표시하는 레이블과 새 객체를 추가하는 양식이 있는 간단한 인터페이스를 사용합니다.
우리의 클라우드 함수는 앱의 모든 영화 리뷰 객체의 평균 평점을 계산합니다. 이는 Parse.Object 클래스이며 텍스트, 평점 및 영화 필드로 구성됩니다. 함수 코드는 다음과 같습니다:
1 Parse.Cloud.define("getMovieAverageRating", async (request) => {
2 const query = new Parse.Query("Review");
3 query.equalTo("movie", request.params.movie);
4 const results = await query.find();
5 let sum = 0;
6 for (let review of results) {
7 sum += review.get("rate");
8 }
9 return {
10 result: sum / results.length,
11 };
12 });React Native에서 이 클라우드 함수를 호출하려면 Parse.Cloud.run 메서드를 사용해야 하며, 함수 이름과 필요한 매개변수를 객체 안에 전달해야 합니다.
1 const runGetMovieAverageRating = async function () {
2 try {
3 const params = {
4 movie: 'Mission Very Possible',
5 };
6 let resultObject = await Parse.Cloud.run(
7 'getMovieAverageRating',
8 params,
9 );
10 // Set query results to state variable using state hook
11 setRatingsAverage(resultObject.result.toFixed(1));
12 return true;
13 } catch (error) {
14 // Error can be caused by lack of Internet connection
15 // or by not having an valid Review object yet
16 Alert.alert('Error!', error.message);
17 return false;
18 }
19 };리뷰의 텍스트가 짧게 유지되도록 beforeSave 트리거 함수를 Review 객체에 대해 사용할 수 있습니다. 함수 코드는 다음과 같습니다:
1 Parse.Cloud.beforeSave("Review", (request) => {
2 const text = request.object.get("text");
3 if (text.length > 20) {
4 // Truncate and add a ...
5 request.object.set("text", text.substring(0, 17) + "...");
6 }
7 });전체 구성 요소 코드는 다음과 같이 배치됩니다:
1 import React, {FC, ReactElement, useState} from 'react';
2 import {
3 Alert,
4 Image,
5 View,
6 Platform,
7 ScrollView,
8 StyleSheet,
9 } from 'react-native';
10 import Parse from 'parse/react-native';
11 import {
12 List,
13 Title,
14 TextInput as PaperTextInput,
15 Button as PaperButton,
16 Text as PaperText,
17 } from 'react-native-paper';
18
19 export const MovieRatings: FC<{}> = ({}): ReactElement => {
20 // State variable
21 const [queryResults, setQueryResults] = useState(null);
22 const [ratingsAverage, setRatingsAverage] = useState('');
23 const [reviewText, setReviewText] = useState('');
24 const [reviewRate, setReviewRate] = useState('');
25
26 const runGetMovieAverageRating = async function (): Promise<boolean> {
27 try {
28 const params: {movie: string} = {
29 movie: 'Mission Very Possible',
30 };
31 let resultObject: {result: number} = await Parse.Cloud.run(
32 'getMovieAverageRating',
33 params,
34 );
35 // Set query results to state variable using state hook
36 setRatingsAverage(resultObject.result.toFixed(1));
37 return true;
38 } catch (error) {
39 // Error can be caused by lack of Internet connection
40 // or by not having an valid Review object yet
41 Alert.alert(
42 'Error!',
43 'Make sure that the cloud function is deployed and that the Review class table is created',
44 );
45 return false;
46 }
47 };
48
49 const doReviewQuery = async function (): Promise<boolean> {
50 // Create our query
51 let parseQuery: Parse.Query = new Parse.Query('Review');
52 try {
53 let results: [Parse.Object] = await parseQuery.find();
54 // Set query results to state variable
55 setQueryResults(results);
56 return true;
57 } catch (error) {
58 // Error can be caused by lack of Internet connection
59 Alert.alert('Error!', error.message);
60 return false;
61 }
62 };
63
64 const createReview = async function (): Promise<boolean> {
65 try {
66 // This values come from state variables linked to
67 // the screen form fields
68 const reviewTextValue: string = reviewText;
69 const reviewRateValue: number = Number(reviewRate);
70
71 // Creates a new parse object instance
72 let Review: Parse.Object = new Parse.Object('Review');
73
74 // Set data to parse object
75 // Simple title field
76 Review.set('text', reviewTextValue);
77
78 // Simple number field
79 Review.set('rate', reviewRateValue);
80
81 // Set default movie
82 Review.set('movie', 'Mission Very Possible');
83
84 // After setting the values, save it on the server
85 try {
86 await Review.save();
87 // Success
88 Alert.alert('Success!');
89 // Updates query result list
90 doReviewQuery();
91 runGetMovieAverageRating();
92 return true;
93 } catch (error) {
94 // Error can be caused by lack of Internet connection
95 Alert.alert('Error!', error.message);
96 return false;
97 }
98 } catch (error) {
99 // Error can be caused by lack of field values
100 Alert.alert('Error!', error.message);
101 return false;
102 }
103 };
104
105 return (
106 <>
107 <View style={Styles.header}>
108 <Image
109 style={Styles.header_logo}
110 source={ {
111 uri:
112 'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',
113 } }
114 />
115 <PaperText style={Styles.header_text}>
116 <PaperText style={Styles.header_text_bold}>
117 {'React Native on Back4App - '}
118 </PaperText>
119 {' Cloud Code Movie Ratings'}
120 </PaperText>
121 </View>
122 <ScrollView style={Styles.wrapper}>
123 <View>
124 <Title>{'Mission Very Possible Reviews'}</Title>
125 <PaperText>{`Ratings Average: ${ratingsAverage}`}</PaperText>
126 {/* Query list */}
127 {queryResults !== null &&
128 queryResults !== undefined &&
129 queryResults.map((result: Parse.Object) => (
130 <List.Item
131 key={result.id}
132 title={`Review text: ${result.get('text')}`}
133 description={`Rate: ${result.get('rate')}`}
134 titleStyle={Styles.list_text}
135 style={Styles.list_item}
136 />
137 ))}
138 {queryResults === null ||
139 queryResults === undefined ||
140 (queryResults !== null &&
141 queryResults !== undefined &&
142 queryResults.length <= 0) ? (
143 <PaperText>{'No results here!'}</PaperText>
144 ) : null}
145 </View>
146 <View>
147 <Title>Action Buttons</Title>
148 <PaperButton
149 onPress={() => runGetMovieAverageRating()}
150 mode="contained"
151 icon="search-web"
152 color={'#208AEC'}
153 style={Styles.list_button}>
154 {'Calculate Review Average'}
155 </PaperButton>
156 <PaperButton
157 onPress={() => doReviewQuery()}
158 mode="contained"
159 icon="search-web"
160 color={'#208AEC'}
161 style={Styles.list_button}>
162 {'Query Reviews'}
163 </PaperButton>
164 </View>
165 <View>
166 <Title>Add new review</Title>
167 <PaperTextInput
168 value={reviewText}
169 onChangeText={text => setReviewText(text)}
170 label="Text"
171 mode="outlined"
172 style={Styles.form_input}
173 />
174 <PaperTextInput
175 value={reviewRate}
176 onChangeText={text => setReviewRate(text)}
177 keyboardType={'number-pad'}
178 label="Rate (1-5)"
179 mode="outlined"
180 style={Styles.form_input}
181 />
182 <PaperButton
183 onPress={() => createReview()}
184 mode="contained"
185 icon="plus"
186 style={Styles.submit_button}>
187 {'Add'}
188 </PaperButton>
189 </View>
190 </ScrollView>
191 </>
192 );
193 };
194
195 // These define the screen component styles
196 const Styles = StyleSheet.create({
197 header: {
198 alignItems: 'center',
199 paddingTop: 30,
200 paddingBottom: 50,
201 backgroundColor: '#208AEC',
202 },
203 header_logo: {
204 height: 50,
205 width: 220,
206 resizeMode: 'contain',
207 },
208 header_text: {
209 marginTop: 15,
210 color: '#f0f0f0',
211 fontSize: 16,
212 },
213 header_text_bold: {
214 color: '#fff',
215 fontWeight: 'bold',
216 },
217 wrapper: {
218 width: '90%',
219 alignSelf: 'center',
220 },
221 list_button: {
222 marginTop: 6,
223 marginLeft: 15,
224 height: 40,
225 },
226 list_item: {
227 borderBottomWidth: 1,
228 borderBottomColor: 'rgba(0, 0, 0, 0.12)',
229 },
230 list_text: {
231 fontSize: 15,
232 },
233 form_input: {
234 height: 44,
235 marginBottom: 16,
236 backgroundColor: '#FFF',
237 fontSize: 14,
238 },
239 submit_button: {
240 width: '100%',
241 maxHeight: 50,
242 alignSelf: 'center',
243 backgroundColor: '#208AEC',
244 },
245 });렌더링 및 쿼리 함수 중 하나로 쿼리한 후 컴포넌트가 어떻게 보여야 하는지입니다:

결론
이 가이드를 마치면서, Parse Cloud Code 기능이 어떻게 작동하는지와 React Native 앱에서 Back4App을 통해 이를 수행하는 방법을 배웠습니다. 다음 가이드에서는 Parse에서 사용자와 작업하는 방법을 배울 것입니다.