---
title: 리액트 네이티브에서 Parse Cloud Functions 구현 가이드
slug: docs-kr/react-native/parse-sdk/cloud-functions/react-native-cloud-functions
description: 리액트 네이티브 앱에서 Parse 클라우드 함수를 사용하여 데이터 처리 최적화.
image: https://www.back4app.com/images/logo-social.png
createdAt: 2024-03-19T17:33:30.675Z
---

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

## 소개

이 가이드에서는 리액트 네이티브 앱에서 Parse 클라우드 코드 함수를 사용하는 방법을 배웁니다. 클라우드 함수를 사용하여 구현된 트리거의 예를 보고, 이러한 함수와 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 클라우드 코드를 실행합니다.

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

애플리케이션에서는 특정 데이터 작업이나 모바일 장치에서 수행해서는 안 되는 무거운 처리를 수행해야 할 때가 있습니다. 이러한 경우에는 클라우드 코드 함수를 사용할 수 있으며, 이는 Parse 서버에서 직접 실행되고 Parse API를 사용하여 호출됩니다. 이는 또한 앱 스토어에서 새 버전을 출시할 필요 없이 앱의 일부 로직을 변경할 수 있게 해주므로 유용할 수 있습니다.

클라우드 코드 함수에는 두 가지 주요 유형이 있습니다. 일반 함수는 <font color="#2166ae">`정의하다`</font>와 원하는 모든 유형의 코드를 보유하고 있으며, 트리거는 Parse 서버에서 특정 이벤트가 발생할 때 자동으로 실행됩니다.

