

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

# 에서 실시간 데이터 애플리케이션에 구독 사용 AWS AppSync
<a name="aws-appsync-real-time-data"></a>

**중요**  
2025년 3월 13일부터 AWS AppSync 이벤트를 사용하여 WebSockets로 구동되는 실시간 PubSub API를 빌드할 수 있습니다. 자세한 내용은 *AWS AppSync 이벤트 개발자 안내서*의 [WebSocket을 통해 이벤트 게시](https://docs.aws.amazon.com/appsync/latest/eventapi/publish-websocket.html) 섹션을 참조하세요.

AWS AppSync를 사용하면 구독을 활용하여 라이브 애플리케이션 업데이트, 푸시 알림 등을 구현할 수 있습니다. 클라이언트가 GraphQL 구독 작업을 호출하면 AWS AppSync에서 보안 WebSocket 연결을 자동으로 설정하고 유지 관리합니다. 그러면 AWS AppSync가 애플리케이션의 연결 및 조정 요구 사항을 지속적으로 관리하는 동안 애플리케이션은 데이터 원본의 데이터를 구독자에게 실시간으로 배포할 수 있습니다. 다음 섹션에서는 AWS AppSync의 구독 작동 방식을 보여줍니다.

## GraphQL 스키마 구독 명령
<a name="graphql-schema-subscription-directives"></a>

 AWS AppSync의 구독은 변형에 대한 응답으로 호출됩니다. 즉, 변형에 GraphQL 스키마 명령을 지정하여 AWS AppSync에서 모든 데이터 소스를 실시간으로 만들 수 있습니다.

 AWS Amplify 클라이언트 라이브러리는 구독 연결 관리를 자동으로 처리합니다. 라이브러리는 순수 WebSocket을 클라이언트와 서비스 간의 네트워크 프로토콜로 사용합니다.

**참고**  
구독에 연결할 때 권한 부여를 제어하려면 필드 수준 권한 부여에 AWS Identity and Access Management (IAM), AWS Lambda Amazon Cognito 자격 증명 풀 또는 Amazon Cognito 사용자 풀을 사용할 수 있습니다. 구독에 대한 세분화된 액세스 제어를 위해 해석기를 구독 필드에 연결하고 호출자 및 AWS AppSync 데이터 소스의 ID를 사용하여 로직을 수행할 수 있습니다. 자세한 내용은 [GraphQL API를 보호하기 위한 권한 부여 및 인증 구성](security-authz.md) 단원을 참조하십시오.

구독이 변형에서 트리거되고 변형 선택 세트가 구독자에게 전송됩니다.

다음 예에서는 GraphQL 구독을 사용하는 방법을 보여 줍니다. 데이터 원본이 Lambda, Amazon DynamoDB 또는 Amazon OpenSearch Service일 수 있으므로 데이터 원본을 지정하지 않습니다.

구독으로 시작하려면 다음과 같이 스키마에 구독 진입점을 추가해야 합니다.

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

블로그 게시물 사이트가 있고 새 블로그와 기존 블로그에 대한 변경 사항을 구독하려 한다고 가정해 보겠습니다. 이를 수행하려면 스키마에 다음 `Subscription` 정의를 추가합니다.

```
type Subscription {
    addedPost: Post
    updatedPost: Post
    deletedPost: Post
}
```

다음과 같은 변형이 있다고 추가로 가정해 보겠습니다.

```
type Mutation {
    addPost(id: ID! author: String! title: String content: String url: String): Post!
    updatePost(id: ID! author: String! title: String content: String url: String ups: Int! downs: Int! expectedVersion: Int!): Post!
    deletePost(id: ID!): Post!
}
```

다음과 같이 알림을 받고자 하는 각 구독에 대해 `@aws_subscribe(mutations: ["mutation_field_1", "mutation_field_2"])` 명령을 추가하여 이러한 필드를 실시간으로 만들 수 있습니다.

```
type Subscription {
    addedPost: Post
    @aws_subscribe(mutations: ["addPost"])
    updatedPost: Post
    @aws_subscribe(mutations: ["updatePost"])
    deletedPost: Post
    @aws_subscribe(mutations: ["deletePost"])
}
```

`@aws_subscribe(mutations: ["",..,""])`는 변형 입력의 배열을 받기 때문에 구독을 시작하는 여러 변형을 지정할 수 있습니다. 클라이언트로부터 구독하는 경우, GraphQL 쿼리는 다음과 같습니다.

```
subscription NewPostSub {
    addedPost {
        __typename
        version
        title
        content
        author
        url
    }
}
```

이 구독 쿼리는 클라이언트 연결 및 도구에 필요합니다.

순수 WebSocket 클라이언트를 사용하는 경우 각 클라이언트가 자체 선택 세트를 정의할 수 있으므로 선택 세트 필터링은 클라이언트별로 수행됩니다. 이 경우 구독 선택 세트는 변형 선택 세트의 하위 집합이어야 합니다. 예를 들어 `addPost(...){id author title url version}` 변형과 연결된 `addedPost{author title}` 구독은 게시물의 작성자와 제목만 수신합니다. 다른 필드는 수신하지 않습니다. 그러나 변형의 선택 집합에 작성자가 없으면 구독자는 작성자 필드에 대한 `null` 값을 가져옵니다(작성자 필드가 스키마에서 필수/null이 아님으로 정의된 경우에는 오류를 가져옴).

순수 WebSocket을 사용할 때는 구독 선택 세트가 필수입니다. 필드가 구독에 명시적으로 정의되지 않은 경우는 필드를 반환 AWS AppSync 하지 않습니다.

이전 예제에서 구독에는 인수가 없었습니다. 스키마가 다음과 같은 모습이라고 가정합니다.

```
type Subscription {
    updatedPost(id:ID! author:String): Post
    @aws_subscribe(mutations: ["updatePost"])
}
```

이 경우 클라이언트는 구독을 다음과 같이 정의합니다.

```
subscription UpdatedPostSub {
    updatedPost(id:"XYZ", author:"ABC") {
        title
        content
    }
}
```

스키마에서 `subscription` 필드의 반환 형식은 해당 변형 필드의 반환 형식과 동일해야 합니다. 이전 예에서는 이러한 내용이 `addPost`와 `addedPost` 형식으로 반환된 `Post` 둘 다로 표시되었습니다.

클라이언트에 대한 구독을 설정하려면 [Amplify 클라이언트를 사용하여 클라이언트 애플리케이션 빌드](building-a-client-app.md) 섹션을 참조하세요.

## 구독 인수 사용
<a name="using-subscription-arguments"></a>

GraphQL 구독 사용 시 중요한 부분은 인수를 사용하는 시기와 방법을 이해하는 것입니다. 발생한 변형에 대해 클라이언트에게 알리는 방법과 시기를 세밀하게 수정할 수 있습니다. 이렇게 하려면 'Todos'를 생성하는 빠른 시작 장의 샘플 스키마를 참조하세요. 이 샘플 스키마에는 다음 변형이 정의되어 있습니다.

```
type Mutation {
    createTodo(input: CreateTodoInput!): Todo
    updateTodo(input: UpdateTodoInput!): Todo
    deleteTodo(input: DeleteTodoInput!): Todo
}
```

기본 샘플에서 클라이언트는 인수 없이 `onUpdateTodo` `subscription`을 사용하여 `Todo`에 대한 업데이트를 구독할 수 있습니다.

```
subscription OnUpdateTodo {
  onUpdateTodo {
    description
    id
    name
    when
  }
}
```

사용자는 해당 인수를 사용하여 `subscription`을 필터링할 수 있습니다. 예를 들어 특정 `ID`를 사용하는 `todo`가 업데이트될 때만 `subscription`을 트리거하려면 `ID` 값을 지정합니다.

```
subscription OnUpdateTodo {
  onUpdateTodo(id: "a-todo-id") {
    description
    id
    name
    when
  }
}
```

또한 여러 인수를 전달할 수도 있습니다. 예를 들어, 다음 `subscription`은 특정 장소와 시간에 `Todo` 업데이트에 대한 알림을 받는 방법을 보여 줍니다.

```
subscription todosAtHome {
  onUpdateTodo(when: "tomorrow", where: "at home") {
    description
    id
    name
    when
    where
  }
}
```

모든 인수는 선택 사항입니다. `subscription`에 인수를 지정하지 않으면 애플리케이션에서 발생하는 모든 `Todo` 업데이트를 구독하게 됩니다. 하지만 `ID` 인수가 필요하도록 `subscription`의 필드 정의를 업데이트할 수 있습니다. 이렇게 하면 모든 `todo`의 응답이 아닌 특정 `todo`의 응답이 발생합니다.

```
onUpdateTodo(
  id: ID!,
  name: String,
  when: String,
  where: String,
  description: String
): Todo
```

### 인수 null 값에는 의미가 있습니다.
<a name="argument-null-value-has-meaning"></a>

 AWS AppSync에서 구독 쿼리를 만들 때 `null` 인수 값은 인수를 완전히 생략하는 것과 다르게 결과를 필터링합니다.

todos를 생성할 수 있는 todos API 샘플로 돌아가 보겠습니다. 빠른 시작 장의 샘플 스키마를 참조하세요.

`Todo` 유형에 소유자를 설명하는 새 `owner` 필드를 포함하도록 스키마를 수정해 보겠습니다. `owner` 필드는 필수가 아니며 `UpdateTodoInput`에만 설정할 수 있습니다. 다음과 같은 스키마의 단순화된 버전을 참조하세요.

```
type Todo {
  id: ID!
  name: String!
  when: String!
  where: String!
  description: String!
  owner: String
}

input CreateTodoInput {
  name: String!
  when: String!
  where: String!
  description: String!
}

input UpdateTodoInput {
  id: ID!
  name: String
  when: String
  where: String
  description: String
  owner: String
}

type Subscription {
    onUpdateTodo(
        id: ID,
        name: String,
        when: String,
        where: String,
        description: String
    ): Todo @aws_subscribe(mutations: ["updateTodo"])
}
```

다음 구독은 모든 `Todo` 업데이트를 반환합니다.

```
subscription MySubscription {
  onUpdateTodo {
    description
    id
    name
    when
    where
  }
}
```

필드 인수 `owner: null`을 추가하도록 이전 구독을 수정하는 경우 이제 다른 질문을 하게 됩니다. 이제 이 구독은 소유자를 제공하지 않은 모든 `Todo` 업데이트에 대한 알림을 받을 수 있도록 클라이언트를 등록합니다.

```
subscription MySubscription {
  onUpdateTodo(owner: null) {
    description
    id
    name
    when
    where
  }
}
```

**참고**  
**2022년 1월 1일부터 WebSockets을 통한 MQTT는 더 이상 AWS AppSync APIs에서 GraphQL 구독의 프로토콜로 사용할 수 없습니다. 순수 WebSockets는에서 지원되는 유일한 프로토콜입니다 AWS AppSync.**  
2019년 11월 이후에 출시된 AWS AppSync SDK 또는 Amplify 라이브러리를 기반으로 하는 클라이언트는 기본적으로 순수 WebSockets을 자동으로 사용합니다. 클라이언트를 최신 버전으로 업그레이드하면 클라이언트가 AWS AppSync의 순수 WebSockets 엔진을 사용할 수 있습니다.  
순수 WebSocket은 더 큰 페이로드 크기(240KB), 더 다양한 클라이언트 옵션, 개선된 CloudWatch 메트릭을 제공합니다. 순수 WebSocket 사용 방법에 관한 추가 정보는 [ AWS AppSync에서 실시간 WebSocket 클라이언트 빌드](real-time-websocket-client.md) 섹션을 참조하세요.

# 에서 서버리스 WebSockets로 구동되는 일반 pub/sub APIs 생성 AWS AppSync
<a name="aws-appsync-real-time-create-generic-api-serverless-websocket"></a>

**중요**  
2025년 3월 13일부터 AWS AppSync 이벤트를 사용하여 WebSockets로 구동되는 실시간 PubSub API를 빌드할 수 있습니다. 자세한 내용은 *AWS AppSync 이벤트 개발자 안내서*의 [WebSocket을 통해 이벤트 게시](https://docs.aws.amazon.com/appsync/latest/eventapi/publish-websocket.html) 섹션을 참조하세요.

일부 애플리케이션에는 클라이언트가 특정 채널 또는 주제를 청취하는 단순한 WebSocket API만 필요합니다. 특정 형태나 엄격한 형식의 요구 사항이 없는 일반 JSON 데이터는 순수하고 단순한 게시-구독(pub/sub) 패턴으로 이러한 채널 중 하나를 청취하는 클라이언트에게 푸시할 수 있습니다.

 AWS AppSync를 사용하면 APIs 백엔드와 클라이언트 측 모두에서 GraphQL 코드를 자동으로 생성하여 몇 분 만에 GraphQL 지식이 거의 또는 전혀 없는 간단한 pub/sub WebSocket API를 구현할 수 있습니다.

## pub-sub API 생성 및 구성
<a name="aws-appsync-real-time-enhanced-filtering-using-pub-sub-apis"></a>

시작하려면 다음을 따르세요.

1. 에 로그인 AWS Management Console 하고 [AppSync 콘솔](https://console.aws.amazon.com/appsync/)을 엽니다.

   1. **대시보드**에서 **API 생성**을 선택합니다.

1. 다음 화면에서 **실시간 API 생성**을 선택한 후 **다음**을 선택합니다.

1. pub/sub API에 대해 기억하기 쉬운 이름을 입력합니다.

1. [프라이빗 API](https://docs.aws.amazon.com/appsync/latest/devguide/using-private-apis.html) 기능을 활성화할 수 있지만, 지금은 사용하지 않는 것이 좋습니다. **다음**을 선택합니다.

1. WebSocket을 사용하여 작동하는 pub/sub API를 자동으로 생성하도록 선택할 수 있습니다. 마찬가지로 지금은 이 기능도 사용하지 않는 것이 좋습니다. **다음**을 선택합니다.

1. **API 생성**을 선택한 다음 몇 분 정도 기다립니다. 계정에 사전 구성된 새로운 AWS AppSync pub/sub API가 생성됩니다 AWS .

API는 AWS AppSync의 내장 로컬 해석기(로컬 해석기 사용에 대한 자세한 내용은 *AWS AppSync 개발자 안내서*의 [자습서: 로컬 해석기](https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-local-resolvers-js.html) 참조)를 사용하여 여러 임시 pub/sub 채널 및 WebSocket 연결을 관리합니다.이 연결은 채널 이름만을 기반으로 구독한 클라이언트에 데이터를 자동으로 전송하고 필터링합니다. API 직접 호출은 API 키로 권한이 부여됩니다.

API가 배포되면 클라이언트 코드를 생성하고 이를 클라이언트 애플리케이션과 통합하기 위한 몇 가지 추가 단계가 제공됩니다. 이 안내서에서는 클라이언트를 빠르게 통합하는 방법의 예로 간단한 React 웹 애플리케이션을 사용합니다.

1. 먼저 로컬 컴퓨터에서 [NPM](https://www.npmjs.com/get-npm)을 사용하여 보일러플레이트 React 앱을 만듭니다.

   ```
   $ npx create-react-app mypubsub-app 
   $ cd mypubsub-app
   ```
**참고**  
이 예제에서는 [Amplify 라이브러리](https://docs.amplify.aws/lib/)를 사용하여 클라이언트를 백엔드 API에 연결합니다. 하지만 Amplify CLI 프로젝트를 로컬에서 생성할 필요는 없습니다. 이 예제에서는 React를 클라이언트로 선택했지만 Amplify 라이브러리는 iOS, Android 및 Flutter 클라이언트도 지원하므로 서로 다른 런타임에서 동일한 기능을 제공합니다. 지원되는 Amplify 클라이언트는 AWS AppSync 실시간 WebSocket 프로토콜과 완벽하게 호환되는 내장 WebSocket 기능을 포함하여 몇 줄의 코드로 AppSync GraphQL API 백엔드와 상호 작용할 수 있는 간단한 추상화를 제공합니다. [AWS AppSync WebSocket ](https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html)  

   ```
   $ npm install @aws-amplify/api
   ```

1.  AWS AppSync 콘솔에서 **JavaScript**를 선택한 다음 **다운로드**를 선택하여 API 구성 세부 정보 및 생성된 GraphQL 작업 코드가 포함된 단일 파일을 다운로드합니다.

1. 다운로드한 파일을 React 프로젝트의 `/src` 폴더에 복사합니다.

1. 다음으로, 기존 보일러플레이트 `src/App.js` 파일의 내용을 콘솔에서 사용할 수 있는 샘플 클라이언트 코드로 대체합니다.

1. 다음 명령을 사용하여 로컬에서 애플리케이션을 시작합니다.

   ```
   $ npm start
   ```

1. 실시간 데이터 전송 및 수신을 테스트하려면 두 개의 브라우저 창을 열고 *localhost:3000*에 액세스하세요. 샘플 애플리케이션은 일반 JSON 데이터를 *robots*라는 하드 코딩된 채널로 전송하도록 구성되어 있습니다.

1.  브라우저 창 중 하나에서 텍스트 상자에 다음 JSON blob을 입력한 다음 **제출**을 클릭합니다.

   ```
   {
     "robot":"r2d2",
     "planet": "tatooine"
   }
   ```

두 브라우저 인스턴스 모두 *robots* 채널을 구독하며 게시된 데이터를 실시간으로 수신하여 웹 애플리케이션 하단에 표시합니다.

![\[pub/sub API용 React 앱 예시\]](http://docs.aws.amazon.com/ko_kr/appsync/latest/devguide/images/pub-sub-react.png)


스키마, 해석기, 작업 등 필요한 모든 GraphQL API 코드가 자동으로 생성되어 일반 pub/sub 사용 사례를 지원합니다. 백엔드에서 데이터는 다음과 같은 GraphQL 변형을 사용하여 AWS AppSync의 실시간 엔드포인트에 게시됩니다.

```
mutation PublishData {
    publish(data: "{\"msg\": \"hello world!\"}", name: "channel") {
        data
        name
    }
}
```

구독자는 관련 GraphQL 구독을 통해 특정 임시 채널로 전송되는 게시된 데이터에 액세스합니다.

```
subscription SubscribeToData {
    subscribe(name:"channel") {
        name
        data
    }
}
```

## 기존 애플리케이션에 pub-sub API 구현
<a name="aws-appsync-real-time-enhanced-filtering-existing-apps"></a>

기존 애플리케이션에 실시간 기능을 구현해야 하는 경우 이 일반 pub/sub API 구성을 모든 애플리케이션 또는 API 기술에 쉽게 통합할 수 있습니다. 단일 API 엔드포인트를 사용하여 GraphQL을 사용한 단일 네트워크 호출에서 하나 이상의 데이터 소스의 데이터에 안전하게 액세스, 조작 및 결합하는 이점이 있지만 AWS , AppSync의 실시간 기능을 활용하기 위해 기존 REST 기반 애플리케이션을 처음부터 변환하거나 다시 빌드할 필요가 없습니다. 예를 들어, 실시간 및 pub/sub 목적으로만 기존 애플리케이션에서 일반 pub/sub API로 메시지나 이벤트를 보내고 받는 클라이언트가 있는 기존 CRUD 워크로드를 별도의 API 엔드포인트에 둘 수 있습니다.

# 에서 향상된 구독 필터 정의 AWS AppSync
<a name="aws-appsync-real-time-enhanced-filtering"></a>

**중요**  
2025년 3월 13일부터 AWS AppSync 이벤트를 사용하여 WebSockets로 구동되는 실시간 PubSub API를 빌드할 수 있습니다. 자세한 내용은 *AWS AppSync 이벤트 개발자 안내서*의 [WebSocket을 통해 이벤트 게시](https://docs.aws.amazon.com/appsync/latest/eventapi/publish-websocket.html) 섹션을 참조하세요.

에서는 추가 논리 연산자를 지원하는 필터를 사용하여 GraphQL API 구독 해석기에서 직접 백엔드의 데이터 필터링을 위한 비즈니스 로직을 정의하고 활성화 AWS AppSync할 수 있습니다. 클라이언트의 구독 쿼리에 정의된 구독 인수와는 다르게, 이러한 필터를 구성할 수 있습니다. 구독 인수 사용에 관한 자세한 정보는 [구독 인수 사용](aws-appsync-real-time-data.md#using-subscription-arguments) 섹션을 참조하세요. 연산자 목록은 [AWS AppSync 해석기 매핑 템플릿 유틸리티 참조](resolver-util-reference.md) 섹션을 참조하세요.

이 문서에서는 실시간 데이터 필터링을 다음과 같은 범주로 분류합니다.
+ **기본 필터링** - 구독 쿼리의 클라이언트 정의 인수를 기반으로 필터링합니다.
+ **향상된 필터링** - AWS AppSync 서비스 백엔드에서 중앙에 정의된 로직을 기반으로 필터링합니다.

다음 섹션에서는 향상된 구독 필터를 구성하는 방법을 설명하고 실제 사용 사례를 보여 줍니다.

## GraphQL 스키마에서 구독 정의
<a name="aws-appsync-real-time-enhanced-filtering-using-subscription-filters"></a>

향상된 구독 필터를 사용하려면 GraphQL 스키마에서 구독을 정의한 다음 필터링 확장을 사용하여 향상된 필터를 정의합니다. 향상된 구독 필터링이 작동하는 방식을 설명하려면 티켓 관리 시스템 API를 정의하는 다음 GraphQL 스키마를 예로 AWS AppSync들 수 있습니다.

```
type Ticket {
	id: ID
	createdAt: AWSDateTime
	content: String
	severity: Int
	priority: Priority
	category: String
	group: String
	status: String
	
}

type Mutation {
	createTicket(input: TicketInput): Ticket
}

type Query {
	getTicket(id: ID!): Ticket
}

type Subscription {
	onSpecialTicketCreated: Ticket @aws_subscribe(mutations: ["createTicket"])
	onGroupTicketCreated(group: String!): Ticket @aws_subscribe(mutations: ["createTicket"])
}



enum Priority {
	none
	lowest
	low
	medium
	high
	highest
}

input TicketInput {
	content: String
	severity: Int
	priority: Priority
	category: String
	group: String
```

API용 `NONE` 데이터 원본을 만든 다음 이 데이터 원본을 사용하여 해석기를 `createTicket` 변형에 연결한다고 가정해 보겠습니다. 핸들러는 다음과 같을 수 있습니다.

```
import { util } from '@aws-appsync/utils';

export function request(ctx) {
	return {
		payload: {
			id: util.autoId(),
			createdAt: util.time.nowISO8601(),
			status: 'pending',
			...ctx.args.input,
		},
	};
}

export function response(ctx) {
	return ctx.result;
}
```

**참고**  
향상된 필터는 지정된 구독에서 GraphQL 해석기의 핸들러에 활성화됩니다. 자세한 내용은 [해석기 참조](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-js-version.html)를 참조하세요.

향상된 필터의 동작을 구현하려면 `extensions.setSubscriptionFilter()` 함수를 사용하여 구독하는 클라이언트가 관심을 가질 수 있는 GraphQL 변형의 게시된 데이터에 대해 평가된 필터 식을 정의해야 합니다. 필터링 확장에 대한 자세한 내용은 [확장](https://docs.aws.amazon.com//appsync/latest/devguide/extensions-js.html)을 참조하세요.

다음 섹션에서는 필터링 확장을 사용하여 향상된 필터를 구현하는 방법을 설명합니다.

## 필터링 확장을 사용하여 향상된 구독 필터 생성
<a name="aws-appsync-real-time-enhanced-filtering-defining-filters"></a>

향상된 필터는 구독 해석기의 응답 핸들러에 JSON으로 작성됩니다. 필터는 `filterGroup`이라는 목록으로 그룹화할 수 있습니다. 필터는 각각 필드, 연산자 및 값이 있는 하나 이상의 규칙을 사용하여 정의됩니다. 향상된 필터를 설정하는 `onSpecialTicketCreated`에 대한 새 해석기를 정의해 보겠습니다. 하나의 필터는 AND 로직을 사용하여 평가되고, 필터 그룹의 여러 필터는 OR 로직을 사용하여 평가되는 여러 규칙을 구성할 수 있습니다.

```
import { util, extensions } from '@aws-appsync/utils';

export function request(ctx) {
	// simplfy return null for the payload
	return { payload: null };
}

export function response(ctx) {
	const filter = {
		or: [
			{ severity: { ge: 7 }, priority: { in: ['high', 'medium'] } },
			{ category: { eq: 'security' }, group: { in: ['admin', 'operators'] } },
		],
	};
	extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));

  // important: return null in the response
	return null;
}
```

이전 예제에 정의된 필터를 기반으로 다음을 사용하여 티켓을 만들면 중요한 티켓이 구독하는 API 클라이언트에게 자동으로 푸시됩니다.
+ `priority` 수준 `high` 또는 `medium`

  AND 
+ `severity` 수준이 `7`보다 크거나 같음(`ge`)

또는 
+ `classification` 티켓이 `Security`로 설정됨 

  AND 
+ `group` 할당이 `admin` 또는 `operators`로 설정됨

![\[티켓 필터링 쿼리를 보여주는 예시\]](http://docs.aws.amazon.com/ko_kr/appsync/latest/devguide/images/aws-priority-example.png)


구독 해석기에 정의된 필터(향상된 필터링)는 구독 인수만을 기반으로 하는 필터링(기본 필터링)보다 우선합니다. 구독 인수 사용에 관한 자세한 정보는 [구독 인수 사용](https://docs.aws.amazon.com//appsync/latest/devguide/aws-appsync-real-time-data.html#using-subscription-arguments)을 참조하세요.

구독의 GraphQL 스키마에서 인수가 정의되고 필요한 경우 해당 인수가 해석기의 `extensions.setSubscriptionFilter()` 메서드에 규칙으로 정의된 경우에만 지정된 인수를 기반으로 필터링됩니다. 하지만 구독 해석기에 `extensions` 필터링 메서드가 없는 경우 클라이언트에 정의된 인수는 기본 필터링에만 사용됩니다. 기본 필터링과 향상된 필터링은 동시에 사용할 수 없습니다.

구독의 필터 확장 로직에 있는 [`context` 변수](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)를 사용하여 요청에 대한 컨텍스트 정보에 액세스할 수 있습니다. 예를 들어 권한 부여를 위해 Amazon Cognito 사용자 풀, OIDC 또는 Lambda 사용자 지정 권한 부여자를 사용하는 경우 구독이 설정되면 `context.identity`의 사용자에 대한 정보를 검색할 수 있습니다. 이 정보를 사용하여 사용자의 자격 증명을 기반으로 필터를 설정할 수 있습니다.

이제 `onGroupTicketCreated`에 대한 향상된 필터 동작을 구현한다고 가정해 보겠습니다. `onGroupTicketCreated` 구독에는 필수 `group` 이름이 인수로 필요합니다. 티켓을 만들면 티켓에 `pending` 상태가 자동으로 지정됩니다. 제공된 그룹에 속하는 새로 만들어진 티켓만 받도록 구독 필터를 설정할 수 있습니다.

```
import { util, extensions } from '@aws-appsync/utils';

export function request(ctx) {
	// simplfy return null for the payload
	return { payload: null };
}

export function response(ctx) {
	const filter = { group: { eq: ctx.args.group }, status: { eq: 'pending' } };
	extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));

	return null;
}
```

다음 예와 같이 변형을 사용하여 데이터를 게시하는 경우:

```
mutation CreateTicket {
  createTicket(input: {priority: medium, severity: 2, group: "aws"}) {
    id
    priority
    severity
    status
    group
    createdAt
  }
}
```

구독한 클라이언트는 `createTicket` 변형이 포함된 티켓이 생성되는 즉시 WebSocket을 통해 데이터가 자동으로 푸시되도록 수신 대기합니다.

```
subscription OnGroup {
  onGroupTicketCreated(group: "aws") {
    category
    status
    severity
    priority
    id
    group
    createdAt
    content
  }
}
```

필터링 로직은 향상된 필터링으로 AWS AppSync 서비스에 구현되어 클라이언트 코드를 간소화하므로 인수 없이 클라이언트를 구독할 수 있습니다. 클라이언트는 정의된 필터 기준을 충족하는 경우에만 데이터를 수신합니다.

## 중첩된 스키마 필드에 대한 향상된 필터 정의
<a name="aws-appsync-real-time-enhanced-filters-nested-schema-fields.title"></a>

향상된 구독 필터링을 사용하여 중첩된 스키마 필드를 필터링할 수 있습니다. 위치 및 주소 유형을 포함하도록 이전 섹션의 스키마를 수정했다고 가정해 보겠습니다.

```
type Ticket {
	id: ID
	createdAt: AWSDateTime
	content: String
	severity: Int
	priority: Priority
	category: String
	group: String
	status: String
	location: ProblemLocation
}

type Mutation {
	createTicket(input: TicketInput): Ticket
}

type Query {
	getTicket(id: ID!): Ticket
}

type Subscription {
	onSpecialTicketCreated: Ticket @aws_subscribe(mutations: ["createTicket"])
	onGroupTicketCreated(group: String!): Ticket @aws_subscribe(mutations: ["createTicket"])
}

type ProblemLocation {
	address: Address
}

type Address {
	country: String
}

enum Priority {
	none
	lowest
	low
	medium
	high
	highest
}

input TicketInput {
	content: String
	severity: Int
	priority: Priority
	category: String
	group: String
	location: AWSJSON
```

이 스키마에서는 `.` 구분 기호를 사용하여 중첩을 나타낼 수 있습니다. 다음 예제에서는 `location.address.country` 아래에 중첩된 스키마 필드에 대한 필터 규칙을 추가합니다. 티켓 주소가 `USA`로 설정되면 구독이 트리거됩니다.

```
import { util, extensions } from '@aws-appsync/utils';

export const request = (ctx) => ({ payload: null });

export function response(ctx) {
	const filter = {
		or: [
			{ severity: { ge: 7 }, priority: { in: ['high', 'medium'] } },
			{ category: { eq: 'security' }, group: { in: ['admin', 'operators'] } },
			{ 'location.address.country': { eq: 'USA' } },
		],
	};
	extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));
	return null;
}
```

위의 예에서 `location`은 중첩 수준 1을, `address`는 중첩 수준 2, `country`는 중첩 수준 3을 나타내며, 모두 `.` 구분 기호로 구분됩니다.

`createTicket` 변형을 사용하여 이 구독을 테스트할 수 있습니다.

```
mutation CreateTicketInUSA {
  createTicket(input: {location: "{\"address\":{\"country\":\"USA\"}}"}) {
    category
    content
    createdAt
    group
    id
    location {
      address {
        country
      }
    }
    priority
    severity
    status
  }
}
```

## 클라이언트에서 향상된 필터 정의
<a name="aws-appsync-real-time-enhanced-filtering-defining-from-client"></a>

GraphQL의 기본 필터링을 [구독 인수](https://docs.aws.amazon.com/appsync/latest/devguide/aws-appsync-real-time-data.html#using-subscription-arguments)와 함께 사용할 수 있습니다. 구독 쿼리에서 호출을 수행하는 클라이언트가 인수 값을 정의합니다. `extensions` 필터링을 사용하여 AWS AppSync 구독 해석기에서 향상된 필터를 활성화하면 해석기에 정의된 백엔드 필터가 우선합니다.

구독의 `filter` 인수를 사용하여 동적 클라이언트 정의 향상된 필터를 구성하세요. 이러한 필터를 구성할 때는 새 인수를 반영하도록 GraphQL 스키마를 업데이트해야 합니다.

```
...
type Subscription {
    onSpecialTicketCreated(filter: String): Ticket
        @aws_subscribe(mutations: ["createTicket"])
}
...
```

그러면 클라이언트는 다음 예와 같이 구독 쿼리를 전송할 수 있습니다.

```
subscription onSpecialTicketCreated($filter: String) {
     onSpecialTicketCreated(filter: $filter) {
        id
        group
        description
        priority
        severity
     }
 }
```

다음 예와 같이 쿼리 변수를 구성할 수 있습니다.

```
{"filter" : "{\"severity\":{\"le\":2}}"}
```

구독 응답 매핑 템플릿에서 `util.transform.toSubscriptionFilter()` 해석기 유틸리티를 구현하여 구독 인수에 정의된 필터를 각 클라이언트의 적용할 수 있습니다.

```
import { util, extensions } from '@aws-appsync/utils';

export function request(ctx) {
	// simplfy return null for the payload
	return { payload: null };
}

export function response(ctx) {
	const filter = ctx.args.filter;
	extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));
	return null;
}
```

이 전략을 통해 클라이언트는 향상된 필터링 로직과 추가 연산자를 사용하는 자체 필터를 정의할 수 있습니다. 지정된 클라이언트가 보안 WebSocket 연결에서 구독 쿼리를 간접적으로 호출하면 필터가 할당됩니다. `filter` 쿼리 변수 페이로드의 형식을 비롯하여 향상된 필터링을 위한 변환 유틸리티에 대한 자세한 내용은 [JavaScript 해석기 개요](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-overview-js.html)를 참조하세요.

## 향상된 필터링 추가 제한 사항
<a name="aws-appsync-real-time-enhanced-filtering-additional-restrictions"></a>

다음은 향상된 필터에 추가 제한이 적용되는 몇 가지 사용 사례입니다.
+ 향상된 필터는 최상위 객체 목록에 대한 필터링을 지원하지 않습니다. 이 사용 사례에서는 향상된 구독을 위해 변형의 게시된 데이터가 무시됩니다.
+ AWS AppSync는 최대 5개의 중첩 수준을 지원합니다. 중첩 수준 5를 초과하는 스키마 필드에 대한 필터는 무시됩니다. 아래 GraphQL 응답을 예로 들어 보겠습니다. `venue.address.country.metadata.continent`의 `continent` 필드는 수준 5 중첩이므로 허용됩니다. 그러나 `venue.address.country.metadata.capital.financial`의 `financial`은 수준 6 중첩이므로 필터가 작동하지 않습니다.

  ```
  {
      "data": {
          "onCreateFilterEvent": {
              "venue": {
                  "address": {
                      "country": {
                          "metadata": {
                              "capital": {
                                  "financial": "New York"
                              },
                              "continent" : "North America"
                          }
                      },
                      "state": "WA"
                  },
                  "builtYear": 2023
              },
              "private": false,
          }
      }
  }
  ```

# 의 필터를 사용하여 WebSocket 연결 구독 취소 AWS AppSync
<a name="aws-appsync-real-time-invalidation"></a>

**중요**  
2025년 3월 13일부터 AWS AppSync 이벤트를 사용하여 WebSockets로 구동되는 실시간 PubSub API를 빌드할 수 있습니다. 자세한 내용은 *AWS AppSync 이벤트 개발자 안내서*의 [WebSocket을 통해 이벤트 게시](https://docs.aws.amazon.com/appsync/latest/eventapi/publish-websocket.html) 섹션을 참조하세요.

에서는 특정 필터링 로직을 기반으로 연결된 클라이언트에서 WebSocket 연결을 강제로 구독 취소하고 닫을 AWS AppSync수 있습니다(유효하지 않음). 이 기능은 그룹에서 사용자를 제거하는 경우와 같은 권한 부여 관련 시나리오에서 유용합니다.

구독 무효화는 변형에 정의된 페이로드에 대한 응답으로 발생합니다. 구독 연결을 무효화하는 데 사용되는 변형는 API의 관리 작업으로 취급하고 그에 따라 관리 사용자, 그룹 또는 백엔드 서비스로 사용을 제한하여 권한 범위를 지정하는 것이 좋습니다. 예를 들어 `@aws_auth(cognito_groups: ["Administrators"])` 또는 `@aws_iam`과 같은 스키마 권한 부여 지시문을 사용할 수 있습니다. 자세한 내용은 [추가 권한 부여 모드 사용](https://docs.aws.amazon.com/appsync/latest/devguide/security-authz.html#using-additional-authorization-modes)을 참조하세요.

무효화 필터는 [향상된 구독 필터](https://docs.aws.amazon.com/appsync/latest/devguide/aws-appsync-real-time-enhanced-filtering.html)와 동일한 구문 및 로직을 사용합니다. 다음 유틸리티를 사용하여 이러한 필터를 정의하세요.
+ `extensions.invalidateSubscriptions()` - 변형에 대한 GraphQL 해석기의 응답 핸들러에서 정의됩니다.
+ `extensions.setSubscriptionInvalidationFilter()` - 변형에 연결된 구독의 GraphQL해석기의 응답 핸들러에서 정의됩니다.

무효화 필터링 확장에 대한 자세한 내용은 [JavaScript 해석기 개요](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-overview-js.html)를 참조하세요.

## 구독 무효화 사용
<a name="aws-appsync-real-time-invalidation-using-invalidations"></a>

구독 무효화의 작동 방식을 확인하려면 다음 GraphQL 스키마를 AWS AppSync사용합니다.

```
type User {
  userId: ID!
  groupId: ID!
}
    
type Group {
  groupId: ID!
  name: String!
  members: [ID!]!
}

type GroupMessage {
  userId: ID!
  groupId: ID!
  message: String!
}

type Mutation {
    createGroupMessage(userId: ID!, groupId : ID!, message: String!): GroupMessage
    removeUserFromGroup(userId: ID!, groupId : ID!) : User @aws_iam
}

type Subscription {
    onGroupMessageCreated(userId: ID!, groupId : ID!): GroupMessage
        @aws_subscribe(mutations: ["createGroupMessage"])
}

type Query {
	none: String
}
```

`removeUserFromGroup` 변형 해석기 코드에서 무효화 필터를 정의하세요.

```
import { extensions } from '@aws-appsync/utils';

export function request(ctx) {
	return { payload: null };
}

export function response(ctx) {
	const { userId, groupId } = ctx.args;
	extensions.invalidateSubscriptions({
		subscriptionField: 'onGroupMessageCreated',
		payload: { userId, groupId },
	});
	return { userId, groupId };
}
```

변형이 간접적으로 호출되면 `subscriptionField`에 정의된 구독을 취소하는 데 `payload` 객체에 정의된 데이터가 사용됩니다. 무효화 필터는 `onGroupMessageCreated` 구독의 응답 매핑 템플릿에도 정의되어 있습니다.

필터에 정의된 대로 구독한 클라이언트의 ID와 일치하는 ID가 `extensions.invalidateSubscriptions()` 페이로드에 포함된 경우 해당 구독은 구독 취소됩니다. 또한 WebSocket 연결도 종료됩니다. `onGroupMessageCreated` 구독의 구독 해석기 코드를 정의하세요.

```
import { util, extensions } from '@aws-appsync/utils';

export function request(ctx) {
	// simplfy return null for the payload
	return { payload: null };
}

export function response(ctx) {
	const filter = { groupId: { eq: ctx.args.groupId } };
	extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter(filter));

	const invalidation = { groupId: { eq: ctx.args.groupId }, userId: { eq: ctx.args.userId } };
	extensions.setSubscriptionInvalidationFilter(util.transform.toSubscriptionFilter(invalidation));

	return null;
}
```

참고로 구독 응답 핸들러에는 구독 필터와 무효화 필터를 동시에 정의할 수 있습니다.

예를 들어 클라이언트 A가 다음 구독 요청을 사용하여 ID가 `group-1`인 그룹에 ID가 `user-1`인 새로운 사용자를 구독한다고 가정해 보겠습니다.

```
onGroupMessageCreated(userId : "user-1", groupId: :"group-1"){...}
```

AWS AppSync 는 이전 `onGroupMessageCreated` 응답 매핑 템플릿에 정의된 대로 구독 및 무효화 필터를 생성하는 구독 해석기를 실행합니다. 클라이언트 A의 경우 구독 필터는 데이터가 `group-1`로만 전송될 수 있게 하며, 무효화 필터는 `user-1` 및 `group-1` 모두에 대해 정의됩니다.

이제 클라이언트 B가 다음 구독 요청을 사용하여 ID가 `group-2`인 그룹에 ID가 `user-2`인 사용자를 구독한다고 가정해 보겠습니다.

```
onGroupMessageCreated(userId : "user-2", groupId: :"group-2"){...}
```

AWS AppSync 는 구독 및 무효화 필터를 생성하는 구독 해석기를 실행합니다. 클라이언트 B의 경우 구독 필터는 데이터가 `group-2`로만 전송될 수 있게 하며, 무효화 필터는 `user-2` 및 `group-2` 모두에 대해 정의됩니다.

다음으로, 다음 예와 같이 변형 요청을 사용하여 ID가 `message-1`인 새로운 그룹 메시지를 생성한다고 가정해 보겠습니다.

```
createGroupMessage(id: "message-1", groupId :
      "group-1", message: "test message"){...}
```

정의된 필터와 일치하는 구독 클라이언트는 WebSocket을 통해 다음 데이터 페이로드를 자동으로 수신합니다.

```
{
  "data": {
    "onGroupMessageCreated": {
      "id": "message-1",
      "groupId": "group-1",
      "message": "test message",
    }
  }
}
```

필터링 기준이 정의된 구독 필터와 일치하므로 클라이언트 A가 메시지를 수신합니다. 하지만 사용자가 `group-1`에 속하지 않으므로 클라이언트 B는 메시지를 수신하지 않습니다. 또한 요청이 구독 해석기에 정의된 구독 필터와 일치하지 않습니다.

마지막으로, 다음 변형 요청을 사용하여 `user-1`이 `group-1`에서 제거되었다고 가정해 보겠습니다.

```
removeUserFromGroup(userId: "user-1", groupId : "group-1"){...}
```

변형은 `extensions.invalidateSubscriptions()` 해석기 응답 핸들러 코드에 정의된 대로 구독 무효화를 시작한 AWS AppSync 다음 클라이언트 A의 구독을 취소하고 WebSocket 연결을 닫습니다. 변형에 정의된 무효화 페이로드가 해당 사용자 또는 그룹과 일치하지 않으므로 클라이언트 B는 영향을 받지 않습니다.

가 연결을 AWS AppSync 무효화하면 클라이언트는 구독이 취소되었음을 확인하는 메시지를 수신합니다.

```
{
  "message": "Subscription complete."
}
```

## 구독 무효화 필터에 컨텍스트 변수 사용
<a name="aws-appsync-real-time-invalidation-context"></a>

향상된 구독 필터와 마찬가지로 구독 무효화 필터 확장의 [`context` 변수](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html)를 사용하여 특정 데이터에 액세스할 수 있습니다.

예를 들어, 이메일 주소를 변형의 무효화 페이로드로 구성한 다음 Amazon Cognito 사용자 풀 또는 OpenID Connect를 통해 권한이 부여된 구독 사용자의 이메일 특성 또는 클레임과 일치시킬 수 있습니다. `extensions.setSubscriptionInvalidationFilter()` 구독 무효 검증기에 정의된 무효화 필터는 변형의 `extensions.invalidateSubscriptions()` 페이로드에 의해 설정된 이메일 주소가 `context.identity.claims.email`에 있는 사용자의 JWT 토큰에서 검색된 이메일 주소와 일치하는지 확인하여 무효화를 시작합니다.

# AWS AppSync에서 실시간 WebSocket 클라이언트 빌드
<a name="real-time-websocket-client"></a>

**중요**  
2025년 3월 13일부터 AWS AppSync 이벤트를 사용하여 WebSockets로 구동되는 실시간 PubSub API를 빌드할 수 있습니다. 자세한 내용은 *AWS AppSync 이벤트 개발자 안내서*의 [WebSocket을 통해 이벤트 게시](https://docs.aws.amazon.com/appsync/latest/eventapi/publish-websocket.html) 섹션을 참조하세요.

AWS AppSync의 실시간 WebSocket 클라이언트는 다단계 프로세스를 통해 GraphQL 구독을 활성화합니다. 클라이언트는 먼저 AWS AppSync 실시간 엔드포인트와 WebSocket 연결을 설정하고 연결 초기화 메시지를 전송한 다음, 확인을 기다립니다. 연결에 성공하면 클라이언트는 고유한 IDs 및 GraphQL 쿼리로 시작 메시지를 전송하여 구독을 등록합니다. AWS AppSync는 승인 메시지로 성공적인 구독을 확인합니다. 그런 다음, 클라이언트는 해당 변형에서 트리거된 구독 이벤트를 수신합니다. 연결을 유지하기 위해 AWS AppSync는 주기적 연결 유지 메시지를 전송합니다. 기간이 종료되면 클라이언트는 중지 메시지를 전송하여 구독 등록을 취소합니다. 이 시스템은 단일 WebSocket 연결에서 여러 구독을 지원하며 API 키, Amazon Cognito 사용자 풀, IAM 및 Lambda를 비롯한 다양한 권한 부여 모드를 수용합니다.

## GraphQL 구독을 위한 실시간 WebSocket 클라이언트 구현
<a name="appsynclong-real-time-websocket-client-implementation-guide-for-graphql-subscriptions"></a>

다음 시퀀스 다이어그램 및 단계는 WebSocket 클라이언트, HTTP 클라이언트 및 AWS AppSync 간의 실시간 구독 워크플로를 보여줍니다.

![\[Sequence diagram showing WebSocket client, AppSync endpoints, and HTTP client interactions for real-time subscriptions.\]](http://docs.aws.amazon.com/ko_kr/appsync/latest/devguide/images/realtime-client-flow.png)


1. 클라이언트는 AWS AppSync 실시간 엔드포인트와 WebSocket 연결을 설정합니다. 네트워크 오류가 있는 경우 클라이언트는 지터 지수 백오프를 수행해야 합니다. 자세한 내용은 AWS 아키텍처 블로그의 [지수 백오프 및 지터](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/)를 참조하세요.

1. (선택 사항) WebSocket 연결이 성공적으로 설정되면 클라이언트는 `connection_init` 메시지를 전송합니다.

1. `connection_init`가 전송되면 클라이언트는 AWS AppSync의 `connection_ack` 메시지를 기다립니다. 이 메시지에는 `"ka"`(연결 유지) 메시지의 최대 대기 시간(밀리초)인 `connectionTimeoutMs` 파라미터가 포함됩니다.

1. AWS AppSync는 주기적으로 `"ka"` 메시지를 전송합니다. 클라이언트는 각 `"ka"` 메시지를 수신한 시간을 추적합니다. 클라이언트가 `connectionTimeoutMs`밀리초 이내에 `"ka"` 메시지를 수신하지 못하면 클라이언트는 연결을 종료해야 합니다.

1. 클라이언트는 `start` 구독 메시지를 전송하여 구독을 등록합니다. 단일 WebSocket 연결은 서로 다른 인증 모드에 있더라도 여러 구독을 지원합니다.

1. 클라이언트는 AWS AppSync가 메시지를 보내 성공적인 구독`start_ack`을 확인할 때까지 기다립니다. 오류가 있는 경우 AWS AppSync는 `"type": "error"` 메시지를 반환합니다.

1. 클라이언트는 해당 변형이 직접적으로 호출된 후 전송되는 구독 이벤트를 수신합니다. 쿼리 및 변형은 일반적으로를 통해 AWS AppSync GraphQL 엔드포인트`https://`로 전송됩니다. 구독은 보안 WebSocket AWS ()을 사용하여 AppSync 실시간 엔드포인트를 통해 흐릅니다`wss://`.

1. 클라이언트는 `stop` 구독 메시지를 전송하여 구독을 등록 취소합니다.

1. 모든 구독을 등록 취소하고 WebSocket을 통해 전송되는 메시지가 없는지 확인한 후, 클라이언트는 WebSocket 연결에서 연결을 해제할 수 있습니다.

## WebSocket 연결을 설정하기 위한 핸드셰이크 세부 정보
<a name="handshake-details-to-establish-the-websocket-connection"></a>

 AWS AppSync를 사용하여 성공적인 핸드셰이크를 연결하고 시작하려면 WebSocket 클라이언트에 다음이 필요합니다.
+  AWS AppSync 실시간 엔드포인트
+ 헤더 - AWS AppSync 엔드포인트 및 권한 부여와 관련된 정보를 포함합니다. AWS AppSync는 헤더를 제공하는 다음 세 가지 방법을 지원합니다.
  + 쿼리 문자열을 통한 헤더
    + 헤더 정보는 문자열화된 JSON 객체에서 파생된 base64 문자열로 인코딩됩니다. 이 JSON 객체에는 AWS AppSync 엔드포인트 및 권한 부여와 관련된 세부 정보가 포함되어 있습니다. JSON 객체 내용은 권한 부여 모드에 따라 다릅니다.
  + `Sec-WebSocket-Protocol`을 통한 헤더
    +  AWS AppSync 엔드포인트 및 권한 부여와 관련된 정보가 포함된 문자열화된 JSON 객체의 base64Url-encoded 문자열이 `Sec-WebSocket-Protocol` 헤더의 프로토콜로 전달됩니다. JSON 객체 내용은 권한 부여 모드에 따라 다릅니다.
  + 표준 HTTP 헤더를 통한 헤더:
    + 헤더는 GraphQL 쿼리 및 변형에 대해 헤더가 AWS AppSync에 전달되는 방식과 마찬가지로 연결 요청에서 표준 HTTP 헤더로 전달될 수 있습니다. 그러나 프라이빗 API 연결 요청에는 표준 HTTP 헤더를 통한 헤더 전달이 지원되지 않습니다.
+  `payload` - `payload`의 Base64로 인코딩된 문자열입니다. 페이로드는 쿼리 문자열을 사용하여 헤더가 제공된 경우에만 필요합니다.

WebSocket 클라이언트는 이러한 요구 사항과 함께 `graphql-ws`를 WebSocket 프로토콜로 사용하여 쿼리 문자열이 있는 API 실시간 엔드포인트가 포함된 URL에 연결할 수 있습니다.

### GraphQL 엔드포인트에서 실시간 엔드포인트 검색
<a name="discovering-the-appsync-real-time-endpoint-from-the-appsync-graphql-endpoint"></a>

 AWS AppSync GraphQL 엔드포인트와 AWS AppSync 실시간 엔드포인트는 프로토콜과 도메인에서 약간 다릅니다. AWS Command Line Interface (AWS CLI) 명령를 사용하여 GraphQL 엔드포인트를 검색할 수 있습니다`aws appsync get-graphql-api`.

****AWS AppSync GraphQL 엔드포인트:****  
 `https://example1234567890000.appsync-api.us-east-1.amazonaws.com/graphql`

****AWS AppSync 실시간 엔드포인트:****  
 `wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql`

애플리케이션은 쿼리 및 변형에 대해 모든 HTTP 클라이언트를 사용하여 AWS AppSync GraphQL 엔드포인트(`https://`)에 연결할 수 있습니다. 애플리케이션은 구독용 WebSocket AWS 클라이언트를 사용하여 AppSync 실시간 엔드포인트(`wss://`)에 연결할 수 있습니다.

사용자 지정 도메인 이름을 사용하면 하나의 도메인을 사용하여 두 엔드포인트와 모두 상호 작용할 수 있습니다. 예를 들어 `api.example.com`을 사용자 지정 도메인으로 구성하면 다음 URL을 사용하여 GraphQL 및 실시간 엔드포인트와 상호 작용할 수 있습니다.

**AWS AppSync 사용자 지정 도메인 GraphQL 엔드포인트:**  
`https://api.example.com/graphql`

**AWS AppSync 사용자 지정 도메인 실시간 엔드포인트:**  
`wss://api.example.com/graphql/realtime`

## AWS AppSync API 권한 부여 모드를 기반으로 하는 헤더 파라미터 형식
<a name="header-parameter-format-based-on-appsync-api-authorization-mode"></a>

연결 쿼리 문자열에 사용되는 `header` 객체의 형식은 AWS AppSync API 권한 부여 모드에 따라 다릅니다. 객체의 `host` 필드는 실시간 엔드포인트에 대해 `wss://` 호출이 이루어진 경우에도 연결을 검증하는 데 사용되는 AWS AppSync GraphQL 엔드포인트를 나타냅니다. 핸드셰이크를 시작하고 인증된 연결을 설정하려면 `payload`가 비어 있는 JSON 객체여야 합니다. 페이로드는 헤더가 쿼리 문자열을 통해 전달되는 경우에만 필요합니다.

다음 섹션에서는 각 권한 부여 모드의 헤더 형식을 보여줍니다.

### API 키
<a name="api-key"></a>

#### API 키 헤더
<a name="api-key-list"></a>

**헤더 콘텐츠**
+  `"host": <string>`: AWS AppSync GraphQL 엔드포인트의 호스트 또는 사용자 지정 도메인 이름입니다.
+  `"x-api-key": <string>`: AWS AppSync API에 대해 구성된 API 키입니다.

**예제**

```
{
    "host":"example1234567890000.appsync-api.us-east-1.amazonaws.com",
    "x-api-key":"da2-12345678901234567890123456"
}
```

**쿼리 문자열을 통한 헤더**

먼저 `host` 및 `x-api-key`를 포함하는 JSON 객체가 문자열로 변환됩니다. 다음으로 이 문자열은 base64 인코딩을 사용하여 인코딩됩니다. 결과 base64 인코딩 문자열은 AWS AppSync 실시간 엔드포인트와의 연결을 설정`header`하기 위해 WebSocket URL에 라는 쿼리 파라미터로 추가됩니다. WebSocket 결과 요청 URL은 다음과 같은 형식을 취합니다.

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql?header=eyJob3N0IjoiZXhhbXBsZTEyMzQ1Njc4OTAwMDAuYXBwc3luYy1hcGkudXMtZWFzdC0xLmFtYXpvbmF3cy5jb20iLCJ4LWFtei1kYXRlIjoiMjAyMDA0MDFUMDAxMDEwWiIsIngtYXBpLWtleSI6ImRhMi16NHc0NHZoczV6Z2MzZHRqNXNranJsbGxqaSJ9&payload=e30=
```

base64로 인코딩된 헤더 객체 외에도 빈 JSON 객체 \$1\$1도 base64로 인코딩되어 WebSocket URL에 `payload`로 명명된 별도의 쿼리 파라미터로 포함됩니다.

**`Sec-WebSocket-Protocol`을 통한 헤더**

`host` 및 `x-api-key`를 포함하는 JSON 객체는 문자열로 변환된 다음 base64Url 인코딩을 사용하여 인코딩됩니다. 결과 base64Url-encoded 문자열에는 접두사 `header-`가 붙습니다. 그런 다음 접두사가 붙은이 문자열은 AWS AppSync 실시간 엔드포인트와 WebSocket 연결을 설정할 때 `Sec-WebSocket-Protocol` 헤더`graphql-ws`에 외에도 새 하위 프로토콜로 사용됩니다.

결과 요청 URL은 다음과 같은 형식을 취합니다.

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

`Sec-WebSocket-Protocol` 헤더에는 다음 값이 포함됩니다.

```
"sec-websocket-protocol" : ["graphql-ws", "header-ewogICAgImhvc3QiOiJleGFtcGxlMTIzNDU2Nzg5MDAwMC5hcHBzeW5jLWFwaS51cy1lYXN0LTEuYW1hem9uYXdzLmNvbSIsCiAgICAieC1hcGkta2V5IjoiZGEyLTEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Igp9"]
```

**표준 HTTP 헤더를 통한 헤더**

이 방법에서 호스트 및 API 키 정보는 AWS AppSync 실시간 엔드포인트와 WebSocket 연결을 설정할 때 표준 HTTP 헤더를 사용하여 전송됩니다. 결과 요청 URL은 다음과 같은 형식을 취합니다.

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

요청 헤더에 다음이 포함될 수 있습니다.

```
"sec-websocket-protocol" : ["graphql-ws"]
"host":"example1234567890000.appsync-api.us-east-1.amazonaws.com",
"x-api-key":"da2-12345678901234567890123456"
```

### Amazon Cognito 사용자 풀 및 OpenID Connect(OIDC)
<a name="amazon-cognito-user-pools-and-openid-connect-oidc"></a>

#### Amazon Cognito 및 OIDC 헤더
<a name="amazon-cognito-user-pools-and-openid-connect-oidc-list"></a>

헤더 콘텐츠:
+  `"Authorization": <string>`: JWT ID 토큰입니다. 헤더는 [보유자 체계](https://datatracker.ietf.org/doc/html/rfc6750#section-2.1)를 사용할 수 있습니다.
+  `"host": <string>`: AWS AppSync GraphQL 엔드포인트의 호스트 또는 사용자 지정 도메인 이름입니다.

예제:

```
{
    "Authorization":"eyEXAMPLEiJjbG5xb3A5eW5MK09QYXIrMTJHWEFLSXBieU5WNHhsQjEXAMPLEnM2WldvPSIsImFsZyI6IlEXAMPLEn0.eyEXAMPLEiJhNmNmMjcwNy0xNjgxLTQ1NDItOWYxOC1lNjY0MTg2NjlkMzYiLCJldmVudF9pZCI6ImVkMzM5MmNkLWNjYTMtNGM2OC1hNDYyLTJlZGI3ZTNmY2FjZiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1Njk0NTc3MTgsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC5hcC1zb3V0aGVhc3QtMi5hbWF6b25hd3MuY29tXC9hcC1zb3V0aGVhc3QtMl83OHY0SVZibVAiLCJleHAiOjE1Njk0NjEzMjAsImlhdCI6MTU2OTQ1NzcyMCwianRpIjoiNTgzZjhmYmMtMzk2MS00YzA4LWJhZTAtYzQyY2IxMTM5NDY5IiwiY2xpZW50X2lkIjoiM3FlajVlMXZmMzd1N3RoZWw0dG91dDJkMWwiLCJ1c2VybmFtZSI6ImVsb3EXAMPLEn0.B4EXAMPLEFNpJ6ikVp7e6DRee95V6Qi-zEE2DJH7sHOl2zxYi7f-SmEGoh2AD8emxQRYajByz-rE4Jh0QOymN2Ys-ZIkMpVBTPgu-TMWDyOHhDUmUj2OP82yeZ3wlZAtr_gM4LzjXUXmI_K2yGjuXfXTaa1mvQEBG0mQfVd7SfwXB-jcv4RYVi6j25qgow9Ew52ufurPqaK-3WAKG32KpV8J4-Wejq8t0c-yA7sb8EnB551b7TU93uKRiVVK3E55Nk5ADPoam_WYE45i3s5qVAP_-InW75NUoOCGTsS8YWMfb6ecHYJ-1j-bzA27zaT9VjctXn9byNFZmEXAMPLExw",
    "host":"example1234567890000.appsync-api.us-east-1.amazonaws.com"
}
```

**쿼리 문자열을 통한 헤더**

먼저 `host` 및 `Authorization`를 포함하는 JSON 객체가 문자열로 변환됩니다. 다음으로 이 문자열은 base64 인코딩을 사용하여 인코딩됩니다. 결과 base64 인코딩 문자열은 AWS AppSync 실시간 엔드포인트와의 연결을 설정`header`하기 위해 WebSocket URL에 라는 쿼리 파라미터로 추가됩니다. WebSocket 결과 요청 URL은 다음과 같은 형식을 취합니다.

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql?header=eyJBdXRob3JpemF0aW9uIjoiZXlKcmFXUWlPaUpqYkc1eGIzQTVlVzVNSzA5UVlYSXJNVEpIV0VGTFNYQmllVTVXTkhoc1FqaFBWVzlZTW5NMldsZHZQU0lzSW1Gc1p5STZJbEpUTWpVMkluMC5leUp6ZFdJaU9pSmhObU5tTWpjd055MHhOamd4TFRRMU5ESXRPV1l4T0MxbE5qWTBNVGcyTmpsa016WWlMQ0psZG1WdWRGOXBaQ0k2SW1Wa016TTVNbU5rTFdOallUTXROR00yT0MxaE5EWXlMVEpsWkdJM1pUTm1ZMkZqWmlJc0luUnZhMlZ1WDNWelpTSTZJbUZqWTJWemN5SXNJbk5qYjNCbElqb2lZWGR6TG1OdloyNXBkRzh1YzJsbmJtbHVMblZ6WlhJdVlXUnRhVzRpTENKaGRYUm9YM1JwYldVaU9qRTFOamswTlRjM01UZ3NJbWx6Y3lJNkltaDBkSEJ6T2x3dlhDOWpiMmR1YVhSdkxXbGtjQzVoY0MxemIzVjBhR1ZoYzNRdE1pNWhiV0Y2YjI1aGQzTXVZMjl0WEM5aGNDMXpiM1YwYUdWaGMzUXRNbDgzT0hZMFNWWmliVkFpTENKbGVIQWlPakUxTmprME5qRXpNakFzSW1saGRDSTZNVFUyT1RRMU56Y3lNQ3dpYW5ScElqb2lOVGd6WmpobVltTXRNemsyTVMwMFl6QTRMV0poWlRBdFl6UXlZMkl4TVRNNU5EWTVJaXdpWTJ4cFpXNTBYMmxrSWpvaU0zRmxhalZsTVhabU16ZDFOM1JvWld3MGRHOTFkREprTVd3aUxDSjFjMlZ5Ym1GdFpTSTZJbVZzYjNKNllXWmxJbjAuQjRjZEp0aDNLRk5wSjZpa1ZwN2U2RFJlZTk1VjZRaS16RUUyREpIN3NIT2wyenhZaTdmLVNtRUdvaDJBRDhlbXhRUllhakJ5ei1yRTRKaDBRT3ltTjJZcy1aSWtNcFZCVFBndS1UTVdEeU9IaERVbVVqMk9QODJ5ZVozd2xaQXRyX2dNNEx6alhVWG1JX0syeUdqdVhmWFRhYTFtdlFFQkcwbVFmVmQ3U2Z3WEItamN2NFJZVmk2ajI1cWdvdzlFdzUydWZ1clBxYUstM1dBS0czMktwVjhKNC1XZWpxOHQwYy15QTdzYjhFbkI1NTFiN1RVOTN1S1JpVlZLM0U1NU5rNUFEUG9hbV9XWUU0NWkzczVxVkFQXy1Jblc3NU5Vb09DR1RzUzhZV01mYjZlY0hZSi0xai1iekEyN3phVDlWamN0WG45YnlORlptS0xwQTJMY3h3IiwiaG9zdCI6ImV4YW1wbGUxMjM0NTY3ODkwMDAwLmFwcHN5bmMtYXBpLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tIn0=&payload=e30=
```

base64로 인코딩된 헤더 객체 외에도 빈 JSON 객체 \$1\$1도 base64로 인코딩되어 WebSocket URL에 `payload`로 명명된 별도의 쿼리 파라미터로 포함됩니다.

**`Sec-WebSocket-Protocol`을 통한 헤더**

`host` 및 `Authorization`를 포함하는 JSON 객체는 문자열로 변환된 다음 base64Url 인코딩을 사용하여 인코딩됩니다. 결과 base64Url-encoded 문자열에는 접두사 `header-`가 붙습니다. 그런 다음 접두사가 붙은이 문자열은 AWS AppSync 실시간 엔드포인트와 WebSocket 연결을 설정할 때 `Sec-WebSocket-Protocol` 헤더`graphql-ws`에 외에도 새 하위 프로토콜로 사용됩니다.

결과 요청 URL은 다음과 같은 형식을 취합니다.

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

`Sec-WebSocket-Protocol` 헤더에는 다음 값이 포함됩니다.

```
"sec-websocket-protocol" : ["graphql-ws", "header-ewogICAgImhvc3QiOiJleGFtcGxlMTIzNDU2Nzg5MDAwMC5hcHBzeW5jLWFwaS51cy1lYXN0LTEuYW1hem9uYXdzLmNvbSIsCiAgICAieC1hcGkta2V5IjoiZGEyLTEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Igp9"]
```

**표준 HTTP 헤더를 통한 헤더**

이 방법에서 호스트 및 권한 부여 정보는 AWS AppSync 실시간 엔드포인트와 WebSocket 연결을 설정할 때 표준 HTTP 헤더를 사용하여 전송됩니다. 결과 요청 URL은 다음과 같은 형식을 취합니다.

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

요청 헤더에 다음이 포함될 수 있습니다.

```
"sec-websocket-protocol" : ["graphql-ws"]
"Authorization":"eyEXAMPLEiJjbG5xb3A5eW5MK09QYXIrMTJHWEFLSXBieU5WNHhsQjEXAMPLEnM2WldvPSIsImFsZyI6IlEXAMPLEn0.eyEXAMPLEiJhNmNmMjcwNy0xNjgxLTQ1NDItOWYxOC1lNjY0MTg2NjlkMzYiLCJldmVudF9pZCI6ImVkMzM5MmNkLWNjYTMtNGM2OC1hNDYyLTJlZGI3ZTNmY2FjZiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1Njk0NTc3MTgsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC5hcC1zb3V0aGVhc3QtMi5hbWF6b25hd3MuY29tXC9hcC1zb3V0aGVhc3QtMl83OHY0SVZibVAiLCJleHAiOjE1Njk0NjEzMjAsImlhdCI6MTU2OTQ1NzcyMCwianRpIjoiNTgzZjhmYmMtMzk2MS00YzA4LWJhZTAtYzQyY2IxMTM5NDY5IiwiY2xpZW50X2lkIjoiM3FlajVlMXZmMzd1N3RoZWw0dG91dDJkMWwiLCJ1c2VybmFtZSI6ImVsb3EXAMPLEn0.B4EXAMPLEFNpJ6ikVp7e6DRee95V6Qi-zEE2DJH7sHOl2zxYi7f-SmEGoh2AD8emxQRYajByz-rE4Jh0QOymN2Ys-ZIkMpVBTPgu-TMWDyOHhDUmUj2OP82yeZ3wlZAtr_gM4LzjXUXmI_K2yGjuXfXTaa1mvQEBG0mQfVd7SfwXB-jcv4RYVi6j25qgow9Ew52ufurPqaK-3WAKG32KpV8J4-Wejq8t0c-yA7sb8EnB551b7TU93uKRiVVK3E55Nk5ADPoam_WYE45i3s5qVAP_-InW75NUoOCGTsS8YWMfb6ecHYJ-1j-bzA27zaT9VjctXn9byNFZmEXAMPLExw",
"host":"example1234567890000.appsync-api.us-east-1.amazonaws.com"
```

### IAM
<a name="iam"></a>

#### IAM 헤더
<a name="iam-list"></a>

**헤더 콘텐츠**
+  `"accept": "application/json, text/javascript"`: 상수 `<string>` 파라미터입니다.
+  `"content-encoding": "amz-1.0"`: 상수 `<string>` 파라미터입니다.
+  `"content-type": "application/json; charset=UTF-8"`: 상수 `<string>` 파라미터입니다.
+  `"host": <string>`: AWS AppSync GraphQL 엔드포인트의 호스트입니다.
  + `"x-amz-date": <string>`: 타임스탬프는 협정 세계시(UTC)이고 YYYYMMDD'T'HHMMSS'Z' ISO 8601 형식이어야 합니다. 예를 들어, 20150830T123600Z는 유효한 타임스탬프입니다. 타임스탬프에 밀리초를 포함하지 마십시오. 자세한 정보는 *AWS 일반 참조*의 [서명 버전 4에서 날짜 처리](https://docs.aws.amazon.com/general/latest/gr/sigv4-date-handling.html)를 참조하세요.
  +  `"X-Amz-Security-Token": <string>`: 임시 보안 자격 증명을 사용할 때 필요한 AWS 세션 토큰입니다. 자세한 내용은 IAM 사용 설명서의 [AWS  리소스에서 임시 보안 인증 사용](https://docs.aws.amazon.com//IAM/latest/UserGuide/id_credentials_temp_use-resources.html)**을 잠조하세요.
  +  `"Authorization": <string>`: AWS AppSync 엔드포인트에 대한 Signature Version 4(SigV4) 서명 정보입니다. 서명 프로세스에 대한 자세한 내용은 *AWS 일반 참조*의 [작업 4: HTTP 요청에 서명 추가](https://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html)를 참조하세요.

SigV4 서명 HTTP 요청에는 표준 URL이 포함되며, 이 URL은 `/connect`가 추가된 AWS AppSync GraphQL 엔드포인트입니다. 서비스 엔드포인트 AWS 리전은 AWS AppSync API를 사용하는 리전과 동일하며 서비스 이름은 'appsync'입니다. 서명할 HTTP 요청은 다음과 같습니다.

```
{
  url: "https://example1234567890000.appsync-api.us-east-1.amazonaws.com/graphql/connect",
  data: "{}",
  method: "POST",
  headers: {
    "accept": "application/json, text/javascript",
    "content-encoding": "amz-1.0",
    "content-type": "application/json; charset=UTF-8",
  }
}
```

**예제**

```
{
  "accept": "application/json, text/javascript",
  "content-encoding": "amz-1.0",
  "content-type": "application/json; charset=UTF-8",
  "host": "example1234567890000.appsync-api.us-east-1.amazonaws.com",
  "x-amz-date": "20200401T001010Z",
  "X-Amz-Security-Token": "AgEXAMPLEZ2luX2VjEAoaDmFwLXNvdXRoZWFEXAMPLEcwRQIgAh97Cljq7wOPL8KsxP3YtDuyc/9hAj8PhJ7Fvf38SgoCIQDhJEXAMPLEPspioOztj++pEagWCveZUjKEn0zyUhBEXAMPLEjj//////////8BEXAMPLExODk2NDgyNzg1NSIMo1mWnpESWUoYw4BkKqEFSrm3DXuL8w+ZbVc4JKjDP4vUCKNR6Le9C9pZp9PsW0NoFy3vLBUdAXEXAMPLEOVG8feXfiEEA+1khgFK/wEtwR+9zF7NaMMMse07wN2gG2tH0eKMEXAMPLEQX+sMbytQo8iepP9PZOzlZsSFb/dP5Q8hk6YEXAMPLEYcKZsTkDAq2uKFQ8mYUVA9EtQnNRiFLEY83aKvG/tqLWNnGlSNVx7SMcfovkFDqQamm+88y1OwwAEYK7qcoceX6Z7GGcaYuIfGpaX2MCCELeQvZ+8WxEgOnIfz7GYvsYNjLZSaRnV4G+ILY1F0QNW64S9Nvj+BwDg3ht2CrNvpwjVYlj9U3nmxE0UG5ne83LL5hhqMpm25kmL7enVgw2kQzmU2id4IKu0C/WaoDRuO2F5zE63vJbxN8AYs7338+4B4HBb6BZ6OUgg96Q15RA41/gIqxaVPxyTpDfTU5GfSLxocdYeniqqpFMtZG2n9d0u7GsQNcFkNcG3qDZm4tDo8tZbuym0a2VcF2E5hFEgXBa+XLJCfXi/77OqAEjP0x7Qdk3B43p8KG/BaioP5RsV8zBGvH1zAgyPha2rN70/tT13yrmPd5QYEfwzexjKrV4mWIuRg8NTHYSZJUaeyCwTom80VFUJXG+GYTUyv5W22aBcnoRGiCiKEYTLOkgXecdKFTHmcIAejQ9Welr0a196Kq87w5KNMCkcCGFnwBNFLmfnbpNqT6rUBxxs3X5ntX9d8HVtSYINTsGXXMZCJ7fnbWajhg/aox0FtHX21eF6qIGT8j1z+l2opU+ggwUgkhUUgCH2TfqBj+MLMVVvpgqJsPKt582caFKArIFIvO+9QupxLnEH2hz04TMTfnU6bQC6z1buVe7h+tOLnh1YPFsLQ88anib/7TTC8k9DsBTq0ASe8R2GbSEsmO9qbbMwgEaYUhOKtGeyQsSJdhSk6XxXThrWL9EnwBCXDkICMqdntAxyyM9nWsZ4bL9JHqExgWUmfWChzPFAqn3F4y896UqHTZxlq3WGypn5HHcem2Hqf3IVxKH1inhqdVtkryEiTWrI7ZdjbqnqRbl+WgtPtKOOweDlCaRs3R2qXcbNgVhleMk4IWnF8D1695AenU1LwHjOJLkCjxgNFiWAFEPH9aEXAMPLExA==",
  "Authorization": "AWS4-HMAC-SHA256 Credential=XXXXXXXXXXXXXXXXXXX/20200401/us-east-1/appsync/aws4_request, SignedHeaders=accept;content-encoding;content-type;host;x-amz-date;x-amz-security-token, Signature=83EXAMPLEbcc1fe3ee69f75cd5ebbf4cb4f150e4f99cec869f149c5EXAMPLEdc"
}
```

**쿼리 문자열을 통한 헤더**

먼저 `host` (AWS AppSync GraphQL 엔드포인트) 및 기타 권한 부여 헤더가 포함된 JSON 객체가 문자열로 변환됩니다. 다음으로 이 문자열은 base64 인코딩을 사용하여 인코딩됩니다. 결과 base64 인코딩 문자열은 WebSocket URL에 `header`로 명명된 쿼리 파라미터로 추가됩니다. 결과 요청 URL은 다음과 같은 형식을 취합니다.

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql?header=eyJBdXRob3JpemF0aW9uIjoiZXlKcmFXUWlPaUpqYkc1eGIzQTVlVzVNSzA5UVlYSXJNVEpIV0VGTFNYQmllVTVXTkhoc1FqaFBWVzlZTW5NMldsZHZQU0lzSW1Gc1p5STZJbEpUTWpVMkluMC5leUp6ZFdJaU9pSmhObU5tTWpjd055MHhOamd4TFRRMU5ESXRPV1l4T0MxbE5qWTBNVGcyTmpsa016WWlMQ0psZG1WdWRGOXBaQ0k2SW1Wa016TTVNbU5rTFdOallUTXROR00yT0MxaE5EWXlMVEpsWkdJM1pUTm1ZMkZqWmlJc0luUnZhMlZ1WDNWelpTSTZJbUZqWTJWemN5SXNJbk5qYjNCbElqb2lZWGR6TG1OdloyNXBkRzh1YzJsbmJtbHVMblZ6WlhJdVlXUnRhVzRpTENKaGRYUm9YM1JwYldVaU9qRTFOamswTlRjM01UZ3NJbWx6Y3lJNkltaDBkSEJ6T2x3dlhDOWpiMmR1YVhSdkxXbGtjQzVoY0MxemIzVjBhR1ZoYzNRdE1pNWhiV0Y2YjI1aGQzTXVZMjl0WEM5aGNDMXpiM1YwYUdWaGMzUXRNbDgzT0hZMFNWWmliVkFpTENKbGVIQWlPakUxTmprME5qRXpNakFzSW1saGRDSTZNVFUyT1RRMU56Y3lNQ3dpYW5ScElqb2lOVGd6WmpobVltTXRNemsyTVMwMFl6QTRMV0poWlRBdFl6UXlZMkl4TVRNNU5EWTVJaXdpWTJ4cFpXNTBYMmxrSWpvaU0zRmxhalZsTVhabU16ZDFOM1JvWld3MGRHOTFkREprTVd3aUxDSjFjMlZ5Ym1GdFpTSTZJbVZzYjNKNllXWmxJbjAuQjRjZEp0aDNLRk5wSjZpa1ZwN2U2RFJlZTk1VjZRaS16RUUyREpIN3NIT2wyenhZaTdmLVNtRUdvaDJBRDhlbXhRUllhakJ5ei1yRTRKaDBRT3ltTjJZcy1aSWtNcFZCVFBndS1UTVdEeU9IaERVbVVqMk9QODJ5ZVozd2xaQXRyX2dNNEx6alhVWG1JX0syeUdqdVhmWFRhYTFtdlFFQkcwbVFmVmQ3U2Z3WEItamN2NFJZVmk2ajI1cWdvdzlFdzUydWZ1clBxYUstM1dBS0czMktwVjhKNC1XZWpxOHQwYy15QTdzYjhFbkI1NTFiN1RVOTN1S1JpVlZLM0U1NU5rNUFEUG9hbV9XWUU0NWkzczVxVkFQXy1Jblc3NU5Vb09DR1RzUzhZV01mYjZlY0hZSi0xai1iekEyN3phVDlWamN0WG45YnlORlptS0xwQTJMY3h3IiwiaG9zdCI6ImV4YW1wbGUxMjM0NTY3ODkwMDAwLmFwcHN5bmMtYXBpLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tIn0=&payload=e30=
```

base64로 인코딩된 헤더 객체 외에도 빈 JSON 객체 \$1\$1도 base64로 인코딩되어 WebSocket URL에 `payload`로 명명된 별도의 쿼리 파라미터로 포함됩니다.

**`Sec-WebSocket-Protocol`을 통한 헤더**

`host` 및 기타 권한 부여 헤더가 포함된 JSON 객체는 문자열로 변환된 다음 base64Url 인코딩을 사용하여 인코딩됩니다. 결과 base64Url-encoded 문자열에는 접두사 `header-`가 붙습니다. 그런 다음 접두사가 붙은이 문자열은 AWS AppSync 실시간 엔드포인트와 WebSocket 연결을 설정할 때 `Sec-WebSocket-Protocol` 헤더`graphql-ws`에 외에도 새 하위 프로토콜로 사용됩니다.

결과 요청 URL은 다음과 같은 형식을 취합니다.

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

`Sec-WebSocket-Protocol` 헤더에는 다음 값이 포함됩니다.

```
"sec-websocket-protocol" : ["graphql-ws", "header-ew0KICAiYWNjZXB0IjogImFwcGxpY2F0aW9uL2pzb24sIHRleHQvamF2YXNjcmlwdCIsDQogICJjb250ZW50LWVuY29kaW5nIjogImFtei0xLjAiLA0KICAiY29udGVudC10eXBlIjogImFwcGxpY2F0aW9uL2pzb247IGNoYXJzZXQ9VVRGLTgiLA0KICAiaG9zdCI6ICJleGFtcGxlMTIzNDU2Nzg5MDAwMC5hcHBzeW5jLWFwaS51cy1lYXN0LTEuYW1hem9uYXdzLmNvbSIsDQogICJ4LWFtei1kYXRlIjogIjIwMjAwNDAxVDAwMTAxMFoiLA0KICAiWC1BbXotU2VjdXJpdHktVG9rZW4iOiAiQWdFWEFNUExFWjJsdVgyVmpFQW9hRG1Gd0xYTnZkWFJvWldGRVhBTVBMRWN3UlFJZ0FoOTdDbGpxN3dPUEw4S3N4UDNZdER1eWMvOWhBajhQaEo3RnZmMzhTZ29DSVFEaEpFWEFNUExFUHNwaW9PenRqKytwRWFnV0N2ZVpVaktFbjB6eVVoQkVYQU1QTEVqai8vLy8vLy8vLy84QkVYQU1QTEV4T0RrMk5EZ3lOemcxTlNJTW8xbVducEVTV1VvWXc0QmtLcUVGU3JtM0RYdUw4dytaYlZjNEpLakRQNHZVQ0tOUjZMZTlDOXBacDlQc1cwTm9GeTN2TEJVZEFYRVhBTVBMRU9WRzhmZVhmaUVFQSsxa2hnRksvd0V0d1IrOXpGN05hTU1Nc2UwN3dOMmdHMnRIMGVLTUVYQU1QTEVRWCtzTWJ5dFFvOGllcFA5UFpPemxac1NGYi9kUDVROGhrNllFWEFNUExFWWNLWnNUa0RBcTJ1S0ZROG1ZVVZBOUV0UW5OUmlGTEVZODNhS3ZHL3RxTFdObkdsU05WeDdTTWNmb3ZrRkRxUWFtbSs4OHkxT3d3QUVZSzdxY29jZVg2WjdHR2NhWXVJZkdwYVgyTUNDRUxlUXZaKzhXeEVnT25JZno3R1l2c1lOakxaU2FSblY0RytJTFkxRjBRTlc2NFM5TnZqK0J3RGczaHQyQ3JOdnB3alZZbGo5VTNubXhFMFVHNW5lODNMTDVoaHFNcG0yNWttTDdlblZndzJrUXptVTJpZDRJS3UwQy9XYW9EUnVPMkY1ekU2M3ZKYnhOOEFZczczMzgrNEI0SEJiNkJaNk9VZ2c5NlExNVJBNDEvZ0lxeGFWUHh5VHBEZlRVNUdmU0x4b2NkWWVuaXFxcEZNdFpHMm45ZDB1N0dzUU5jRmtOY0czcURabTR0RG84dFpidXltMGEyVmNGMkU1aEZFZ1hCYStYTEpDZlhpLzc3T3FBRWpQMHg3UWRrM0I0M3A4S0cvQmFpb1A1UnNWOHpCR3ZIMXpBZ3lQaGEyck43MC90VDEzeXJtUGQ1UVlFZnd6ZXhqS3JWNG1XSXVSZzhOVEhZU1pKVWFleUN3VG9tODBWRlVKWEcrR1lUVXl2NVcyMmFCY25vUkdpQ2lLRVlUTE9rZ1hlY2RLRlRIbWNJQWVqUTlXZWxyMGExOTZLcTg3dzVLTk1Da2NDR0Zud0JORkxtZm5icE5xVDZyVUJ4eHMzWDVudFg5ZDhIVnRTWUlOVHNHWFhNWkNKN2ZuYldhamhnL2FveDBGdEhYMjFlRjZxSUdUOGoxeitsMm9wVStnZ3dVZ2toVVVnQ0gyVGZxQmorTUxNVlZ2cGdxSnNQS3Q1ODJjYUZLQXJJRkl2Tys5UXVweExuRUgyaHowNFRNVGZuVTZiUUM2ejFidVZlN2grdE9MbmgxWVBGc0xRODhhbmliLzdUVEM4azlEc0JUcTBBU2U4UjJHYlNFc21POXFiYk13Z0VhWVVoT0t0R2V5UXNTSmRoU2s2WHhYVGhyV0w5RW53QkNYRGtJQ01xZG50QXh5eU05bldzWjRiTDlKSHFFeGdXVW1mV0NoelBGQXFuM0Y0eTg5NlVxSFRaeGxxM1dHeXBuNUhIY2VtMkhxZjNJVnhLSDFpbmhxZFZ0a3J5RWlUV3JJN1pkamJxbnFSYmwrV2d0UHRLT093ZURsQ2FSczNSMnFYY2JOZ1ZobGVNazRJV25GOEQxNjk1QWVuVTFMd0hqT0pMa0NqeGdORmlXQUZFUEg5YUVYQU1QTEV4QT09IiwNCiAgIkF1dGhvcml6YXRpb24iOiAiQVdTNC1ITUFDLVNIQTI1NiBDcmVkZW50aWFsPVhYWFhYWFhYWFhYWFhYWFhYWFgvMjAyMDA0MDEvdXMtZWFzdC0xL2FwcHN5bmMvYXdzNF9yZXF1ZXN0LCBTaWduZWRIZWFkZXJzPWFjY2VwdDtjb250ZW50LWVuY29kaW5nO2NvbnRlbnQtdHlwZTtob3N0O3gtYW16LWRhdGU7eC1hbXotc2VjdXJpdHktdG9rZW4sIFNpZ25hdHVyZT04M0VYQU1QTEViY2MxZmUzZWU2OWY3NWNkNWViYmY0Y2I0ZjE1MGU0Zjk5Y2VjODY5ZjE0OWM1RVhBTVBMRWRjIg0KfQ"]
```

**표준 HTTP 헤더를 통한 헤더**

이 방법에서 호스트 및 기타 권한 부여 정보는 AWS AppSync 실시간 엔드포인트와 WebSocket 연결을 설정할 때 표준 HTTP 헤더를 사용하여 전송됩니다. 결과 요청 URL은 다음과 같은 형식을 취합니다.

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

요청 헤더에 다음이 포함될 수 있습니다.

```
"sec-websocket-protocol" : ["graphql-ws"]
"accept": "application/json, text/javascript",
"content-encoding": "amz-1.0",
"content-type": "application/json; charset=UTF-8",
"host": "example1234567890000.appsync-api.us-east-1.amazonaws.com",
"x-amz-date": "20200401T001010Z",
"X-Amz-Security-Token": "AgEXAMPLEZ2luX2VjEAoaDmFwLXNvdXRoZWFEXAMPLEcwRQIgAh97Cljq7wOPL8KsxP3YtDuyc/9hAj8PhJ7Fvf38SgoCIQDhJEXAMPLEPspioOztj++pEagWCveZUjKEn0zyUhBEXAMPLEjj//////////8BEXAMPLExODk2NDgyNzg1NSIMo1mWnpESWUoYw4BkKqEFSrm3DXuL8w+ZbVc4JKjDP4vUCKNR6Le9C9pZp9PsW0NoFy3vLBUdAXEXAMPLEOVG8feXfiEEA+1khgFK/wEtwR+9zF7NaMMMse07wN2gG2tH0eKMEXAMPLEQX+sMbytQo8iepP9PZOzlZsSFb/dP5Q8hk6YEXAMPLEYcKZsTkDAq2uKFQ8mYUVA9EtQnNRiFLEY83aKvG/tqLWNnGlSNVx7SMcfovkFDqQamm+88y1OwwAEYK7qcoceX6Z7GGcaYuIfGpaX2MCCELeQvZ+8WxEgOnIfz7GYvsYNjLZSaRnV4G+ILY1F0QNW64S9Nvj+BwDg3ht2CrNvpwjVYlj9U3nmxE0UG5ne83LL5hhqMpm25kmL7enVgw2kQzmU2id4IKu0C/WaoDRuO2F5zE63vJbxN8AYs7338+4B4HBb6BZ6OUgg96Q15RA41/gIqxaVPxyTpDfTU5GfSLxocdYeniqqpFMtZG2n9d0u7GsQNcFkNcG3qDZm4tDo8tZbuym0a2VcF2E5hFEgXBa+XLJCfXi/77OqAEjP0x7Qdk3B43p8KG/BaioP5RsV8zBGvH1zAgyPha2rN70/tT13yrmPd5QYEfwzexjKrV4mWIuRg8NTHYSZJUaeyCwTom80VFUJXG+GYTUyv5W22aBcnoRGiCiKEYTLOkgXecdKFTHmcIAejQ9Welr0a196Kq87w5KNMCkcCGFnwBNFLmfnbpNqT6rUBxxs3X5ntX9d8HVtSYINTsGXXMZCJ7fnbWajhg/aox0FtHX21eF6qIGT8j1z+l2opU+ggwUgkhUUgCH2TfqBj+MLMVVvpgqJsPKt582caFKArIFIvO+9QupxLnEH2hz04TMTfnU6bQC6z1buVe7h+tOLnh1YPFsLQ88anib/7TTC8k9DsBTq0ASe8R2GbSEsmO9qbbMwgEaYUhOKtGeyQsSJdhSk6XxXThrWL9EnwBCXDkICMqdntAxyyM9nWsZ4bL9JHqExgWUmfWChzPFAqn3F4y896UqHTZxlq3WGypn5HHcem2Hqf3IVxKH1inhqdVtkryEiTWrI7ZdjbqnqRbl+WgtPtKOOweDlCaRs3R2qXcbNgVhleMk4IWnF8D1695AenU1LwHjOJLkCjxgNFiWAFEPH9aEXAMPLExA==",
"Authorization": "AWS4-HMAC-SHA256 Credential=XXXXXXXXXXXXXXXXXXX/20200401/us-east-1/appsync/aws4_request, SignedHeaders=accept;content-encoding;content-type;host;x-amz-date;x-amz-security-token, Signature=83EXAMPLEbcc1fe3ee69f75cd5ebbf4cb4f150e4f99cec869f149c5EXAMPLEdc"
```

사용자 지정 도메인을 사용하여 요청에 서명하려면 다음을 따르세요.

```
{
  url: "https://api.example.com/graphql/connect",
  data: "{}",
  method: "POST",
  headers: {
    "accept": "application/json, text/javascript",
    "content-encoding": "amz-1.0",
    "content-type": "application/json; charset=UTF-8",
  }
}
```

**예제**

```
{
  "accept": "application/json, text/javascript",
  "content-encoding": "amz-1.0",
  "content-type": "application/json; charset=UTF-8",
  "host": "api.example.com",
  "x-amz-date": "20200401T001010Z",
  "X-Amz-Security-Token": "AgEXAMPLEZ2luX2VjEAoaDmFwLXNvdXRoZWFEXAMPLEcwRQIgAh97Cljq7wOPL8KsxP3YtDuyc/9hAj8PhJ7Fvf38SgoCIQDhJEXAMPLEPspioOztj++pEagWCveZUjKEn0zyUhBEXAMPLEjj//////////8BEXAMPLExODk2NDgyNzg1NSIMo1mWnpESWUoYw4BkKqEFSrm3DXuL8w+ZbVc4JKjDP4vUCKNR6Le9C9pZp9PsW0NoFy3vLBUdAXEXAMPLEOVG8feXfiEEA+1khgFK/wEtwR+9zF7NaMMMse07wN2gG2tH0eKMEXAMPLEQX+sMbytQo8iepP9PZOzlZsSFb/dP5Q8hk6YEXAMPLEYcKZsTkDAq2uKFQ8mYUVA9EtQnNRiFLEY83aKvG/tqLWNnGlSNVx7SMcfovkFDqQamm+88y1OwwAEYK7qcoceX6Z7GGcaYuIfGpaX2MCCELeQvZ+8WxEgOnIfz7GYvsYNjLZSaRnV4G+ILY1F0QNW64S9Nvj+BwDg3ht2CrNvpwjVYlj9U3nmxE0UG5ne83LL5hhqMpm25kmL7enVgw2kQzmU2id4IKu0C/WaoDRuO2F5zE63vJbxN8AYs7338+4B4HBb6BZ6OUgg96Q15RA41/gIqxaVPxyTpDfTU5GfSLxocdYeniqqpFMtZG2n9d0u7GsQNcFkNcG3qDZm4tDo8tZbuym0a2VcF2E5hFEgXBa+XLJCfXi/77OqAEjP0x7Qdk3B43p8KG/BaioP5RsV8zBGvH1zAgyPha2rN70/tT13yrmPd5QYEfwzexjKrV4mWIuRg8NTHYSZJUaeyCwTom80VFUJXG+GYTUyv5W22aBcnoRGiCiKEYTLOkgXecdKFTHmcIAejQ9Welr0a196Kq87w5KNMCkcCGFnwBNFLmfnbpNqT6rUBxxs3X5ntX9d8HVtSYINTsGXXMZCJ7fnbWajhg/aox0FtHX21eF6qIGT8j1z+l2opU+ggwUgkhUUgCH2TfqBj+MLMVVvpgqJsPKt582caFKArIFIvO+9QupxLnEH2hz04TMTfnU6bQC6z1buVe7h+tOLnh1YPFsLQ88anib/7TTC8k9DsBTq0ASe8R2GbSEsmO9qbbMwgEaYUhOKtGeyQsSJdhSk6XxXThrWL9EnwBCXDkICMqdntAxyyM9nWsZ4bL9JHqExgWUmfWChzPFAqn3F4y896UqHTZxlq3WGypn5HHcem2Hqf3IVxKH1inhqdVtkryEiTWrI7ZdjbqnqRbl+WgtPtKOOweDlCaRs3R2qXcbNgVhleMk4IWnF8D1695AenU1LwHjOJLkCjxgNFiWAFEPH9aEXAMPLExA==",
  "Authorization": "AWS4-HMAC-SHA256 Credential=XXXXXXXXXXXXXXXXXXX/20200401/us-east-1/appsync/aws4_request, SignedHeaders=accept;content-encoding;content-type;host;x-amz-date;x-amz-security-token, Signature=83EXAMPLEbcc1fe3ee69f75cd5ebbf4cb4f150e4f99cec869f149c5EXAMPLEdc"
}
```

**쿼리 문자열이 있는 요청 URL**

```
wss://api.example.com/graphql?header=eyEXAMPLEHQiOiJhcHBsaWNhdGlvbi9qc29uLCB0ZXh0L2phdmFEXAMPLEQiLCJjb250ZW50LWVuY29kaW5nIjoEXAMPLEEuMCIsImNvbnRlbnQtdHlwZSI6ImFwcGxpY2F0aW9EXAMPLE47IGNoYXJzZXQ9VVRGLTgiLCJob3N0IjoiZXhhbXBsZEXAMPLENjc4OTAwMDAuYXBwc3luYy1hcGkudXMtZWFzdC0xLmFtYEXAMPLEcy5jb20iLCJ4LWFtei1kYXRlIjoiMjAyMDA0MDFUMDAxMDEwWiIsIlgtEXAMPLElY3VyaXR5LVRva2VuIjoiQWdvSmIzSnBaMmx1WDJWakVBb2FEbUZ3TFhOdmRYUm9aV0Z6ZEMweUlrY3dSUUlnQWg5N0NsanE3d09QTDhLc3hQM1l0RHV5Yy85aEFqOFBoSjdGdmYzOFNnb0NJUURoSllKYkpsbmpQc3Bpb096dGorK3BFYWdXQ3ZlWlVqS0VuMHp5VWhCbXhpck5CUWpqLy8vLy8vLy8vLzhCRUFBYUREY3hPRGsyTkRneU56ZzFOU0lNbzFtV25wRVNXVW9ZdzRCa0txRUZTcm0zRFh1TDh3K1piVmM0SktqRFA0dlVDS05SNkxlOUM5cFpwOVBzVzBOb0Z5M3ZMQlVkQVh3dDZQSld1T1ZHOGZlWGZpRUVBKzFraGdGSy93RXR3Uis5ekY3TmFNTU1zZTA3d04yZ0cydEgwZUtNVFhuOEF3QVFYK3NNYnl0UW84aWVwUDlQWk96bFpzU0ZiL2RQNVE4aGs2WWpHVGFMMWVZY0tac1RrREFxMnVLRlE4bVlVVkE5RXRRbk5SaUZMRVk4M2FLdkcvdHFMV05uR2xTTlZ4N1NNY2ZvdmtGRHFRYW1tKzg4eTFPd3dBRVlLN3Fjb2NlWDZaN0dHY2FZdUlmR3BhWDJNQ0NFTGVRdlorOFd4RWdPbklmejdHWXZzWU5qTFpTYVJuVjRHK0lMWTFGMFFOVzY0UzlOdmorQndEZzNodDJDck52cHdqVllsajlVM25teEUwVUc1bmU4M0xMNWhocU1wbTI1a21MN2VuVmd3MmtRem1VMmlkNElLdTBDL1dhb0RSdU8yRjV6RTYzdkpieE44QVlzNzMzOCs0QjRIQmI2Qlo2T1VnZzk2UTE1UkE0MS9nSXF4YVZQeHlUcERmVFU1R2ZTTHhvY2RZZW5pcXFwRk10WkcybjlkMHU3R3NRTmNGa05jRzNxRFptNHREbzh0WmJ1eW0wYTJWY0YyRTVoRkVnWEJhK1hMSkNmWGkvNzdPcUFFalAweDdRZGszQjQzcDhLRy9CYWlvUDVSc1Y4ekJHdkgxekFneVBoYTJyTjcwL3RUMTN5cm1QZDVRWUVmd3pleGpLclY0bVdJdVJnOE5USFlTWkpVYWV5Q3dUb204MFZGVUpYRytHWVRVeXY1VzIyYUJjbm9SR2lDaUtFWVRMT2tnWGVjZEtGVEhtY0lBZWpROVdlbHIwYTE5NktxODd3NUtOTUNrY0NHRm53Qk5GTG1mbmJwTnFUNnJVQnh4czNYNW50WDlkOEhWdFNZSU5Uc0dYWE1aQ0o3Zm5iV2FqaGcvYW94MEZ0SFgyMWVGNnFJR1Q4ajF6K2wyb3BVK2dnd1Vna2hVVWdDSDJUZnFCaitNTE1WVnZwZ3FKc1BLdDU4MmNhRktBcklGSXZPKzlRdXB4TG5FSDJoejA0VE1UZm5VNmJRQzZ6MWJ1VmU3aCt0T0xuaDFZUEZzTFE4OGFuaWIvN1RUQzhrOURzQlRxMEFTZThSMkdiU0VzbU85cWJiTXdnRWFZVWhPS3RHZXlRc1NKZGhTazZYeFhUaHJXTDlFbndCQ1hEa0lDTXFkbnRBeHl5TTluV3NaNGJMOUpIcUV4Z1dVbWZXQ2h6UEZBcW4zRjR5ODk2VXFIVFp4bHEzV0d5cG41SEhjZW0ySHFmM0lWeEtIMWluaHFkVnRrcnlFaVRXckk3WmRqYnFucVJibCtXZ3RQdEtPT3dlRGxDYVJzM1IycVhjYk5nVmhsZU1rNElXbkY4RDE2OTVBZW5VMUx3SGpPSkxrQ2p4Z05GaVdBRkVQSDlhTklhcXMvWnhBPT0iLCJBdXRob3JpemF0aW9uIjoiQVdTNC1ITUFDLVNIQTI1NiBDcmVkZW50aWFsPVhYWFhYWFhYWFhYWFhYWFhYWFgvMjAxOTEwMDIvdXMtZWFzdC0xEXAMPLE5bmMvYXdzNF9yZXF1ZXN0LCBTaWduZWRIZWFkZXJzPWFjY2VwdDtjb250ZWEXAMPLE29kaW5nO2NvbnRlbnQtdHlwZTtob3EXAMPLEW16LWRhdGU7eC1hbXotc2VjdXJpdHktdG9rZW4sIFNpZ25hdHVyZT04MzE4EXAMPLEiY2MxZmUzZWU2OWY3NWNkEXAMPLE0Y2I0ZjE1MGU0Zjk5Y2VjODY5ZjE0OWM1ZDAzNDEXAMPLEn0=&payload=e30=
```

**참고**  
하나의 WebSocket 연결에는 여러 개의 구독이 있을 수 있습니다(각 구독이 서로 다른 인증 모드를 사용하더라도 상관없음). 이 연결을 구현하는 한 가지 방법은 첫 번째 구독에 대한 WebSocket 연결을 만든 후 마지막 구독이 등록 취소되면 이 연결을 닫는 것입니다. 마지막 구독이 등록 취소된 직후에 앱이 구독되는 경우 WebSocket 연결을 닫기 전에 몇 초 동안 대기하여 이 연결을 최적화할 수 있습니다. 모바일 앱 예제의 경우, 한 화면에서 다른 화면으로 변경할 때 *탑재 해제* 이벤트에서 구독을 중지하고 *탑재* 이벤트에서 다른 구독을 시작합니다.

### Lambda 권한 부여
<a name="lambda-auth"></a>

#### Lambda 권한 부여 헤더
<a name="lambda-auth-list"></a>

**헤더 콘텐츠**
+  `"Authorization": <string>`: `authorizationToken`으로 전달되는 값입니다.
+  `"host": <string>`: AWS AppSync GraphQL 엔드포인트의 호스트 또는 사용자 지정 도메인 이름입니다.

**예제**

```
{
    "Authorization":"M0UzQzM1MkQtMkI0Ni00OTZCLUI1NkQtMUM0MTQ0QjVBRTczCkI1REEzRTIxLTk5NzItNDJENi1BQjMwLTFCNjRFNzQ2NzlCNQo=",
    "host":"example1234567890000.appsync-api.us-east-1.amazonaws.com"
}
```

**쿼리 문자열을 통한 헤더**

먼저 `host` 및 `Authorization`를 포함하는 JSON 객체가 문자열로 변환됩니다. 다음으로 이 문자열은 base64 인코딩을 사용하여 인코딩됩니다. 결과 base64 인코딩 문자열은 AWS AppSync 실시간 엔드포인트와의 연결을 설정`header`하기 위해 WebSocket URL에 라는 쿼리 파라미터로 추가됩니다. WebSocket 결과 요청 URL은 다음과 같은 형식을 취합니다.

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql?header=eyJBdXRob3JpemF0aW9uIjoiZXlKcmFXUWlPaUpqYkc1eGIzQTVlVzVNSzA5UVlYSXJNVEpIV0VGTFNYQmllVTVXTkhoc1FqaFBWVzlZTW5NMldsZHZQU0lzSW1Gc1p5STZJbEpUTWpVMkluMC5leUp6ZFdJaU9pSmhObU5tTWpjd055MHhOamd4TFRRMU5ESXRPV1l4T0MxbE5qWTBNVGcyTmpsa016WWlMQ0psZG1WdWRGOXBaQ0k2SW1Wa016TTVNbU5rTFdOallUTXROR00yT0MxaE5EWXlMVEpsWkdJM1pUTm1ZMkZqWmlJc0luUnZhMlZ1WDNWelpTSTZJbUZqWTJWemN5SXNJbk5qYjNCbElqb2lZWGR6TG1OdloyNXBkRzh1YzJsbmJtbHVMblZ6WlhJdVlXUnRhVzRpTENKaGRYUm9YM1JwYldVaU9qRTFOamswTlRjM01UZ3NJbWx6Y3lJNkltaDBkSEJ6T2x3dlhDOWpiMmR1YVhSdkxXbGtjQzVoY0MxemIzVjBhR1ZoYzNRdE1pNWhiV0Y2YjI1aGQzTXVZMjl0WEM5aGNDMXpiM1YwYUdWaGMzUXRNbDgzT0hZMFNWWmliVkFpTENKbGVIQWlPakUxTmprME5qRXpNakFzSW1saGRDSTZNVFUyT1RRMU56Y3lNQ3dpYW5ScElqb2lOVGd6WmpobVltTXRNemsyTVMwMFl6QTRMV0poWlRBdFl6UXlZMkl4TVRNNU5EWTVJaXdpWTJ4cFpXNTBYMmxrSWpvaU0zRmxhalZsTVhabU16ZDFOM1JvWld3MGRHOTFkREprTVd3aUxDSjFjMlZ5Ym1GdFpTSTZJbVZzYjNKNllXWmxJbjAuQjRjZEp0aDNLRk5wSjZpa1ZwN2U2RFJlZTk1VjZRaS16RUUyREpIN3NIT2wyenhZaTdmLVNtRUdvaDJBRDhlbXhRUllhakJ5ei1yRTRKaDBRT3ltTjJZcy1aSWtNcFZCVFBndS1UTVdEeU9IaERVbVVqMk9QODJ5ZVozd2xaQXRyX2dNNEx6alhVWG1JX0syeUdqdVhmWFRhYTFtdlFFQkcwbVFmVmQ3U2Z3WEItamN2NFJZVmk2ajI1cWdvdzlFdzUydWZ1clBxYUstM1dBS0czMktwVjhKNC1XZWpxOHQwYy15QTdzYjhFbkI1NTFiN1RVOTN1S1JpVlZLM0U1NU5rNUFEUG9hbV9XWUU0NWkzczVxVkFQXy1Jblc3NU5Vb09DR1RzUzhZV01mYjZlY0hZSi0xai1iekEyN3phVDlWamN0WG45YnlORlptS0xwQTJMY3h3IiwiaG9zdCI6ImV4YW1wbGUxMjM0NTY3ODkwMDAwLmFwcHN5bmMtYXBpLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tIn0=&payload=e30=
```

base64로 인코딩된 헤더 객체 외에도 빈 JSON 객체 \$1\$1도 base64로 인코딩되어 WebSocket URL에 `payload`로 명명된 별도의 쿼리 파라미터로 포함됩니다.

**`Sec-WebSocket-Protocol`을 통한 헤더**

`host` 및 `Authorization`를 포함하는 JSON 객체는 문자열로 변환된 다음 base64Url 인코딩을 사용하여 인코딩됩니다. 결과 base64Url-encoded 문자열에는 접두사 `header-`가 붙습니다. 그런 다음 접두사가 붙은이 문자열은 AWS AppSync 실시간 엔드포인트와 WebSocket 연결을 설정할 때 `Sec-WebSocket-Protocol` 헤더`graphql-ws`에 외에도 새 하위 프로토콜로 사용됩니다.

결과 요청 URL은 다음과 같은 형식을 취합니다.

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

`Sec-WebSocket-Protocol` 헤더에는 다음 값이 포함됩니다.

```
"sec-websocket-protocol" : ["graphql-ws", "header-ewogICAgImhvc3QiOiJleGFtcGxlMTIzNDU2Nzg5MDAwMC5hcHBzeW5jLWFwaS51cy1lYXN0LTEuYW1hem9uYXdzLmNvbSIsCiAgICAieC1hcGkta2V5IjoiZGEyLTEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Igp9"]
```

**표준 HTTP 헤더를 통한 헤더**

이 방법에서 호스트 및 권한 부여 정보는 AWS AppSync 실시간 엔드포인트와 WebSocket 연결을 설정할 때 표준 HTTP 헤더를 사용하여 전송됩니다. 결과 요청 URL은 다음과 같은 형식을 취합니다.

```
wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql
```

요청 헤더에 다음이 포함될 수 있습니다.

```
"sec-websocket-protocol" : ["graphql-ws"]
"Authorization":"eyEXAMPLEiJjbG5xb3A5eW5MK09QYXIrMTJHWEFLSXBieU5WNHhsQjEXAMPLEnM2WldvPSIsImFsZyI6IlEXAMPLEn0.eyEXAMPLEiJhNmNmMjcwNy0xNjgxLTQ1NDItOWYxOC1lNjY0MTg2NjlkMzYiLCJldmVudF9pZCI6ImVkMzM5MmNkLWNjYTMtNGM2OC1hNDYyLTJlZGI3ZTNmY2FjZiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1Njk0NTc3MTgsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC5hcC1zb3V0aGVhc3QtMi5hbWF6b25hd3MuY29tXC9hcC1zb3V0aGVhc3QtMl83OHY0SVZibVAiLCJleHAiOjE1Njk0NjEzMjAsImlhdCI6MTU2OTQ1NzcyMCwianRpIjoiNTgzZjhmYmMtMzk2MS00YzA4LWJhZTAtYzQyY2IxMTM5NDY5IiwiY2xpZW50X2lkIjoiM3FlajVlMXZmMzd1N3RoZWw0dG91dDJkMWwiLCJ1c2VybmFtZSI6ImVsb3EXAMPLEn0.B4EXAMPLEFNpJ6ikVp7e6DRee95V6Qi-zEE2DJH7sHOl2zxYi7f-SmEGoh2AD8emxQRYajByz-rE4Jh0QOymN2Ys-ZIkMpVBTPgu-TMWDyOHhDUmUj2OP82yeZ3wlZAtr_gM4LzjXUXmI_K2yGjuXfXTaa1mvQEBG0mQfVd7SfwXB-jcv4RYVi6j25qgow9Ew52ufurPqaK-3WAKG32KpV8J4-Wejq8t0c-yA7sb8EnB551b7TU93uKRiVVK3E55Nk5ADPoam_WYE45i3s5qVAP_-InW75NUoOCGTsS8YWMfb6ecHYJ-1j-bzA27zaT9VjctXn9byNFZmEXAMPLExw",
"host":"example1234567890000.appsync-api.us-east-1.amazonaws.com"
```

## 실시간 WebSocket 작업
<a name="real-time-websocket-operation"></a>

 AWS AppSync로 성공적인 WebSocket 핸드셰이크를 시작한 후 클라이언트는 후속 메시지를 전송하여 다양한 작업을 위해 AWS AppSync에 연결해야 합니다. 이러한 메시지에는 다음 데이터가 필요합니다.
+  `type`: 작업의 유형입니다.
+  `id`: 구독의 고유 식별자입니다. UUID를 이러한 목적으로 사용하는 것이 좋습니다.
+  `payload`: 작업 유형에 따라 연결된 페이로드입니다.

`type` 필드만 필수 필드이며, `id` 및 `payload` 필드는 선택 사항입니다.

### 이벤트 순서
<a name="sequence-of-events"></a>

구독 요청을 성공적으로 시작, 설정, 등록 및 처리하려면 클라이언트는 다음 순서를 따라야 단계를 완료해야 합니다.

1. 연결 초기화(`connection_init`)

1. 연결 승인(`connection_ack`)

1. 구독 등록(`start`)

1. 구독 승인(`start_ack`)

1. 구독 처리(`data`)

1. 구독 등록 취소(`stop`)

## 연결 초기화 메시지
<a name="connection-init-message"></a>

(선택 사항) 핸드셰이크가 성공하면 클라이언트는 `connection_init` 메시지를 전송하여 AWS AppSync 실시간 엔드포인트와 통신을 시작할 수 있습니다. 메시지는 JSON 객체를 다음과 같이 문자열로 변환하여 얻은 문자열입니다.

```
{ "type": "connection_init" }
```

## 연결 승인 메시지
<a name="connection-acknowledge-message"></a>

`connection_init` 메시지를 전송한 후 클라이언트는 `connection_ack` 메시지를 대기해야 합니다. `connection_ack`를 수신하기 전에 전송된 모든 메시지는 무시됩니다. 메시지는 다음과 같은 내용이어야 합니다.

```
{
  "type": "connection_ack",
  "payload": {
    // Time in milliseconds waiting for ka message before the client should terminate the WebSocket connection
    "connectionTimeoutMs": 300000
  }
}
```

## 연결 유지 메시지
<a name="keep-alive-message"></a>

클라이언트는 연결 승인 메시지 외에도 연결 유지 메시지를 주기적으로 수신합니다. 연결 제한 시간 내에 클라이언트가 연결 유지 메시지를 수신하지 않으면 클라이언트는 연결을 닫아야 합니다. AWS AppSync는 연결을 자동으로 종료할 때까지(24시간 후) 이러한 메시지를 계속 전송하고 등록된 구독을 처리합니다. 연결 유지 메시지는 하트비트이며 클라이언트가 승인할 필요가 없습니다.

```
{ "type": "ka" }
```

## 구독 등록 메시지
<a name="subscription-registration-message"></a>

클라이언트가 `connection_ack` 메시지를 수신한 후 클라이언트는 구독 등록 메시지를 AWS AppSync에 보낼 수 있습니다. 이 메시지 유형은 다음 필드가 포함된 문자열화된 JSON 객체입니다.
+  `"id": <string>`: 구독의 ID입니다. 이 ID는 각 구독에 고유해야 합니다. 그렇지 않으면 서버에서 구독 ID가 중복됨을 나타내는 오류를 반환합니다.
+  `"type": "start"`: 상수 `<string>` 파라미터입니다.
+  `"payload": <Object>`: 구독에 관련된 정보가 포함된 객체입니다.
  +  `"data": <string>`: GraphQL 쿼리 및 변수를 포함하는 문자열화된 JSON 객체입니다.
    +  `"query": <string>`: GraphQL 작업입니다.
    +  `"variables": <Object>`: 쿼리에 대한 변수가 포함된 객체입니다.
  +  `"extensions": <Object>`: 권한 부여 객체가 포함된 객체입니다.
+  `"authorization": <Object>`:권한 부여에 필요한 필드가 포함된 객체입니다.

### 구독 등록을 위한 권한 부여 개체
<a name="authorization-object-for-subscription-registration"></a>

[AWS AppSync API 권한 부여 모드를 기반으로 하는 헤더 파라미터 형식](#header-parameter-format-based-on-appsync-api-authorization-mode) 섹션의 동일한 규칙이 권한 부여 객체에도 적용됩니다. 유일한 예외는 IAM입니다. 이 경우 SigV4 서명 정보가 약간 다릅니다. 자세한 내용은 IAM 예제를 참조하십시오.

Amazon Cognito 사용자 풀 사용 예:

```
{
  "id": "ee849ef0-cf23-4cb8-9fcb-152ae4fd1e69",
  "payload": {
    "data": "{\"query\":\"subscription onCreateMessage {\\n onCreateMessage {\\n __typename\\n message\\n }\\n }\",\"variables\":{}}",
      "extensions": {
        "authorization": {
          "Authorization": "eyEXAMPLEiJjbG5xb3A5eW5MK09QYXIrMTJEXAMPLEBieU5WNHhsQjhPVW9YMnM2WldvPSIsImFsZyI6IlEXAMPLEn0.eyJzdWIiOiJhNmNmMjcwNy0xNjgxLTQ1NDItEXAMPLENjY0MTg2NjlkMzYiLCJldmVudF9pZCI6ImU3YWVmMzEyLWUEXAMPLEY0Zi04YjlhLTRjMWY5M2Q5ZTQ2OCIsInRva2VuX3VzZSI6ImFjY2VzcyIsIEXAMPLEIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJhdXRoX3RpbWUiOjE1Njk2MTgzMzgsImlzcyI6Imh0dEXAMPLEXC9jb2duaXRvLWlkcC5hcC1zb3V0aGVhc3QtMi5hbWF6b25hd3MuY29tXC9hcC1zbEXAMPLEc3QtMl83OHY0SVZibVAiLCJleHAiOjE1NzAyNTQ3NTUsImlhdCI6MTU3MDI1MTE1NSwianRpIjoiMmIEXAMPLEktZTVkMi00ZDhkLWJiYjItNjA0YWI4MDEwOTg3IiwiY2xpZW50X2lkIjoiM3FlajVlMXZmMzd1EXAMPLE0dG91dDJkMWwiLCJ1c2VybmFtZSI6ImVsb3J6YWZlIn0.CT-qTCtrYeboUJ4luRSTPXaNewNeEXAMPLE14C6sfg05tO0fOMpiUwj9k19gtNCCMqoSsjtQoUweFnH4JYa5EXAMPLEVxOyQEQ4G7jQrt5Ks6STn53vuseR3zRW9snWgwz7t3ZmQU-RWvW7yQU3sNQRLEXAMPLEcd0yufBiCYs3dfQxTTdvR1B6Wz6CD78lfNeKqfzzUn2beMoup2h6EXAMPLE4ow8cUPUPvG0DzRtHNMbWskjPanu7OuoZ8iFO_Eot9kTtAlVKYoNbWkZhkD8dxutyoU4RSH5JoLAnrGF5c8iKgv0B2dfEXAMPLEIihxaZVJ9w9w48S4EXAMPLEcA",
          "host": "example1234567890000.appsync-api.us-east-1.amazonaws.com"
         }
      }
  },
  "type": "start"
}
```

IAM 사용 예:

```
{
  "id": "eEXAMPLE-cf23-1234-5678-152EXAMPLE69",
  "payload": {
    "data": "{\"query\":\"subscription onCreateMessage {\\n onCreateMessage {\\n __typename\\n message\\n }\\n }\",\"variables\":{}}",
    "extensions": {
      "authorization": {
        "accept": "application/json, text/javascript",
        "content-type": "application/json; charset=UTF-8",
        "X-Amz-Security-Token": "AgEXAMPLEZ2luX2VjEAoaDmFwLXNvdXRoZWFEXAMPLEcwRQIgAh97Cljq7wOPL8KsxP3YtDuyc/9hAj8PhJ7Fvf38SgoCIQDhJEXAMPLEPspioOztj++pEagWCveZUjKEn0zyUhBEXAMPLEjj//////////8BEXAMPLExODk2NDgyNzg1NSIMo1mWnpESWUoYw4BkKqEFSrm3DXuL8w+ZbVc4JKjDP4vUCKNR6Le9C9pZp9PsW0NoFy3vLBUdAXEXAMPLEOVG8feXfiEEA+1khgFK/wEtwR+9zF7NaMMMse07wN2gG2tH0eKMEXAMPLEQX+sMbytQo8iepP9PZOzlZsSFb/dP5Q8hk6YEXAMPLEYcKZsTkDAq2uKFQ8mYUVA9EtQnNRiFLEY83aKvG/tqLWNnGlSNVx7SMcfovkFDqQamm+88y1OwwAEYK7qcoceX6Z7GGcaYuIfGpaX2MCCELeQvZ+8WxEgOnIfz7GYvsYNjLZSaRnV4G+ILY1F0QNW64S9Nvj+BwDg3ht2CrNvpwjVYlj9U3nmxE0UG5ne83LL5hhqMpm25kmL7enVgw2kQzmU2id4IKu0C/WaoDRuO2F5zE63vJbxN8AYs7338+4B4HBb6BZ6OUgg96Q15RA41/gIqxaVPxyTpDfTU5GfSLxocdYeniqqpFMtZG2n9d0u7GsQNcFkNcG3qDZm4tDo8tZbuym0a2VcF2E5hFEgXBa+XLJCfXi/77OqAEjP0x7Qdk3B43p8KG/BaioP5RsV8zBGvH1zAgyPha2rN70/tT13yrmPd5QYEfwzexjKrV4mWIuRg8NTHYSZJUaeyCwTom80VFUJXG+GYTUyv5W22aBcnoRGiCiKEYTLOkgXecdKFTHmcIAejQ9Welr0a196Kq87w5KNMCkcCGFnwBNFLmfnbpNqT6rUBxxs3X5ntX9d8HVtSYINTsGXXMZCJ7fnbWajhg/aox0FtHX21eF6qIGT8j1z+l2opU+ggwUgkhUUgCH2TfqBj+MLMVVvpgqJsPKt582caFKArIFIvO+9QupxLnEH2hz04TMTfnU6bQC6z1buVe7h+tOLnh1YPFsLQ88anib/7TTC8k9DsBTq0ASe8R2GbSEsmO9qbbMwgEaYUhOKtGeyQsSJdhSk6XxXThrWL9EnwBCXDkICMqdntAxyyM9nWsZ4bL9JHqExgWUmfWChzPFAqn3F4y896UqHTZxlq3WGypn5HHcem2Hqf3IVxKH1inhqdVtkryEiTWrI7ZdjbqnqRbl+WgtPtKOOweDlCaRs3R2qXcbNgVhleMk4IWnF8D1695AenU1LwHjOJLkCjxgNFiWAFEPH9aEXAMPLExA==",
        "Authorization": "AWS4-HMAC-SHA256 Credential=XXXXXXXXXXXXXXXXXXXX/20200401/us-east-1/appsync/aws4_request, SignedHeaders=accept;content-encoding;content-type;host;x-amz-date;x-amz-security-token, Signature=b90131a61a7c4318e1c35ead5dbfdeb46339a7585bbdbeceeaff51f4022eb1fd",
        "content-encoding": "amz-1.0",
        "host": "example1234567890000.appsync-api.us-east-1.amazonaws.com",
        "x-amz-date": "20200401T001010Z"
      }
    }
  },
  "type": "start"
}
```

사용자 지정 도메인 이름을 사용하는 예:

```
{
  "id": "key-cf23-4cb8-9fcb-152ae4fd1e69",
  "payload": {
    "data": "{\"query\":\"subscription onCreateMessage {\\n onCreateMessage {\\n __typename\\n message\\n }\\n }\",\"variables\":{}}",
      "extensions": {
        "authorization": {
          "x-api-key": "da2-12345678901234567890123456",
          "host": "api.example.com"
         }
      }
  },
  "type": "start"
}
```

SigV4 서명은 URL에 `/connect`를 추가할 필요가 없으며 JSON 문자열화된 GraphQL 작업은 `data`를 대체합니다. 다음은 SigV4 서명 요청의 예입니다.

```
{
  url: "https://example1234567890000.appsync-api.us-east-1.amazonaws.com/graphql",
  data: "{\"query\":\"subscription onCreateMessage {\\n onCreateMessage {\\n __typename\\n message\\n }\\n }\",\"variables\":{}}",
  method: "POST",
  headers: {
    "accept": "application/json, text/javascript",
    "content-encoding": "amz-1.0",
    "content-type": "application/json; charset=UTF-8",
  }
}
```

## 구독 승인 메시지
<a name="subscription-acknowledge-message"></a>

구독 시작 메시지를 전송한 후 클라이언트는 AWS AppSync가 `start_ack` 메시지를 전송할 때까지 기다려야 합니다. `start_ack` 메시지는 구독이 성공했음을 나타냅니다.

구독 승인 예:

```
{
  "type": "start_ack",
  "id": "eEXAMPLE-cf23-1234-5678-152EXAMPLE69"
}
```

## 오류 메시지
<a name="error-message"></a>

연결 초기화 또는 구독 등록이 실패하거나 서버에서 구독이 종료되는 경우 서버는 클라이언트에 오류 메시지를 전송합니다. 연결 시작 시간 중에 오류가 발생하면 서버에 의해 연결이 종료됩니다.
+  `"type": "error"`: 상수 `<string>` 파라미터입니다.
+  `"id": <string>`: 관련된 경우 해당 등록 구독의 ID입니다.
+  `"payload" <Object>`: 해당 오류 정보가 포함된 객체입니다.

예제:

```
{
  "type": "error",
  "payload": {
    "errors": [
      {
        "errorType": "LimitExceededError",
        "message": "Rate limit exceeded"
      }
    ]
  }
}
```

## 데이터 메시지 처리
<a name="processing-data-messages"></a>

클라이언트가 변형을 제출하면 AWS AppSync는 해당 변형에 관심이 있는 모든 구독자를 식별하고 구독 작업`id`의 해당 구독을 사용하여 각 `"start"` 구독자에게 `"type":"data"` 메시지를 보냅니다. 클라이언트는 데이터 메시지를 수신할 때 클라이언트가 해당 구독과 일치시킬 수 있도록 전송하는 구독 `id`를 추적해야 합니다.
+  `"type": "data"`: 상수 `<string>` 파라미터입니다.
+  `"id": <string>`: 해당 등록 구독의 ID입니다.
+  `"payload" <Object>`: 구독 정보가 포함된 객체입니다.

예제:

```
{
  "type": "data",
  "id": "ee849ef0-cf23-4cb8-9fcb-152ae4fd1e69",
  "payload": {
    "data": {
      "onCreateMessage": {
        "__typename": "Message",
        "message": "test"
      }
    }
  }
}
```

## 구독 등록 취소 메시지
<a name="subscription-unregistration-message"></a>

앱이 구독 이벤트 수신 대기를 중지하려는 경우 클라이언트는 다음과 같은 문자열화된 JSON 객체가 있는 메시지를 전송해야 합니다.
+  `"type": "stop"`: 상수 `<string>` 파라미터입니다.
+  `"id": <string>`: 등록 취소할 구독의 ID입니다.

예제:

```
{
  "type":"stop",
  "id":"ee849ef0-cf23-4cb8-9fcb-152ae4fd1e69"
}
```

AWS AppSync는 다음과 같은 문자열화된 JSON 객체와 함께 확인 메시지를 다시 보냅니다.
+  `"type": "complete"`: 상수 `<string>` 파라미터입니다.
+  `"id": <string>`: 등록되지 않은 구독의 ID입니다.

클라이언트는 확인 메시지를 받은 후 이 특정 구독에 대한 메시지를 더 이상 받지 않습니다.

예제:

```
{
  "type":"complete",
  "id":"eEXAMPLE-cf23-1234-5678-152EXAMPLE69"
}
```

## WebSocket 연결 해제
<a name="disconnecting-the-websocket"></a>

연결을 해제하기 전에 클라이언트는 데이터 손실을 방지하기 위해 WebSocket 연결을 통해 현재 진행 중인 작업이 없는지 확인하는 데 필요한 논리를 갖춰야 합니다. WebSocket에서 연결 해제하기 전에 모든 구독을 등록 취소해야 합니다.