---
title: React에서 Parse로 관계형 쿼리 구현 및 사용 방법
slug: docs-kr/react/data-objects/relational-query
description: Parse에서 React 앱으로 데이터베이스 관계형 쿼리 구현 및 실행 방법을 알아보세요.
image: https://www.back4app.com/images/logo-social.png
createdAt: 2024-03-19T16:06:41.125Z
---

# 파스에서 리액트를 사용한 관계형 쿼리

## 소개

이 가이드에서는 Parse에서 관계형 쿼리를 수행하고 이러한 쿼리를 사용하여 React 컴포넌트를 구현합니다. Back4App과 React를 사용하여 현실적인 데이터를 설정하고 쿼리하는 방법을 배웁니다.

## 전제 조건

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

- React 앱이 생성되고 [Back4App에 연결됨](https://www.back4app.com/docs/react/quickstart).
-  이 가이드의 예제 프로젝트를 실행하려면 [<font color="#2166ae">`Ant Design`</font> 라이브러리를 설정해야 합니다.](https://ant.design/)
:::

## 목표

React 앱에서 Back4App에 저장된 관계형 데이터를 쿼리합니다.

## 1 - Parse.Query 클래스 이해하기

모든 Parse 쿼리 작업은 <font color="#2166ae">`Parse.Query`</font> 객체 유형을 사용하며, 이를 통해 앱 전반에 걸쳐 데이터베이스에서 특정 데이터를 검색할 수 있습니다. <font color="#2166ae">`Parse.Query` </font>는 검색 메서드(예: <font color="#2166ae">`Parse.Query.find`</font> 또는 <font color="#2166ae">`Parse.Query.first`</font> 호출 후에만 해결된다는 것을 아는 것이 중요합니다. 따라서 쿼리를 설정하고 실제로 호출되기 전에 여러 수정자를 연결할 수 있습니다.

새로운 <font color="#2166ae">`Parse.Query`</font>를 생성하려면, 원하는 <font color="#2166ae">`Parse.Object`</font> 서브클래스를 매개변수로 전달해야 합니다. 아래에서 볼 수 있는 예제 쿼리는 허구의 <font color="#2166ae">`Book` </font> 서브클래스를 쿼리하고 있습니다.

```javascript
1	// This will create your query
2	let parseQuery = new Parse.Query("Book");
3	// The query will resolve only after calling this method
4	let queryResult = await parseQuery.find();
```

다음에 대해 더 읽을 수 있습니다 <font color="#2166ae">`Parse.Query`</font> 클래스 [공식 문서에서 여기](https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Query.html).

## 2 - Back4App에 데이터 저장하기

다양한 클래스를 생성해 보겠습니다. 이 클래스들은 이 가이드에서 우리의 쿼리 대상이 될 것입니다. 클래스는 다음과 같습니다: <font color="#2166ae">`저자`</font>, <font color="#2166ae">`책`</font>, <font color="#2166ae">`출판사`</font> 및 <font color="#2166ae">`서점`</font>, 여기서 <font color="#2166ae">`책` </font>은 <font color="#2166ae">`출판사` </font>와 1\:N 관계를 가지며 <font color="#2166ae">`저자`</font>와 N\:N 관계를 가집니다. 그리고 <font color="#2166ae">`서점` </font>은 <font color="#2166ae">`책`</font>과 N\:N 관계를 가집니다.

Parse JS 콘솔에서는 JavaScript 코드를 직접 실행하여 JS SDK 명령을 사용하여 애플리케이션 데이터베이스 내용을 쿼리하고 업데이트할 수 있습니다. 아래 코드를 JS 콘솔에서 실행하고 Back4App에 데이터를 삽입하세요. 대시보드에서 JS 콘솔의 모습은 다음과 같습니다:

![](https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/MrG1tC6gVb0yyGeaOpFdm_image.png)

계속해서 다음 예제 내용을 사용하여 클래스를 생성하세요:

```javascript
1	// Add objects and create tables
2	// Authors
3	const AuthorA = new Parse.Object('Author');
4	AuthorA.set('name', 'Aaron Writer');
5	await AuthorA.save();
6	
7	const AuthorB = new Parse.Object('Author');
8	AuthorB.set('name', 'Beatrice Novelist');
9	await AuthorB.save();
10	
11	const AuthorC = new Parse.Object('Author');
12	AuthorC.set('name', 'Casey Columnist');
13	await AuthorC.save();
14	
15	// Publishers
16	const PublisherA = new Parse.Object('Publisher');
17	PublisherA.set('name', 'Acacia Publishings');
18	await PublisherA.save();
19	
20	const PublisherB = new Parse.Object('Publisher');
21	PublisherB.set('name', 'Birch Distributions');
22	await PublisherB.save();
23	
24	// Books
25	const BookA = new Parse.Object('Book');
26	BookA.set('title', 'A Love Story');
27	BookA.set('publisher', PublisherA);
28	BookA.set('publishingDate', new Date('05/07/1998'));
29	const BookARelation = BookA.relation("authors");
30	BookARelation.add(AuthorA);
31	await BookA.save();
32	
33	const BookB = new Parse.Object('Book');
34	BookB.set('title', 'Benevolent Elves');
35	BookB.set('publisher', PublisherB);
36	BookB.set('publishingDate', new Date('11/31/2008'));
37	const BookBRelation = BookB.relation("authors");
38	BookBRelation.add(AuthorB);
39	await BookB.save();
40	
41	const BookC = new Parse.Object('Book');
42	BookC.set('title', 'Can You Believe It?');
43	BookC.set('publisher', PublisherB);
44	BookC.set('publishingDate', new Date('08/21/2018'));
45	const BookCRelation = BookC.relation("authors");
46	BookCRelation.add(AuthorA);
47	BookCRelation.add(AuthorC);
48	await BookC.save();
49	
50	// BookStore
51	const BookStoreA = new Parse.Object('BookStore');
52	BookStoreA.set('name', 'Books of Love');
53	const BookStoreARelation = BookStoreA.relation("books");
54	BookStoreARelation.add(BookA);
55	await BookStoreA.save();
56	
57	const BookStoreB = new Parse.Object('BookStore');
58	BookStoreB.set('name', 'Fantasy Books');
59	const BookStoreBRelation = BookStoreB.relation("books");
60	BookStoreBRelation.add(BookB);
61	await BookStoreB.save();
62	
63	const BookStoreC = new Parse.Object('BookStore');
64	BookStoreC.set('name', 'General Books');
65	const BookStoreCRelation = BookStoreC.relation("books");
66	BookStoreCRelation.add(BookA);
67	BookStoreCRelation.add(BookC);
68	await BookStoreC.save();
69	
70	console.log('Success');
```

## 3 - 데이터 쿼리

모든 클래스를 채웠으므로 이제 그 안에서 관계형 쿼리를 수행할 수 있습니다. 먼저 <font color="#2166ae">`책` </font>결과를 <font color="#2166ae">`출판사`</font>로 필터링하여 “Acacia Publishings” (또는 “Publisher A”)에 속하는 책을 찾기 위해 기본 <font color="#2166ae">`Parse.Query.equalTo`</font> 메서드를 사용합니다:

```javascript
1	// Get PublisherA object
2	const PublisherAQuery = new Parse.Query('Publisher');
3	PublisherAQuery.equalTo('name', 'Acacia Publishings');
4	const PublisherA = await PublisherAQuery.first();
5	
6	// Query Books with PublisherA
7	const bookQuery = new Parse.Query('Book');
8	bookQuery.equalTo('publisher', PublisherA);
9	let queryResults = await bookQuery.find();
10	
11	// Let's show the results
12	for (let result of queryResults) {
13	  // You access `Parse.Objects` attributes by using `.get`
14	  console.log(result.get('title'));
15	};
```

이제 <font color="#2166ae">`BookStore`</font> 객체가 <font color="#2166ae">`Book` </font> 객체를 포함하는지 쿼리해 보겠습니다. 출판 날짜가 2010년 1월 1일 이후인 객체를 찾기 위해 <font color="#2166ae">`Parse.Query.greaterThan`</font> 메서드를 사용하고, 그 다음에 <font color="#2166ae">`Parse.Query.matchesQuery`</font> 메서드를 사용합니다:

```javascript
1	// Create inner Book query
2	const bookQuery = new Parse.Query('Book');
3	bookQuery.greaterThan('publishingDate', new Date('01/01/2010'));
4	
5	// Query BookStore using inner Book query
6	const bookStoreQuery = new Parse.Query('BookStore');
7	bookStoreQuery.matchesQuery('books', bookQuery);
8	let queryResults = await bookStoreQuery.find();
9	
10	// Let's show the results
11	for (let result of queryResults) {
12	  // You access `Parse.Objects` attributes by using `.get`
13	  console.log(result.get('name'));
14	};
```

이제 최소한 하나의 <font color="#2166ae">`BookStore` </font>객체를 찾는 더 복잡한 관계 쿼리를 만들어 보겠습니다. <font color="#2166ae">`Book`</font>이 <font color="#2166ae">`Author` </font>“Aaron Writer” (또는 “AuthorA”)에 의해 작성된 <font color="#2166ae">`equalTo` </font>와 <font color="#2166ae">`matchesQuery`</font>:

```javascript
1	// Get AuthorA object
2	const AuthorAQuery = new Parse.Query('Author');
3	AuthorAQuery.equalTo('name', 'Aaron Writer');
4	const AuthorA = await AuthorAQuery.first();
5	
6	// Create inner Book query
7	const bookQuery = new Parse.Query('Book');
8	bookQuery.equalTo('authors', AuthorA);
9	
10	// Query BookStore using inner Book query
11	const bookStoreQuery = new Parse.Query('BookStore');
12	bookStoreQuery.matchesQuery('books', bookQuery);
13	let queryResults = await bookStoreQuery.find();
14	
15	// Let's show the results
16	for (let result of queryResults) {
17	  // You access `Parse.Objects` attributes by using `.get`
18	  console.log(result.get('name'));
19	};
```

## 4 - React 컴포넌트에서 쿼리하기

이제 React의 컴포넌트 내에서 예제 쿼리를 사용해 보겠습니다. 결과를 보여주는 목록과 쿼리를 호출하는 버튼이 있는 간단한 인터페이스를 가지고 있습니다. 컴포넌트 코드가 이렇게 배치되어 있습니다. <font color="#2166ae">`doQuery` </font> 함수는 이전의 예제 코드를 포함하고 있습니다.

:::CodeblockTabs
JavaScript

```javascript
1	import React, { useState } from 'react';
2	import Parse from 'parse/dist/parse.min.js';
3	import './App.css';
4	import { Button, Divider } from 'antd';
5	import { CloseOutlined, SearchOutlined } from '@ant-design/icons';
6	
7	export const QueryRelational = () => {
8	  // State variable
9	  const [queryResults, setQueryResults] = useState();
10	
11	  const doQueryA = async function () {
12	    // Get PublisherA object
13	    const PublisherAQuery = new Parse.Query('Publisher');
14	    PublisherAQuery.equalTo('name', 'Acacia Publishings');
15	    const PublisherA = await PublisherAQuery.first();
16	
17	    // Query Books with PublisherA
18	    const bookQuery = new Parse.Query('Book');
19	    bookQuery.equalTo('publisher', PublisherA);
20	
21	    try {
22	      let results = await bookQuery.find();
23	      setQueryResults(results);
24	      return true;
25	    } catch (error) {
26	      // Error can be caused by lack of Internet connection
27	      alert(`Error! ${error.message}`);
28	      return false;
29	    }
30	  };
31	
32	  const doQueryB = async function () {
33	    // Create inner Book query
34	    const bookQuery = new Parse.Query('Book');
35	    bookQuery.greaterThan('publishingDate', new Date('01/01/2010'));
36	
37	    // Query BookStore using inner Book query
38	    const bookStoreQuery = new Parse.Query('BookStore');
39	    bookStoreQuery.matchesQuery('books', bookQuery);
40	
41	    try {
42	      let results = await bookStoreQuery.find();
43	      setQueryResults(results);
44	      return true;
45	    } catch (error) {
46	      // Error can be caused by lack of Internet connection
47	      alert(`Error! ${error.message}`);
48	      return false;
49	    }
50	  };
51	
52	  const doQueryC = async function () {
53	    // Get AuthorA object
54	    const AuthorAQuery = new Parse.Query('Author');
55	    AuthorAQuery.equalTo('name', 'Aaron Writer');
56	    const AuthorA = await AuthorAQuery.first();
57	
58	    // Create inner Book query
59	    const bookQuery = new Parse.Query('Book');
60	    bookQuery.equalTo('authors', AuthorA);
61	
62	    // Query BookStore using inner Book query
63	    const bookStoreQuery = new Parse.Query('BookStore');
64	    bookStoreQuery.matchesQuery('books', bookQuery);
65	
66	    try {
67	      let results = await bookStoreQuery.find();
68	      setQueryResults(results);
69	      return true;
70	    } catch (error) {
71	      // Error can be caused by lack of Internet connection
72	      alert(`Error! ${error.message}`);
73	      return false;
74	    }
75	  };
76	
77	  const clearQueryResults = async function () {
78	    setQueryResults(undefined);
79	    return true;
80	  };
81	
82	  return (
83	    <div>
84	      <div className="header">
85	        <img
86	          className="header_logo"
87	          alt="Back4App Logo"
88	          src={
89	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
90	          }
91	        />
92	        <p className="header_text_bold">{'React on Back4App'}</p>
93	        <p className="header_text">{'Relational Queries'}</p>
94	      </div>
95	      <div className="container">
96	        <div className="flex_between">
97	          <h2 className="heading">{'Query List'}</h2>
98	          <div className="flex">
99	            <Button
100	              onClick={() => doQueryA()}
101	              type="primary"
102	              className="heading_button"
103	              color={'#208AEC'}
104	              icon={<SearchOutlined />}
105	            >
106	              QUERY A
107	            </Button>
108	            <Button
109	              onClick={() => doQueryB()}
110	              type="primary"
111	              className="heading_button"
112	              color={'#208AEC'}
113	              icon={<SearchOutlined />}
114	            >
115	              QUERY B
116	            </Button>
117	            <Button
118	              onClick={() => doQueryC()}
119	              type="primary"
120	              className="heading_button"
121	              color={'#208AEC'}
122	              icon={<SearchOutlined />}
123	            >
124	              QUERY C
125	            </Button>
126	            <Button
127	              onClick={() => clearQueryResults()}
128	              type="primary"
129	              className="heading_button"
130	              color={'#208AEC'}
131	              icon={<CloseOutlined />}
132	            >
133	              CLEAR RESULTS
134	            </Button>
135	          </div>
136	        </div>
137	        <Divider />
138	        <div className="flex_between">
139	          <div className="flex_child">
140	            {/* Query list */}
141	            {queryResults !== undefined &&
142	              queryResults.map((result, index) => (
143	                <div className="list_item" key={`${index}`}>
144	                  <p className="list_item_title">{`${
145	                    result.get('name') !== undefined
146	                      ? result.get('name')
147	                      : result.get('title')
148	                  }`}</p>
149	                </div>
150	              ))}
151	            {queryResults !== undefined && queryResults.length <= 0 ? (
152	              <p>{'No results here!'}</p>
153	            ) : null}
154	          </div>
155	        </div>
156	      </div>
157	    </div>
158	  );
159	};
```

TypeScript

```typescript
1	import React, { useState, FC, ReactElement } from 'react';
2	import './App.css';
3	import { Button, Divider } from 'antd';
4	import { CloseOutlined, SearchOutlined } from '@ant-design/icons';
5	const Parse = require('parse/dist/parse.min.js');
6	
7	export const QueryRelational: FC<{}> = (): ReactElement => {
8	  // State variable
9	  const [queryResults, setQueryResults] = useState<Parse.Object[]>();
10	
11	  const doQueryA = async function (): Promise<boolean> {
12	    // Get PublisherA object
13	    const PublisherAQuery: Parse.Query = new Parse.Query('Publisher');
14	    PublisherAQuery.equalTo('name', 'Acacia Publishings');
15	    const PublisherA: Parse.Object | undefined = await PublisherAQuery.first();
16	
17	    // Query Books with PublisherA
18	    const bookQuery: Parse.Query = new Parse.Query('Book');
19	    bookQuery.equalTo('publisher', PublisherA);
20	
21	    try {
22	      let results: Parse.Object[] = await bookQuery.find();
23	      setQueryResults(results);
24	      return true;
25	    } catch (error) {
26	      // Error can be caused by lack of Internet connection
27	      alert(`Error! ${error.message}`);
28	      return false;
29	    }
30	  };
31	
32	  const doQueryB = async function (): Promise<boolean> {
33	    // Create inner Book query
34	    const bookQuery: Parse.Query = new Parse.Query('Book');
35	    bookQuery.greaterThan('publishingDate', new Date('01/01/2010'));
36	
37	    // Query BookStore using inner Book query
38	    const bookStoreQuery: Parse.Query = new Parse.Query('BookStore');
39	    bookStoreQuery.matchesQuery('books', bookQuery);
40	
41	    try {
42	      let results: Parse.Object[] = await bookStoreQuery.find();
43	      setQueryResults(results);
44	      return true;
45	    } catch (error) {
46	      // Error can be caused by lack of Internet connection
47	      alert(`Error! ${error.message}`);
48	      return false;
49	    }
50	  };
51	
52	  const doQueryC = async function (): Promise<boolean> {
53	    // Get AuthorA object
54	    const AuthorAQuery: Parse.Query = new Parse.Query('Author');
55	    AuthorAQuery.equalTo('name', 'Aaron Writer');
56	    const AuthorA: Parse.Object | undefined = await AuthorAQuery.first();
57	
58	    // Create inner Book query
59	    const bookQuery: Parse.Query = new Parse.Query('Book');
60	    bookQuery.equalTo('authors', AuthorA);
61	
62	    // Query BookStore using inner Book query
63	    const bookStoreQuery: Parse.Query = new Parse.Query('BookStore');
64	    bookStoreQuery.matchesQuery('books', bookQuery);
65	
66	    try {
67	      let results: Parse.Object[] = await bookStoreQuery.find();
68	      setQueryResults(results);
69	      return true;
70	    } catch (error) {
71	      // Error can be caused by lack of Internet connection
72	      alert(`Error! ${error.message}`);
73	      return false;
74	    }
75	  };
76	
77	  const clearQueryResults = async function (): Promise<boolean> {
78	    setQueryResults(undefined);
79	    return true;
80	  };
81	
82	  return (
83	    <div>
84	      <div className="header">
85	        <img
86	          className="header_logo"
87	          alt="Back4App Logo"
88	          src={
89	            'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
90	          }
91	        />
92	        <p className="header_text_bold">{'React on Back4App'}</p>
93	        <p className="header_text">{'Relational Queries'}</p>
94	      </div>
95	      <div className="container">
96	        <div className="flex_between">
97	          <h2 className="heading">{'Query List'}</h2>
98	          <div className="flex">
99	            <Button
100	              onClick={() => doQueryA()}
101	              type="primary"
102	              className="heading_button"
103	              color={'#208AEC'}
104	              icon={<SearchOutlined />}
105	            >
106	              QUERY A
107	            </Button>
108	            <Button
109	              onClick={() => doQueryB()}
110	              type="primary"
111	              className="heading_button"
112	              color={'#208AEC'}
113	              icon={<SearchOutlined />}
114	            >
115	              QUERY B
116	            </Button>
117	            <Button
118	              onClick={() => doQueryC()}
119	              type="primary"
120	              className="heading_button"
121	              color={'#208AEC'}
122	              icon={<SearchOutlined />}
123	            >
124	              QUERY C
125	            </Button>
126	            <Button
127	              onClick={() => clearQueryResults()}
128	              type="primary"
129	              className="heading_button"
130	              color={'#208AEC'}
131	              icon={<CloseOutlined />}
132	            >
133	              CLEAR RESULTS
134	            </Button>
135	          </div>
136	        </div>
137	        <Divider />
138	        <div className="flex_between">
139	          <div className="flex_child">
140	            {/* Query list */}
141	            {queryResults !== undefined &&
142	              queryResults.map((result: Parse.Object, index: number) => (
143	                <div className="list_item" key={`${index}`}>
144	                  <p className="list_item_title">{`${result.get('name') !== undefined ? result.get('name') : result.get('title')}`}</p>
145	                </div>
146	              ))}
147	            {queryResults !== undefined &&
148	            queryResults.length <= 0 ? (
149	              <p>{'No results here!'}</p>
150	            ) : null}
151	          </div>
152	        </div>
153	      </div>
154	    </div>
155	  );
156	};
```
:::

또한 이 클래스를 <font color="#2166ae">`App.css`</font> 파일에 추가하여 구성 요소 레이아웃을 완전히 렌더링하세요:

:::CodeblockTabs
App.css

```css
1	@import '~antd/dist/antd.css';
2	
3	.App {
4	  text-align: center;
5	}
6	
7	html {
8	  box-sizing: border-box;
9	  outline: none;
10	  overflow: auto;
11	}
12	
13	*,
14	*:before,
15	*:after {
16	  margin: 0;
17	  padding: 0;
18	  box-sizing: inherit;
19	}
20	
21	h1,
22	h2,
23	h3,
24	h4,
25	h5,
26	h6 {
27	  margin: 0;
28	  font-weight: bold;
29	}
30	
31	p {
32	  margin: 0;
33	}
34	
35	body {
36	  margin: 0;
37	  background-color: #fff;
38	}
39	
40	.container {
41	  width: 100%;
42	  max-width: 900px;
43	  margin: auto;
44	  padding: 20px 0;
45	  text-align: left;
46	}
47	
48	.header {
49	  align-items: center;
50	  padding: 25px 0;
51	  background-color: #208AEC;
52	}
53	
54	.header_logo {
55	  height: 55px;
56	  margin-bottom: 20px;
57	  object-fit: contain;
58	}
59	
60	.header_text_bold {
61	  margin-bottom: 3px;
62	  color: rgba(255, 255, 255, 0.9);
63	  font-size: 16px;
64	  font-weight: bold;
65	}
66	
67	.header_text {
68	  color: rgba(255, 255, 255, 0.9);
69	  font-size: 15px;
70	}
71	
72	.heading {
73	  font-size: 22px;
74	}
75	
76	.flex {
77	  display: flex;
78	}
79	
80	.flex_between {
81	  display: flex;
82	  justify-content: space-between;
83	}
84	
85	.flex_child {
86	  flex: 0 0 45%;
87	}
88	
89	.heading_button {
90	  margin-left: 12px;
91	}
92	
93	.list_item {
94	  padding-bottom: 15px;
95	  margin-bottom: 15px;
96	  border-bottom: 1px solid rgba(0,0,0,0.06);
97	  text-align: left;
98	}
99	
100	.list_item_title {
101	  color: rgba(0,0,0,0.87);
102	  font-size: 17px;
103	}
```
:::

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

![](https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/y52ncClgNZmWznwkc1c4C_image.png)

## 결론

이 가이드의 끝에서, Parse에서 관계형 쿼리가 어떻게 작동하는지와 React 앱에서 Back4App에서 이를 수행하는 방법을 배웠습니다. 다음 가이드에서는 Parse에서 사용자와 작업하는 방법을 배울 것입니다.
