---
title: 리액트 네이티브에서 Parse 클라우드 함수 및 컨텍스트 활용
slug: docs-kr/react-native/parse-sdk/cloud-functions/react-native-cloud-functions-validators
description: 리액트 네이티브에서 Parse 클라우드 함수 검증기와 컨텍스트 활용하기
image: https://www.back4app.com/images/logo-social.png
createdAt: 2024-03-19T17:47:50.682Z
---

# 리액트 네이티브 앱에서 클라우드 함수 검증기 사용하기

## 소개

이 가이드에서는 리액트 네이티브 앱에서 Parse Cloud Code 함수 검증기와 컨텍스트 사용법을 배우게 됩니다. 클라우드 함수에 구현된 검증기의 예시, 서로 다른 함수 간의 컨텍스트 사용법, 그리고 이러한 개선된 함수를 사용하여 Back4App에서 리액트 네이티브 컴포넌트를 구현하는 방법을 확인할 수 있습니다.

## 전제 조건

:::hint{type="info"}
**이 튜토리얼을 완료하려면 다음이 필요합니다:**

- 생성된 리액트 네이티브 앱과 [Back4App에 연결된](https://www.back4app.com/docs/react-native/parse-sdk/react-native-sdk).
- 다음 방법을 이해해야 합니다: [클라우드 함수 배포하기](https://www.back4app.com/docs/get-started/cloud-functions) Back4App에서.
- 이 가이드의 예제 애플리케이션을 실행하려면 다음을 설정해야 합니다.<font color="#2166ae">`react-native-paper`</font>[라이브러리](https://github.com/callstack/react-native-paper).
:::

## 목표

리액트 네이티브 앱에서 Back4App의 검증기 및 컨텍스트 사용으로 Parse Cloud Code 함수를 실행합니다.

## 1 - 클라우드 코드 함수 검증기 이해하기

애플리케이션에서 <font color="#2166ae">`클라우드 코드 함수`</font>를 사용하면 코드의 유연성이 크게 향상되어 재사용 가능한 메서드를 앱에서 분리하고 그 동작을 더 잘 제어할 수 있습니다. [우리의 클라우드 함수 시작 가이드](https://www.back4app.com/docs/get-started/cloud-functions)를 통해 사용하는 방법을 확인하거나 검토할 수 있습니다.

이 클라우드 함수에 전송된 데이터는 예기치 않은 오류를 피하기 위해 검증되어야 하며, Parse Cloud Code(버전 4.4.0부터)는 완전한 통합된 <font color="#2166ae">`검증기`</font>, 이를 <font color="#2166ae">`객체`</font>로 전달하거나 다른 함수로 전달할 수 있습니다. 이러한 테스트는 함수 내의 코드를 실행하기 전에 호출되므로, 검증기가 데이터를 수락하면 데이터가 유효하다고 항상 가정할 수 있습니다.

다음 클라우드 함수를 예로 들어보겠습니다. 이 함수는 특정 영화 평점의 평균을 계산합니다. 영화 이름은 필수입니다(<font color="#2166ae">`영화`</font>), 따라서 다음 객체를 사용하여 전달되도록 할 수 있습니다. 영화 매개변수가 전달되지 않으면 함수는 “검증 실패. 영화를 위한 데이터를 지정해 주세요.”라는 메시지를 포함한 오류를 반환합니다.

```javascript
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	},{
13	  fields : ['movie'],
14	});
```

또한 <font color="#2166ae">`검증기`</font>, 예를 들어 함수를 호출하는 사용자가 인증되었는지 확인하는 <font color="#2166ae">`requireUser`</font> 플래그와 같은 더 고급 옵션을 전달할 수 있습니다. 또 다른 유용한 추가 사항은 매개변수 값의 유효성을 검증하는 조건을 추가하는 것입니다. 예를 들어, <font color="#2166ae">`영화`</font> 값이 문자열이고 최소 3자 이상인지 확인하는 것입니다.

```javascript
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	},{
13	  fields : {
14	    movie : {
15	      required: true,
16	      type: String,
17	      options: val => {
18	        return val.length >= 3;
19	      },
20	      error: 'Movie must have at least 3 characters'
21	    }
22	  },
23	});
```

전체 <font color="#2166ae">`유효성 검사`</font> 옵션 목록은 Parse [문서](https://docs.parseplatform.org/cloudcode/guide/#implementing-cloud-function-validation)에서 확인할 수 있습니다.

## 2 - 클라우드 코드 함수 컨텍스트 이해하기

클라우드 코드(버전 4.3.0부터)를 사용하여 애플리케이션에서 저장 트리거를 사용할 때, <font color="#2166ae">`컨텍스트`</font> 사전을 <font color="#2166ae">`Parse.Object.save`</font> 메서드에 전달할 수 있으며, 또한 <font color="#2166ae">`beforeSave`</font> 핸들러에서 <font color="#2166ae">`afterSave`</font> 핸들러로 전달할 수 있습니다. 이는 데이터 일관성을 보장하거나 객체를 성공적으로 저장한 후에만 수행할 수 있는 비동기 작업을 수행하는 데 사용될 수 있습니다.

다음 클라우드 함수 트리거를 예로 들어보겠습니다. 여기서 <font color="#2166ae">`리뷰`</font> 객체가 저장되며, 객체 저장 호출이 애플리케이션에서 이루어졌는지 대시보드에서 이루어졌는지를 구분하고자 합니다. 이는 애플리케이션 코드에서 객체를 저장하기 전에 컨텍스트를 설정하여 달성할 수 있습니다:

```javascript
1	const Review = new Parse.Object('Review');
2	Review.set('text', 'Great movie!');
3	Review.set('rate', 5);
4	Review.set('movie', 'Mission Very Possible');
5	
6	const context = { fromApplication: true };
7	const savedReview = await Review.save(null, {context: context});
```

여기 저장 트리거 함수가 있습니다. 두 함수 모두에서 요청에서 컨텍스트 객체에 직접 접근할 수 있습니다:

```javascript
1	Parse.Cloud.beforeSave('Review', async (request) => {
2	  // Try to get context
3	  try {
4	    const context = request.context;
5	    if (context.fromApplication === true) {
6	      // Set a flag in the object before saving
7	      request.object.set('fromApplication', true);
8	    }
9	  } catch(error){}
10	
11	  const text = request.object.get('text');
12	  if (text.length > 20) {
13	    // Truncate and add a ...
14	    request.object.set('text', text.substring(0, 17) + '...');
15	  }
16	});
17	
18	Parse.Cloud.afterSave('Review', async (request) => {
19	  // Try to get context
20	  try {
21	    const context = request.context;
22	    if (context.fromApplication === true) {
23	      // Do something when coming from application, like sending a notification or email
24	      console.log('Got context fromApplication when saving Review object');
25	    }
26	  } catch(error){}
27	});
```

## 3 - React Native 컴포넌트에서 검증된 클라우드 코드 사용하기

이제 마지막 [가이드](https://www.back4app.com/docs/react-native/parse-sdk/cloud-functions/react-native-cloud-functions)에서 동일한 컴포넌트 예제를 기반으로 사용하고, 이제 검증자와 컨텍스트를 사용하는 함수를 강조하기 위해 몇 가지 변경 사항을 추가하겠습니다. 1단계와 2단계에서 보여준 클라우드 함수를 배포하는 것을 잊지 말고, 그 후에 “리뷰” 객체 생성 함수를 다음과 같이 변경하여 save 메서드에 컨텍스트 인수를 추가하세요:

:::CodeblockTabs
JavaScript

```javascript
1	const createReview = async function () {
2	  try {
3	    // This values come from state variables linked to
4	    // the screen form fields
5	    const reviewTextValue = reviewText;
6	    const reviewRateValue = Number(reviewRate);
7	
8	    // Creates a new parse object instance
9	    let Review = new Parse.Object('Review');
10	
11	    // Set data to parse object
12	    // Simple title field
13	    Review.set('text', reviewTextValue);
14	
15	    // Simple number field
16	    Review.set('rate', reviewRateValue);
17	
18	    // Set default movie
19	    Review.set('movie', 'Mission Very Possible');
20	
21	    // After setting the values, save it on the server
22	    try {
23	      // Add 
24	      const context = {fromApplication: true};
25	      await Review.save(null, {context: context});
26	      // Success
27	      Alert.alert('Success!');
28	      // Updates query result list
29	      doReviewQuery();
30	      runGetMovieAverageRating();
31	      return true;
32	    } catch (error) {
33	      // Error can be caused by lack of Internet connection
34	      Alert.alert('Error!', error.message);
35	      return false;
36	    }
37	  } catch (error) {
38	    // Error can be caused by lack of field values
39	    Alert.alert('Error!', error.message);
40	    return false;
41	  }
42	};
```

```typescript
1	const createReview = async function (): Promise<boolean> {
2	  try {
3	    // This values come from state variables linked to
4	    // the screen form fields
5	    const reviewTextValue: string = reviewText;
6	    const reviewRateValue: number = Number(reviewRate);
7	
8	    // Creates a new parse object instance
9	    let Review: Parse.Object = new Parse.Object('Review');
10	
11	    // Set data to parse object
12	    // Simple title field
13	    Review.set('text', reviewTextValue);
14	
15	    // Simple number field
16	    Review.set('rate', reviewRateValue);
17	
18	    // Set default movie
19	    Review.set('movie', 'Mission Very Possible');
20	
21	    // After setting the values, save it on the server
22	    try {
23	      const context = {fromApplication: true};
24	      await Review.save(null, {context: context});
25	      // Success
26	      Alert.alert('Success!');
27	      // Updates query result list
28	      doReviewQuery();
29	      runGetMovieAverageRating();
30	      return true;
31	    } catch (error) {
32	      // Error can be caused by lack of Internet connection
33	      Alert.alert('Error!', error.message);
34	      return false;
35	    }
36	  } catch (error) {
37	    // Error can be caused by lack of field values
38	    Alert.alert('Error!', error.message);
39	    return false;
40	  }
41	};
```
:::

영화 평균 계산 함수 검증기를 강조하기 위해, “Me”라는 영화를 쿼리하는 새로운 함수를 추가하고 이를 호출하는 또 다른 버튼을 추가하세요. 우리의 검증기는 영화 이름 길이 때문에 요청이 실패하게 만들 것임을 기억하세요. 여기 새로운 함수 코드가 있습니다:

:::CodeblockTabs
JavaScript

```javascript
1	const createReview = async function () {
2	  try {
3	    // This values come from state variables linked to
4	    // the screen form fields
5	    const reviewTextValue = reviewText;
6	    const reviewRateValue = Number(reviewRate);
7	
8	    // Creates a new parse object instance
9	    let Review = new Parse.Object('Review');
10	
11	    // Set data to parse object
12	    // Simple title field
13	    Review.set('text', reviewTextValue);
14	
15	    // Simple number field
16	    Review.set('rate', reviewRateValue);
17	
18	    // Set default movie
19	    Review.set('movie', 'Mission Very Possible');
20	
21	    // After setting the values, save it on the server
22	    try {
23	      // Add 
24	      const context = {fromApplication: true};
25	      await Review.save(null, {context: context});
26	      // Success
27	      Alert.alert('Success!');
28	      // Updates query result list
29	      doReviewQuery();
30	      runGetMovieAverageRating();
31	      return true;
32	    } catch (error) {
33	      // Error can be caused by lack of Internet connection
34	      Alert.alert('Error!', error.message);
35	      return false;
36	    }
37	  } catch (error) {
38	    // Error can be caused by lack of field values
39	    Alert.alert('Error!', error.message);
40	    return false;
41	  }
42	};
```

```typescript
1	const createReview = async function (): Promise<boolean> {
2	  try {
3	    // This values come from state variables linked to
4	    // the screen form fields
5	    const reviewTextValue: string = reviewText;
6	    const reviewRateValue: number = Number(reviewRate);
7	
8	    // Creates a new parse object instance
9	    let Review: Parse.Object = new Parse.Object('Review');
10	
11	    // Set data to parse object
12	    // Simple title field
13	    Review.set('text', reviewTextValue);
14	
15	    // Simple number field
16	    Review.set('rate', reviewRateValue);
17	
18	    // Set default movie
19	    Review.set('movie', 'Mission Very Possible');
20	
21	    // After setting the values, save it on the server
22	    try {
23	      const context = {fromApplication: true};
24	      await Review.save(null, {context: context});
25	      // Success
26	      Alert.alert('Success!');
27	      // Updates query result list
28	      doReviewQuery();
29	      runGetMovieAverageRating();
30	      return true;
31	    } catch (error) {
32	      // Error can be caused by lack of Internet connection
33	      Alert.alert('Error!', error.message);
34	      return false;
35	    }
36	  } catch (error) {
37	    // Error can be caused by lack of field values
38	    Alert.alert('Error!', error.message);
39	    return false;
40	  }
41	};
```
:::

새로운 전체 구성 요소 코드가 이렇게 배치되어 있습니다. 리뷰가 애플리케이션에서 생성되었는지 여부를 보여주는 목록 항목의 제목에 새 줄이 있다는 점에 유의하세요:



:::CodeblockTabs
JavaScript

```javascript
1	import React, {useState} from 'react';
2	import {Alert, Image, View, ScrollView, StyleSheet} from 'react-native';
3	import Parse from 'parse/react-native';
4	import {
5	  List,
6	  Title,
7	  TextInput as PaperTextInput,
8	  Button as PaperButton,
9	  Text as PaperText,
10	} from 'react-native-paper';
11	
12	export const MovieRatings = () => {
13	  // State variable
14	  const [queryResults, setQueryResults] = useState(null);
15	  const [ratingsAverage, setRatingsAverage] = useState('');
16	  const [reviewText, setReviewText] = useState('');
17	  const [reviewRate, setReviewRate] = useState('');
18	
19	  const runGetMovieAverageRating = async function () {
20	    try {
21	      const params = {
22	        movie: 'Mission Very Possible',
23	      };
24	      let resultObject = await Parse.Cloud.run(
25	        'getMovieAverageRating',
26	        params,
27	      );
28	      // Set query results to state variable using state hook
29	      setRatingsAverage(resultObject.result.toFixed(1));
30	      return true;
31	    } catch (error) {
32	      // Error can be caused by lack of Internet connection
33	      // or by not having an valid Review object yet
34	      Alert.alert(
35	        'Error!',
36	        'Make sure that the cloud function is deployed and that the Review class table is created',
37	      );
38	      return false;
39	    }
40	  };
41	
42	  const runGetMeMovieAverageRating = async function () {
43	    try {
44	      const params = {
45	        movie: 'Me',
46	      };
47	      let resultObject = await Parse.Cloud.run(
48	        'getMovieAverageRating',
49	        params,
50	      );
51	      return true;
52	    } catch (error) {
53	      // Error can be caused by lack of Internet connection
54	      // or by not having an valid Review object yet
55	      Alert.alert('Error!', JSON.stringify(error.message));
56	      return false;
57	    }
58	  };
59	
60	  const doReviewQuery = async function () {
61	    // Create our query
62	    let parseQuery = new Parse.Query('Review');
63	    try {
64	      let results = await parseQuery.find();
65	      // Set query results to state variable
66	      setQueryResults(results);
67	      return true;
68	    } catch (error) {
69	      // Error can be caused by lack of Internet connection
70	      Alert.alert('Error!', error.message);
71	      return false;
72	    }
73	  };
74	
75	  const createReview = async function () {
76	    try {
77	      // This values come from state variables linked to
78	      // the screen form fields
79	      const reviewTextValue = reviewText;
80	      const reviewRateValue = Number(reviewRate);
81	
82	      // Creates a new parse object instance
83	      let Review = new Parse.Object('Review');
84	
85	      // Set data to parse object
86	      // Simple title field
87	      Review.set('text', reviewTextValue);
88	
89	      // Simple number field
90	      Review.set('rate', reviewRateValue);
91	
92	      // Set default movie
93	      Review.set('movie', 'Mission Very Possible');
94	
95	      // After setting the values, save it on the server
96	      try {
97	        const context = {fromApplication: true};
98	        await Review.save(null, {context: context});
99	        // Success
100	        Alert.alert('Success!');
101	        // Updates query result list
102	        doReviewQuery();
103	        runGetMovieAverageRating();
104	        return true;
105	      } catch (error) {
106	        // Error can be caused by lack of Internet connection
107	        Alert.alert('Error!', error.message);
108	        return false;
109	      }
110	    } catch (error) {
111	      // Error can be caused by lack of field values
112	      Alert.alert('Error!', error.message);
113	      return false;
114	    }
115	  };
116	
117	  return (
118	    <>
119	      <View style={Styles.header}>
120	        <Image
121	          style={Styles.header_logo}
122	          source={ {uri: 'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',} }
123	        />
124	        <PaperText style={Styles.header_text}>
125	          <PaperText style={Styles.header_text_bold}>
126	            {'React Native on Back4App - '}
127	          </PaperText>
128	          {' Cloud Code Movie Ratings'}
129	        </PaperText>
130	      </View>
131	      <ScrollView style={Styles.wrapper}>
132	        <View>
133	          <Title>{'Mission Very Possible Reviews'}</Title>
134	          <PaperText>{`Ratings Average: ${ratingsAverage}`}</PaperText>
135	          {/* Query list */}
136	          {queryResults !== null &&
137	            queryResults !== undefined &&
138	            queryResults.map((result) => (
139	              <List.Item
140	                key={result.id}
141	                title={`Review text: ${result.get('text')}`}
142	                description={`Rate: ${result.get('rate')}\nFrom app: ${
143	                  result.get('fromApplication') !== undefined ? 'Yes' : 'No'
144	                }`}
145	                titleStyle={Styles.list_text}
146	                style={Styles.list_item}
147	              />
148	            ))}
149	          {queryResults === null ||
150	          queryResults === undefined ||
151	          (queryResults !== null &&
152	            queryResults !== undefined &&
153	            queryResults.length <= 0) ? (
154	            <PaperText>{'No results here!'}</PaperText>
155	          ) : null}
156	        </View>
157	        <View>
158	          <Title>Action Buttons</Title>
159	          <PaperButton
160	            onPress={() => runGetMovieAverageRating()}
161	            mode="contained"
162	            icon="search-web"
163	            color={'#208AEC'}
164	            style={Styles.list_button}>
165	            {'Calculate Review Average'}
166	          </PaperButton>
167	          <PaperButton
168	            onPress={() => runGetMeMovieAverageRating()}
169	            mode="contained"
170	            icon="search-web"
171	            color={'#208AEC'}
172	            style={Styles.list_button}>
173	            {'Calculate Me Movie Review Average'}
174	          </PaperButton>
175	          <PaperButton
176	            onPress={() => doReviewQuery()}
177	            mode="contained"
178	            icon="search-web"
179	            color={'#208AEC'}
180	            style={Styles.list_button}>
181	            {'Query Reviews'}
182	          </PaperButton>
183	        </View>
184	        <View>
185	          <Title>Add new review</Title>
186	          <PaperTextInput
187	            value={reviewText}
188	            onChangeText={text => setReviewText(text)}
189	            label="Text"
190	            mode="outlined"
191	            style={Styles.form_input}
192	          />
193	          <PaperTextInput
194	            value={reviewRate}
195	            onChangeText={text => setReviewRate(text)}
196	            keyboardType={'number-pad'}
197	            label="Rate (1-5)"
198	            mode="outlined"
199	            style={Styles.form_input}
200	          />
201	          <PaperButton
202	            onPress={() => createReview()}
203	            mode="contained"
204	            icon="plus"
205	            style={Styles.submit_button}>
206	            {'Add'}
207	          </PaperButton>
208	        </View>
209	      </ScrollView>
210	    </>
211	  );
212	};
213	
214	// These define the screen component styles
215	const Styles = StyleSheet.create({
216	  header: {
217	    alignItems: 'center',
218	    paddingTop: 30,
219	    paddingBottom: 50,
220	    backgroundColor: '#208AEC',
221	  },
222	  header_logo: {
223	    height: 50,
224	    width: 220,
225	    resizeMode: 'contain',
226	  },
227	  header_text: {
228	    marginTop: 15,
229	    color: '#f0f0f0',
230	    fontSize: 16,
231	  },
232	  header_text_bold: {
233	    color: '#fff',
234	    fontWeight: 'bold',
235	  },
236	  wrapper: {
237	    width: '90%',
238	    alignSelf: 'center',
239	  },
240	  list_button: {
241	    marginTop: 6,
242	    marginLeft: 15,
243	    height: 40,
244	  },
245	  list_item: {
246	    borderBottomWidth: 1,
247	    borderBottomColor: 'rgba(0, 0, 0, 0.12)',
248	  },
249	  list_text: {
250	    fontSize: 15,
251	  },
252	  form_input: {
253	    height: 44,
254	    marginBottom: 16,
255	    backgroundColor: '#FFF',
256	    fontSize: 14,
257	  },
258	  submit_button: {
259	    width: '100%',
260	    maxHeight: 50,
261	    alignSelf: 'center',
262	    backgroundColor: '#208AEC',
263	  },
264	});
```

```typescript
1	import React, {FC, ReactElement, useState} from 'react';
2	import {Alert, Image, View, ScrollView, StyleSheet} from 'react-native';
3	import Parse from 'parse/react-native';
4	import {
5	  List,
6	  Title,
7	  TextInput as PaperTextInput,
8	  Button as PaperButton,
9	  Text as PaperText,
10	} from 'react-native-paper';
11	
12	export const MovieRatings: FC<{}> = ({}): ReactElement => {
13	  // State variable
14	  const [queryResults, setQueryResults] = useState(null);
15	  const [ratingsAverage, setRatingsAverage] = useState('');
16	  const [reviewText, setReviewText] = useState('');
17	  const [reviewRate, setReviewRate] = useState('');
18	
19	  const runGetMovieAverageRating = async function (): Promise<boolean> {
20	    try {
21	      const params: {movie: string} = {
22	        movie: 'Mission Very Possible',
23	      };
24	      let resultObject: {result: number} = await Parse.Cloud.run(
25	        'getMovieAverageRating',
26	        params,
27	      );
28	      // Set query results to state variable using state hook
29	      setRatingsAverage(resultObject.result.toFixed(1));
30	      return true;
31	    } catch (error) {
32	      // Error can be caused by lack of Internet connection
33	      // or by not having an valid Review object yet
34	      Alert.alert(
35	        'Error!',
36	        'Make sure that the cloud function is deployed and that the Review class table is created',
37	      );
38	      return false;
39	    }
40	  };
41	
42	  const runGetMeMovieAverageRating = async function (): Promise<boolean> {
43	    try {
44	      const params: {movie: string} = {
45	        movie: 'Me',
46	      };
47	      let resultObject: object = await Parse.Cloud.run(
48	        'getMovieAverageRating',
49	        params,
50	      );
51	      return true;
52	    } catch (error) {
53	      // Error can be caused by lack of Internet connection
54	      // or by not having an valid Review object yet
55	      Alert.alert('Error!', JSON.stringify(error.message));
56	      return false;
57	    }
58	  };
59	
60	  const doReviewQuery = async function (): Promise<boolean> {
61	    // Create our query
62	    let parseQuery: Parse.Query = new Parse.Query('Review');
63	    try {
64	      let results: [Parse.Object] = await parseQuery.find();
65	      // Set query results to state variable
66	      setQueryResults(results);
67	      return true;
68	    } catch (error) {
69	      // Error can be caused by lack of Internet connection
70	      Alert.alert('Error!', error.message);
71	      return false;
72	    }
73	  };
74	
75	  const createReview = async function (): Promise<boolean> {
76	    try {
77	      // This values come from state variables linked to
78	      // the screen form fields
79	      const reviewTextValue: string = reviewText;
80	      const reviewRateValue: number = Number(reviewRate);
81	
82	      // Creates a new parse object instance
83	      let Review: Parse.Object = new Parse.Object('Review');
84	
85	      // Set data to parse object
86	      // Simple title field
87	      Review.set('text', reviewTextValue);
88	
89	      // Simple number field
90	      Review.set('rate', reviewRateValue);
91	
92	      // Set default movie
93	      Review.set('movie', 'Mission Very Possible');
94	
95	      // After setting the values, save it on the server
96	      try {
97	        const context = {fromApplication: true};
98	        await Review.save(null, {context: context});
99	        // Success
100	        Alert.alert('Success!');
101	        // Updates query result list
102	        doReviewQuery();
103	        runGetMovieAverageRating();
104	        return true;
105	      } catch (error) {
106	        // Error can be caused by lack of Internet connection
107	        Alert.alert('Error!', error.message);
108	        return false;
109	      }
110	    } catch (error) {
111	      // Error can be caused by lack of field values
112	      Alert.alert('Error!', error.message);
113	      return false;
114	    }
115	  };
116	
117	  return (
118	    <>
119	      <View style={Styles.header}>
120	        <Image
121	          style={Styles.header_logo}
122	          source={ {
123	            uri:
124	              'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png',
125	          } }
126	        />
127	        <PaperText style={Styles.header_text}>
128	          <PaperText style={Styles.header_text_bold}>
129	            {'React Native on Back4App - '}
130	          </PaperText>
131	          {' Cloud Code Movie Ratings'}
132	        </PaperText>
133	      </View>
134	      <ScrollView style={Styles.wrapper}>
135	        <View>
136	          <Title>{'Mission Very Possible Reviews'}</Title>
137	          <PaperText>{`Ratings Average: ${ratingsAverage}`}</PaperText>
138	          {/* Query list */}
139	          {queryResults !== null &&
140	            queryResults !== undefined &&
141	            queryResults.map((result: Parse.Object) => (
142	              <List.Item
143	                key={result.id}
144	                title={`Review text: ${result.get('text')}`}
145	                description={`Rate: ${result.get('rate')}\nFrom app: ${
146	                  result.get('fromApplication') !== undefined ? 'Yes' : 'No'
147	                }`}
148	                titleStyle={Styles.list_text}
149	                style={Styles.list_item}
150	              />
151	            ))}
152	          {queryResults === null ||
153	          queryResults === undefined ||
154	          (queryResults !== null &&
155	            queryResults !== undefined &&
156	            queryResults.length <= 0) ? (
157	            <PaperText>{'No results here!'}</PaperText>
158	          ) : null}
159	        </View>
160	        <View>
161	          <Title>Action Buttons</Title>
162	          <PaperButton
163	            onPress={() => runGetMovieAverageRating()}
164	            mode="contained"
165	            icon="search-web"
166	            color={'#208AEC'}
167	            style={Styles.list_button}>
168	            {'Calculate Review Average'}
169	          </PaperButton>
170	          <PaperButton
171	            onPress={() => runGetMeMovieAverageRating()}
172	            mode="contained"
173	            icon="search-web"
174	            color={'#208AEC'}
175	            style={Styles.list_button}>
176	            {'Calculate Me Movie Review Average'}
177	          </PaperButton>
178	          <PaperButton
179	            onPress={() => doReviewQuery()}
180	            mode="contained"
181	            icon="search-web"
182	            color={'#208AEC'}
183	            style={Styles.list_button}>
184	            {'Query Reviews'}
185	          </PaperButton>
186	        </View>
187	        <View>
188	          <Title>Add new review</Title>
189	          <PaperTextInput
190	            value={reviewText}
191	            onChangeText={text => setReviewText(text)}
192	            label="Text"
193	            mode="outlined"
194	            style={Styles.form_input}
195	          />
196	          <PaperTextInput
197	            value={reviewRate}
198	            onChangeText={text => setReviewRate(text)}
199	            keyboardType={'number-pad'}
200	            label="Rate (1-5)"
201	            mode="outlined"
202	            style={Styles.form_input}
203	          />
204	          <PaperButton
205	            onPress={() => createReview()}
206	            mode="contained"
207	            icon="plus"
208	            style={Styles.submit_button}>
209	            {'Add'}
210	          </PaperButton>
211	        </View>
212	      </ScrollView>
213	    </>
214	  );
215	};
216	
217	// These define the screen component styles
218	const Styles = StyleSheet.create({
219	  header: {
220	    alignItems: 'center',
221	    paddingTop: 30,
222	    paddingBottom: 50,
223	    backgroundColor: '#208AEC',
224	  },
225	  header_logo: {
226	    height: 50,
227	    width: 220,
228	    resizeMode: 'contain',
229	  },
230	  header_text: {
231	    marginTop: 15,
232	    color: '#f0f0f0',
233	    fontSize: 16,
234	  },
235	  header_text_bold: {
236	    color: '#fff',
237	    fontWeight: 'bold',
238	  },
239	  wrapper: {
240	    width: '90%',
241	    alignSelf: 'center',
242	  },
243	  list_button: {
244	    marginTop: 6,
245	    marginLeft: 15,
246	    height: 40,
247	  },
248	  list_item: {
249	    borderBottomWidth: 1,
250	    borderBottomColor: 'rgba(0, 0, 0, 0.12)',
251	  },
252	  list_text: {
253	    fontSize: 15,
254	  },
255	  form_input: {
256	    height: 44,
257	    marginBottom: 16,
258	    backgroundColor: '#FFF',
259	    fontSize: 14,
260	  },
261	  submit_button: {
262	    width: '100%',
263	    maxHeight: 50,
264	    alignSelf: 'center',
265	    backgroundColor: '#208AEC',
266	  },
267	});
```
:::

렌더링 및 쿼리 함수 중 하나로 쿼리한 후 컴포넌트가 어떻게 보여야 하는지입니다:

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/qdnyTBPvSmISzETSkq2xO_image.png" size="50" width="363" height="749" position="center" showCaption="false"}

## 결론

이 가이드의 끝에서, Parse Cloud Code 함수 검증기 및 컨텍스트를 사용하는 방법을 배웠습니다. 다음 가이드에서는 Parse에서 사용자와 작업하는 방법을 배울 것입니다.
