

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# GraphQL 스키마
<a name="schema-components"></a>

GraphQL 스키마는 GraphQL API의 기반으로, 데이터의 형태를 정의하는 청사진 역할을 합니다. 또한 데이터를 검색 또는 수정하는 방법을 정의하는 클라이언트와 서버 간의 계약이기도 합니다.

GraphQL 스키마는 *스키마 정의 언어*(SDL)로 작성됩니다. SDL은 구조가 확립된 유형과 필드로 구성되어 있습니다.
+ **유형**: 유형은 GraphQL이 데이터의 형태와 동작을 정의하는 방식입니다. GraphQL은 이 섹션의 뒷부분에서 설명할 여러 유형을 지원합니다. 스키마에 정의된 각 유형에는 고유한 범위가 포함됩니다. 범위 내에는 GraphQL 서비스에서 사용할 값이나 로직을 포함할 수 있는 하나 이상의 필드가 있습니다. 유형은 다양한 역할을 수행하며, 가장 일반적으로는 객체 또는 스칼라(기본 값 유형) 역할을 합니다.
+ **필드**: 필드는 유형의 범위 내에 존재하며 GraphQL 서비스에서 요청한 값을 보유합니다. 이는 다른 프로그래밍 언어의 변수와 매우 유사합니다. 필드에 정의하는 데이터의 형태에 따라 요청/응답 작업에서 데이터가 구조화되는 방식이 결정됩니다. 이를 통해 개발자는 서비스의 백엔드가 구현되는 방식을 모르더라도 어떤 결과가 반환될지 예측할 수 있습니다.

스키마의 모습을 시각적으로 확인하기 위해 간단한 GraphQL 스키마의 내용을 살펴보겠습니다. 프로덕션 코드에서 스키마는 일반적으로 `schema.graphql` 또는 `schema.json`이라는 파일에 있습니다. GraphQL 서비스를 구현하는 프로젝트를 살펴본다고 가정해 보겠습니다. 이 프로젝트는 회사 직원 데이터를 저장하고 있으며, `schema.graphql` 파일은 직원 데이터를 검색하고 데이터베이스에 새 직원을 추가하는 데 사용됩니다. 코드는 다음과 같을 수 있습니다.

------
#### [ schema.graphql ]

```
type Person {                                  
   id: ID!
   name: String                                  
   age: Int
}
type Query {                                   
  people: [Person]
}
type Mutation {
  addPerson(id: ID!, name: String, age: Int): Person
}
```

------

스키마에는 `Person`, `Query`, `Mutation`이라는 세 가지 유형이 정의되어 있는 것을 볼 수 있습니다. `Person`을 보면, 이 유형을 객체로 만드는 회사 직원 인스턴스의 청사진이라는 것을 짐작할 수 있습니다. 범위 내에는 `id`, `name`, `age`가 있습니다. 이러한 필드는 `Person`의 속성을 정의하는 필드입니다. 즉, 데이터 원본은 각 `Person`의 `name`을 `String` 스칼라(기본) 유형으로 저장하고 `age`를 `Int` 스칼라(기본) 유형으로 저장합니다. `id`는 각 `Person`에 대해 특별하고 고유한 식별자 역할을 합니다. 또한 `!` 기호로 표시되는 필수 값이기도 합니다.

다음 두 객체 유형은 다르게 동작합니다. GraphQL은 스키마에 데이터가 채워지는 방식을 정의하는 특수 객체 유형에 대한 몇 가지 키워드를 예약합니다. `Query` 유형은 소스에서 데이터를 검색합니다. 이 예에서는 쿼리가 데이터베이스에서 `Person` 객체를 검색할 수 있습니다. 이 작업은 RESTful 용어로 `GET` 작업을 떠올리게 할 수 있습니다. `Mutation`은 데이터를 수정합니다. 이 예에서는 변형으로 인해 데이터베이스에 더 많은 `Person` 객체가 추가될 수 있습니다. 이 작업은 `PUT` 또는 `POST`와 같은 상태 변경 작업을 떠올리게 할 수 있습니다. 모든 특수 객체 유형의 동작은 이 섹션의 뒷부분에서 설명합니다.

