---
title: 관계 스키마
slug: docs-kr/get-started/relational-schema
description: Parse SDK를 사용하여 Back4App에서 관계형 스키마를 생성하고 관리합니다. 포인터와 관계를 활용하여 효율적인 쿼리를 수행하세요.
image: https://www.back4app.com/_public/img/back4app-og.png
createdAt: 2024-01-24T13:29:02.752Z
---

이 가이드는 Back4app에서 관계형 스키마를 사용하는 방법을 설명합니다. 관련 클래스를 생성하고 Parse Server를 사용하여 효율적인 쿼리를 수행하는 방법을 포함합니다. `포인터`와 `관계`를 효과적으로 사용하는 방법과 실용적인 예제를 배울 수 있습니다.

## 관계형 스키마란 무엇인가요?

관계형 스키마는 데이터를 서로 연결된 다양한 클래스로 구성합니다. Parse Server에서는 이러한 관계가 다음을 통해 관리됩니다:

- **포인터**: 단일 객체를 직접 참조합니다.
- **관계**: 여러 연결(다대다 관계)을 관리합니다.

이 도구들은 복잡한 쿼리를 효율적이고 일관되게 수행할 수 있게 해줍니다.

## 목표

이 가이드를 마치면 다음을 할 수 있게 됩니다:

- &#x20;*포인터*와 *관계*를 사용하여 클래스 간의 관계를 생성합니다.
- 연결된 데이터를 검색하기 위한 관계형 쿼리를 수행합니다.
- 더 나은 성능을 위해 스키마를 최적화합니다.

## 전제 조건

