React에서 Parse로 관계형 쿼리 구현 및 사용 방법
파스에서 리액트를 사용한 관계형 쿼리
소개
이 가이드에서는 Parse에서 관계형 쿼리를 수행하고 이러한 쿼리를 사용하여 React 컴포넌트를 구현합니다. Back4App과 React를 사용하여 현실적인 데이터를 설정하고 쿼리하는 방법을 배웁니다.
전제 조건
이 튜토리얼을 완료하려면 다음이 필요합니다:
- 이 가이드의 예제 프로젝트를 실행하려면 Ant Design 라이브러리를 설정해야 합니다.
목표
React 앱에서 Back4App에 저장된 관계형 데이터를 쿼리합니다.
1 - Parse.Query 클래스 이해하기
모든 Parse 쿼리 작업은 Parse.Query 객체 유형을 사용하며, 이를 통해 앱 전반에 걸쳐 데이터베이스에서 특정 데이터를 검색할 수 있습니다. Parse.Query 는 검색 메서드(예: Parse.Query.find 또는 Parse.Query.first 호출 후에만 해결된다는 것을 아는 것이 중요합니다. 따라서 쿼리를 설정하고 실제로 호출되기 전에 여러 수정자를 연결할 수 있습니다.
새로운 Parse.Query를 생성하려면, 원하는 Parse.Object 서브클래스를 매개변수로 전달해야 합니다. 아래에서 볼 수 있는 예제 쿼리는 허구의 Book 서브클래스를 쿼리하고 있습니다.
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();다음에 대해 더 읽을 수 있습니다 Parse.Query 클래스 공식 문서에서 여기.
2 - Back4App에 데이터 저장하기
다양한 클래스를 생성해 보겠습니다. 이 클래스들은 이 가이드에서 우리의 쿼리 대상이 될 것입니다. 클래스는 다음과 같습니다: 저자, 책, 출판사 및 서점, 여기서 책 은 출판사 와 1:N 관계를 가지며 저자와 N:N 관계를 가집니다. 그리고 서점 은 책과 N:N 관계를 가집니다.
Parse JS 콘솔에서는 JavaScript 코드를 직접 실행하여 JS SDK 명령을 사용하여 애플리케이션 데이터베이스 내용을 쿼리하고 업데이트할 수 있습니다. 아래 코드를 JS 콘솔에서 실행하고 Back4App에 데이터를 삽입하세요. 대시보드에서 JS 콘솔의 모습은 다음과 같습니다:

계속해서 다음 예제 내용을 사용하여 클래스를 생성하세요:
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 - 데이터 쿼리
모든 클래스를 채웠으므로 이제 그 안에서 관계형 쿼리를 수행할 수 있습니다. 먼저 책 결과를 출판사로 필터링하여 “Acacia Publishings” (또는 “Publisher A”)에 속하는 책을 찾기 위해 기본 Parse.Query.equalTo 메서드를 사용합니다:
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 };이제 BookStore 객체가 Book 객체를 포함하는지 쿼리해 보겠습니다. 출판 날짜가 2010년 1월 1일 이후인 객체를 찾기 위해 Parse.Query.greaterThan 메서드를 사용하고, 그 다음에 Parse.Query.matchesQuery 메서드를 사용합니다:
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 };이제 최소한 하나의 BookStore 객체를 찾는 더 복잡한 관계 쿼리를 만들어 보겠습니다. Book이 Author “Aaron Writer” (또는 “AuthorA”)에 의해 작성된 equalTo 와 matchesQuery:
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의 컴포넌트 내에서 예제 쿼리를 사용해 보겠습니다. 결과를 보여주는 목록과 쿼리를 호출하는 버튼이 있는 간단한 인터페이스를 가지고 있습니다. 컴포넌트 코드가 이렇게 배치되어 있습니다. doQuery 함수는 이전의 예제 코드를 포함하고 있습니다.
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 };또한 이 클래스를 App.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 }렌더링 및 쿼리 함수 중 하나로 쿼리한 후 컴포넌트가 어떻게 보여야 하는지입니다:

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