이 예제의 `Query`가 데이터베이스에서 무언가를 검색한다고 가정해 보겠습니다. `Query`의 필드를 살펴보면 `people`이라는 필드 하나를 볼 수 있습니다. 필드 값은 `[Person]`입니다. 즉, 데이터베이스에서 `Person`의 일부 인스턴스를 검색하려고 합니다. 하지만 대괄호를 추가하면 특정 인스턴스뿐만 아니라 모든 `Person` 인스턴스의 목록을 반환하게 됩니다.

`Mutation` 유형은 데이터 수정과 같은 상태 변경 작업을 수행하는 역할을 합니다. 변형은 데이터 원본에서 일부 상태 변경 작업을 수행하는 역할을 합니다. 이 예제에서 변형에는 데이터베이스에 새 `Person` 객체를 추가하는 `addPerson`이라는 작업이 포함되어 있습니다. 이 변형은 `Person`을 사용하며 `id`, `name`, `age` 필드에 대한 입력이 필요합니다.

이 시점에서 어떤 동작을 수행하며 함수 이름과 파라미터가 있는 함수와 매우 비슷해 보인다고 가정한다면 코드 구현 없이 `addPerson`과 같은 작업이 어떻게 작동하는지 궁금할 수 있습니다. 스키마는 선언 역할만 하기 때문에 지금은 작동하지 않습니다. `addPerson`의 동작을 구현하려면 해석기를 추가해야 합니다. 해석기는 관련 필드(이 경우 `addPerson` 작업)가 호출될 때마다 실행되는 코드 단위입니다. 작업을 사용하려면 어느 시점에 해석기 구현을 추가해야 합니다. 어떤 면에서는 스키마 작업을 함수 선언으로, 해석기는 정의로 생각할 수 있습니다. 해석기에 대해서는 다른 섹션에서 설명하겠습니다.

이 예제에서는 스키마가 데이터를 조작할 수 있는 가장 간단한 방법만 보여 줍니다. GraphQL 및 AWS AppSync의 기능을 활용하여 복잡하고 강력하며 확장 가능한 애플리케이션을 구축합니다. 다음 섹션에서는 스키마에서 활용할 수 있는 다양한 유형과 필드 동작을 모두 정의하겠습니다.

# GraphQL 유형
<a name="graphql-types"></a>

GraphQL은 다양한 유형을 지원합니다. 이전 섹션에서 보았듯이 유형은 데이터의 형태나 동작을 정의합니다. 유형은 GraphQL 스키마의 기본 구성 요소입니다.