:::hint{type="info"}
- Back4app 애플리케이션. [앱 만들기 가이드](https://www.back4app.com/docs/get-started/new-parse-app).
- Parse SDK 설치됨. [설치 가이드](https://www.back4app.com/docs/get-started/parse-sdk).
:::

## 1 - 관련 클래스 만들기

### 실용적인 예: 주와 도시

도시가 주와 연관된 시스템을 모델링하고 싶다고 상상해 보세요:

- state\_name 필드를 가진 State 클래스.
- city\_name 필드를 가진 City 클래스와 *State*에 대한 포인터.

### 클래스 및 관계 생성

:::CodeblockTabs
JavaScript

```javascript
async function createStateAndCity() {
  try {
    // Create a State
    const State = Parse.Object.extend('State');
    const california = new State();
    california.set('state_name', 'California');
    const savedState = await california.save();

    console.log(`State created with objectId: ${savedState.id}`);

    // Create a City with a pointer to State
    const City = Parse.Object.extend('City');
    const losAngeles = new City();
    losAngeles.set('city_name', 'Los Angeles');
    losAngeles.set('state', savedState);

    const savedCity = await losAngeles.save();
    console.log(`City created with objectId: ${savedCity.id}`);
  } catch (error) {
    console.error('Error creating state and city:', error.message);
  }
}

createStateAndCity();
```

Flutter

```dart
Future<void> createClasses() async {
  await Parse().initialize('YOUR_APP_ID', 'https://parseapi.back4app.com/',
      clientKey: 'YOUR_CLIENT_KEY', autoSendSessionId: true);

  // Create a State
  final state = ParseObject('State')
    ..set('state_name', 'California');
  final stateResult = await state.save();

  if (stateResult.success) {
    // Create a City with a pointer to State
    final city = ParseObject('City')
      ..set('city_name', 'Los Angeles')
      ..set('state', state);
    final cityResult = await city.save();

    if (cityResult.success) {
      print('State and City created successfully.');
    }
  }
}
```

Android

```java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Initialize Parse
        Parse.initialize(new Parse.Configuration.Builder(this)
                .applicationId("YOUR_APP_ID")
                .clientKey("YOUR_CLIENT_KEY")
                .server("https://parseapi.back4app.com/")
                .build()
        );

        // Create State and City
        createClasses();
    }

    private void createClasses() {
        // Create a State
        ParseObject state = new ParseObject("State");
        state.put("state_name", "California");

        state.saveInBackground(e -> {
            if (e == null) {
                Log.d("Parse", "State created successfully with objectId: " + state.getObjectId());

                // Save City with a pointer to State
                ParseObject city = new ParseObject("City");
                city.put("city_name", "Los Angeles");
                city.put("state", state);

                city.saveInBackground(ex -> {
                    if (ex == null) {
                        Log.d("Parse", "City created successfully with objectId: " + city.getObjectId());
                    } else {
                        Log.e("Parse", "Failed to create City: " + ex.getMessage());
                    }
                });
            } else {
                Log.e("Parse", "Failed to create State: " + e.getMessage());
            }
        });
    }
}
```

iOS

```swift
struct State: ParseObject {
    var objectId: String?
    var state_name: String?
    var createdAt: Date?
    var updatedAt: Date?
}

struct City: ParseObject {
    var objectId: String?
    var city_name: String?
    var state: State?
    var createdAt: Date?
    var updatedAt: Date?
}

func createClasses() async throws {
    try await ParseSwift.initialize(applicationId: "YOUR_APP_ID",
                                    clientKey: "YOUR_CLIENT_KEY",
                                    serverURL: URL(string: "https://parseapi.back4app.com")!)

    let state = State(state_name: "California")
    let savedState = try await state.save()

    var city = City(city_name: "Los Angeles", state: savedState)
    city = try await city.save()

    print("State and City created successfully.")
}
```

PHP

```php
require 'vendor/autoload.php';

use Parse\ParseClient;
use Parse\ParseObject;

ParseClient::initialize('YOUR_APP_ID', 'YOUR_CLIENT_KEY', 'https://parseapi.back4app.com/');

try {
    // Create a State
    $state = new ParseObject("State");
    $state->set("state_name", "California");
    $state->save();

    // Create a City with a pointer to State
    $city = new ParseObject("City");
    $city->set("city_name", "Los Angeles");
    $city->set("state", $state);
    $city->save();

    echo "State and City created successfully.";
} catch (Exception $ex) {
    echo "Error: " . $ex->getMessage();
}
```

.NET

```csharp
namespace ParseExampleApp
{
    class Program
    {
        static async Task Main(string[] args)
        {
            // Initialize Parse
            ParseClient.Initialize(new ParseClient.Configuration
            {
                ApplicationId = "YOUR_APP_ID",
                Server = "https://parseapi.back4app.com/",
                Key = "YOUR_CLIENT_KEY"
            });

            // Call the method to create State and City
            await CreateClassesAsync();
        }

        static async Task CreateClassesAsync()
        {
            try
            {
                // Create a State
                var state = new ParseObject("State");
                state["state_name"] = "California";
                await state.SaveAsync();
                Console.WriteLine($"State created with objectId: {state.ObjectId}");

                // Create a City with a Pointer to the State
                var city = new ParseObject("City");
                city["city_name"] = "Los Angeles";
                city["state"] = state; // Set the pointer to the State
                await city.SaveAsync();
                Console.WriteLine($"City created with objectId: {city.ObjectId}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
            }
        }
    }
}
```

REST API

```curl
# Create State
curl -X POST \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_API_KEY" \
-H "Content-Type: application/json" \
-d '{"state_name": "California"}' \
https://parseapi.back4app.com/classes/State

# Use the objectId of the created state to create a city
curl -X POST \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_API_KEY" \
-H "Content-Type: application/json" \
-d '{"city_name": "Los Angeles", "state": {"__type": "Pointer", "className": "State", "objectId": "STATE_OBJECT_ID"}}' \
https://parseapi.back4app.com/classes/City
```
:::

## 2 - 관련 데이터 쿼리하기

이제 데이터가 관련되었으므로, 이를 검색하기 위한 쿼리를 수행할 수 있습니다.

### 예제 1: 특정 주의 도시 가져오기

:::CodeblockTabs
JavaScript

```javascript
const stateQuery = new Parse.Query("State");
stateQuery.equalTo("state_name", "California");

stateQuery.first().then(state => {
  const cityQuery = new Parse.Query("City");
  cityQuery.equalTo("state", state);
  return cityQuery.find();
}).then(cities => {
  cities.forEach(city => {
    console.log(`City: ${city.get("city_name")}`);
  });
}).catch(error => {
  console.error("Error fetching cities:", error.message);
});
```

Flutter

```dart
final stateQuery = QueryBuilder(ParseObject('State'))..whereEqualTo('state_name', 'California');

final stateResult = await stateQuery.query();
if (stateResult.success && stateResult.results != null) {
  final state = stateResult.results!.first;
  final cityQuery = QueryBuilder(ParseObject('City'))..whereEqualTo('state', state);
  final cityResult = await cityQuery.query();

  if (cityResult.success && cityResult.results != null) {
    for (var city in cityResult.results!) {
      print('City: ${city.get<String>('city_name')}');
    }
  }
}
```

Android

```java
ParseQuery<ParseObject> stateQuery = ParseQuery.getQuery("State");
stateQuery.whereEqualTo("state_name", "California");

stateQuery.getFirstInBackground((state, e) -> {
    if (e == null) {
        ParseQuery<ParseObject> cityQuery = ParseQuery.getQuery("City");
        cityQuery.whereEqualTo("state", state);
        cityQuery.findInBackground((cities, ex) -> {
            if (ex == null) {
                for (ParseObject city : cities) {
                    Log.d("Parse", "City: " + city.getString("city_name"));
                }
            } else {
                Log.e("Parse", "Error fetching cities: " + ex.getMessage());
            }
        });
    } else {
        Log.e("Parse", "Error fetching state: " + e.getMessage());
    }
});
```

iOS

```swift
let stateQuery = State.query("state_name" == "California")

stateQuery.first { result in
    switch result {
    case .success(let state):
        let cityQuery = City.query("state" == state)
        cityQuery.find { cityResult in
            switch cityResult {
            case .success(let cities):
                cities.forEach { city in
                    print("City: \(city.city_name ?? "Unknown")")
                }
            case .failure(let error):
                print("Error fetching cities: \(error.localizedDescription)")
            }
        }
    case .failure(let error):
        print("Error fetching state: \(error.localizedDescription)")
    }
}
```

PHP

```php
use Parse\ParseQuery;

// Query State
$stateQuery = new ParseQuery("State");
$stateQuery->equalTo("state_name", "California");
$state = $stateQuery->first();

if ($state) {
    // Query Cities
    $cityQuery = new ParseQuery("City");
    $cityQuery->equalTo("state", $state);
    $cities = $cityQuery->find();

    foreach ($cities as $city) {
        echo "City: " . $city->get("city_name") . "\n";
    }
}
```

.NET

```csharp
var stateQuery = new ParseQuery<ParseObject>("State").WhereEqualTo("state_name", "California");
var state = await stateQuery.FirstAsync();

if (state != null)
{
    var cityQuery = new ParseQuery<ParseObject>("City").WhereEqualTo("state", state);
    var cities = await cityQuery.FindAsync();

    foreach (var city in cities)
    {
        Console.WriteLine($"City: {city["city_name"]}");
    }
}
```

REST API

```curl
# Query State:
curl -X GET \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_API_KEY" \
"https://parseapi.back4app.com/classes/State?where={\"state_name\":\"California\"}"

# Query Cities:
# Replace STATE_OBJECT_ID with the objectId from the state query.
curl -X GET \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_API_KEY" \
"https://parseapi.back4app.com/classes/City?where={\"state\":{\"__type\":\"Pointer\",\"className\":\"State\",\"objectId\":\"STATE_OBJECT_ID\"}}"
```
:::

### 예제 2: 관련 도시와 함께 쿼리 상태

도시에 연결된 주를 반환하는 쿼리를 생성하십시오:

:::CodeblockTabs
JavaScript

```javascript
const stateQuery = new Parse.Query("State");
const cityQuery = new Parse.Query("City");
cityQuery.matchesQuery("state", stateQuery);
cityQuery.include("state");

cityQuery.find().then(cities => {
  cities.forEach(city => {
    const state = city.get("state");
    console.log(`City: ${city.get("city_name")} belongs to state: ${state.get("state_name")}`);
  });
}).catch(error => {
  console.error("Error fetching data:", error.message);
});
```

Flutter

```dart
final stateQuery = QueryBuilder(ParseObject('State'));
final cityQuery = QueryBuilder(ParseObject('City'))
  ..whereMatchesQuery('state', stateQuery)
  ..includeObject(['state']);

final cityResult = await cityQuery.query();

if (cityResult.success && cityResult.results != null) {
  for (var city in cityResult.results!) {
    final state = city.get<ParseObject>('state');
    print('City: ${city.get<String>('city_name')} belongs to state: ${state?.get<String>('state_name')}');
  }
}
```

Android

```java
ParseQuery<ParseObject> stateQuery = ParseQuery.getQuery("State");
ParseQuery<ParseObject> cityQuery = ParseQuery.getQuery("City");
cityQuery.whereMatchesQuery("state", stateQuery);
cityQuery.include("state");

cityQuery.findInBackground((cities, e) -> {
    if (e == null) {
        for (ParseObject city : cities) {
            ParseObject state = city.getParseObject("state");
            Log.d("Parse", "City: " + city.getString("city_name") + " belongs to state: " + state.getString("state_name"));
        }
    } else {
        Log.e("Parse", "Error: " + e.getMessage());
    }
});
```

iOS

```swift
let stateQuery = State.query()
let cityQuery = City.query(matchesQuery(key: "state", query: stateQuery))
cityQuery.include("state")

cityQuery.find { result in
    switch result {
    case .success(let cities):
        cities.forEach { city in
            if let state = city.state {
                print("City: \(city.city_name ?? "Unknown") belongs to state: \(state.state_name ?? "Unknown")")
            }
        }
    case .failure(let error):
        print("Error: \(error.localizedDescription)")
    }
}
```

PHP

```php
use Parse\ParseQuery;

$stateQuery = new ParseQuery("State");
$cityQuery = new ParseQuery("City");
$cityQuery->matchesQuery("state", $stateQuery);
$cityQuery->includeKey("state");

$cities = $cityQuery->find();
foreach ($cities as $city) {
    $state = $city->get("state");
    echo "City: " . $city->get("city_name") . " belongs to state: " . $state->get("state_name") . "\n";
}
```

.NET

```csharp
var stateQuery = new ParseQuery<ParseObject>("State");
var cityQuery = new ParseQuery<ParseObject>("City").WhereMatchesQuery("state", stateQuery).Include("state");

var cities = await cityQuery.FindAsync();
foreach (var city in cities)
{
    var state = city.Get<ParseObject>("state");
    Console.WriteLine($"City: {city["city_name"]} belongs to state: {state["state_name"]}");
}
```

REST API

```curl
curl -X GET \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_API_KEY" \
"https://parseapi.back4app.com/classes/City?where={\"state\":{\"$inQuery\":{\"className\":\"State\"}}}&include=state"
```
:::

## 모범 사례

Back4App의 Parse Server를 사용하여 관계형 스키마로 효과적으로 작업하려면 성능, 유지 관리 및 확장성을 보장하기 위해 다음 모범 사례를 따르십시오:

### 올바른 관계 유형 선택

- **포인터 사용** for **일대일 관계**, 예를 들어 사용자를 프로필에 연결하는 경우.
- **관계 사용** for **다대다 관계**, 예를 들어 프로젝트를 여러 작업에 연결하는 경우.

### 효율적인 쿼리 작성

- **사용하기&#x20;**`.include()` 관련 데이터를 동일한 쿼리에서 로드하여 여러 요청의 필요성을 줄입니다.
- **결과 제한하기** `limit()` 및 `skip()`를 사용하여 한 번에 대량의 데이터 세트를 가져오는 것을 피합니다.
- **자주 쿼리되는 필드 인덱싱하기** 검색 속도를 높입니다.

### 과도한 중첩 피하기

- **쿼리를 평탄하게 유지하기** 복잡성을 줄이고 성능을 향상시킵니다. 중첩 쿼리는 가급적 적게 사용하고 필요할 때만 사용하세요.

### 복잡한 쿼리를 클라우드 코드로 오프로드하기

- 여러 관계가 포함된 **복잡한 쿼리** 또는 대량의 데이터 세트에 대해서는 **클라우드 코드**로 오프로드하여 클라이언트를 가볍고 반응성이 있도록 유지하세요.

## 결론

이 가이드에서는 클래스 간의 관계를 생성하고 Back4app에서 관련 객체를 쿼리하는 방법을 배웠습니다. [특정 SDK 문서](https://docs.parseplatform.org)를 계속 탐색하여 더 깊이 들어가세요!
