---
title: 안드로이드용 Parse에서 1:N 및 N:N 관계 생성 및 조회
slug: docs-kr/android/data-objects/relationships
description: 안드로이드에서 Parse를 사용해 1:다 및 다:다 데이터 객체 관계를 생성하고 쿼리하는 방법을 알아보세요.
image: https://www.back4app.com/images/logo-social.png
createdAt: 2024-03-13T13:16:59.042Z
---

# 안드로이드의 관계

## 소개

Parse를 사용하면 데이터 객체를 저장하고 그들 간의 관계를 설정할 수 있습니다. 이러한 동작을 모델링하기 위해, 다른 <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">`포인터`</font>를 사용하는 것입니다. <font color="#2166ae">`자식 클래스,`</font> 이는 생성 및 쿼리 시간에서 가장 빠릅니다.
- 두 번째는 <font color="#2166ae">`배열`</font>을 사용하는 것입니다. <font color="#2166ae">`포인터`</font>를 부모 클래스에서 사용하는데, 이는 크기에 따라 느린 쿼리 시간으로 이어질 수 있습니다. 이러한 성능 문제로 인해 우리는 포인터 예제만 사용할 것입니다.

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

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

이 튜토리얼은 Android Studio 4.1.1에서 생성된 기본 앱을 사용합니다. <font color="#2166ae">`buildToolsVersion=30.0.2`</font> , <font color="#2166ae">`Compile SDK Version = 30.0.2`</font> 및 <font color="#2166ae">`targetSdkVersion 30`</font>

:::hint{type="success"}
언제든지 이 튜토리얼로 구축된 전체 Android 프로젝트에 접근할 수 있습니다. 우리의 Github 저장소에서 확인하세요.