다음 단계에서는 이러한 함수 유형의 대부분에 대한 예제가 제공되며, 이는 Back4App 대시보드 또는 CLI를 통해 직접 생성하고 배포할 수 있습니다. [클라우드 함수 시작 가이드](https://www.back4app.com/docs/get-started/cloud-functions)를 참조하여 이 작업을 수행하는 방법을 확인할 수 있습니다.

## 2 - 클라우드 코드 참조

### 클라우드 함수

:::CodeblockTabs
Cloud Functions

```javascript
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	});
```
:::

응답:

:::BlockQuote
1   \{ "result": 3.5 }
:::

### 저장 트리거

:::CodeblockTabs
beforeSave

```javascript
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	});
```

afterSave

```javascript
1	// This will always run after saving an object of this class
2	Parse.Cloud.afterSave("Comment", async (request) => {
3	  // You can use this method for changing other classes, like handling
4	  // a Comment counter for a Post
5	  const query = new Parse.Query("Post");
6	  try {
7	    let post = await query.get(request.object.get("post").id);
8	    post.increment("comments");
9	    return post.save();
10	  } catch (error) {
11	    console.error("Got an error " + error.code + " : " + error.message);
12	  }
13	});
```
:::

### 삭제 트리거

:::CodeblockTabs
beforeDelete

```javascript
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	});
```

afterDelete

```javascript
1	// Called after deleting an object, useful for cascade deleting related
2	// objects
3	Parse.Cloud.afterDelete("Post", async (request) => {
4	  // Delete all post Comments after deleting the Post
5	  const query = new Parse.Query("Comment");
6	  query.equalTo("post", request.object);
7	  try {
8	    let results = await query.find();
9	    await Parse.Object.destroyAll(results);
10	  } catch (error) {
11	    console.error("Error finding related comments " + error.code + ": " + error.message);
12	  }
13	});
```
:::

### 파일 트리거

:::CodeblockTabs
beforeSaveFile

```javascript
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	});
```

afterSaveFile

```javascript
1	// Can be called for managing file metadata and creating
2	// your file referencing object
3	Parse.Cloud.afterSaveFile(async (request) => {
4	  const { file, fileSize, user } = request;
5	  const fileObject = new Parse.Object('FileObject');
6	  fileObject.set('file', file);
7	  fileObject.set('fileSize', fileSize);
8	  fileObject.set('createdBy', user);
9	  const token = { sessionToken: user.getSessionToken() };
10	  await fileObject.save(null, token);
11	});
```

beforeDeleteFile

```javascript
1	// Useful for checking conditions in objects using the file to be deleted
2	Parse.Cloud.beforeDeleteFile(async (request) => {
3	  // Only deletes file if the object storing it was also deleted before
4	  const { file, user } = request;
5	  const query = new Parse.Query('FileObject');
6	  query.equalTo('fileName', file.name());
7	  const fileObjectCount = await query.count({ useMasterKey: true });
8	  if (fileObjectCount > 0) {
9	    throw 'The FileObject should be delete first!';
10	  }
11	});
```

afterDeleteFile

```javascript
1	// Useful for cleaning up objects related to the file
2	Parse.Cloud.afterDeleteFile(async (request) => {
3	  // Note that this example is the opposite of the beforeDeleteFile one,
4	  // this one will clean up FileObjects after deleting the file
5	  const { file } = request;
6	  const query = new Parse.Query('FileObject');
7	  query.equalTo('fileName', file.name());
8	  const fileObject = await query.first({ useMasterKey: true });
9	  await fileObject.destroy({ useMasterKey: true });
10	});
```
:::

### 찾기 트리거

:::CodeblockTabs
beforeFind

```javascript
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	});
```

afterFind

```javascript
1	// This will be called after the query is done in the database
2	Parse.Cloud.afterFind('MyObject', (req) => {
3	  // Can be used in rare cases, like force reversing result ordering
4	  let results = req.objects;
5	  results = results.reverse();
6	  return req.objects;
7	});
```
:::

### 세션 트리거

:::CodeblockTabs
beforeLogin

```javascript
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	});
```

afterLogout

```javascript
1	// Can be used for cleaning up user actions after logging out
2	Parse.Cloud.afterLogout(async (request) => {
3	  // Sets custom online flag to false
4	  const { object: session } = request;
5	  const user = session.get('user');
6	  user.set('isOnline', false);
7	  await user.save(null, { useMasterKey: true } );
8	});
```
:::

## 3 - React Native 컴포넌트에서 Cloud Code 사용하기

이제 예제 Parse Cloud Code 함수를 만들고 React Native의 컴포넌트 내에서 호출해 보겠습니다. 함수 결과를 표시하는 레이블과 새 객체를 추가하는 양식이 있는 간단한 인터페이스를 사용합니다.

우리의 클라우드 함수는 앱의 모든 영화 <font color="#2166ae">`리뷰`</font> 객체의 평균 평점을 계산합니다. 이는 <font color="#2166ae">`Parse.Object`</font> 클래스이며 <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	});
```

React Native에서 이 클라우드 함수를 호출하려면 <font color="#2166ae">`Parse.Cloud.run`</font> 메서드를 사용해야 하며, 함수 이름과 필요한 매개변수를 객체 안에 전달해야 합니다.

:::CodeblockTabs
JavaScript

```javascript
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	};
```

```typescript
1	const runGetMovieAverageRating = async function (): Promise<boolean> {
2	  try {
3	    const params: {movie: string} = {
4	      movie: 'Mission Very Possible',
5	    };
6	    let resultObject: {result: number} = 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	};
```
:::

리뷰의 텍스트가 짧게 유지되도록 <font color="#2166ae">`beforeSave`</font> 트리거 함수를 <font color="#2166ae">`Review`</font> 객체에 대해 사용할 수 있습니다. 함수 코드는 다음과 같습니다:

:::CodeblockTabs
JavaScript

```javascript
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	});
```
:::

전체 구성 요소 코드는 다음과 같이 배치됩니다:

:::CodeblockTabs
JavaScript

```javascript
1	import React, {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 = () => {
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 () {
27	    try {
28	      const params = {
29	        movie: 'Mission Very Possible',
30	      };
31	      let resultObject = 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 () {
50	    // Create our query
51	    let parseQuery = new Parse.Query('Review');
52	    try {
53	      let results = 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 () {
65	    try {
66	      // This values come from state variables linked to
67	      // the screen form fields
68	      const reviewTextValue = reviewText;
69	      const reviewRateValue = Number(reviewRate);
70	
71	      // Creates a new parse object instance
72	      let Review = 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) => (
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	});
```

```typescript
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	});
```
:::

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

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

## 결론

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