유형은 입력과 출력으로 분류할 수 있습니다. 입력은 특수 객체 유형(`Query`, `Mutation` 등)에 대한 인수로 전달될 수 있는 유형인 반면, 출력 유형은 데이터를 저장하고 반환하는 데만 사용됩니다. 유형 및 분류 목록은 아래와 같습니다.
+ **객체**: 객체에는 엔터티를 설명하는 필드가 있습니다. 예를 들어 객체는 `authorName`, `publishingYear`와 같이 특성을 설명하는 필드가 있는 `book` 등이 될 수 있습니다. 이러한 유형은 엄밀히 말하면 출력 유형입니다.
+ **스칼라**: int, 문자열 등과 같은 기본 유형입니다. 일반적으로 필드에 할당됩니다. `authorName` 필드를 예로 들면 'John Smith'와 같은 이름을 저장하는 `String` 스칼라에 할당할 수 있습니다. 스칼라는 입력 유형과 출력 유형이 모두 될 수 있습니다.
+ **입력**: 입력을 사용하면 필드 그룹을 인수로 전달할 수 있습니다. 객체와 구조가 매우 비슷하지만, 특수 객체에 인수로 전달할 수 있습니다. 입력을 사용하면 범위에 스칼라, 열거형 및 기타 입력을 정의할 수 있습니다. 입력은 입력 유형만 가능합니다.
+ **특수 객체**: 특수 객체는 상태 변경 작업을 수행하며 서비스의 까다로운 작업 대부분을 수행합니다. 쿼리, 변형, 구독이라는 세 가지 특수 객체 유형이 있습니다. 일반적으로 쿼리는 데이터를 가져오고, 변형은 데이터를 조작하며, 구독은 지속적인 통신을 위해 클라이언트와 서버 간의 양방향 연결을 열고 유지합니다. 특수 객체는 기능으로 볼 때 입력도 출력도 아닙니다.
+ **열거형**: 열거형은 미리 정의된 유효 값 목록입니다. 열거형을 호출하는 경우 범위에 정의된 값만 열거형 값이 될 수 있습니다. 예를 들어 교통 신호 목록을 나타내는 `trafficLights`라는 열거형이 있는 경우 `redLight` 및 `greenLight`와 같은 값을 가질 수 있지만 `purpleLight` 값은 가질 수 없습니다. 실제 신호등에는 신호가 너무 많기 때문에 열거형을 사용하여 신호를 정의하고 `trafficLight`를 참조할 때 이러한 열거형만 유효 값이 되도록 할 수 있습니다. 열거형은 입력 유형과 출력 유형이 모두 될 수 있습니다.
+ **결합/인터페이스**: 결합을 사용하면 클라이언트가 요청한 데이터에 따라 요청에서 하나 이상의 항목을 반환할 수 있습니다. 예를 들어 `title` 필드가 포함된 `Book` 유형과 `name` 필드가 포함된 `Author` 유형이 있는 경우 두 유형 간에 결합을 생성할 수 있습니다. 클라이언트가 데이터베이스에서 'Julius Caesar'라는 문구를 쿼리하려는 경우 결합은 `Book` `title`에서 *Julius Caesar*(윌리엄 셰익스피어의 희곡)를 반환하고 `Author` `name`에서 *Julius Caesar*(*Commentarii de Bello Gallico*의 저자)를 반환할 수 있습니다. 결합은 출력 유형만 될 수 있습니다.

  인터페이스는 객체가 구현해야 하는 필드 집합으로, 인터페이스에 정의된 필드를 구현해야 하는 Java와 같은 프로그래밍 언어의 인터페이스와 약간 유사합니다. 예를 들어 `title` 필드를 포함하는 `Book`이라는 인터페이스를 만들었다고 가정해 보겠습니다. 이후 `Book`을 구현한 `Novel`이라는 유형을 만들었다고 가정합니다. `Novel`은 `title` 필드를 포함해야 합니다. 하지만 `Novel`은 `pageCount` 또는 `ISBN`와 같이 인터페이스에 없는 다른 필드도 포함할 수 있습니다. 인터페이스는 출력 유형만 될 수 있습니다.

다음 섹션에서는 GraphQL에서 각 유형이 작동하는 방식을 설명합니다.

## Objects
<a name="object-components"></a>

GraphQL 객체는 프로덕션 코드에서 볼 수 있는 기본 유형입니다. GraphQL에서는 객체를 다른 언어의 변수와 비슷한 여러 필드의 그룹으로 생각할 수 있으며, 각 필드는 값을 보유할 수 있는 유형(일반적으로 스칼라 또는 다른 객체)으로 정의됩니다. 객체는 서비스 구현에서 검색/조작할 수 있는 데이터 단위를 나타냅니다.

객체 유형은 `Type` 키워드를 사용하여 선언됩니다. 스키마 예제를 약간 수정해 보겠습니다.

```
type Person {
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}

type Occupation {
  title: String
}
```

여기에서 객체 유형은 `Person` 및 `Occupation`입니다. 각 객체에는 고유한 유형이 있는 고유한 필드가 있습니다. GraphQL에는 필드를 다른 유형으로 설정할 수 있는 기능이 있습니다. `Person`의 `occupation` 필드에 `Occupation` 객체 유형이 포함되어 있는 것을 볼 수 있습니다. GraphQL은 데이터를 설명할 뿐 서비스 구현은 설명하지 않기 때문에 이렇게 연결할 수 있습니다.

## 스칼라
<a name="scalar-components"></a>

스칼라는 본질적으로 값을 보유하는 기본 유형입니다. 에는 기본 GraphQL 스칼라와 스칼라라는 두 AWS AppSync가지 유형의 AWS AppSync 스칼라가 있습니다. 스칼라는 일반적으로 객체 유형 내에 필드 값을 저장하는 데 사용됩니다. 기본 GraphQL 유형에는 `Int`, `Float`, `String`, `Boolean`, `ID`가 포함됩니다. 이전 예제를 다시 사용해 보겠습니다.

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}

