---
title: React에서 Parse 객체 관계 생성 및 쿼리 방법
slug: docs-kr/react/data-objects/relationships
description: React에서 Parse를 사용해 데이터 객체 관계를 생성하고 쿼리하는 방법을 배우세요. 일대일, 일대다, 다대다 관계를 구현합니다.
image: https://www.back4app.com/images/logo-social.png
createdAt: 2024-03-18T19:00:20.273Z
---

# 관계

## 소개

Parse를 사용하면 데이터 객체를 저장하고 그들 간의 관계를 설정할 수 있습니다. 이 동작을 모델링하기 위해, 어떤 <font color="#2166ae">`ParseObject` </font>도 다른 <font color="#2166ae">`ParseObject`</font>. 내부적으로 Parse 프레임워크는 참조된 객체를 한 곳에 저장하여 일관성을 유지합니다. 이는 복잡한 쿼리를 구축하고 실행할 때 추가적인 힘을 줄 수 있습니다. 세 가지 주요 관계 유형이 있습니다:

- <font color="#2166ae">`일대다`</font>, 하나의 객체가 여러 다른 객체와 관련될 수 있는 경우;
- <font color="#2166ae">`다대다`</font>, 여러 객체 간에 복잡한 관계를 생성할 수 있는 경우.
- <font color="#2166ae">`일대일`</font>, 두 객체 간에 직접적인 관계를 설정하고 오직 그들만을 위한 경우;

Parse에서 <font color="#2166ae">`일대다`</font> 관계를 생성하는 두 가지 방법이 있습니다:

- (추천) 첫 번째는 자식 클래스에서 <font color="#2166ae">`Parse 포인터`</font>를 사용하는 것으로, 생성 및 쿼리 시간에서 가장 빠릅니다. 이 가이드에서는 이를 사용할 것입니다.
- 두 번째는 부모 클래스에서 <font color="#2166ae">`배열`</font>을 사용하는 것으로, 크기에 따라 느린 쿼리 시간으로 이어질 수 있습니다. 이러한 성능 문제로 인해 우리는 포인터 예제만 사용할 것입니다.

Parse에서 <font color="#2166ae">`다대다`</font> 관계를 생성하는 세 가지 방법이 있습니다.

- (추천) 첫 번째는 <font color="#2166ae">`Parse Relations`</font>, 생성 및 쿼리 시간에서 가장 빠릅니다. 이 가이드에서 이를 사용할 것입니다.
- 두 번째는 <font color="#2166ae">`배열` </font>의 <font color="#2166ae">`Parse Pointers`</font>로, 크기에 따라 느린 쿼리 시간이 발생할 수 있습니다.
- 세 번째는 <font color="#2166ae">`JoinTable`</font>로, 고전 데이터베이스의 아이디어에서 출발합니다. 다대다 관계가 있을 때, 우리는 모든 <font color="#2166ae">`objectId` </font> 또는 <font color="#2166ae">`Pointer`</font>를 양쪽에서 결합하여 관계가 추적되는 새로운 별도의 테이블을 만듭니다.

이 가이드에서는 세 가지 주요 데이터 연관을 포함하는 React 도서 등록 애플리케이션을 구현합니다. Back4App과 React를 사용하여 데이터 관계를 생성하고 쿼리하는 방법을 배웁니다.

:::hint{type="success"}
언제든지 이 프로젝트에 접근하여 스타일과 전체 코드를 확인할 수 있습니다.