- [코틀린 예제 저장소](https://github.com/templates-back4app/Android-Parse-Sdk-Kotlin)
- [자바 예제 저장소](https://github.com/templates-back4app/Android-Parse-Sdk-Java)
:::

## 목표

우리의 목표는 실용적인 도서 앱을 만들어 Parse 관계를 이해하는 것입니다.

우리가 달성할 목표의 미리보기입니다:

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/4GItAcqwXy8pwNIso5U51_image.png" size="30" width="346" height="750" position="center" showCaption="false"}

## 전제 조건

:::hint{type="info"}
**이 튜토리얼을 완료하기 위해 우리는 다음이 필요합니다:**

- [안드로이드 스튜디오](https://developer.android.com/studio/index.html)
- Back4App에서 생성된 앱.
  - **참고:&#x20;**&#x20;[새 파스 앱 튜토리얼](https://www.back4app.com/docs/get-started/new-parse-app)을 따라 Back4App에서 파스 앱을 만드는 방법을 배우세요.
- Back4App에 연결된 안드로이드 앱.
  - **참고:&#x20;**&#x20;[파스 SDK 설치 튜토리얼](https://www.back4app.com/docs/android/parse-android-sdk)을 따라 Back4App에 연결된 안드로이드 스튜디오 프로젝트를 만드세요.
- 안드로이드 4.1 (젤리빈) 이상에서 실행되는 장치 (또는[ 가상 장치](https://developer.android.com/studio/run/managing-avds.html))
:::

## 책 앱 이해하기

당신이 사용할 주요 객체 클래스는 <font color="#2166ae">`Book`</font> 클래스이며, 각 책 항목을 등록에 저장합니다. 또한, 다음은 다른 세 개의 객체 클래스입니다:

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

이 데이터 모델의 시각적 표현:

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

## 시작해봅시다!

:::hint{type="info"}
다음 단계에 앞서, <font color="#2166ae">`Back4App`</font>을(를) 우리 애플리케이션에 연결해야 합니다. <font color="#2166ae">`appId`</font>와 <font color="#2166ae">`clientKey`</font>를 <font color="#2166ae">`Back4App`</font>에서 <font color="#2166ae">`string.xml`</font> 파일에 저장한 후, <font color="#2166ae">`Parse`</font>를 우리 <font color="#2166ae">`App.java`</font> 또는 <font color="#2166ae">`App.kt`</font> 파일에서 초기화하세요.
[새 Parse 앱 튜토리얼](https://www.back4app.com/docs/android/parse-android-sdk)을(를) 따라가세요. <font color="#2166ae">`Parse`</font>를(을) 앱에 초기화하는 방법을 모른다면.

또는 위에 공유한 깃허브 링크에서 프로젝트를 다운로드하고 <font color="#2166ae">`appId`</font>와 <font color="#2166ae">`clientKey`</font> 부분만 수정할 수 있습니다.
:::

## 1 - 책의 관련 객체 저장 및 목록화

이 단계에서는 <font color="#2166ae">`장르`</font>, <font color="#2166ae">`출판사`</font> 및 <font color="#2166ae">`저자`</font> 클래스가 <font color="#2166ae">`책`</font> 클래스와 관련된 방법을 살펴보겠습니다.

### 1.1 - 장르 저장 및 목록화

다음 스니펫을 사용하여 <font color="#2166ae">`장르`</font>를 등록할 수 있습니다.

:::CodeblockTabs
```java
1   private void addGenre(String name) {
2     //We are taking this name parameter from the input.
3     progressDialog.show();
4     ParseObject parseObject = new ParseObject("Genre");
5     parseObject.put("name", name);
6     parseObject.saveInBackground(e -> {
7       progressDialog.dismiss();
8       if (e == null) {
9         getGenres();
10        inputGenre.setText("");
11        Toast.makeText(this, "Genre saved successfully", Toast.LENGTH_SHORT).show();
12      } else { 
13      Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
14      }
15    });
16  }
```

```kotlin
1   private fun addGenre(name: String) {
2     //We are taking this name parameter from the input.
3     progressDialog.show()
4     val parseObject = ParseObject("Genre")
5     parseObject.put("name", name)
6     parseObject.saveInBackground {
7       progressDialog.dismiss()
8       if (it == null) {
9         getGenres()
10        inputGenre.setText("")
11        Toast.makeText(this, "Genre saved successfully", Toast.LENGTH_SHORT).show()
12      } else {
13          Toast.makeText(this, it.localizedMessage, Toast.LENGTH_SHORT).show()
14      }
15    }
16  }
```
:::

우리는 다음 스니펫을 사용하여<font color="#2166ae">`장르`</font>를 등록할 수 있습니다.

:::CodeblockTabs
```java
1   private void getGenres() {
2     progressDialog.show();
3     ParseQuery<ParseObject> query = new ParseQuery<>("Genre");
4     query.findInBackground((objects, e) -> {
5       progressDialog.dismiss();
6       List<ParseObjectModel> list = new ArrayList<>();
7       for (ParseObject parseObject : objects) {
8         list.add(new ParseObjectModel(parseObject));
9       }
10
11      GenreAdapter adapter = new GenreAdapter(list, this);
12      recyclerView.setLayoutManager(new LinearLayoutManager(this));
13      recyclerView.setAdapter(adapter);
14    });
15  }
```

```kotlin
1   private fun getGenres() {
2     progressDialog.show()
3     val query = ParseQuery<ParseObject>("Genre")
4     query.findInBackground { objects, e ->
5     progressDialog.dismiss()
6     var list: MutableList<ParseObjectModel> = ArrayList()
7     for (parseObject in objects) {
8       list.add(ParseObjectModel(parseObject))
9     }
10    val adapter = GenreAdapter(this, list)
11    recyclerView.layoutManager = LinearLayoutManager(this)
12    recyclerView.adapter = adapter
13    }
14  }
```
:::

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/27F_Ufr36FnQWAFtLAzkg_image.png" size="30" width="532" height="1084" position="center" showCaption="false"}

### 1.2 - 퍼블리셔 저장 및 목록화

다음 스니펫을 사용하여<font color="#2166ae">`퍼블리셔`</font>를 등록할 수 있습니다.

:::CodeblockTabs
```java
1   private void addPublisher(String name) {
2     //We are taking this name parameter from the input.
3     progressDialog.show();
4     ParseObject parseObject = new ParseObject("Publisher");
5     parseObject.put("name", name);
6     parseObject.saveInBackground(e -> {
7       progressDialog.dismiss();
8       if (e == null) {
9         getPublishers();
10        inputPublisher.setText("");
11        Toast.makeText(this, "Publisher saved successfully", Toast.LENGTH_SHORT).show();
12      } else {
13          Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
14      }
15    });
16  }
```

```kotlin
1   private fun addPublisher(name: String) {
2     //We are taking this name parameter from the input.
3     progressDialog.show()
4     val parseObject = ParseObject("Publisher")
5     parseObject.put("name", name)
6     parseObject.saveInBackground {
7       progressDialog.dismiss()
8       if (it == null) {
9         getPublishers()
10        inputPublisher.setText("")
11        Toast.makeText(this, "Publisher saved successfully", Toast.LENGTH_SHORT).show()
12      } else {
13        Toast.makeText(this, it.localizedMessage, Toast.LENGTH_SHORT).show()
14      }
15    }
16  }
```
:::

다음 스니펫을 사용하여<font color="#2166ae">`퍼블리셔`</font>를 등록할 수 있습니다.

:::CodeblockTabs
```java
1   private void getPublishers() {
2     progressDialog.show();
3     ParseQuery<ParseObject> query = new ParseQuery<>("Publisher");
4     query.findInBackground((objects, e) -> {
5       progressDialog.dismiss();
6       List<ParseObjectModel> list = new ArrayList<>();
7         for (ParseObject parseObject : objects) {
8           list.add(new ParseObjectModel(parseObject));
9         }
10      
11        PublisherAdapter adapter = new PublisherAdapter(list, this);
12        recyclerView.setLayoutManager(new LinearLayoutManager(this));
13        recyclerView.setAdapter(adapter);
14    });
15  }
```

```kotlin
1   private fun getPublishers() {
2     progressDialog.show()
3     val query = ParseQuery<ParseObject>("Publisher")
4     query.findInBackground { objects, e ->
5       progressDialog.dismiss()
6       val list: ArrayList<ParseObjectModel> = ArrayList()
7       for (parseObject in objects) {
8         list.add(ParseObjectModel(parseObject))
9       }
10  
11      val adapter = PublisherAdapter(this, list)
12      recyclerView.layoutManager = LinearLayoutManager(this)
13      recyclerView.adapter = adapter
14    }
15  }
```
:::

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/As2skdoT9UlsTddqEfirE_image.png" size="30" width="532" height="1084" position="center" showCaption="false"}

### 1.3 - 저자 저장 및 목록

다음 스니펫을 사용하여<font color="#2166ae">`저자`</font>를 등록할 수 있습니다.

:::CodeblockTabs
```java
1   private void addAuthor(String name){
2     //We are taking this name parameter from the input.
3     progressDialog.show();
4     ParseObject parseObject = new ParseObject("Author");
5     parseObject.put("name", name);
6     parseObject.saveInBackground(e -> {
7       progressDialog.dismiss();
8       if (e == null) {
9         getAuthors();
10        inputAuthor.setText("");
11        Toast.makeText(this, "Author saved successfully", Toast.LENGTH_SHORT).show();
12      } else {
13        Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
14      }
15    });
16  }
```

```kotlin
1   private fun addAuthor(name: String) {
2     //We are taking this name parameter from the input.
3     progressDialog.show()
4     val parseObject = ParseObject("Author")
5     parseObject.put("name", name)
6     parseObject.saveInBackground {
7       progressDialog.dismiss()
8       if (it == null) {
9         getAuthors()
10        inputAuthor.setText("")
11        Toast.makeText(this, "Author saved successfully", Toast.LENGTH_SHORT).show()
12      } else {
13          Toast.makeText(this, it.localizedMessage, Toast.LENGTH_SHORT).show()
14      }
15    }
16  }
```
:::

우리는 다음 코드 조각을 사용하여<font color="#2166ae">`저자`</font>를 등록할 수 있습니다.

:::CodeblockTabs
```java
1   private void getAuthors() {
2     progressDialog.show();
3     ParseQuery<ParseObject> query = new ParseQuery<>("Author");
4     query.findInBackground((objects, e) -> {
5       progressDialog.dismiss();
6       List<ParseObjectModel> list = new ArrayList<>();
7       for (ParseObject parseObject : objects) {
8         list.add(new ParseObjectModel(parseObject));
9       }
10
11      AuthorAdapter adapter = new AuthorAdapter(list, this);
12      recyclerView.setLayoutManager(new LinearLayoutManager(this));
13      recyclerView.setAdapter(adapter);
14    });
15  }
```

```kotlin
1   private fun getAuthors() {
2     progressDialog.show()
3     val query = ParseQuery<ParseObject>("Author")
4     query.findInBackground { objects: List<ParseObject>, e: ParseException? ->
5       progressDialog.dismiss()
6       val list: ArrayList<ParseObjectModel> = ArrayList()
7       for (parseObject in objects) {
8          list.add(ParseObjectModel(parseObject))
9       }
10      val adapter = AuthorAdapter(this, list)
11      recyclerView.layoutManager = LinearLayoutManager(this)
12      recyclerView.adapter = adapter
13    }
14  }
```
:::

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/KNTZxKCHMAjZAKFGtW9j5_image.png" size="30" width="532" height="1084" position="center" showCaption="false"}

이 부분에서는 <font color="#2166ae">`ParseObjectModel`</font>이라는 모델 클래스를 사용합니다. 이 모델 클래스에서는 데이터를 읽을 수 있도록 <font color="#2166ae">`ParseObject`</font> 변수를 가지고 있으며, 다음 단계에서 책을 저장하는 데 사용할 <font color="#2166ae">`isChecked`</font> 변수가 있습니다. 우리는 <font color="#2166ae">`isChecked`</font> 변수를 사용하여 선택된 객체를 쉽게 검색할 수 있습니다.
여기 우리의 <font color="#2166ae">`ParseObjectModel`</font> 모델이 있습니다.

:::CodeblockTabs
```java
1   public class ParseObjectModel {
2     ParseObject object;
3     boolean isChecked = false;
4  
5     public ParseObjectModel(ParseObject object) {
6       this.object = object;
7     }
8
9     public ParseObject getObject() {
10      return object;
11    }
12 
13    public ParseObjectModel setObject(ParseObject object) {
14      this.object = object;
15      return this;
16    }
17
18    public boolean isChecked() {
19      return isChecked;
20    }
21 
22    public ParseObjectModel setChecked(boolean checked) {
23      isChecked = checked;
24      return this;
25    }
26  }
```

```kotlin
1   class ParseObjectModel(obj: ParseObject) {
2     var obj: ParseObject? = null
3     var isChecked: Boolean = false
4  
5     init {
6       this.obj = obj
7     }
8   }
```
:::

## 2 - 책 객체와 그 관계 저장하기

### 2.1 - 1\:N 관계로 책 객체 저장하기

이 기능은 Back4app 데이터베이스에 1\:N 관계를 가진 새로운 <font color="#2166ae">`책`</font>을 생성합니다.

:::CodeblockTabs
```java
1   progressDialog.show();
2   book.put("genre", genre);
3   book.put("publisher", publisher);
4   book.put("title", title);
5   book.put("year", year);
6   book.saveInBackground(e -> {
7     progressDialog.hide();
8     if (e == null) {
9       Toast.makeText(AddBookActivity.this, "Book saved successfully", Toast.LENGTH_SHORT).show();
10      startActivity(new Intent(AddBookActivity.this, BookListActivity.class));
11      finish();
12    } else {
13      Toast.makeText(AddBookActivity.this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
14    }
15  });
```

```kotlin
1   progressDialog.show()
2   book.put("genre", genre)
3   book.put("publisher", publisher!!)
4   book.put("title", title)
5   book.put("year", year)
6   book.saveInBackground {
7     progressDialog.hide()
8     if (it == null) {
9       Toast.makeText(this, "Book saved successfully", Toast.LENGTH_SHORT).show()
10      startActivity(Intent(this@AddBookActivity, BookListActivity::class.java))
11      finish()
12    } else {
13      Toast.makeText(this, it.localizedMessage, Toast.LENGTH_SHORT).show()
14    }
15  }
```
:::

### 2.2 N\:N 관계를 가진 책 객체 저장하기

이 기능은 Back4app 데이터베이스에 N\:N 관계를 가진 새로운 <font color="#2166ae">`책`</font>을 생성합니다. <font color="#2166ae">`저자`</font> 관계의 경우, 우리는 <font color="#2166ae">`authorRecyclerView`</font>의 어댑터에서 선택된 저자를 찾고 그들을 <font color="#2166ae">`Parse Relation`</font>으로 저장합니다.

:::CodeblockTabs
```java
1   progressDialog.show();
2   book.put("genre", genre);
3   book.put("publisher", publisher);
4   book.put("title", title);
5   book.put("year", year);
6
7   //Here we are setting book relation with getSelectedItem function of BookAuthorAdapter.
8   if (recyclerViewAuthors.getAdapter() != null) {
9     relation = ((BookAuthorAdapter) recyclerViewAuthors.getAdapter()).getSelectedItems(book);
10      if (relation == null) {
11        Toast.makeText(this, "Please select Author/s", Toast.LENGTH_SHORT).show();
12        return;
13      }
14    } else {
15      Toast.makeText(this, "Something went wrong!!", Toast.LENGTH_SHORT).show();
16      return;
17    }
18
19  book.saveInBackground(e -> {
20    progressDialog.hide();
21    if (e == null) {
22      Toast.makeText(AddBookActivity.this, "Book saved successfully", Toast.LENGTH_SHORT).show();
23      startActivity(new Intent(AddBookActivity.this, BookListActivity.class));
24      finish();
25    } else {
26      Toast.makeText(AddBookActivity.this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
27    }
28  });
29
30  //This is the function for save Author/s relation of Book object.This function in BookAuthorAdapter.
31  public ParseRelation<ParseObject> getSelectedItems(ParseObject parseObject) {
32    ParseRelation<ParseObject> relation = parseObject.getRelation("author_relation");
33    for (ParseObjectModel object : this.list) {
34      if (object.isChecked())
35      relation.add(object.getObject());
36    }
37    return relation;
38  } 
```

```kotlin
1   progressDialog.show()
2   book.put("genre", genre)
3   book.put("publisher", publisher!!)
4   book.put("title", title)
5   book.put("year", year)
6
7   //Here we are setting book relation with getSelectedItem function of BookAuthorAdapter.
8
9   if (recyclerViewAuthors.adapter != null) {
10    relation = (recyclerViewAuthors.adapter as BookAuthorAdapter).getSelectedItems(book)
11    if (relation == null) {
12      Toast.makeText(this, "Please select Author/s", Toast.LENGTH_SHORT).show()
13      return
14    }
15  } else {
16    Toast.makeText(this, "Something went wrong!!", Toast.LENGTH_SHORT).show()
17    return
18  }
19
20  book.saveInBackground {
21    progressDialog.hide()
22    if (it == null) {
23      Toast.makeText(this, "Book saved successfully", Toast.LENGTH_SHORT).show()
24      startActivity(Intent(this@AddBookActivity, BookListActivity::class.java))
25      finish()
26    } else {
27      Toast.makeText(this, it.localizedMessage, Toast.LENGTH_SHORT).show()
28    }
29  }
30
31  //This is the function for save Author/s relation of Book object.This function in BookAuthorAdapter.
32
33  fun getSelectedItems(parseObject: ParseObject): ParseRelation<ParseObject>? {
34    var relation:ParseRelation<ParseObject>? = parseObject.getRelation("author_relation")
35    for (obj in this.list) {
36      if (obj.isChecked)
37        relation?.add(obj.obj)
38    }
39    return relation
40  }
```
:::

## 3 - 책 세부정보를 관계와 함께 조회하기

이 기능을 사용하여 우리는 우리의 <font color="#2166ae">`책`</font>을 그들의 <font color="#2166ae">`출판사`</font>에 따라 나열할 것입니다. 먼저, 우리는 Publisher 클래스에 쿼리를 던집니다.

:::CodeblockTabs
```java
1   progressDialog.show();
2   ParseQuery<ParseObject> query = new ParseQuery<>("Publisher");
3   query.findInBackground((objects, e) -> {
4     progressDialog.hide();
5     if (e == null) {
6       BookListAdapter adapter = new BookListAdapter(objects, this);
7       recyclerView.setLayoutManager(new LinearLayoutManager(this));
8       recyclerView.setAdapter(adapter);
9     } else {
10      Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
11    }
12  });
```

```kotlin
1   progressDialog.show()
2   val query = ParseQuery<ParseObject>("Publisher")
3   query.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
4     progressDialog.hide()
5     if (e == null) {
6       val adapter = BookListAdapter(this, objects!!)
7       recyclerView.layoutManager = LinearLayoutManager(this)
8       recyclerView.adapter = adapter
9     } else {
10      Toast.makeText(this, e.localizedMessage, Toast.LENGTH_SHORT).show()
11    }
12  }
```
:::

그리고 우리는 각 <font color="#2166ae">`책`</font>이 관련된 <font color="#2166ae">`출판사`</font>를 나열하기 위해 쿼리합니다.

:::CodeblockTabs
```java
1   ParseObject object = list.get(position);
2   holder.title.setText(object.getString("name"));
3   ParseQuery<ParseObject> query = new ParseQuery<>("Book");
4   query.whereEqualTo("publisher", object);
5   query.findInBackground((objects, e) -> {
6     if (e == null) {
7       BooksAdapter adapter = new BooksAdapter(objects, context);
8       holder.books.setLayoutManager(new LinearLayoutManager(context));
9       holder.books.setAdapter(adapter);
10    } else {
11      Toast.makeText(context, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
12    }
13  });
```

```kotlin
1   val `object` = list[position]
2   holder.title.text = `object`.getString("name")
3   val query = ParseQuery<ParseObject>("Book")
4   query.whereEqualTo("publisher", `object`)
5   query.findInBackground { objects: List<ParseObject>?, e: ParseException? ->
6     if (e == null) {
7       val adapter = BooksAdapter(context, objects!!)
8       holder.books.layoutManager = LinearLayoutManager(context)
9       holder.books.adapter = adapter
10    } else {
11      Toast.makeText(context, e.localizedMessage, Toast.LENGTH_SHORT).show()
12    }
13  }
```
:::

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/_mdZWSy6L--bhqBCF_Dz3_image.png" size="30" width="532" height="1084" position="center" showCaption="false"}

이제, 우리가 어떤 <font color="#2166ae">`책`</font> 객체를 클릭하면, 우리는 이 <font color="#2166ae">`책`</font>의 객체 ID를 해당 <font color="#2166ae">`책`</font>의 세부 정보를 보여줄 페이지로 전송합니다. 그리고 우리는 이 객체 ID를 사용하여 데이터베이스에서 <font color="#2166ae">`책` </font>의 모든 세부 정보를 가져옵니다.

:::CodeblockTabs
```java
1   private void getBookWithDetails() {
2     progressDialog.show();
3     ParseQuery<ParseObject> query = new ParseQuery<>("Book");
4     query.getInBackground(getIntent().getStringExtra("objectId"), (object, e) -> {
5       if (e == null) {
6         bookTitle.setText("Title: " +object.getString("title"));
7         bookYear.setText("Year: " +object.getString("year"));
8         try {
9           bookGenre.setText("Genre: " +object.getParseObject("genre").fetchIfNeeded().getString("name"));
10        } catch (ParseException parseException) {
11            parseException.printStackTrace();
12        }
13        try {
14          bookPublisher.setText("Publisher: " + object.getParseObject("publisher").fetchIfNeeded().getString("name"));
15        } catch (ParseException parseException) {
16          parseException.printStackTrace();
17        }
18      
19        object.getRelation("author_relation").getQuery().findInBackground((objects, e1) -> {
20          progressDialog.hide();
21          if (e1 == null) {
22            BookDetailAuthorAdapter adapter = new BookDetailAuthorAdapter(objects, this);
23            authorRecyclerView.setLayoutManager(new LinearLayoutManager(this));
24            authorRecyclerView.setAdapter(adapter);
25         } else {
26            Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
27          }
28        });
29      } else {
30        progressDialog.hide();
31        Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
32      }
33    });
34  }
```

```kotlin
1   private fun getBookWithDetails() {
2     progressDialog.show()
3     val query = ParseQuery<ParseObject>("Book")
4  
5     query.getInBackground(intent.getStringExtra("objectId")) { `object`, e ->
6     if (e == null) {
7       bookTitle.text = "Title: " + `object`.getString("title")
8       bookYear.text = "Year: " + `object`.getString("year")
9       try {
10        bookGenre.text = "Genre: " + `object`.getParseObject("genre")?.fetchIfNeeded<ParseObject>()?.getString("name")
11      } catch (parseException: ParseException) {
12        parseException.printStackTrace()
13      }
14      try {
15        bookPublisher.text =
16        "Publisher: " + `object`.getParseObject("publisher")?.fetchIfNeeded<ParseObject>()?.getString("name")
17      } catch (parseException: ParseException) {
18        parseException.printStackTrace()
19      }
20
21      `object`.getRelation<ParseObject>("author_relation").query.findInBackground { objects, e1 ->
22      progressDialog.hide()
23        if (e1 == null) {
24          val adapter = BookDetailAuthorAdapter(this, objects)
25          authorRecyclerView.layoutManager = LinearLayoutManager(this)
26          authorRecyclerView.adapter = adapter
27        } else {
28          Toast.makeText(this, e1.localizedMessage, Toast.LENGTH_SHORT).show()
29        }
30     }
31    } else {
32        progressDialog.hide()
33        Toast.makeText(this, e.localizedMessage, Toast.LENGTH_SHORT).show()
34      }
35    }
36  }
```
:::

## 완료되었습니다!

이 시점에서 우리는 <font color="#2166ae">`Parse 관계`</font>에 대해 배웠습니다 <font color="#2166ae">`안드로이드`</font>.