type Occupation {
  title: String
}
```

`name` 및 `title` 필드를 분리해 보면 둘 다 `String` 스칼라를 보유합니다. `Name`은 '`John Smith`'와 같은 문자열 값을 반환하고 제목은 '`firefighter`'와 같은 결과를 반환할 수 있습니다. 일부 GraphQL 구현에서는 `Scalar` 키워드를 사용하고 유형의 동작을 구현하는 사용자 지정 스칼라도 지원합니다. 그러나 지금은 AWS AppSync 에서 사용자 지정 스칼라를 **지원하지 않습니다**. 스칼라 목록은 [AWS AppSync의 스칼라 유형](https://docs.aws.amazon.com//appsync/latest/devguide/scalars.html)을 참조하세요.

## 입력
<a name="input-components"></a>

입력 및 출력 유형의 개념으로 인해 인수를 전달할 때 특정 제한이 발생합니다. 공통으로 전달해야 하는 유형, 특히 객체가 제한됩니다. 입력 유형을 사용하여 이 규칙을 우회할 수 있습니다. 입력은 스칼라, 열거형 및 기타 입력 유형을 포함하는 유형입니다.

입력은 `input` 키워드를 사용하여 정의됩니다.

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}

type Occupation {
  title: String
}

input personInput { 
  id: ID!
  name: String
  age: Int
  occupation: occupationInput
}

input occupationInput {
  title: String
}
```

보는 것과 같이 원래 유형을 모방하는 별도의 입력이 있을 수 있습니다. 이러한 입력은 다음과 같이 필드 작업에 자주 사용됩니다.

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}

type Occupation {
  title: String
}

input occupationInput {
  title: String
}

type Mutation {
  addPerson(id: ID!, name: String, age: Int, occupation: occupationInput): Person
}
```

`Person`을 생성하기 위해 `Occupation`의 자리에 `occupationInput`을 계속해서 전달하는 방식을 주목하세요.

이 방식은 입력에 대한 한 가지 시나리오에 불과합니다. 객체를 반드시 1:1로 복사할 필요는 없으며, 프로덕션 코드에서는 이 방법으로 사용하지 않을 가능성이 큽니다. 입력해야 하는 항목만 인수로 정의하여 GraphQL 스키마를 활용하는 것이 좋습니다.

또한 동일한 입력을 여러 작업에 사용할 수 있지만 권장되지는 않습니다. 스키마의 요구 사항이 변경될 경우를 대비하여 각 작업에는 입력의 고유한 사본이 포함되는 것이 이상적입니다.

## 특수 객체
<a name="special-object-components"></a>

GraphQL은 스키마가 데이터를 검색/조작하는 방식에 대한 일부 비즈니스 로직을 정의하는 특수 객체를 위한 몇 가지 키워드를 예약합니다. 스키마에는 이러한 키워드가 최대 하나씩만 있을 수 있습니다. 이러한 키워드는 클라이언트가 GraphQL 서비스에 대해 실행하는 모든 요청 데이터의 진입점 역할을 합니다.

`type` 키워드를 사용하여 특수 객체도 정의합니다. 일반 객체 유형과는 다르게 사용되지만 구현은 매우 비슷합니다.

------
#### [ Queries ]

쿼리는 읽기 전용 가져오기를 수행하여 소스에서 데이터를 가져온다는 점에서 `GET` 작업과 매우 유사합니다. GraphQL에서 `Query`는 서버에 요청을 보내는 클라이언트의 모든 진입점을 정의합니다. GraphQL 구현에는 항상 `Query`가 있습니다.

다음은 이전 스키마 예제에서 사용한 `Query` 및 수정된 객체 유형입니다.

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}
type Occupation {
  title: String
}
type Query {                                   
  people: [Person]
}
```

`Query`에는 데이터 원본의 `Person` 인스턴스 목록을 반환하는 `people`라는 필드가 포함되어 있습니다. 애플리케이션의 동작을 변경해야 하는데 이제 별도의 목적으로 `Occupation` 인스턴스 목록만 반환해야 한다고 가정해 보겠습니다. 쿼리에 간단히 추가할 수 있습니다.

```
type Query {                                   
  people: [Person]
  occupations: [Occupation]
}
```

