플러터에서 Parse 관계형 데이터 쿼리 구현
플러터에서 관계형 쿼리 파싱하기
소개
당신은 이미 관계형 데이터 객체를 저장하는 방법을 보았습니다. Parse.Pointer 및 Parse.Relations 데이터 유형을 사용하여 Parse에서는 다른 ParseObject의 값으로 어떤 ParseObject도 사용할 수 있다는 것을 배웠습니다. 이를 통해 두 객체 간의 관계를 설정할 수 있습니다. 내부적으로 Parse 프레임워크는 일관성을 유지하기 위해 참조된 객체를 한 곳에 저장합니다. 이는 복잡한 쿼리를 구축하고 실행할 때 추가적인 힘을 줄 수 있습니다.
또한, 당신은 이미 QueryBuilder를 사용하는 방법을 배웠습니다. get을 사용하면 Back4App에서 단일 ParseObject를 검색할 수 있습니다. QueryBuilder를 사용하여 데이터를 검색하는 다른 많은 방법이 있습니다.
이 가이드에서는 QueryBuilder 클래스에 대해 깊이 파고들고 관계형 쿼리를 구축하는 데 사용할 수 있는 메서드를 살펴볼 것입니다. 간단한 데이터베이스 클래스를 사용하여 일부 모의 데이터를 사용하여 Back4App에서 Flutter를 사용하여 쿼리를 수행할 것입니다.
전제 조건
이 튜토리얼을 완료하려면 다음이 필요합니다:
- Back4app에 연결된 Flutter 앱.
- Android 또는 iOS를 실행하는 장치(또는 가상 장치).
목표
Flutter 앱에서 Back4App에 저장된 관계형 데이터 쿼리.
1 - QueryBuilder 클래스 이해하기
모든 Parse 쿼리 작업은 QueryBuilder 객체 유형을 사용하며, 이를 통해 앱 전반에 걸쳐 데이터베이스에서 특정 데이터를 검색할 수 있습니다.
새 QueryBuilder를 만들려면 원하는 ParseObject 하위 클래스를 매개변수로 전달해야 하며, 이는 쿼리 결과를 포함할 것입니다.
쿼리 빌더는 retrieve 메서드 쿼리를 호출한 후에만 해결된다는 것을 아는 것이 중요합니다. 따라서 쿼리를 설정하고 실제로 호출되기 전에 여러 수정자를 연결할 수 있습니다.
1 // This will create your query
2 QueryBuilder<ParseObject> queryBook =
3 QueryBuilder<ParseObject>(ParseObject('Book'));
4
5 // The query will resolve only after calling this method
6 final ParseResponse apiResponse = await queryBook.query();
7
8 if (apiResponse.success && apiResponse.results != null) {
9 ....
10 } else {
11 ...
12 }QueryBuilder 클래스에 대해 더 읽어보실 수 있습니다 여기에서 공식 문서.
Back4App에서 JavaScript 콘솔 사용하기
Back4App 애플리케이션 대시보드 내에서 JavaScript 코드를 직접 실행할 수 있는 매우 유용한 API 콘솔을 찾을 수 있습니다. 이 가이드를 사용하여 Back4App에 데이터 객체를 저장할 것입니다. 앱의 주요 대시보드에서 Core->API Console->Javascript로 이동하세요.