- [JavaScript 예제 저장소](https://github.com/templates-back4app/react-js-associations)
- [TypeScript 예제 저장소](https://github.com/templates-back4app/react-ts-associations)
:::

## 전제 조건

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

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

## 목표

React에서 Parse를 사용하여 현실적인 시나리오에서 데이터베이스 관계를 수행하고 시연하기 위해.

## 1 - Book 클래스 이해하기

이 가이드에서는 도서 등록 애플리케이션 예제를 사용할 것이므로, 먼저 이 데이터베이스에서 객체 관계가 어떻게 구성되어 있는지 이해해야 합니다. 사용하게 될 주요 객체 클래스는 <font color="#2166ae">`Book` </font>클래스로, 등록된 각 도서 항목을 저장합니다. 나머지 네 개의 객체 클래스는 다음과 같습니다:

- <font color="#2166ae">`출판사`</font>: 도서 출판사 이름, <font color="#2166ae">`Book`</font>과의 일대다 관계;
- <font color="#2166ae">`장르`</font>: 도서 장르, <font color="#2166ae">`Book`</font>, 과의 일대다 관계. 이 예제에서는 도서가 하나의 장르만 가질 수 있다고 가정합니다;
- <font color="#2166ae">`저자`</font>: 도서 저자, <font color="#2166ae">`Book`</font>, 과의 다대다 관계, 도서는 여러 저자를 가질 수 있고 저자도 여러 도서를 가질 수 있습니다;
- <font color="#2166ae">`ISDB`</font>: 도서 ISDB 식별 번호, <font color="#2166ae">`Book`</font>, 과의 일대일 관계, 이 번호는 각 도서에 대해 고유합니다.

다음은 이러한 데이터베이스 테이블의 시각적 표현입니다:

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

간단히 하기 위해, 각 객체 클래스가 문자열 유형의 <font color="#2166ae">`이름`</font> 속성만 가지고 있다고 가정하겠습니다 (<font color="#2166ae">`제목` </font>은 <font color="#2166ae">`책`</font>에 대해, 추가적인 관계 속성은 제외합니다.

### 단계 2 - 관계 생성

이 단계로 들어가기 전에 React 앱 예제를 복제하고 실행할 것을 권장합니다 ([JavaScript 예제 저장소](https://github.com/templates-back4app/react-js-associations), [TypeScript 예제 저장소](https://github.com/templates-back4app/react-ts-associations)). 이 애플리케이션은 등록된 책을 나열하는 화면과 새로운 책을 생성하는 화면의 두 가지 주요 화면을 가지고 있습니다. 책 등록 양식에는 다른 관련 객체에 대한 직접 링크와 책의 ISBD 값에 할당된 텍스트 입력 필드가 있으며, 이는 일대일 관계를 생성하는 데 사용됩니다.

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/1o5nsf4eGyxJRRflul2ZA_image.png" size="50" width="467" height="678" position="center" showCaption="false"}

이 양식을 제출할 때 호출되는 책 생성 방법을 살펴보겠습니다:

:::CodeblockTabs
JavaScript

```javascript
1   const createBook = async function () {
2     try {
3       // These values come from state variables linked to
4       // the screen form fields, retrieving the user choices
5       // as a complete Parse.Object, when applicable;
6       const bookTitleValue = bookTitle;
7       const bookISBDValue = bookISBD;
8       // For example, bookPublisher holds the value from
9       // RadioGroup field with its options being every
10      // Publisher parse object instance saved on server, which is
11      // queried on screen load via useEffect
12      const bookPublisherObject = bookPublisher;
13      const bookGenreObject = bookGenre;
14      // bookAuthors can be an array of Parse.Objects, since the book
15      // may have more than one Author
16      const bookAuthorsObjects = bookAuthors;
17
18      // Creates a new parse object instance
19      let Book = new Parse.Object('Book');
20
21      // Set data to parse object
22      // Simple title field
23      Book.set('title', bookTitleValue);
24
25      // ONE-TO-ONE (1:1)
26      // 1:1 relation, need to check for uniqueness of value before creating a new ISBD object
27      let isbdQuery = new Parse.Query('ISBD');
28      isbdQuery.equalTo('name', bookISBDValue);
29      let isbdQueryResult = await isbdQuery.first();
30      if (isbdQueryResult !== null && isbdQueryResult !== undefined) {
31        // If first returns a valid object instance, it means that there
32        // is at least one instance of ISBD with the informed value
33        alert(
34          'Error! There is already an ISBD instance with this value!',
35        );
36        return false;
37      } else {
38        // Create a new ISBD object instance to create a one-to-one relation on saving
39        let ISBD = new Parse.Object('ISBD');
40        ISBD.set('name', bookISBDValue);
41        ISBD = await ISBD.save();
42        // Set the new object to the new book object ISBD field
43        Book.set('isbd', ISBD);
44      }
45
46      // ONE-TO-MANY (1:N)
47      // One-to-many relations can be set in two ways:
48      // add direct object to field (Parse will convert to pointer on save)
49      Book.set('publisher', bookPublisherObject);
50      // or add pointer to field
51      Book.set('genre', bookGenreObject.toPointer());
52
53      // MANY-TO-MANY (N:N)
54      // Create a new relation so data can be added
55      let authorsRelation = Book.relation('authors');
56      // bookAuthorsObjects is an array of Parse.Objects,
57      // you can add to relation by adding the whole array or object by object
58      authorsRelation.add(bookAuthorsObjects);
59
60      // After setting the values, save it on the server
61      try {
62        await Book.save();
63        // Success
64        alert('Success!');
65        // Navigate back to home screen using react-router
66        history.push('/');
67        return true;
68      } catch (error) {
69        // Error can be caused by lack of Internet connection
70        alert(`Error! ${error.message}`);
71        return false;
72      }
73    } catch (error) {
74      // Error can be caused by lack of value selection
75      alert(
76        'Error! Make sure to select valid choices in Publisher, Genre and Author fields!',
77      );
78      return false;
79    }
80  };
```

TypeScript

```typescript
1   const createBook = async function (): Promise<boolean> {
2     try {
3       // These values come from state variables linked to
4       // the screen form fields, retrieving the user choices
5       // as a complete Parse.Object, when applicable;
6       const bookTitleValue: string = bookTitle;
7       const bookISBDValue: string = bookISBD;
8       // For example, bookPublisher holds the value from
9       // RadioButton.Group field with its options being every
10      // Publisher parse object instance saved on server, which is
11      // queried on screen load via useEffect
12      const bookPublisherObject: Parse.Object = bookPublisher;
13      const bookGenreObject: Parse.Object = bookGenre;
14      // bookAuthors can be an array of Parse.Objects, since the book
15      // may have more than one Author
16      const bookAuthorsObjects: [Parse.Object] = bookAuthors;
17
18      // Creates a new parse object instance
19      let Book: Parse.Object = new Parse.Object('Book');
20
21      // Set data to parse object
22      // Simple title field
23      Book.set('title', bookTitleValue);
24
25      // ONE-TO-ONE (1:1)
26      // 1:1 relation, need to check for uniqueness of value before creating a new ISBD object
27      let isbdQuery: Parse.Query = new Parse.Query('ISBD');
28      isbdQuery.equalTo('name', bookISBDValue);
29      let isbdQueryResult: Parse.Object = await isbdQuery.first();
30      if (isbdQueryResult !== null && isbdQueryResult !== undefined) {
31        // If first returns a valid object instance, it means that there
32        // is at least one instance of ISBD with the informed value
33        alert(
34          'Error! There is already an ISBD instance with this value!',
35        );
36        return false;
37      } else {
38        // Create a new ISBD object instance to create a one-to-one relation on saving
39        let ISBD: Parse.Object = new Parse.Object('ISBD');
40        ISBD.set('name', bookISBDValue);
41        ISBD = await ISBD.save();
42        // Set the new object to the new book object ISBD field
43        Book.set('isbd', ISBD);
44      }
45
46      // ONE-TO-MANY (1:N)
47      // One-to-many relations can be set in two ways:
48      // add direct object to field (Parse will convert to pointer on save)
49      Book.set('publisher', bookPublisherObject);
50      // or add pointer to field
51      Book.set('genre', bookGenreObject.toPointer());
52
53      // MANY-TO-MANY (N:N)
54      // Create a new relation so data can be added
55      let authorsRelation = Book.relation('authors');
56      // bookAuthorsObjects is an array of Parse.Objects,
57      // you can add to relation by adding the whole array or object by object
58      authorsRelation.add(bookAuthorsObjects);
59
60      // After setting the values, save it on the server
61      try {
62        await Book.save();
63        // Success
64        alert('Success!');
65        // Navigate back to home screen using react-router
66        history.push('/');
67        return true;
68      } catch (error) {
69        // Error can be caused by lack of Internet connection
70        alert(`Error! ${error.message}`);
71        return false;
72      }
73    } catch (error) {
74      // Error can be caused by lack of value selection
75      alert(
76        'Error! Make sure to select valid choices in Publisher, Genre and Author fields!',
77      );
78      return false;
79    }
80  };
```
:::

이제 세 가지 유형의 연관 관계가 <font color="#2166ae">`책` </font> 객체를 생성할 때 어떻게 이루어지는지 따로 살펴보겠습니다.

### 일대다 관계

어떻게 <font color="#2166ae">`bookPublisherObject` </font>와 <font color="#2166ae">`bookGenreObject`</font>가 새로운 책 <font color="#2166ae">`Parse.Object`</font> 인스턴스에 설정되는지 주목하세요. Parse에서 일대다 관계를 생성하는 것이 얼마나 간단한지 보세요: 대상 객체 인스턴스를 할당하거나 <font color="#2166ae">`Parse.Object.set` </font> 메서드를 사용하여 포인터를 설정할 수 있습니다. 이 메서드는 두 개의 인수를 받습니다: 필드 이름과 설정할 값입니다. Parse는 포인터 데이터 유형 열을 생성하고 대시보드에서 빠른 액세스를 위한 직접 링크를 생성합니다.

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

### 다대다 관계

어떻게 <font color="#2166ae">`bookAuthorsObjects` </font>가 새로운 책 <font color="#2166ae">`Parse.Object`</font> 인스턴스에 설정되는지 보세요. 다대다 관계를 생성하려면 먼저 새로운 <font color="#2166ae">`Parse.Object.relation`</font>을 생성한 다음 관련 객체를 하나씩 추가하거나 <font color="#2166ae">`Parse.Object`</font>의 배열을 전달하여 추가해야 합니다. <font color="#2166ae">`Parse.Object.relation.add`</font> 메서드를 사용합니다. Parse는 관계 유형 열을 생성하고 데이터베이스에 관계형 테이블을 생성합니다. 또한 Parse는 대시보드의 필드 열에서 이 새로운 테이블에 쉽게 접근할 수 있는 링크를 생성합니다.

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

### 일대일 관계

다음과 같이 <font color="#2166ae">`bookISBDValue` </font>가 새로운 책 <font color="#2166ae">`Parse.Objec`</font>t 인스턴스로 설정되는지 확인하세요. Parse에서 일대일 및 일대다 관계를 생성하고 저장하는 과정은 유사하며, <font color="#2166ae">`Parse.Object` </font> 인스턴스를 <font color="#2166ae">`Parse.Object.set`</font> 메서드를 사용하여 인수로 전달합니다. 이 메서드는 필드 이름과 설정할 값을 인수로 받습니다.

여기서 주의할 점은 저장하기 전에 데이터베이스에 정보가 제공된 ISBD ID 문자열 값을 포함하는 <font color="#2166ae">`ISBD`</font> 객체가 없고, 이미 관련된 <font color="#2166ae">`Book` </font> 객체가 없도록 해야 한다는 것입니다. 두 번째 부분은 항상 참이 될 것입니다. 왜냐하면 매번 새로운 <font color="#2166ae">`Book` </font> 객체를 생성하고 있기 때문입니다. <font color="#2166ae">`ISBD` </font>의 고유성을 보장하기 위해 다음과 같은 하이라이트된 쿼리를 사용할 수 있습니다:

:::CodeblockTabs
JavaScript

```javascript
1   let isbdQuery = new Parse.Query('ISBD');
2   isbdQuery.equalTo('name', bookISBDValue);
3   let isbdQueryResult = await isbdQuery.first();
4   if (isbdQueryResult !== null && isbdQueryResult !== undefined) {
5     // If first returns a valid object instance, it means that there
6     // is at least one instance of ISBD with the informed value
7     alert(
8       'Error! There is already an ISBD instance with this value!',
9     );
10    return false;
11  }
```

TypeScript

```typescript
1   let isbdQuery: Parse.Query = new Parse.Query('ISBD');
2   isbdQuery.equalTo('name', bookISBDValue);
3   let isbdQueryResult: Parse.Object = await isbdQuery.first();
4   if (isbdQueryResult !== null && isbdQueryResult !== undefined) {
5     // If first returns a valid object instance, it means that there
6     // is at least one instance of ISBD with the informed value
7     alert(
8       'Error! There is already an ISBD instance with this value!',
9     );
10    return false;
11  }
```
:::

객체를 성공적으로 저장한 후, Parse는 포인터 데이터 유형 열과 대시보드에 직접 링크를 생성합니다.

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

## 3 - 관계 쿼리하기

관련 객체를 쿼리하는 것은 Parse에서 대부분 처리되기 때문에 매우 간단합니다. 책 등록 목록 화면의 쿼리 기능을 살펴보고, 각 관계 유형이 어떻게 쿼리되는지 강조하겠습니다:

:::CodeblockTabs
JavaScript

```javascript
1   const queryBooks = async function () {
2     // These values come from state variables linked to
3     // the screen query RadioButton.Group fields, with its options being every
4     // parse object instance saved on server from the referred class, which is
5     // queried on screen load via useEffect; these variables retrievie the user choices
6     // as a complete Parse.Object;
7     const queryPublisherValue = queryPublisher;
8     const queryGenreValue = queryGenre;
9     const queryAuthorValue = queryAuthor;
10    const queryIsbdValue = queryIsbd;
11
12    // Reading parse objects is done by using Parse.Query
13    const parseQuery = new Parse.Query('Book');
14
15    // One-to-many queries
16    if (queryPublisherValue !== '') {
17      parseQuery.equalTo('publisher', queryPublisherValue);
18    }
19    if (queryGenreValue !== '') {
20      parseQuery.equalTo('genre', queryGenreValue);
21    }
22
23    // One-to-one query
24    if (queryIsbdValue !== '') {
25      parseQuery.equalTo('isbd', queryIsbdValue);
26    }
27
28    // Many-to-many query
29    // In this case, we need to retrieve books related to the chosen author
30    if (queryAuthorValue !== '') {
31      parseQuery.equalTo('authors', queryAuthorValue);
32    }
33
34    try {
35      let books = await parseQuery.find();
36      // Many-to-many objects retrieval
37      // In this example we need to get every related author Parse.Object
38      // and add it to our query result objects
39      for (let book of books) {
40        // This query is done by creating a relation and querying it
41        let bookAuthorsRelation = book.relation('authors');
42        book.authorsObjects = await bookAuthorsRelation.query().find();
43      }
44      setQueriedBooks(books);
45      return true;
46    } catch (error) {
47      // Error can be caused by lack of Internet connection
48      alert(`Error! ${error.message}`);
49      return false;
50    }
51  };
```

TypeScript

```typescript
1   const queryBooks = async function (): Promise<boolean> {
2     // These values come from state variables linked to
3     // the screen query RadioButton.Group fields, with its options being every
4     // parse object instance saved on server from the referred class, which is
5     // queried on screen load via useEffect; these variables retrievie the user choices
6     // as a complete Parse.Object;
7     const queryPublisherValue: Parse.Object = queryPublisher;
8     const queryGenreValue: Parse.Object = queryGenre;
9     const queryAuthorValue: Parse.Object = queryAuthor;
10    const queryIsbdValue: Parse.Object = queryIsbd;
11
12    // Reading parse objects is done by using Parse.Query
13    const parseQuery: Parse.Query = new Parse.Query('Book');
14
15    // One-to-many queries
16    if (queryPublisherValue !== '') {
17      parseQuery.equalTo('publisher', queryPublisherValue);
18    }
19    if (queryGenreValue !== '') {
20      parseQuery.equalTo('genre', queryGenreValue);
21    }
22
23    // One-to-one query
24    if (queryIsbdValue !== '') {
25      parseQuery.equalTo('isbd', queryIsbdValue);
26    }
27
28    // Many-to-many query
29    // In this case, we need to retrieve books related to the chosen author
30    if (queryAuthorValue !== '') {
31      parseQuery.equalTo('authors', queryAuthorValue);
32    }
33
34    try {
35      let books: [Parse.Object] = await parseQuery.find();
36      // Many-to-many objects retrieval
37      // In this example we need to get every related author Parse.Object
38      // and add it to our query result objects
39      for (let book of books) {
40        // This query is done by creating a relation and querying it
41        let bookAuthorsRelation = book.relation('authors');
42        book.authorsObjects = await bookAuthorsRelation.query().find();
43      }
44      setQueriedBooks(books);
45      return true;
46    } catch (error) {
47      // Error can be caused by lack of Internet connection
48      alert(`Error! ${error.message}`);
49      return false;
50    }
51  };
```
:::

### 일대다 쿼리

특정 출판사나 장르와 관련된 책을 쿼리하려면 <font color="#2166ae">`Parse.Query.equalT`</font> 메서드를 호출하고 <font color="#2166ae">`Parse.Object`</font> 인스턴스를 매개변수로 전달해야 합니다. 쿼리 후, Parse는 결과 객체 내부에 일대다 관계 필드의 완전한 인스턴스를 저장합니다. 이러한 객체 인스턴스에서 데이터를 검색하고 표시하려면 다음과 같이 <font color="#2166ae">`Parse.Object.get`</font> 메서드를 체이닝할 수 있습니다: <font color="#2166ae">`BookParseObject.get(('publisher').get('name').`</font>

### 다대다 쿼리

특정 저자와 관련된 책을 쿼리하기 위해서는 쿼리가 <font color="#2166ae">`Parse.Query.equalTo`</font> 메서드만 사용합니다. 그러나 쿼리 후, Parse는 <font color="#2166ae">`Parse.Object`</font> 인스턴스를 다대다 관계 필드에서 저장하지 않고, 관련 클래스 이름에 대한 참조만 저장합니다. 예를 들어 <font color="#2166ae">`{"__type": "Relation", "className": "Author"}`</font>. 이러한 객체 인스턴스에서 데이터를 검색하고 표시하려면 관계를 생성하고 다시 쿼리하여 결과를 자신의 객체 배열에 저장해야 합니다.

### 일대일 쿼리

이전과 마찬가지로 특정 ISBD와 관련된 책을 쿼리하려면 <font color="#2166ae">`Parse.Query.equalTo`</font> 메서드를 사용하여 <font color="#2166ae">`Parse.Object`</font> 인스턴스를 매개변수로 전달해야 합니다. 쿼리 후, Parse는 결과 객체 내에 일대일 관계 필드의 전체 인스턴스를 저장하며, 이는 일대다 관계 필드와 동일한 방식으로 수행됩니다.

## 결론

이 가이드의 끝에서, 여러분은 React에서 Parse의 관계를 생성하고 쿼리하는 방법을 배웠습니다. 다음 가이드에서는 사용자 등록 방법을 보여드리겠습니다.