GraphQL에서는 쿼리를 요청의 단일 소스로 취급할 수 있습니다. 보는 것과 같이 서로 다른 엔드포인트를 사용하여 동일한 결과(`.../api/1/people` 및 `.../api/1/occupations`)를 달성할 수 있는 RESTful 구현보다 훨씬 간단할 수 있습니다.

이 쿼리에 대한 해석기 구현이 있다고 가정하면 이제 실제 쿼리를 수행할 수 있습니다. `Query` 유형이 존재하지만 애플리케이션의 코드에서 실행되도록 하려면 명시적으로 호출해야 합니다. 다음과 같은 `query` 키워드를 사용하여 수행할 수 있습니다.

```
query getItems {
   people {
      name
   }
   occupations {
      title
   }
}
```

보는 것과 같이 이 쿼리는 `getItems`라고 하며 `people`(`Person` 객체의 목록) 및 `occupations`(`Occupation` 객체의 목록)를 반환합니다. `people`에서는 각 `Person`의 `name` 필드만 반환하는 반면, 각 `Occupation`의 `title` 필드를 반환합니다. 응답은 다음과 같을 수 있습니다.

```
{
  "data": {
    "people": [
      {
        "name": "John Smith"
      },
      {
        "name": "Andrew Miller"
      },
      .
      .
      .
    ],
    "occupations": [
      {
        "title": "Firefighter"
      },
      {
        "title": "Bookkeeper"
      },
      .
      .
      .
    ]
  }
}
```

예제 응답은 데이터가 쿼리의 형태를 어떻게 따르는지 보여 줍니다. 검색된 각 항목은 필드 범위 내에 나열됩니다. `people` 및 `occupations`는 항목을 별도의 목록으로 반환합니다. 이 방법이 유용하긴 하지만, 사람들의 이름과 직업 목록을 반환하도록 쿼리를 수정하는 것이 더 편리할 수도 있습니다.

```
query getItems {
   people {
      name   
      occupation {
        title
      }
}
```

`Person` 유형에 유형 `Occupation`의 `occupation` 필드가 포함되어 있기 때문에 이는 유효한 수정입니다. `people`의 범위 내에 나열되면 각 `Person`의 `name`과 함께 관련된 `Occupation`이 `title`별로 반환됩니다. 응답은 다음과 같을 수 있습니다.

```
}
  "data": {
    "people": [
      {
        "name": "John Smith",
        "occupation": {
          "title": "Firefighter"
        }
      },
      {
        "name": "Andrew Miller",
        "occupation": {
          "title": "Bookkeeper"
        }
      },
      .
      .
      .
    ]
  }
}
```

------
#### [ Mutations ]

변형은 `PUT` 또는 `POST`와 같은 상태 변경 작업과 비슷합니다. 이러한 작업은 쓰기 작업을 수행하여 소스의 데이터를 수정한 다음 응답을 가져옵니다. 또한 데이터 수정 요청의 진입점을 정의합니다. 쿼리와 달리 변형은 프로젝트의 필요에 따라 스키마에 포함되거나 포함되지 않을 수 있습니다. 다음은 스키마 예제의 변형입니다.

```
type Mutation {
  addPerson(id: ID!, name: String, age: Int): Person
}
```

`addPerson` 필드는 데이터 원본에 `Person`을 추가하는 하나의 진입점을 나타냅니다. `addPerson`은 필드 이름이며 `id`, `name`, `age`는 파라미터, `Person`은 반환 유형입니다. `Person` 유형을 다시 살펴보면 다음과 같습니다.

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}
```

`occupation` 필드를 추가했습니다. 하지만 객체를 인수로 전달할 수 없기 때문에 이 필드를 `Occupation`으로 직접 설정할 수는 없습니다. 엄밀히 말하면 출력 유형이기 때문입니다. 대신 인수와 동일한 필드를 포함하는 입력을 전달해야 합니다.

```
input occupationInput {
  title: String
}
```

 새 `Person` 인스턴스를 만들 때 이 필드를 파라미터로 포함하도록 `addPerson`을 간편하게 업데이트할 수도 있습니다.

```
type Mutation {
  addPerson(id: ID!, name: String, age: Int, occupation: occupationInput): Person
}
```

업데이트된 스키마는 다음과 같습니다.

```
type Person { 
  id: ID!
  name: String
  age: Int
  occupation: Occupation
}