2 - Back4app에 데이터 저장하기
이 가이드에서 쿼리를 실행하려면 먼저 앱에 데이터를 채워야 합니다.
클래스는 다음과 같습니다: Author, Book, Publisher 및 BookStore. 어떤 Book은 1:N 관계를 가지고 있으며 Publisher와 N:N 관계를 가지고 있습니다. 그리고 BookStore는 Book과 N:N 관계를 가지고 있습니다.
여기 Parse.Object 클래스 생성 코드가 있습니다. API 콘솔에서 실행해 보세요:
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 const AuthorD = new Parse.Object('Author');
16 AuthorD.set('name', 'Gary Stur');
17 await AuthorD.save();
18
19 const AuthorE = new Parse.Object('Author');
20 AuthorE.set('name', 'Mary Sue');
21 await AuthorE.save();
22
23 // Publishers
24 const PublisherA = new Parse.Object('Publisher');
25 PublisherA.set('name', 'Acacia Publishings');
26 await PublisherA.save();
27
28 const PublisherB = new Parse.Object('Publisher');
29 PublisherB.set('name', 'Birch Distributions');
30 await PublisherB.save();
31
32 const PublisherC = new Parse.Object('Publisher');
33 PublisherC.set('name', 'Acacia Distributions');
34 await PublisherC.save();
35
36
37 // Books
38 const BookA = new Parse.Object('Book');
39 BookA.set('title', 'A Love Story');
40 BookA.set('publisher', PublisherA);
41 BookA.set('publishingDate', new Date('05/07/1998'));
42 const BookARelation = BookA.relation("authors");
43 BookARelation.add(AuthorA);
44 await BookA.save();
45
46 const BookB = new Parse.Object('Book');
47 BookB.set('title', 'Benevolent Elves');
48 BookB.set('publisher', PublisherB);
49 BookB.set('publishingDate', new Date('11/31/2008'));
50 const BookBRelation = BookB.relation("authors");
51 BookBRelation.add(AuthorB);
52 await BookB.save();
53
54 const BookC = new Parse.Object('Book');
55 BookC.set('title', 'Can You Believe It?');
56 BookC.set('publisher', PublisherB);
57 BookC.set('publishingDate', new Date('08/21/2018'));
58 const BookCRelation = BookC.relation("authors");
59 BookCRelation.add(AuthorA);
60 BookCRelation.add(AuthorC);
61 await BookC.save();
62
63 // BookStore
64 const BookStoreA = new Parse.Object('BookStore');
65 BookStoreA.set('name', 'Books of Love');
66 const BookStoreARelation = BookStoreA.relation("books");
67 BookStoreARelation.add(BookA);
68 await BookStoreA.save();
69
70 const BookStoreB = new Parse.Object('BookStore');
71 BookStoreB.set('name', 'Fantasy Books');
72 const BookStoreBRelation = BookStoreB.relation("books");
73 BookStoreBRelation.add(BookB);
74 await BookStoreB.save();
75
76 const BookStoreC = new Parse.Object('BookStore');
77 BookStoreC.set('name', 'General Books');
78 const BookStoreCRelation = BookStoreC.relation("books");
79 BookStoreCRelation.add(BookA);
80 BookStoreCRelation.add(BookC);
81 await BookStoreC.save();
82
83 console.log('Success');이 코드를 실행한 후, 데이터베이스에 Author, Publisher, Book 및 BookStore 클래스가 생성되어야 합니다.
당신의 새로운 클래스는 다음과 같아야 합니다:

