React에서 Parse를 사용한 고효율 지리 쿼리 수행 기술
리액트 지리정보를 사용하여 Parse로 지리 쿼리 수행하기
소개
이 가이드에서는 리액트 지리정보를 사용하여 Parse에서 지리 쿼리를 수행합니다. 이러한 쿼리를 사용하여 리액트 컴포넌트를 구현하고 Back4App과 리액트를 사용하여 현실적인 데이터를 설정하고 쿼리하는 방법을 배웁니다.
전제 조건
이 튜토리얼을 완료하려면 다음이 필요합니다:
- 이 가이드의 예제 프로젝트를 실행하려면 Ant Design 라이브러리를 설정해야 합니다.
목표
Back4App에 저장된 GeoPoints와 리액트 지리정보를 사용하여 지리 쿼리를 수행합니다.
1 - Parse.Query 클래스 이해하기
모든 Parse 쿼리 작업은 Parse.Query 객체 유형을 사용하며, 이는 앱 전반에 걸쳐 데이터베이스에서 특정 데이터를 검색하는 데 도움이 됩니다. Parse.Query는 검색 메서드(예: Parse.Query.find 또는 Parse.Query.get 호출 후에만 해결되므로, 쿼리를 설정하고 실제로 호출되기 전에 여러 수정자를 연결할 수 있습니다.
새로운 Parse.Query를 생성하려면, 원하는 Parse.Object 하위 클래스를 매개변수로 전달해야 합니다. 아래에서 볼 수 있는 예제 쿼리에서는 가상의 Profile 하위 클래스가 쿼리되고 있습니다.
1 // This will create your query
2 let parseQuery = new Parse.Query("Profile");
3 // The query will resolve only after calling this method
4 let queryResult = await parseQuery.find();다음에 대해 더 읽을 수 있습니다 Parse.Query 클래스 공식 문서에서 여기를 참조하세요.
2 - Back4App에 데이터 저장하기
우리의 쿼리 대상이 될 City 클래스를 생성해 봅시다. Parse JS 콘솔에서는 JavaScript 코드를 직접 실행하여 JS SDK 명령을 사용하여 애플리케이션 데이터베이스 내용을 쿼리하고 업데이트할 수 있습니다. 아래 코드를 JS 콘솔에서 실행하고 Back4App에 데이터를 삽입하세요.
대시보드에서 JS 콘솔이 어떻게 보이는지 확인해 보세요:

계속해서 다음 예제 내용을 사용하여 City 클래스를 생성하세요:
1 // Add City objects and create table
2 // Note how GeoPoints are created, passing latitude and longitude as arguments
3 // Montevideo
4 var City = new Parse.Object('City');
5 City.set('name', 'Montevideo - Uruguay');
6 City.set('location', new Parse.GeoPoint(-34.85553195363169, -56.207280375137955));
7 await City.save();
8
9 // Brasília
10 City = new Parse.Object('City');
11 City.set('name', 'Brasília - Brazil');
12 City.set('location', new Parse.GeoPoint(-15.79485821477289, -47.88391074690196));
13 await City.save();
14
15 // Bogotá
16 City = new Parse.Object('City');
17 City.set('name', 'Bogotá - Colombia');
18 City.set('location', new Parse.GeoPoint(4.69139880891712, -74.06936691331047));
19 await City.save();
20
21 // Mexico City
22 City = new Parse.Object('City');
23 City.set('name', 'Mexico City - Mexico');
24 City.set('location', new Parse.GeoPoint(19.400977162618933, -99.13311378164776));
25 await City.save();
26
27 // Washington, D.C.
28 City = new Parse.Object('City');
29 City.set('name', 'Washington, D.C. - USA');
30 City.set('location', new Parse.GeoPoint(38.930727220189944, -77.04626261880388));
31 await City.save();
32
33 // Ottawa
34 City = new Parse.Object('City');
35 City.set('name', 'Ottawa - Canada');
36 City.set('location', new Parse.GeoPoint(45.41102167733425, -75.695414598736));
37 await City.save();
38
39 console.log('Success!');3 - 데이터 쿼리하기
이제 데이터가 채워진 클래스를 가지고 있으므로, GeoPoint 쿼리를 수행할 수 있습니다. 자, 이제 도시 결과를 자메이카 킹스턴(위도 18.01808695059913, 경도 -76.79894232253473)에서 가장 가까운 순서로 정렬해 보겠습니다. 다음은 Parse.Query.near 메서드를 사용합니다:
1 // Create your query
2 let parseQuery = new Parse.Query('City');
3
4 // Create our GeoPoint for the query
5 let kingstonGeoPoint = new Parse.GeoPoint(18.018086950599134, -76.79894232253473);
6
7 // `near` will order results based on distance between the GeoPoint type field from the class and the GeoPoint argument
8 parseQuery.near('location', kingstonGeoPoint);
9
10 // The query will resolve only after calling this method, retrieving
11 // an array of `Parse.Objects`
12 let queryResults = await parseQuery.find();
13
14 // Let's show the results
15 for (let result of queryResults) {
16 // You access `Parse.Objects` attributes by using `.get`
17 console.log(result.get('name'));
18 };이제 Parse.Query.withinKilometers, 메서드를 사용하여 최대 거리 내에 위치한 모든 결과를 검색해 보겠습니다. 킹스턴을 다시 참조로 사용하고 거리 제한은 3000km로 설정하겠습니다.
1 // Create your query
2 let parseQuery = new Parse.Query('City');
3
4 // Create our GeoPoint for the query
5 let kingstonGeoPoint = new Parse.GeoPoint(18.018086950599134, -76.79894232253473);
6
7 // You can also use `withinMiles` and `withinRadians` the same way,
8 // but with different measuring unities
9 parseQuery.withinKilometers('location', kingstonGeoPoint, 3000);
10
11 // The query will resolve only after calling this method, retrieving
12 // an array of `Parse.Objects`
13 let queryResults = await parseQuery.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 };또 다른 유용한 쿼리 방법은 Parse.Query.withinPolygon, 이는 지정된 다각형 내에 있는 GeoPoint 필드 값을 가진 결과를 쿼리합니다. 이 다각형은 최소 세 개의 GeoPoints로 구성된 배열로 이루어져 있습니다. 다각형 경로가 열려 있으면 Parse가 마지막 점과 첫 번째 점을 연결하여 자동으로 닫습니다.
이 예제에서는 대략 남미 대륙을 포함하는 간단한 다각형을 사용하며, 이는 바다에 있는 5개의 먼 GeoPoints로 구성됩니다.
1 // Create your query
2 let parseQuery = new Parse.Query('City');
3
4 // Create our GeoPoint polygon for the query
5 let geoPoint1 = new Parse.GeoPoint(15.822238344514378, -72.42845934415942);
6 let geoPoint2 = new Parse.GeoPoint(-0.7433770196268968, -97.44765968406668);
7 let geoPoint3 = new Parse.GeoPoint(-59.997149373299166, -76.52969196322749);
8 let geoPoint4 = new Parse.GeoPoint(-9.488786415007201, -18.346101586021952);
9 let geoPoint5 = new Parse.GeoPoint(15.414859532811047, -60.00625459569375);
10
11 // Note that the polygon is merely an array of GeoPoint objects and that the first and last are not connected, so Parse connects them for you
12 parseQuery.withinPolygon('location', [geoPoint1, geoPoint2, geoPoint3, geoPoint4, geoPoint5]);
13
14 // The query will resolve only after calling this method, retrieving
15 // an array of `Parse.Objects`
16 let queryResults = await parseQuery.find();
17
18 // Let's show the results
19 for (let result of queryResults) {
20 // You access `Parse.Objects` attributes by using `.get`
21 console.log(result.get('name'));
22 };4 - React 컴포넌트에서 쿼리하기
이제 React의 컴포넌트 내에서 예제 쿼리를 사용해 보겠습니다. 결과를 보여주는 목록과 쿼리를 호출하는 3개의 버튼이 있는 간단한 인터페이스를 가지고 있습니다. 이 컴포넌트는 또한 navigator.geolocation JavaScript API를 사용하여 장치의 현재 위치를 가져옵니다. 따라서 사용자가 브라우저에서 위치 접근을 비활성화하지 않는 한 쿼리는 실제 데이터를 사용하게 됩니다.
컴포넌트 코드가 이렇게 배치되어 있습니다. 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 type LocationObject = {latitude: number, longitude: number};
8
9 export const QueryGeo: FC<{}> = (): ReactElement => {
10 // State variable
11 const [queryResults, setQueryResults] = useState<Parse.Object[]>();
12
13 const doQueryNear = async function (): Promise<boolean> {
14 // Check if geolocation API is available in the browser
15 if ("geolocation" in navigator) {
16 // Get current location and create the GeoPoint for the query
17 navigator.geolocation.getCurrentPosition(async function (position) {
18 const currentPosition: LocationObject = { latitude: position.coords.latitude, longitude: position.coords.longitude };
19 // Create our GeoPoint
20 let currentLocationGeoPoint: Parse.GeoPoint = new Parse.GeoPoint(
21 currentPosition.latitude,
22 currentPosition.longitude,
23 );
24
25 // Create our query
26 let parseQuery: Parse.Query = new Parse.Query('City');
27
28 // `near` will order results based on distance between the GeoPoint type field from the class and the GeoPoint argument
29 parseQuery.near('location', currentLocationGeoPoint);
30
31 try {
32 let results: Parse.Object[] = await parseQuery.find();
33 // Set query results to state variable
34 setQueryResults(results);
35 return true;
36 } catch (error) {
37 // Error can be caused by lack of Internet connection
38 alert(`Error! ${error.message}`);
39 }
40 }, function (_error: any) {
41 alert("You need to allow geolocation to retrieve your location on the browser to test this. If you are on an Apple OS, enable it on your system preferences.");
42 });
43 } else {
44 alert("Geolocation services are not supported by your browser.");
45 }
46 return false;
47 };
48
49 const doQueryWithinKilometers = async function (): Promise<boolean> {
50 // Check if geolocation API is available in the browser
51 if ("geolocation" in navigator) {
52 // Get current location and create the GeoPoint for the query
53 navigator.geolocation.getCurrentPosition(async function (position) {
54 const currentPosition: LocationObject = { latitude: position.coords.latitude, longitude: position.coords.longitude };
55 // Create our GeoPoint
56 let currentLocationGeoPoint: Parse.GeoPoint = new Parse.GeoPoint(
57 currentPosition.latitude,
58 currentPosition.longitude,
59 );
60
61 // Create our query
62 let parseQuery: Parse.Query = new Parse.Query('City');
63
64 // You can also use `withinMiles` and `withinRadians` the same way,
65 // but with different measuring unities
66 parseQuery.withinKilometers('location', currentLocationGeoPoint, 3000);
67
68 try {
69 let results: Parse.Object[] = await parseQuery.find();
70 // Set query results to state variable
71 setQueryResults(results);
72 } catch (error) {
73 // Error can be caused by lack of Internet connection
74 alert(`Error! ${error.message}`);
75 }
76 }, function (_error: any) {
77 alert("You need to allow geolocation to retrieve your location on the browser to test this. If you are on an Apple OS, enable it on your system preferences.");
78 });
79 } else {
80 alert("Geolocation services are not supported by your browser.");
81 }
82 return false;
83 };
84
85 const doQueryWithinPolygon = async function (): Promise<boolean> {
86 // Create our GeoPoint polygon points
87 // In React TypeScript types ('@types/parse'), Parse.Query.withinPolygon expects
88 // an array of number arrays, so you need to change the declarations
89 let geoPoint1: number[] = [
90 -7.242.845.934.415.940,00
91 15.822.238.344.514.300,00
92 ];
93 let geoPoint2: number[] = [
94 -9.744.765.968.406.660,00
95 -0.7433770196268968,
96 ];
97 let geoPoint3: number[] = [
98 -7.652.969.196.322.740,00
99 -59.997.149.373.299.100,00
100 ];
101 let geoPoint4: number[] = [
102 -18.346.101.586.021.900,00
103 -9.488.786.415.007.200,00
104 ];
105 let geoPoint5: number[] = [
106 -6.000.625.459.569.370,00
107 15.414.859.532.811.000,00
108 ];
109
110 // Create our query
111 let parseQuery: Parse.Query = new Parse.Query('City');
112
113 // Note that the polygon is merely an array of GeoPoint objects and that the first and
114 // last are not connected, so Parse connects them for you
115 parseQuery.withinPolygon('location', [
116 geoPoint1,
117 geoPoint2,
118 geoPoint3,
119 geoPoint4,
120 geoPoint5,
121 ]);
122
123 try {
124 let results: Parse.Object[] = await parseQuery.find();
125 // Set query results to state variable
126 setQueryResults(results);
127 } catch (error) {
128 // Error can be caused by lack of Internet connection
129 alert(`Error! ${error}`);
130 return false;
131 }
132 return true;
133 };
134
135 const clearQueryResults = async function (): Promise<boolean> {
136 setQueryResults(undefined);
137 return true;
138 };
139
140 return (
141 <div>
142 <div className="header">
143 <img
144 className="header_logo"
145 alt="Back4App Logo"
146 src={
147 'https://blog.back4app.com/wp-content/uploads/2019/05/back4app-white-logo-500px.png'
148 }
149 />
150 <p className="header_text_bold">{'React on Back4App'}</p>
151 <p className="header_text">{'GeoPoint Queries'}</p>
152 </div>
153 <div className="container">
154 <div className="flex_between">
155 <h2 className="heading">{'Query List'}</h2>
156 <div className="flex">
157 <Button
158 onClick={() => doQueryNear()}
159 type="primary"
160 className="heading_button"
161 color={'#208AEC'}
162 icon={<SearchOutlined />}
163 >
164 QUERY NEAR
165 </Button>
166 <Button
167 onClick={() => doQueryWithinKilometers()}
168 type="primary"
169 className="heading_button"
170 color={'#208AEC'}
171 icon={<SearchOutlined />}
172 >
173 QUERY WITHIN KM
174 </Button>
175 <Button
176 onClick={() => doQueryWithinPolygon()}
177 type="primary"
178 className="heading_button"
179 color={'#208AEC'}
180 icon={<SearchOutlined />}
181 >
182 QUERY WITHIN POLYGON
183 </Button>
184 <Button
185 onClick={() => clearQueryResults()}
186 type="primary"
187 className="heading_button"
188 color={'#208AEC'}
189 icon={<CloseOutlined />}
190 >
191 CLEAR RESULTS
192 </Button>
193 </div>
194 </div>
195 <Divider />
196 <div className="flex_between">
197 <div className="flex_child">
198 {/* Query list */}
199 {queryResults !== undefined &&
200 queryResults.map((result: Parse.Object, index: number) => (
201 <div className="list_item" key={`${index}`}>
202 <p className="list_item_title">{`${result.get('name')}`}</p>
203 </div>
204 ))}
205 {queryResults !== undefined &&
206 queryResults.length <= 0 ? (
207 <p>{'No results here!'}</p>
208 ) : null}
209 </div>
210 </div>
211 </div>
212 </div>
213 );
214 };이 클래스를 추가하여 App.css 파일을 완전히 렌더링하세요:
1 html {
2 box-sizing: border-box;
3 outline: none;
4 overflow: auto;
5 }
6
7 *,
8 *:before,
9 *:after {
10 margin: 0;
11 padding: 0;
12 box-sizing: inherit;
13 }
14
15 h1,
16 h2,
17 h3,
18 h4,
19 h5,
20 h6 {
21 margin: 0;
22 font-weight: bold;
23 }
24
25 p {
26 margin: 0;
27 }
28
29 body {
30 margin: 0;
31 background-color: #fff;
32 }
33
34 .container {
35 width: 100%;
36 max-width: 900px;
37 margin: auto;
38 padding: 20px 0;
39 text-align: left;
40 }
41
42 .header {
43 align-items: center;
44 padding: 25px 0;
45 background-color: #208AEC;
46 }
47
48 .header_logo {
49 height: 55px;
50 margin-bottom: 20px;
51 object-fit: contain;
52 }
53
54 .header_text_bold {
55 margin-bottom: 3px;
56 color: rgba(255, 255, 255, 0.9);
57 font-size: 16px;
58 font-weight: bold;
59 }
60
61 .header_text {
62 color: rgba(255, 255, 255, 0.9);
63 font-size: 15px;
64 }
65
66 .heading {
67 font-size: 22px;
68 }
69
70 .flex {
71 display: flex;
72 }
73
74 .flex_between {
75 display: flex;
76 justify-content: space-between;
77 }
78
79 .flex_child {
80 flex: 0 0 45%;
81 }
82
83 .heading_button {
84 margin-left: 12px;
85 }
86
87 .list_item {
88 padding-bottom: 15px;
89 margin-bottom: 15px;
90 border-bottom: 1px solid rgba(0,0,0,0.06);
91 text-align: left;
92 }
93
94 .list_item_title {
95 color: rgba(0,0,0,0.87);
96 font-size: 17px;
97 }렌더링 후 쿼리 함수 중 하나를 쿼리한 후 컴포넌트가 어떻게 보여야 하는지입니다:

결론
이 가이드의 끝에서, Parse에서 GeoPoint 데이터 쿼리가 어떻게 작동하는지와 React 애플리케이션에서 Back4App에서 이를 수행하는 방법을 배웠습니다. 다음 가이드에서는 Parse에서 사용자를 생성하고 관리하는 방법을 확인할 것입니다.