type Occupation {
  title: String
}

input occupationInput {
  title: String
}

type Mutation {
  addPerson(id: ID!, name: String, age: Int, occupation: occupationInput): Person
}
```

`occupation`은 `occupationInput`에서 `title` 필드를 전달하여 원래 `Occupation` 객체 대신 `Person` 생성을 완료합니다. `addPerson`에 대한 해석기 구현이 있다고 가정하면 이제 실제 변형을 수행할 수 있습니다. `Mutation` 유형이 존재하지만 애플리케이션의 코드에서 실행되도록 하려면 명시적으로 호출해야 합니다. 다음과 같은 `mutation` 키워드를 사용하여 수행할 수 있습니다.

```
mutation createPerson {
  addPerson(id: ID!, name: String, age: Int, occupation: occupationInput) {
    name
    age
    occupation {
      title
    }
  }
}
```

이 변형을 `createPerson`이라고 하며 `addPerson`은 작업입니다. 새 `Person`을 만들려면 `id`, `name`, `age`, `occupation`의 인수를 입력하면 됩니다. `addPerson`의 범위에서는 `name`, `age` 등과 같은 다른 필드도 볼 수 있습니다. 응답은 다음과 같으며, 이러한 필드는 `addPerson` 작업이 완료된 후 반환됩니다. 예제의 마지막 부분은 다음과 같습니다.

```
mutation createPerson {
  addPerson(id: "1", name: "Steve Powers", age: "50", occupation: "Miner") {
    id
    name
    age
    occupation {
      title
    }
  }
}
```

이 변형을 사용하면 결과는 다음과 같을 수 있습니다.

```
{
  "data": {
    "addPerson": {
      "id": "1",
      "name": "Steve Powers",
      "age": "50",
      "occupation": {
        "title": "Miner"
      }
    }
  }
}
```

보는 것과 같이 응답은 요청한 값을 변형에 정의된 것과 동일한 형식으로 반환했습니다. 혼란을 줄이고 이후 더 많은 쿼리가 필요한 상황을 방지하기 위해 수정된 모든 값을 반환하는 것이 좋습니다. 변형을 사용하면 범위 내에 여러 작업을 포함할 수 있습니다. 변형에 나열된 순서대로 순차적으로 실행됩니다. 예를 들어 데이터 소스에 직함을 추가하는 `addOccupation`이라는 또 다른 작업을 만들면 `addPerson` 뒤에 변형에서 이를 직접적으로 호출할 수 있습니다. `addPerson`이 먼저 처리된 후 `addOccupation`이 처리됩니다.

------
#### [ Subscriptions ]

구독은 [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications)을 사용하여 서버와 클라이언트 간의 지속적인 양방향 연결을 제공합니다. 일반적으로 클라이언트는 서버를 구독하거나 수신합니다. 서버에 서버 측 변경이 있거나 서버에서 이벤트를 수행할 때마다 구독한 클라이언트가 업데이트를 받게 됩니다. 이 유형의 프로토콜은 여러 클라이언트가 구독하고 서버나 다른 클라이언트에서 발생하는 변경 사항에 대해 알림을 받아야 하는 경우에 유용합니다. 예를 들어 구독을 사용하여 소셜 미디어 피드를 업데이트할 수 있습니다. 사용자 A와 사용자 B라는 두 명의 사용자가 있는데, 둘 다 다이렉트 메시지를 받을 때마다 자동 알림 업데이트를 받도록 구독합니다. 클라이언트 A의 사용자 A가 클라이언트 B의 사용자 B에게 다이렉트 메시지를 보냅니다. 사용자 A의 클라이언트가 다이렉트 메시지를 보내면 서버에서 처리됩니다. 그러면 서버는 사용자 B의 계정에 다이렉트 메시지를 보내고 클라이언트 B에는 자동 알림을 보냅니다.

다음은 스키마 예제에 추가할 수 있는 `Subscription`의 예입니다.

```
type Subscription {                                   
  personAdded: Person
}
```

이 `personAdded` 필드는 데이터 원본에 새 `Person`이 추가될 때마다 구독한 클라이언트에게 메시지를 보냅니다. `personAdded`에 대한 해석기 구현이 있다고 가정하면 이제 구독을 사용할 수 있습니다. `Subscription` 유형이 존재하지만 애플리케이션의 코드에서 실행되도록 하려면 명시적으로 호출해야 합니다. 다음과 같은 `subscription` 키워드를 사용하여 수행할 수 있습니다.

```
subscription personAddedOperation {
  personAdded {
    id
    name
  }
}
```

구독은 `personAddedOperation`이며, 작업은 `personAdded`입니다. `personAdded`는 새 `Person` 인스턴스의 `id` 및 `name` 필드를 반환합니다. 변형 예제를 살펴보면 이 작업을 사용하여 `Person`을 추가했습니다.

```
addPerson(id: "1", name: "Steve Powers", age: "50", occupation: "Miner")
```

클라이언트가 새로 추가된 `Person`에 대한 업데이트를 구독한 경우 `addPerson` 실행 후 다음과 같은 결과를 확인할 수 있습니다.

```
{
  "data": {
    "personAdded": {
      "id": "1",
      "name": "Steve Powers"
    }
  }
}
```

구독이 제공하는 내용을 요약하면 다음과 같습니다.

구독은 클라이언트와 서버가 빠르면서 안정적인 업데이트를 받을 수 있게 하는 양방향 채널입니다. 일반적으로 표준화되고 안전한 연결을 생성하는 WebSocket 프로토콜을 사용합니다.

구독은 연결 설정 오버헤드를 줄인다는 점에서 유연합니다. 구독하면 클라이언트는 해당 구독을 오랫동안 계속 사용할 수 있습니다. 대부분의 경우 개발자가 구독 기간을 조정하고 요청할 정보를 구성할 수 있도록 함으로써 컴퓨팅 리소스를 효율적으로 사용합니다.

일반적으로 구독을 통해 클라이언트는 한 번에 여러 구독을 설정할 수 있습니다. 이와 관련하여 AWS AppSync구독은 AWS AppSync 서비스로부터 실시간 업데이트를 수신하는 데에만 사용됩니다. 쿼리나 변형을 수행하는 데는 사용할 수 없습니다.

구독의 주요 대안은 지정된 간격으로 쿼리를 전송하여 데이터를 요청하는 폴링입니다. 이 프로세스는 일반적으로 구독보다 덜 효율적이며 클라이언트와 백엔드 모두에 많은 부담을 줍니다.

------

스키마 예제에서 언급되지 않은 한 가지는 특수 객체 유형도 `schema` 루트에 정의되어야 한다는 사실입니다. 따라서 스키마를 내보낼 때 다음과 같 AWS AppSync을 수 있습니다.

------
#### [ schema.graphql ]

```
schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