이제 모든 QueryBuilder 메서드의 예를 살펴보고, 그들이 하는 일에 대한 간단한 설명을 추가하겠습니다.
3 - 데이터 쿼리하기
이제 클래스가 채워졌으므로, 몇 가지 관계형 쿼리를 수행할 수 있습니다.
모든 클래스가 채워졌으므로, 이제 그 안에서 몇 가지 관계형 쿼리를 수행할 수 있습니다.
출판사로 책 결과를 필터링하는 것부터 시작하겠습니다. “Acacia Publishings” (또는 “Publisher A”)에 속하는 것을 기본 .whereEqualTo 메서드를 사용하여 관계형 포인터로 검색합니다:
1 // Get PublisherA object
2 final QueryBuilder<ParseObject> publisherQueryA =
3 QueryBuilder<ParseObject>(ParseObject('Publisher'))
4 ..whereEqualTo('name', 'Acacia Publishings');
5
6 final ParseResponse publisherResponse = await publisherQueryA.query();
7 if (!publisherResponse.success) {
8 return;
9 }
10 final publisherA = publisherResponse.results?.first as ParseObject;
11
12 // Query Books with PublisherA
13 final QueryBuilder<ParseObject> bookQuery =
14 QueryBuilder<ParseObject>(ParseObject('Book'))
15 ..whereEqualTo('publisher', publisherA);
16
17 final ParseResponse bookResponse = await bookQuery.query();
18 if (!bookResponse.success) {
19 return;
20 }
21
22 for (var book in bookResponse.results as List<ParseObject>) {
23 print(book.get('title'));
24 }이제 출판 날짜가 2010년 1월 1일 이후인 책 객체를 포함하는 BookStore 객체를 쿼리해 보겠습니다. whereGreaterThan 메서드와 그 다음에 whereMatchesQuery 메서드를 사용하여 내부 쿼리를 수행합니다:
1 // Create inner Book query
2 final QueryBuilder<ParseObject> bookQuery =
3 QueryBuilder<ParseObject>(ParseObject('Book'))
4 ..whereGreaterThan('publishingDate', DateTime(2010, 01, 01));
5
6 // Query BookStore using inner Book query
7 final QueryBuilder<ParseObject> bookStoreQuery =
8 QueryBuilder<ParseObject>(ParseObject('BookStore'))
9 ..whereMatchesQuery('books', bookQuery);
10
11 final ParseResponse bookStoreResponse = await bookStoreQuery.query();
12 if (!bookStoreResponse.success) {
13 return;
14 }
15
16 for (var b in bookStoreResponse.results as List<ParseObject>) {
17 print(b.get('name'));
18 }이제 책과 관련된 저자를 찾는 또 다른 쿼리를 생성해 보겠습니다 Can You Believe It?, whereRelatedTo를 사용하여:
1 // Get Book object
2 final QueryBuilder<ParseObject> bookQuery =
3 QueryBuilder<ParseObject>(ParseObject('Book'))
4 ..whereEqualTo('title', 'Can You Believe It?');
5
6 final ParseResponse bookResponse = await bookQuery.query();
7 if (!bookResponse.success) {
8 return;
9 }
10
11 final book = bookResponse.results?.first as ParseObject;
12
13 // Get Author with relation with Book Can You Believe It?
14 final QueryBuilder<ParseObject> authorsQuery =
15 QueryBuilder<ParseObject>(ParseObject('Author'))
16 ..whereRelatedTo('authors', 'Book', book.objectId!);
17
18 final ParseResponse authorResponse = await authorsQuery.query();
19
20 // Let's show the results
21 if (authorResponse.success && authorResponse.results != null) {
22 for (var a in authorResponse.results! as List<ParseObject>) {
23 print(a.get('name'));
24 }
25 }4 - Flutter에서의 쿼리
이제 Flutter 앱 내에서 예제 쿼리를 사용해 보겠습니다. 결과를 보여주는 목록과 쿼리를 호출하는 3개의 버튼이 있는 간단한 인터페이스를 갖추고 있습니다.
Flutter 프로젝트를 열고, main.dart 파일로 이동하여 모든 코드를 정리하고 다음으로 교체하세요:
1 import 'package:flutter/cupertino.dart';
2 import 'package:flutter/material.dart';
3 import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
4
5 void main() async {
6 WidgetsFlutterBinding.ensureInitialized();
7
8 final keyApplicationId = 'YOUR_APP_ID_HERE';
9 final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
10 final keyParseServerUrl = 'https://parseapi.back4app.com';
11
12 await Parse().initialize(keyApplicationId, keyParseServerUrl,
13 clientKey: keyClientKey, debug: true);
14
15 runApp(MaterialApp(
16 title: 'Flutter - GeoPoint',
17 debugShowCheckedModeBanner: false,
18 home: HomePage(),
19 ));
20 }
21
22 class HomePage extends StatefulWidget {
23 @override
24 _HomePageState createState() => _HomePageState();
25 }
26
27 class _HomePageState extends State<HomePage> {
28 List<ParseObject> results = <ParseObject>[];
29 double selectedDistance = 3000;
30
31 void doQueryPointer() async {
32 // Get PublisherA object
33 final QueryBuilder<ParseObject> publisherQueryA =
34 QueryBuilder<ParseObject>(ParseObject('Publisher'))
35 ..whereEqualTo('name', 'Acacia Publishings');
36
37 final ParseResponse publisherResponse = await publisherQueryA.query();
38 if (!publisherResponse.success) {
39 return;
40 }
41 final publisherA = publisherResponse.results?.first as ParseObject;
42
43 // Query Books with PublisherA
44 final QueryBuilder<ParseObject> bookQuery =
45 QueryBuilder<ParseObject>(ParseObject('Book'))
46 ..whereEqualTo('publisher', publisherA);
47
48 final ParseResponse bookResponse = await bookQuery.query();
49
50 if (!bookResponse.success) {
51 setState(() {
52 results.clear();
53 });
54 } else {
55 setState(() {
56 results = bookResponse.results as List<ParseObject>;
57 });
58 }
59 }
60
61 void doQueryMatches() async {
62 // Create inner Book query
63 final QueryBuilder<ParseObject> bookQuery =
64 QueryBuilder<ParseObject>(ParseObject('Book'))
65 ..whereGreaterThan('publishingDate', DateTime(2010, 01, 01));
66
67 // Query BookStore using inner Book query
68 final QueryBuilder<ParseObject> bookStoreQuery =
69 QueryBuilder<ParseObject>(ParseObject('BookStore'))
70 ..whereMatchesQuery('books', bookQuery);
71
72 final ParseResponse bookStoreResponse = await bookStoreQuery.query();
73 if (!bookStoreResponse.success) {
74 setState(() {
75 results.clear();
76 });
77 } else {
78 setState(() {
79 results = bookStoreResponse.results as List<ParseObject>;
80 });
81 }
82 }
83
84 void doQueryRelatedTo() async {
85 // Get Book object
86 final QueryBuilder<ParseObject> bookQuery =
87 QueryBuilder<ParseObject>(ParseObject('Book'))
88 ..whereEqualTo('title', 'Can You Believe It?');
89
90 final ParseResponse bookResponse = await bookQuery.query();
91 if (!bookResponse.success) {
92 return;
93 }
94
95 final book = bookResponse.results?.first as ParseObject;
96
97 // Get Author with relation with Book Can You Believe It?
98 final QueryBuilder<ParseObject> authorsQuery =
99 QueryBuilder<ParseObject>(ParseObject('Author'))
100 ..whereRelatedTo('authors', 'Book', book.objectId!);
101
102 final ParseResponse authorResponse = await authorsQuery.query();
103
104 if (!authorResponse.success) {
105 setState(() {
106 results.clear();
107 });
108 } else {
109 setState(() {
110 results = authorResponse.results as List<ParseObject>;
111 });
112 }
113 }
114
115 void doClearResults() async {
116 setState(() {
117 results.clear();
118 });
119 }
120
121 @override
122 Widget build(BuildContext context) {
123 return Scaffold(
124 body: Padding(
125 padding: const EdgeInsets.all(16.0),
126 child: Column(
127 crossAxisAlignment: CrossAxisAlignment.stretch,
128 children: [
129 Container(
130 height: 200,
131 child: Image.network(
132 'https://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
133 ),
134 Center(
135 child: const Text('Flutter on Back4app - GeoPoint',
136 style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
137 ),
138 SizedBox(
139 height: 8,
140 ),
141 Container(
142 height: 50,
143 child: ElevatedButton(
144 onPressed: doQueryPointer,
145 child: Text('Query A'),
146 style: ElevatedButton.styleFrom(primary: Colors.blue)),
147 ),
148 SizedBox(
149 height: 8,
150 ),
151 Container(
152 height: 50,
153 child: ElevatedButton(
154 onPressed: doQueryMatches,
155 child: Text('Query B'),
156 style: ElevatedButton.styleFrom(primary: Colors.blue)),
157 ),
158 SizedBox(
159 height: 8,
160 ),
161 Container(
162 height: 50,
163 child: ElevatedButton(
164 onPressed: doQueryRelatedTo,
165 child: Text('Query C'),
166 style: ElevatedButton.styleFrom(primary: Colors.blue)),
167 ),
168 SizedBox(
169 height: 8,
170 ),
171 Container(
172 height: 50,
173 child: ElevatedButton(
174 onPressed: doClearResults,
175 child: Text('Clear results'),
176 style: ElevatedButton.styleFrom(primary: Colors.blue)),
177 ),
178 SizedBox(
179 height: 8,
180 ),
181 Text(
182 'Result List: ${results.length}',
183 ),
184 Expanded(
185 child: ListView.builder(
186 itemCount: results.length,
187 itemBuilder: (context, index) {
188 final o = results[index];
189 return Container(
190 padding: const EdgeInsets.all(4),
191 decoration:
192 BoxDecoration(border: Border.all(color: Colors.black)),
193 child: Text('${o.toString()}'),
194 );
195 }),
196 )
197 ],
198 ),
199 ));
200 }
201 }애플리케이션 ID와 클라이언트 키 자격 증명을 찾으려면 Back4App 웹사이트로 이동하세요.
Back4app에서 프로젝트의 ApplicationId와 ClientKey 값을 사용하여 main.dart의 코드를 업데이트하세요.
- keyApplicationId = 앱 ID
- keyClientKey = 클라이언트 키
프로젝트를 실행하면 앱이 이미지와 같이 로드됩니다.

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