.
.
.

type Query {                                   
  # code goes here
}
type Mutation {                                   
  # code goes here
}
type Subscription {                                   
  # code goes here
}
```

------

## 열거
<a name="enum-components"></a>

열거 또는 열거형은 유형이나 필드에 사용할 수 있는 유효한 인수를 제한하는 특수 스칼라입니다. 즉, 스키마에 열거형이 정의될 때마다 관련 유형이나 필드가 열거형의 값으로 제한됩니다. 열거형은 문자열 스칼라로 직렬화됩니다. 프로그래밍 언어에 따라 GraphQL 열거형을 다르게 처리할 수 있다는 점에 유의하세요. 예를 들어 JavaScript에는 기본 열거형 지원이 없으므로 열거형 값을 int 값에 대신 매핑할 수 있습니다.

열거형은 `enum` 키워드를 사용하여 정의됩니다. 다음은 그 예입니다.

```
enum trafficSignals {
  solidRed
  solidYellow
  solidGreen
  greenArrowLeft
  ...
}
```

`trafficLights` 열거형을 호출할 때 인수는 `solidRed`, `solidYellow`, `solidGreen` 등만 사용할 수 있습니다. 명확하지만 제한된 수의 선택 사항이 있는 항목을 묘사하는 데 열거형을 사용하는 것이 일반적입니다.

## 결합/인터페이스
<a name="union-interface-components"></a>

GraphQL의 [인터페이스 및 결합](https://docs.aws.amazon.com/appsync/latest/devguide/interfaces-and-unions.html)을 참조하세요.

# GraphQL 필드
<a name="graphql-fields"></a>

필드는 유형의 범위 내에 존재하며 GraphQL 서비스에서 요청한 값을 보유합니다. 이는 다른 프로그래밍 언어의 변수와 매우 유사합니다. 예를 들어, `Person` 객체 유형은 다음과 같습니다.

```
type Person {                                  
   name: String                                  
   age: Int
}
```

이 경우 필드는 `name` 및 `age`이고 각각 `String` 및 `Int` 값을 보유합니다. 위에 표시된 것과 같은 객체 필드를 쿼리 및 변형의 필드(작업)에서 입력으로 사용할 수 있습니다. 그 예로 아래 `Query`를 참조하세요.

```
type Query {                                   
  people: [Person]
}
```

`people` 필드는 데이터 원본의 `Person`에 있는 모든 인스턴스를 요청합니다. GraphQL 서버에서 `Person`을 추가하거나 검색할 때 데이터가 유형 및 필드의 형식을 따를 것으로 예상할 수 있습니다. 즉, 스키마의 데이터 구조에 따라 응답에서 데이터가 구조화되는 방식이 결정됩니다.

```
}
  "data": {
    "people": [
      {
        "name": "John Smith",
        "age": "50"
      },
      {
        "name": "Andrew Miller",
        "age": "60"
      },
      .
      .
      .
    ]
  }
}
```

필드는 데이터를 구조화하는 데 중요한 역할을 합니다. 추가 사용자 지정을 위해 필드에 적용할 수 있는 몇 가지 추가 속성이 아래에 설명되어 있습니다.

## Lists
<a name="list-components"></a>

목록은 지정된 유형의 모든 항목을 반환합니다. 대괄호 `[]`를 사용하여 필드 유형에 목록을 추가할 수 있습니다.

```
type Person { 
  name: String
  age: Int
}
type Query {                                   
  people: [Person]
}
```

`Query`에서 `Person` 주위의 대괄호는 데이터 원본의 `Person`에 있는 모든 인스턴스를 배열로 반환한다는 것을 나타냅니다. 응답에서 각 `Person`의 `name` 및 `age` 값은 구분된 단일 목록으로 반환됩니다.

```
}
  "data": {
    "people": [
      {
        "name": "John Smith",         # Data of Person 1
        "age": "50"
      },
      {
        "name": "Andrew Miller",      # Data of Person 2
        "age": "60"
      },
      .                               # Data of Person N
      .
      .
    ]
  }
}
```

특수 객체 유형에만 국한되지 않습니다. 일반 객체 유형의 필드에서도 목록을 사용할 수 있습니다.

## null 아님
<a name="non-null-components"></a>

null 아님 필드는 응답에서 null이 될 수 없는 필드를 나타냅니다. `!` 기호를 사용하여 필드를 null이 아닌 필드로 설정할 수 있습니다.

```
type Person { 
  name: String!
  age: Int
}
type Query {                                   
  people: [Person]
}
```

`name` 필드는 명시적으로 null이 될 수 없습니다. 데이터 원본을 쿼리하고 이 필드에 null 입력을 제공하면 오류가 발생합니다.

목록과 null이 아닌 필드를 조합할 수 있습니다. 다음 쿼리를 비교해 보세요.

```
type Query {                                   
  people: [Person!]      # Use case 1
}

.
.
.

type Query {                                   
  people: [Person]!      # Use case 2
}

.
.
.

type Query {                                   
  people: [Person!]!     # Use case 3
}
```

사용 사례 1에서는 목록에 null 항목을 포함할 수 없습니다. 사용 사례 2에서는 목록 자체를 null로 설정할 수 없습니다. 사용 사례 3에서는 목록과 해당 항목이 null일 수 없습니다. 하지만 어떤 경우에도 빈 목록을 반환할 수 있습니다.

보는 것과 같이 GraphQL에는 움직이는 구성 요소가 많이 있습니다. 이 섹션에서는 간단한 스키마의 구조와 스키마가 지원하는 여러 유형 및 필드를 보여드렸습니다. 다음 섹션에서는 GraphQL API의 기타 구성 요소와 이러한 구성 요소가 스키마에서 작동하는 방식에 대해 알아봅니다.