

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

# DynamoDB 해석기를 사용하여 간단한 게시 애플리케이션 생성
<a name="tutorial-dynamodb-resolvers"></a>

**참고**  
이제 우리는 주로 APPSYNC\$1JS 런타임과 해당 문서를 지원합니다. [여기](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)에서 APPSYNC\$1JS 런타임과 해당 안내서를 사용해 보세요.

이 자습서에서는 자체 Amazon DynamoDB 테이블을 AWS AppSync로 가져와 GraphQL API에 연결하는 방법을 보여줍니다.

사용자를 대신하여 AWS AppSync가 DynamoDB 리소스를 프로비저닝하도록 할 수 있습니다. 또는 선호하는 경우 데이터 원본 및 해석기를 생성하여 기존 테이블을 GraphQL 스키마에 연결할 수 있습니다. 두 경우 모두 GraphQL 문을 통해 DynamoDB 데이터베이스를 읽고 여기에 쓸 수 있고 실시간 데이터를 구독할 수 있습니다.

GraphQL 문을 DynamoDB 작업으로 변환하고 응답을 다시 GraphQL로 변환하기 위해서는 순서대로 완료해야 하는 특정한 구성 단계가 있습니다. 이 자습서에서는 여러 가지 실제 시나리오와 데이터 액세스 패턴을 통해 구성 프로세스를 설명합니다.

## DynamoDB 테이블 설정
<a name="setting-up-your-ddb-tables"></a>

이 자습서를 시작하려면 먼저 아래 단계에 따라 AWS 리소스를 프로비저닝해야 합니다.

1. CLI에서 다음 AWS CloudFormation 템플릿을 사용하여 AWS 리소스를 프로비저닝합니다.

   ```
   aws cloudformation create-stack \
       --stack-name AWSAppSyncTutorialForAmazonDynamoDB \
       --template-url https://s3.us-west-2.amazonaws.com/awsappsync/resources/dynamodb/AmazonDynamoDBCFTemplate.yaml \
       --capabilities CAPABILITY_NAMED_IAM
   ```

   또는 계정의 미국 서부 2(오레곤) 리전에서 다음 CloudFormation 스택을 시작할 수 있습니다 AWS .

   [https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/dynamodb/AmazonDynamoDBCFTemplate.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/dynamodb/AmazonDynamoDBCFTemplate.yaml)

   다음을 생성합니다.
   + `Post` 데이터를 가지고 있는 `AppSyncTutorial-Post`라는 DynamoDB 테이블
   +  AWS AppSync가 `Post` 테이블과 상호 작용할 수 있도록 허용하는 IAM 역할 및 관련 IAM 관리형 정책입니다.

1. 스택 및 생성된 리소스에 대해 자세히 알아보려면 다음 CLI 명령을 실행합니다.

   ```
   aws cloudformation describe-stacks --stack-name AWSAppSyncTutorialForAmazonDynamoDB
   ```

1. 나중에 리소스를 삭제하려면 다음을 실행할 수 있습니다.

   ```
   aws cloudformation delete-stack --stack-name AWSAppSyncTutorialForAmazonDynamoDB
   ```

## GraphQL API 생성
<a name="creating-your-graphql-api"></a>

 AWS AppSync에서 GraphQL API를 생성하려면:

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

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

1. **API 사용자 지정 또는 Amazon DynamoDB에서 가져오기** 창에서 **처음부터 빌드**를 선택합니다.

   1. 같은 창 오른쪽에 있는 **시작**을 선택합니다.

1. **API 이름** 필드에서 API 이름을 `AWSAppSyncTutorial`로 설정합니다.

1. **생성(Create)**을 선택합니다.

 AWS AppSync 콘솔은 API 키 인증 모드를 사용하여 새 GraphQL API를 생성합니다. 콘솔에서 GraphQL API의 나머지 부분을 설정하고 이 자습서의 나머지 부분에서 이 API에 대한 쿼리를 실행할 수 있습니다.

## 기본 Post API 정의
<a name="defining-a-basic-post-api"></a>

이제 AWS AppSync GraphQL API를 생성했으므로 게시물 데이터의 기본 생성, 검색 및 삭제를 허용하는 기본 스키마를 설정할 수 있습니다.

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

   1. **API 대시보드**에서 방금 만든 API를 선택합니다.

1. **사이드바**에서 **스키마**를 선택합니다.

   1. **스키마** 창에서 파일 콘텐츠를 다음 코드로 바꿉니다.

     ```
     schema {
         query: Query
         mutation: Mutation
     }
     
     type Query {
         getPost(id: ID): Post
     }
     
     type Mutation {
         addPost(
             id: ID!
             author: String!
             title: String!
             content: String!
             url: String!
         ): Post!
     }
     
     type Post {
         id: ID!
         author: String
         title: String
         content: String
         url: String
         ups: Int!
         downs: Int!
         version: Int!
     }
     ```

1. **저장**을 선택합니다.

이 스키마는 `Post` 객체를 추가하고 가져올 작업과 `Post` 형식을 정의합니다.

## DynamoDB 테이블에 대한 데이터 소스 구성
<a name="configuring-the-data-source-for-the-ddb-tables"></a>

다음으로 스키마에 정의된 쿼리 및 뮤테이션을 `AppSyncTutorial-Post` DynamoDB 테이블에 연결합니다.

먼저 AWS AppSync는 테이블을 인식해야 합니다. 다음과 같이 AWS AppSync에서 데이터 소스를 설정하여 인식하도록 할 수 있습니다.

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

   1. **API 대시보드**에서 GraphQL API를 선택합니다.

   1. **사이드바**에서 **데이터 소스**를 선택합니다.

1. **데이터 원본 생성**을 선택합니다.

   1. **데이터 소스 이름**에 `PostDynamoDBTable`을 입력합니다.

   1. **데이터 소스 유형**에서 **Amazon DynamoDB 테이블**을 선택합니다.

   1. **리전**에서 **US-WEST-2**를 선택합니다.

   1. **테이블 이름**에서 **AppSyncTutorial-Post** DynamoDB 테이블을 선택합니다.

   1. 새 IAM 역할을 만들거나(권장) `lambda:invokeFunction` IAM 권한이 있는 기존 역할을 선택합니다. 기존 역할에는 [데이터 원본 연결](attaching-a-data-source.md) 섹션에 설명된 대로 신뢰 정책이 필요합니다.

      다음은 리소스에서 작업을 수행하는 데 필요한 권한을 가진 IAM 정책의 예입니다.

------
#### [ JSON ]

****  

      ```
      { 
           "Version":"2012-10-17",		 	 	  
           "Statement": [ 
               { 
                   "Effect": "Allow", 
                   "Action": [ "lambda:invokeFunction" ], 
                   "Resource": [ 
                       "arn:aws:lambda:us-east-1:111122223333:function:myFunction", 
                       "arn:aws:lambda:us-east-1:111122223333:function:myFunction:*" 
                   ] 
               } 
           ] 
       }
      ```

------

1. **생성(Create)**을 선택합니다.

## addPost 해석기 설정(DynamoDB PutItem)
<a name="setting-up-the-addpost-resolver-dynamodb-putitem"></a>

 AWS AppSync가 DynamoDB 테이블을 인식한 후 **해석기를** 정의하여 개별 쿼리 및 변형에 연결할 수 있습니다. 생성한 첫 번째 해석기는 `addPost` 해석기로, 이 해석기를 통해 `AppSyncTutorial-Post` DynamoDB 테이블에서 게시물을 생성할 수 있습니다.

해석기의 구성 요소는 다음과 같습니다.
+ 해석기를 연결할 GraphQL 스키마의 위치. 이 경우 `addPost` 형식의 `Mutation` 필드에서 해석기를 설정합니다. 이 해석기는 호출자가 `mutation { addPost(...){...} }`를 호출하면 호출됩니다.
+ 이 해석기에 사용할 데이터 원본. 이 경우 앞서 정의한 `PostDynamoDBTable` 데이터 원본을 사용하려고 합니다. 따라서 `AppSyncTutorial-Post` DynamoDB 테이블에 항목을 추가할 수 있습니다.
+ 요청 매핑 템플릿. 요청 매핑 템플릿의 목적은 호출자로부터 수신 요청을 받아 AWS AppSync가 DynamoDB에 대해 수행할 수 있는 지침으로 변환하는 것입니다.
+ 응답 매핑 템플릿. 응답 매핑 템플릿의 작업은 DynamoDB의 응답을 받아 GraphQL에 필요한 것으로 변환하는 것입니다. 이러한 템플릿은 DynamoDB의 데이터 모양과 GraphQL의 `Post` 형식이 다른 경우 유용하지만 이 경우 모양이 서로 동일하기 때문에 데이터를 그냥 전달하면 됩니다.

 해석기를 설정하려면:

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

   1. **API 대시보드**에서 GraphQL API를 선택합니다.

   1. **사이드바**에서 **데이터 소스**를 선택합니다.

1. **데이터 원본 생성**을 선택합니다.

   1. **데이터 소스 이름**에 `PostDynamoDBTable`을 입력합니다.

   1. **데이터 소스 유형**에서 **Amazon DynamoDB 테이블**을 선택합니다.

   1. **리전**에서 **US-WEST-2**를 선택합니다.

   1. **테이블 이름**에서 **AppSyncTutorial-Post** DynamoDB 테이블을 선택합니다.

   1. 새 IAM 역할을 만들거나(권장) `lambda:invokeFunction` IAM 권한이 있는 기존 역할을 선택합니다. 기존 역할에는 [데이터 원본 연결](attaching-a-data-source.md) 섹션에 설명된 대로 신뢰 정책이 필요합니다.

      다음은 리소스에서 작업을 수행하는 데 필요한 권한을 가진 IAM 정책의 예입니다.

------
#### [ JSON ]

****  

      ```
      { 
           "Version":"2012-10-17",		 	 	  
           "Statement": [ 
               { 
                   "Effect": "Allow", 
                   "Action": [ "lambda:invokeFunction" ], 
                   "Resource": [ 
                       "arn:aws:lambda:us-west-2:123456789012:function:myFunction", 
                       "arn:aws:lambda:us-west-2:123456789012:function:myFunction:*" 
                   ] 
               } 
           ] 
       }
      ```

------

1. **생성(Create)**을 선택합니다.

1. **스키마** 탭을 선택합니다.

1. 오른쪽의 **데이터 유형** 창의 **뮤테이션** 유형에서 **addPost** 필드를 찾은 다음 **연결**을 선택합니다.

1. **작업 메뉴**에서 **런타임 업데이트**를 선택한 다음 **단위 해석기(VTL만 해당)**를 선택합니다.

1. **Data source name(데이터 원본 이름)**에서 **PostDynamoDBTable**을 선택합니다.

1. 다음을 **Configure the request mapping template(요청 매핑 템플릿 구성)**에 붙여 넣습니다.

   ```
   {
       "version" : "2017-02-28",
       "operation" : "PutItem",
       "key" : {
           "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
       },
       "attributeValues" : {
           "author" : $util.dynamodb.toDynamoDBJson($context.arguments.author),
           "title" : $util.dynamodb.toDynamoDBJson($context.arguments.title),
           "content" : $util.dynamodb.toDynamoDBJson($context.arguments.content),
           "url" : $util.dynamodb.toDynamoDBJson($context.arguments.url),
           "ups" : { "N" : 1 },
           "downs" : { "N" : 0 },
           "version" : { "N" : 1 }
       }
   }
   ```

   **참고:** *형식*은 모든 키와 속성 값에 대해 지정됩니다. 예를 들어, `author` 필드를 `{ "S" : "${context.arguments.author}" }`로 설정합니다. `S` 부분은 to AWS AppSync 및 DynamoDB에 값이 문자열 값임을 나타냅니다. 실제 값은 `author` 인수에서 채워집니다. 마찬가지로 `version` 필드는 형식에 `N`을 사용하기 때문에 숫자 필드입니다. 마지막으로, `ups`, `downs` 및 `version` 필드를 초기화합니다.

   이 자습서에서는 DynamoDB에 삽입된 새 항목을 인덱싱하는 GraphQL `ID!` 유형이의 형식으로도 사용할 수 `$utils.autoId()` 있는 라는 자동 ID 생성을 위한 유틸리티와 함께 클라이언트 arguments. AWS AppSync com의 일부로 제공되도록 지정했습니다`"id" : { "S" : "${$utils.autoId()}" }`. 그런 다음 `id: ID!`의 스키마 정의 외부에 `addPost()`를 둘 수 있는데 자동으로 삽입됩니다. 이 자습서에서는 이러한 기술을 사용하지 않지만 DynamoDB 테이블에 쓰는 경우에는 이 기술의 사용을 고려해야 합니다.

   매핑 템플릿에 대한 자세한 내용은 [해석기 매핑 템플릿 개요](resolver-mapping-template-reference-overview.md#aws-appsync-resolver-mapping-template-reference-overview) 참조 문서를 참조하십시오. GetItem 요청 매핑에 대한 자세한 내용은 [GetItem](aws-appsync-resolver-mapping-template-reference-dynamodb-getitem.md) 참조 문서를 참조하십시오. 행식에 대한 자세한 내용은 [형식 시스템(요청 매핑)](aws-appsync-resolver-mapping-template-reference-dynamodb-typed-values-request.md) 참조 문서를 참조하십시오.

1. 다음을 **Configure the response mapping template(응답 매핑 템플릿 구성)**에 붙여 넣습니다.

   ```
   $utils.toJson($context.result)
   ```

    **참고:** `AppSyncTutorial-Post` 테이블의 데이터 모양이 GraphQL의 `Post` 형식 모양과 정확하게 일치하기 때문에 응답 매핑 템플릿에서는 결과를 바로 전달하기만 합니다. 또한 파일을 하나만 생성할 수 있도록 이 자습서의 모든 예에서는 동일한 응답 매핑 템플릿을 사용합니다.

1. **저장**을 선택합니다.

### 게시물 추가를 위한 API 직접 호출
<a name="call-the-api-to-add-a-post"></a>

해석기가 설정되었으므로 AWS AppSync는 들어오는 `addPost` 변형을 DynamoDB PutItem 작업으로 변환할 수 있습니다. 이제 변형을 실행해 테이블에 데이터를 입력할 수 있습니다.
+ **Queries** 탭을 선택합니다.
+ **쿼리** 창에 다음 변형을 붙여 넣습니다.

  ```
  mutation addPost {
    addPost(
      id: 123
      author: "AUTHORNAME"
      title: "Our first post!"
      content: "This is our first post."
      url: "https://aws.amazon.com/appsync/"
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ 새로 생성한 게시물의 결과가 쿼리 창 오른쪽에 있는 결과 창에 나타나야 합니다. 예를 들면 다음과 같아야 합니다.

  ```
  {
    "data": {
      "addPost": {
        "id": "123",
        "author": "AUTHORNAME",
        "title": "Our first post!",
        "content": "This is our first post.",
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 1
      }
    }
  }
  ```

다음과 같은 작업이 수행되었습니다.
+ AWS AppSync가 `addPost` 변형 요청을 받았습니다.
+ AWS AppSync는 요청과 요청 매핑 템플릿을 가져와 요청 매핑 문서를 생성했습니다. 이 문서는 다음과 같은 형태입니다.

  ```
  {
      "version" : "2017-02-28",
      "operation" : "PutItem",
      "key" : {
          "id" : { "S" : "123" }
      },
      "attributeValues" : {
          "author": { "S" : "AUTHORNAME" },
          "title": { "S" : "Our first post!" },
          "content": { "S" : "This is our first post." },
          "url": { "S" : "https://aws.amazon.com/appsync/" },
          "ups" : { "N" : 1 },
          "downs" : { "N" : 0 },
          "version" : { "N" : 1 }
      }
  }
  ```
+ AWS AppSync는 요청 매핑 문서를 사용하여 DynamoDB`PutItem` 요청을 생성하고 실행했습니다.
+ AWS AppSync는 `PutItem` 요청 결과를 가져와 GraphQL 유형으로 다시 변환했습니다.

  ```
  {
      "id" : "123",
      "author": "AUTHORNAME",
      "title": "Our first post!",
      "content": "This is our first post.",
      "url": "https://aws.amazon.com/appsync/",
      "ups" : 1,
      "downs" : 0,
      "version" : 1
  }
  ```
+ 응답 매핑 문서를 통해 전달했습니다(변경 없이 전달됨).
+ GraphQL 응답에서 새로 생성된 객체가 반환되었습니다.

## getPost 해석기 설정(DynamoDB GetItem)
<a name="setting-up-the-getpost-resolver-ddb-getitem"></a>

이제 `AppSyncTutorial-Post` DynamoDB 테이블에 데이터를 추가할 수 있으므로 `AppSyncTutorial-Post` 테이블에서 데이터를 검색할 수 있도록 `getPost` 쿼리를 설정해야 합니다. 이렇게 하기 위해 다른 해석기를 설정합니다.
+ **스키마** 탭을 선택합니다.
+ 오른쪽의 **데이터 유형** 창의 **쿼리** 유형에서 **getPost** 필드를 찾은 다음 **연결**을 선택합니다.
+ **작업 메뉴**에서 **런타임 업데이트**를 선택한 다음 **단위 해석기(VTL만 해당)**를 선택합니다.
+ **Data source name(데이터 원본 이름)**에서 **PostDynamoDBTable**을 선택합니다.
+ 다음을 **Configure the request mapping template(요청 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  {
      "version" : "2017-02-28",
      "operation" : "GetItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
      }
  }
  ```
+ 다음을 **Configure the response mapping template(응답 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  $utils.toJson($context.result)
  ```
+ **저장**을 선택합니다.

### 게시물 가져오기를 위한 API 직접 호출
<a name="call-the-api-to-get-a-post"></a>

이제 해석기가 설정되었으며 AWS AppSync는 수신 `getPost` 쿼리를 DynamoDB`GetItem` 작업으로 변환하는 방법을 알고 있습니다. 이제 앞에서 생성한 게시물을 검색하는 쿼리를 실행할 수 있습니다.
+ **Queries** 탭을 선택합니다.
+ **쿼리** 창에 다음을 붙여 넣습니다.

  ```
  query getPost {
    getPost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ DynamoDB에서 가져온 게시물이 쿼리 창 오른쪽에 있는 결과 창에 나타나야 합니다. 예를 들면 다음과 같아야 합니다.

  ```
  {
    "data": {
      "getPost": {
        "id": "123",
        "author": "AUTHORNAME",
        "title": "Our first post!",
        "content": "This is our first post.",
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 1
      }
    }
  }
  ```

다음과 같은 작업이 수행되었습니다.
+ AWS AppSync가 `getPost` 쿼리 요청을 받았습니다.
+ AWS AppSync는 요청과 요청 매핑 템플릿을 가져와 요청 매핑 문서를 생성했습니다. 이 문서는 다음과 같은 형태입니다.

  ```
  {
      "version" : "2017-02-28",
      "operation" : "GetItem",
      "key" : {
          "id" : { "S" : "123" }
      }
  }
  ```
+ AWS AppSync는 요청 매핑 문서를 사용하여 DynamoDB GetItem 요청을 생성하고 실행했습니다.
+ AWS AppSync는 `GetItem` 요청 결과를 가져와 GraphQL 유형으로 다시 변환했습니다.

  ```
  {
      "id" : "123",
      "author": "AUTHORNAME",
      "title": "Our first post!",
      "content": "This is our first post.",
      "url": "https://aws.amazon.com/appsync/",
      "ups" : 1,
      "downs" : 0,
      "version" : 1
  }
  ```
+ 응답 매핑 문서를 통해 전달했습니다(변경 없이 전달됨).
+ 응답에서 가져온 객체가 반환되었습니다.

또는 다음 예를 사용합니다.

```
query getPost {
  getPost(id:123) {
    id
    author
    title
  }
}
```

`getPost` 쿼리에 `id`, `author`, `title`만 필요한 경우, 요청 매핑 템플릿을 변경하여 프로젝션 표현식을 사용하여 DynamoDB 테이블에서 원하는 속성만 지정하면 DynamoDB에서 AWS AppSync로 불필요한 데이터 전송을 방지할 수 있습니다. 예를 들어 요청 매핑 템플릿은 아래 코드 조각과 비슷할 수 있습니다.

```
{
    "version" : "2017-02-28",
    "operation" : "GetItem",
    "key" : {
        "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
    },
    "projection" : {
     "expression" : "#author, id, title",
     "expressionNames" : { "#author" : "author"}
    }
}
```

## updatePost 뮤테이션 생성(DynamoDB UpdateItem)
<a name="create-an-updatepost-mutation-ddb-updateitem"></a>

이제 DynamoDB에서 `Post` 객체를 생성하고 검색할 수 있습니다. 다음에는, 객체 업데이트할 수 있도록 새 변형을 설정합니다. 이렇게 하기 위해 UpdateItem DynamoDB 작업을 사용합니다.
+ **스키마** 탭을 선택합니다.
+ **스키마** 창에서 다음과 같이 `Mutation` 형식을 수정하여 새로운 `updatePost` 변형을 추가할 수 있습니다.

  ```
  type Mutation {
      updatePost(
          id: ID!,
          author: String!,
          title: String!,
          content: String!,
          url: String!
      ): Post
      addPost(
          author: String!
          title: String!
          content: String!
          url: String!
      ): Post!
  }
  ```
+ **저장**을 선택합니다.
+ 오른쪽의 **데이터 유형** 창의 **뮤테이션** 유형에서 새로 생성한 **updatePost** 필드를 찾은 다음 **연결**을 선택합니다.
+ **작업 메뉴**에서 **런타임 업데이트**를 선택한 다음 **단위 해석기(VTL만 해당)**를 선택합니다.
+ **Data source name(데이터 원본 이름)**에서 **PostDynamoDBTable**을 선택합니다.
+ 다음을 **Configure the request mapping template(요청 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "SET author = :author, title = :title, content = :content, #url = :url ADD version :one",
          "expressionNames": {
              "#url" : "url"
          },
          "expressionValues": {
              ":author" : $util.dynamodb.toDynamoDBJson($context.arguments.author),
              ":title" : $util.dynamodb.toDynamoDBJson($context.arguments.title),
              ":content" : $util.dynamodb.toDynamoDBJson($context.arguments.content),
              ":url" : $util.dynamodb.toDynamoDBJson($context.arguments.url),
              ":one" : { "N": 1 }
          }
      }
  }
  ```

   **참고:** 이 해석기는 PutItem 작업과 크게 다른 DynamoDB UpdateItem 작업을 사용합니다. 전체 항목을 쓰는 대신 특정 속성을 업데이트하도록 DynamoDB에 요청합니다. DynamoDB 업데이트 표현식을 사용하여 이 작업을 완료합니다. 표현식 자체는 `expression` 섹션의 `update` 필드에서 지정합니다. 이 표현식은 `author`, `title`, `content` 및 url 속성을 설정한 다음 `version` 필드를 증분하도록 합니다. 사용되는 값은 표현식 자체에 나타나지 않습니다. 대신 표현식에는 이름이 콜론으로 시작되는 자리 표시자가 있습니다. 표현식은 `expressionValues` 필드에서 정의됩니다. 마지막으로, DynamoDB에는 `expression`에 표시할 수 없는 예약어가 있습니다. 예를 들어, `url`은 예약어기 때문에 `url` 필드를 업데이트하려면 이름 자리 표시자를 사용해 `expressionNames` 필드에 해당 자리 표시자를 정의합니다.

  `UpdateItem` 요청 매핑에 대한 자세한 내용은 [UpdateItem](aws-appsync-resolver-mapping-template-reference-dynamodb-updateitem.md) 참조 문서를 참조하십시오. 업데이트 표현식을 작성하는 방법에 대한 자세한 내용은 [DynamoDB UpdateExpressions 문서](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html)를 참조하십시오.
+ 다음을 **Configure the response mapping template(응답 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  $utils.toJson($context.result)
  ```

### 게시물 업데이트를 위한 API 직접 호출
<a name="call-the-api-to-update-a-post"></a>

이제 해석기가 설정되었으며 AWS AppSync는 들어오는 `update` 변형을 DynamoDB`Update` 작업으로 변환하는 방법을 알고 있습니다. 이제 앞서 작성한 항목을 업데이트하는 변형을 실행할 수 있습니다.
+ **Queries** 탭을 선택합니다.
+ **쿼리** 창에 다음 변형을 붙여 넣습니다. 또한 앞서 적어둔 값을 갖도록 `id` 인수를 업데이트해야 합니다.

  ```
  mutation updatePost {
    updatePost(
      id:"123"
      author: "A new author"
      title: "An updated author!"
      content: "Now with updated content!"
      url: "https://aws.amazon.com/appsync/"
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ DynamoDB에서 업데이트된 게시물이 쿼리 창 오른쪽에 있는 결과 창에 나타나야 합니다. 예를 들면 다음과 같아야 합니다.

  ```
  {
    "data": {
      "updatePost": {
        "id": "123",
        "author": "A new author",
        "title": "An updated author!",
        "content": "Now with updated content!",
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 2
      }
    }
  }
  ```

이 예제에서는 요청 매핑 템플릿이 AWS AppSync 및 DynamoDB에 해당 `downs` 필드로 어떤 작업도 수행하도록 요청하지 않았기 때문에 `ups` 및 필드가 수정되지 않았습니다. 또한 AWS AppSync및 DynamoDB에 `version` 필드에 1을 추가하도록 요청했기 때문에 `version` 필드가 1씩 증가했습니다.

## updatePost 해석기 수정(DynamoDB UpdateItem)
<a name="modifying-the-updatepost-resolver-dynamodb-updateitem"></a>

이 작업은 `updatePost` 변형을 시작하기 좋은 지점이지만 다음과 같이 두 가지 주요한 문제가 있습니다.
+ 필드를 하나만 업데이트하고자 하는 경우에도 필드를 모두 업데이트해야 합니다.
+ 두 사람이 객체를 수정하는 경우 정보가 손실될 수 있습니다.

이러한 문제를 해결하기 위해 요청에 지정된 인수만 수정한 다음 `UpdateItem` 작업에 조건을 추가하도록 `updatePost` 변형을 수정하려고 합니다.

1. **스키마** 탭을 선택합니다.

1. **스키마** 패널에서 `Mutation` 유형의 `updatePost` 필드를 수정하여 `author`, `title`, `content` 및 `url` 인수에서 느낌표를 제거하고 `id` 필드는 그대로 둡니다. 이렇게 하면 해당 인수가 선택적 인수가 됩니다. 또한 새로운 필수 인수인 `expectedVersion` 버전을 추가합니다.

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

1. **저장**을 선택합니다.

1. 오른쪽의 **데이터 유형** 창의 **뮤테이션** 유형에서 **updatePost** 필드를 찾습니다.

1. **PostDynamoDBTable**을 선택하여 기존 해석기를 엽니다.

1. **Configure the request mapping template(요청 매핑 템플릿 구성)**에서 다음과 같이 요청 매핑 템플릿을 수정합니다.

   ```
   {
       "version" : "2017-02-28",
       "operation" : "UpdateItem",
       "key" : {
           "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
       },
   
       ## Set up some space to keep track of things you're updating **
       #set( $expNames  = {} )
       #set( $expValues = {} )
       #set( $expSet = {} )
       #set( $expAdd = {} )
       #set( $expRemove = [] )
   
       ## Increment "version" by 1 **
       $!{expAdd.put("version", ":one")}
       $!{expValues.put(":one", { "N" : 1 })}
   
       ## Iterate through each argument, skipping "id" and "expectedVersion" **
       #foreach( $entry in $context.arguments.entrySet() )
           #if( $entry.key != "id" && $entry.key != "expectedVersion" )
               #if( (!$entry.value) && ("$!{entry.value}" == "") )
                   ## If the argument is set to "null", then remove that attribute from the item in DynamoDB **
   
                   #set( $discard = ${expRemove.add("#${entry.key}")} )
                   $!{expNames.put("#${entry.key}", "$entry.key")}
               #else
                   ## Otherwise set (or update) the attribute on the item in DynamoDB **
   
                   $!{expSet.put("#${entry.key}", ":${entry.key}")}
                   $!{expNames.put("#${entry.key}", "$entry.key")}
                   $!{expValues.put(":${entry.key}", { "S" : "${entry.value}" })}
               #end
           #end
       #end
   
       ## Start building the update expression, starting with attributes you're going to SET **
       #set( $expression = "" )
       #if( !${expSet.isEmpty()} )
           #set( $expression = "SET" )
           #foreach( $entry in $expSet.entrySet() )
               #set( $expression = "${expression} ${entry.key} = ${entry.value}" )
               #if ( $foreach.hasNext )
                   #set( $expression = "${expression}," )
               #end
           #end
       #end
   
       ## Continue building the update expression, adding attributes you're going to ADD **
       #if( !${expAdd.isEmpty()} )
           #set( $expression = "${expression} ADD" )
           #foreach( $entry in $expAdd.entrySet() )
               #set( $expression = "${expression} ${entry.key} ${entry.value}" )
               #if ( $foreach.hasNext )
                   #set( $expression = "${expression}," )
               #end
           #end
       #end
   
       ## Continue building the update expression, adding attributes you're going to REMOVE **
       #if( !${expRemove.isEmpty()} )
           #set( $expression = "${expression} REMOVE" )
   
           #foreach( $entry in $expRemove )
               #set( $expression = "${expression} ${entry}" )
               #if ( $foreach.hasNext )
                   #set( $expression = "${expression}," )
               #end
           #end
       #end
   
       ## Finally, write the update expression into the document, along with any expressionNames and expressionValues **
       "update" : {
           "expression" : "${expression}"
           #if( !${expNames.isEmpty()} )
               ,"expressionNames" : $utils.toJson($expNames)
           #end
           #if( !${expValues.isEmpty()} )
               ,"expressionValues" : $utils.toJson($expValues)
           #end
       },
   
       "condition" : {
           "expression"       : "version = :expectedVersion",
           "expressionValues" : {
               ":expectedVersion" : $util.dynamodb.toDynamoDBJson($context.arguments.expectedVersion)
           }
       }
   }
   ```

1. **저장**을 선택합니다.

이 템플릿은 보다 복잡한 예 중 하나입니다. 이 템플릿은 보다 복잡한 예 중 하나로, 매핑 템플릿의 이점과 유연성을 보여줍니다. 이 템플릿은 모든 인수를 루핑하면서 `id`와 `expectedVersion`을 건너뜁니다. 인수가 무언가로 설정된 경우 AWS AppSync 및 DynamoDB에 DynamoDB의 객체에서 해당 속성을 업데이트하도록 요청합니다. 속성이 null로 설정된 경우 사후 객체에서 해당 속성을 제거하도록 AWS AppSync 및 DynamoDB에 요청합니다. 인수가 지정되어 있지 않은 경우에는 속성을 그냥 둡니다. 이 템플릿 역시 `version` 필드를 증가시킵니다.

또한 새로운 `condition` 섹션이 있습니다. 조건 표현식을 사용하면 작업이 수행되기 전에 이미 DynamoDB에 있는 객체의 상태를 기반으로 요청이 성공해야 하는지 여부를 AWS AppSync와 DynamoDB에 알릴 수 있습니다. 이 경우에는 DynamoDB에 현재 있는 항목의 `version` 필드가 `expectedVersion` 인수와 정확하게 일치하는 경우에만 `UpdateItem` 요청에 성공하면 됩니다.

조건 표현식에 대한 자세한 내용은 [조건 표현식](aws-appsync-resolver-mapping-template-reference-dynamodb-condition-expressions.md) 참조 문서를 참조하십시오.

### 게시물 업데이트를 위한 API 직접 호출
<a name="id1"></a>

새 해석기를 사용해 `Post` 객체를 업데이트해 보십시오.
+ **Queries** 탭을 선택합니다.
+ **쿼리** 창에 다음 변형을 붙여 넣습니다. 또한 앞서 적어둔 값을 갖도록 `id` 인수를 업데이트해야 합니다.

  ```
  mutation updatePost {
    updatePost(
      id:123
      title: "An empty story"
      content: null
      expectedVersion: 2
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ DynamoDB에서 업데이트된 게시물이 쿼리 창 오른쪽에 있는 결과 창에 나타나야 합니다. 예를 들면 다음과 같아야 합니다.

  ```
  {
    "data": {
      "updatePost": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 3
      }
    }
  }
  ```

이 요청에서는 AWS AppSync와 DynamoDB에 `title` 및 `content` 필드만 업데이트하도록 요청했습니다. `version` 필드를 증분하는 것을 제외하고 다른 모든 필드는 그대로 둡니다. `title` 속성을 새 값으로 설정하고 게시물에서 `content` 속성을 제거했습니다. `author`, `url`, `ups` 및 `downs` 필드는 그대로 남아 있습니다.

변형 요청을 다시 실행해도 요청은 정확하게 그대로 유지됩니다. 다음과 유사한 응답이 나타납니다.

```
{
  "data": {
    "updatePost": null
  },
  "errors": [
    {
      "path": [
        "updatePost"
      ],
      "data": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 3
      },
      "errorType": "DynamoDB:ConditionalCheckFailedException",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "message": "The conditional request failed (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ConditionalCheckFailedException; Request ID: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ)"
    }
  ]
}
```

조건 표현식이 false로 평가되었으므로 요청에 실패합니다.
+ 요청을 처음 실행했을 때 DynamoDB에 있는 게시물의 `version` 필드 값은 `2`였고, 이는 `expectedVersion` 인수와 일치했습니다. 요청에 성공하자 DynamoDB의 `version` 필드가 `3`으로 증가했습니다.
+ 요청을 두 번째로 실행했을 때 DynamoDB에 있는 게시물의 `version` 필드 값은 `3`이었고, 이는 `expectedVersion` 인수와 일치하지 않았습니다.

이러한 패턴을 일반적으로 *낙관적 잠금*이라고 합니다.

 AWS AppSync DynamoDB 해석기의 기능은 DynamoDB에서 포스트 객체의 현재 값을 반환한다는 것입니다. 현재 값은 GraphQL 응답의 `data` 섹션에 있는 `errors` 필드에서 찾을 수 있습니다. 애플리케이션에서는 이러한 정보를 사용하여 처리 방식을 결정합니다. 이 경우, DynamoDB에 있는 객체의 `version` 필드가 `3`으로 설정되어 있은데, 방금 `expectedVersion` 인수를 `3`으로 업데이트했으므로 결과는 다시 성공입니다.

조건 검사 실패 처리에 대한 자세한 내용은 [조건 표현식](aws-appsync-resolver-mapping-template-reference-dynamodb-condition-expressions.md) 매핑 템플릿 참조 문서를 참조하십시오.

## upvotePost 및 downvotePost 뮤테이션 생성(DynamoDB UpdateItem)
<a name="create-upvotepost-and-downvotepost-mutations-ddb-updateitem"></a>

`Post` 형식에는 `ups` 및 `downs` 필드가 있어 추천 수 및 비추천 수를 기록할 수 있습니다. 하지만 지금까지 API를 사용하면 이러한 작업을 수행할 수 없었습니다. 게시물을 추천 및 비추천하도록 하는 몇 가지 변형을 추가해 보겠습니다.
+ **스키마** 탭을 선택합니다.
+ **스키마** 창에서 다음과 같이 `Mutation` 유형을 수정하여 새로운 `upvotePost` 및 `downvotePost` 뮤테이션을 추가할 수 있습니다.

  ```
  type Mutation {
      upvotePost(id: ID!): Post
      downvotePost(id: ID!): Post
      updatePost(
          id: ID!,
          author: String,
          title: String,
          content: String,
          url: String,
          expectedVersion: Int!
      ): Post
      addPost(
          author: String!,
          title: String!,
          content: String!,
          url: String!
      ): Post!
  }
  ```
+ **저장**을 선택합니다.
+ 오른쪽의 **데이터 유형** 창의 **뮤테이션** 유형에서 새로 생성한 **upvotePost** 필드를 찾은 다음 **연결**을 선택합니다.
+ **작업 메뉴**에서 **런타임 업데이트**를 선택한 다음 **단위 해석기(VTL만 해당)**를 선택합니다.
+ **Data source name(데이터 원본 이름)**에서 **PostDynamoDBTable**을 선택합니다.
+ 다음을 **Configure the request mapping template(요청 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "ADD ups :plusOne, version :plusOne",
          "expressionValues" : {
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ 다음을 **Configure the response mapping template(응답 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  $utils.toJson($context.result)
  ```
+ **저장**을 선택합니다.
+ 오른쪽의 **데이터 유형** 창의 **뮤테이션** 유형에서 새로 생성한 `downvotePost` 필드를 찾은 다음 **연결**을 선택합니다.
+ **Data source name(데이터 원본 이름)**에서 **PostDynamoDBTable**을 선택합니다.
+ 다음을 **Configure the request mapping template(요청 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "ADD downs :plusOne, version :plusOne",
          "expressionValues" : {
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ 다음을 **Configure the response mapping template(응답 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  $utils.toJson($context.result)
  ```
+ **저장**을 선택합니다.

### 게시물을 추천 및 비추천하는 API 직접 호출
<a name="call-the-api-to-upvote-and-downvote-a-post"></a>

이제 새 해석기가 설정되었으며 AWS AppSync는 수신 `upvotePost` 또는 `downvote` 변형을 DynamoDB UpdateItem 작업으로 변환하는 방법을 알고 있습니다. 이제 앞서 생성한 게시물을 추천 또는 비추천하는 변형을 실행할 수 있습니다.
+ **Queries** 탭을 선택합니다.
+ **쿼리** 창에 다음 변형을 붙여 넣습니다. 또한 앞서 적어둔 값을 갖도록 `id` 인수를 업데이트해야 합니다.

  ```
  mutation votePost {
    upvotePost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ 게시물이 DynamoDB에서 업데이트되고 쿼리 창 오른쪽에 있는 결과 창에 나타나야 합니다. 예를 들면 다음과 같아야 합니다.

  ```
  {
    "data": {
      "upvotePost": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 6,
        "downs": 0,
        "version": 4
      }
    }
  }
  ```
+ **Execute query(쿼리 실행)**를 몇 번 더 선택합니다. 쿼리를 실행할 때마다 `ups` 및 `version` 필드가 1씩 증가하는 것이 보여야 합니다.
+ 다음과 같이 `downvotePost` 변형을 호출하도록 쿼리를 변경합니다.

  ```
  mutation votePost {
    downvotePost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다. 쿼리를 실행할 때마다 `downs` 및 `version` 필드가 1씩 증가하는 것이 보여야 합니다.

  ```
  {
    "data": {
      "downvotePost": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 6,
        "downs": 4,
        "version": 12
      }
    }
  }
  ```

## deletePost 해석기 설정(DynamoDB DeleteItem)
<a name="setting-up-the-deletepost-resolver-ddb-deletepost"></a>

설정하려는 다음 변형은 게시물을 삭제하는 변형입니다. 이 작업은 `DeleteItem` DynamoDB 작업을 사용하여 수행합니다.
+ **스키마** 탭을 선택합니다.
+ **스키마** 창에서 다음과 같이 `Mutation` 형식을 수정하여 새로운 `deletePost` 변형을 추가할 수 있습니다.

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

  이번에는`expectedVersion` 필드를 선택 사항으로 설정했는데, 이에 관한 사항은 나중에 요청 매핑 템플릿을 추가할 때 설명하겠습니다.
+ **저장**을 선택합니다.
+ 오른쪽의 **데이터 유형** 창의 **뮤테이션** 유형에서 새로 생성한 **delete** 필드를 찾은 다음 **연결**을 선택합니다.
+ **작업 메뉴**에서 **런타임 업데이트**를 선택한 다음 **단위 해석기(VTL만 해당)**를 선택합니다.
+ **Data source name(데이터 원본 이름)**에서 **PostDynamoDBTable**을 선택합니다.
+ 다음을 **Configure the request mapping template(요청 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  {
      "version" : "2017-02-28",
      "operation" : "DeleteItem",
      "key": {
          "id": $util.dynamodb.toDynamoDBJson($context.arguments.id)
      }
      #if( $context.arguments.containsKey("expectedVersion") )
          ,"condition" : {
              "expression"       : "attribute_not_exists(id) OR version = :expectedVersion",
              "expressionValues" : {
                  ":expectedVersion" : $util.dynamodb.toDynamoDBJson($context.arguments.expectedVersion)
              }
          }
      #end
  }
  ```

   **참고:** `expectedVersion` 인수는 선택적 인수입니다. 호출자가 요청에서 `expectedVersion` 인수를 설정하면, 템플릿에서는 항목이 이미 삭제된 경우 또는 DynamoDB에 있는 `version` 속성이 `expectedVersion`과 정확하게 일치하는 경우, `DeleteItem` 요청이 완료되게 하는 조건을 추가합니다. 그냥 두면 `DeleteItem` 요청에 아무런 조건 표현식도 지정되지 않습니다. 이는 `version`의 값이나 해당 항목이 DynamoDB에 존재하는지 여부에 관계없이 완료됩니다.
+ 다음을 **Configure the response mapping template(응답 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  $utils.toJson($context.result)
  ```

   **참고:** 항목을 삭제하고 있더라도 항목이 아직 삭제되지 않은 경우 삭제된 항목을 반환할 수 있습니다.
+ **저장**을 선택합니다.

`DeleteItem` 요청 매핑에 대한 자세한 내용은 [DeleteItem](aws-appsync-resolver-mapping-template-reference-dynamodb-deleteitem.md) 참조 문서를 참조하십시오.

### 게시물 삭제를 위한 API 직접 호출
<a name="call-the-api-to-delete-a-post"></a>

이제 해석기가 설정되었으며 AWS AppSync는 들어오는 `delete` 변형을 DynamoDB`DeleteItem` 작업으로 변환하는 방법을 알고 있습니다. 이제 변형을 실행해 테이블에 데이터를 삭제할 수 있습니다.
+ **Queries** 탭을 선택합니다.
+ **쿼리** 창에 다음 변형을 붙여 넣습니다. 또한 앞서 적어둔 값을 갖도록 `id` 인수를 업데이트해야 합니다.

  ```
  mutation deletePost {
    deletePost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ DynamoDB에서 게시물이 삭제되었습니다. 참고로 AWS AppSync는 DynamoDB에서 삭제된 항목의 값을 반환합니다.이 값은 쿼리 창 오른쪽에 있는 결과 창에 나타나야 합니다. 예를 들면 다음과 같아야 합니다.

  ```
  {
    "data": {
      "deletePost": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 6,
        "downs": 4,
        "version": 12
      }
    }
  }
  ```

`deletePost`에 대한 호출이 DynamoDB에서 실제로 삭제된 항목인 경우에만 값이 반환됩니다.
+ **Execute query(쿼리 실행)**을 다시 선택합니다.
+ 호출에는 계속 성공하지만 반환되는 값은 없습니다.

  ```
  {
    "data": {
      "deletePost": null
    }
  }
  ```

이제 게시물을 삭제해 보는데, 이번에는 `expectedValue`를 지정해 보겠습니다. 지금까지 작업한 게시물 하나를 방금 삭제했기 때문에 먼저 새 게시물을 생성해야 합니다.
+ **쿼리** 창에 다음 변형을 붙여 넣습니다.

  ```
  mutation addPost {
    addPost(
      id:123
      author: "AUTHORNAME"
      title: "Our second post!"
      content: "A new post."
      url: "https://aws.amazon.com/appsync/"
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ 새로 생성한 게시물의 결과가 쿼리 창 오른쪽에 있는 결과 창에 나타나야 합니다. 곧 필요할 것이기 때문에 새로 생성한 객체의 `id`를 적어 둡니다. 예를 들면 다음과 같아야 합니다.

  ```
  {
    "data": {
      "addPost": {
        "id": "123",
        "author": "AUTHORNAME",
        "title": "Our second post!",
        "content": "A new post.",
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 1
      }
    }
  }
  ```

이제 게시물을 삭제해 보는데 `expectedVersion`에 대해 잘못된 값을 입력할 것입니다.
+ **쿼리** 창에 다음 변형을 붙여 넣습니다. 또한 앞서 적어둔 값을 갖도록 `id` 인수를 업데이트해야 합니다.

  ```
  mutation deletePost {
    deletePost(
      id:123
      expectedVersion: 9999
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.

  ```
  {
    "data": {
      "deletePost": null
    },
    "errors": [
      {
        "path": [
          "deletePost"
        ],
        "data": {
          "id": "123",
          "author": "AUTHORNAME",
          "title": "Our second post!",
          "content": "A new post.",
          "url": "https://aws.amazon.com/appsync/",
          "ups": 1,
          "downs": 0,
          "version": 1
        },
        "errorType": "DynamoDB:ConditionalCheckFailedException",
        "locations": [
          {
            "line": 2,
            "column": 3
          }
        ],
        "message": "The conditional request failed (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ConditionalCheckFailedException; Request ID: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ)"
      }
    ]
  }
  ```

  DynamoDB에 있는 게시물의 `version` 값이 인수에 지정된 `expectedValue`와 일치하지 않아 조건 표현식이 false로 평가되었기 때문에 요청에 실패했습니다. 객체의 현재 값은 GraphQL 응답에서 `data` 섹션의 `errors` 필드에 반환됩니다.
+ 이 요청을 다시 실행하되 `expectedVersion`을 수정합니다.

  ```
  mutation deletePost {
    deletePost(
      id:123
      expectedVersion: 1
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ 이번에는 요청에 성공하고 DynamoDB에서 삭제된 값이 반환됩니다.

  ```
  {
    "data": {
      "deletePost": {
        "id": "123",
        "author": "AUTHORNAME",
        "title": "Our second post!",
        "content": "A new post.",
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 1
      }
    }
  }
  ```
+ **Execute query(쿼리 실행)**을 다시 선택합니다.
+ 호출에 계속해서 성공하지만 이번에는 게시물이 이미 DynamoDB에서 삭제되었으므로 반환되는 값이 없습니다.

```
{
  "data": {
    "deletePost": null
  }
}
```

## allPost 해석기 설정(DynamoDB Scan)
<a name="setting-up-the-allpost-resolver-dynamodb-scan"></a>

지금까지 API는 살펴보고자 하는 각 게시물의 `id`를 아는 경우에만 유용했습니다. 테이블의 게시물을 모두 반환하는 새 해석기를 추가해 보겠습니다.
+ **스키마** 탭을 선택합니다.
+ **스키마** 창에서 다음과 같이 `Query` 형식을 수정하여 새로운 `allPost` 쿼리를 추가할 수 있습니다.

  ```
  type Query {
      allPost(count: Int, nextToken: String): PaginatedPosts!
      getPost(id: ID): Post
  }
  ```
+ 새로운 `PaginationPosts` 형식을 다음과 같이 추가합니다.

  ```
  type PaginatedPosts {
      posts: [Post!]!
      nextToken: String
  }
  ```
+ **저장**을 선택합니다.
+ 오른쪽의 **데이터 유형** 창의 **쿼리** 유형에서 새로 생성한 **allPost** 필드를 찾은 다음 **연결**을 선택합니다.
+ **작업 메뉴**에서 **런타임 업데이트**를 선택한 다음 **단위 해석기(VTL만 해당)**를 선택합니다.
+ **Data source name(데이터 원본 이름)**에서 **PostDynamoDBTable**을 선택합니다.
+ 다음을 **Configure the request mapping template(요청 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  {
      "version" : "2017-02-28",
      "operation" : "Scan"
      #if( ${context.arguments.count} )
          ,"limit": $util.toJson($context.arguments.count)
      #end
      #if( ${context.arguments.nextToken} )
          ,"nextToken": $util.toJson($context.arguments.nextToken)
      #end
  }
  ```

  이 해석기에는 두 가지 선택적 인수가 있는데, `count`는 단일 호출 시 반환되는 최대 항목 수를 지정하고, `nextToken`은 다음 결과 집합을 가져오는 데 사용할 수 있습니다(`nextToken`의 값을 가져오는 위치는 나중에 설명할 예정임).
+ 다음을 **Configure the response mapping template(응답 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  {
      "posts": $utils.toJson($context.result.items)
      #if( ${context.result.nextToken} )
          ,"nextToken": $util.toJson($context.result.nextToken)
      #end
  }
  ```

   **참고: ** 이 응답 매핑 템플릿은 지금까지 사용한 다른 모든 템플릿과 다릅니다. `allPost` 쿼리의 결과는 게시물 목록과 페이지 매김 토큰을 포함하고 있는 `PaginatedPosts`입니다. 이 객체의 모양은 AWS AppSync DynamoDB Resolver에서 반환되는 것과 다릅니다. 게시물 목록은 AWS AppSync DynamoDB Resolver 결과`items`에서 호출되지만 `posts`에서는 호출됩니다`PaginatedPosts`.
+ **저장**을 선택합니다.

`Scan` 요청 매핑에 대한 자세한 내용은 [스캔](aws-appsync-resolver-mapping-template-reference-dynamodb-scan.md) 참조 문서를 참조하십시오.

### 모든 게시물을 스캔하는 API 직접 호출
<a name="call-the-api-to-scan-all-posts"></a>

이제 해석기가 설정되었으며 AWS AppSync는 수신 `allPost` 쿼리를 DynamoDB`Scan` 작업으로 변환하는 방법을 알고 있습니다. 이제 테이블을 스캔해 게시물을 모두 검색할 수 있습니다.

지금까지 작업했던 데이터를 모두 삭제했기 때문에 이 요청을 수행하기 전에 몇 가지 데이터로 테이블을 채워야 합니다.
+ **Queries** 탭을 선택합니다.
+ **쿼리** 창에 다음 변형을 붙여 넣습니다.

  ```
  mutation addPost {
    post1: addPost(id:1 author: "AUTHORNAME" title: "A series of posts, Volume 1" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post2: addPost(id:2 author: "AUTHORNAME" title: "A series of posts, Volume 2" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post3: addPost(id:3 author: "AUTHORNAME" title: "A series of posts, Volume 3" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post4: addPost(id:4 author: "AUTHORNAME" title: "A series of posts, Volume 4" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post5: addPost(id:5 author: "AUTHORNAME" title: "A series of posts, Volume 5" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post6: addPost(id:6 author: "AUTHORNAME" title: "A series of posts, Volume 6" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post7: addPost(id:7 author: "AUTHORNAME" title: "A series of posts, Volume 7" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post8: addPost(id:8 author: "AUTHORNAME" title: "A series of posts, Volume 8" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post9: addPost(id:9 author: "AUTHORNAME" title: "A series of posts, Volume 9" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.

이제 테이블을 스캔해 보겠습니다. 한 번에 5개 결과를 반환합니다.
+ **쿼리** 창에 다음 쿼리를 붙여 넣습니다.

  ```
  query allPost {
    allPost(count: 5) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ 처음 5개 게시물이 쿼리 창 오른쪽에 있는 결과 창에 나타나야 합니다. 예를 들면 다음과 같아야 합니다.

  ```
  {
    "data": {
      "allPost": {
        "posts": [
          {
            "id": "5",
            "title": "A series of posts, Volume 5"
          },
          {
            "id": "1",
            "title": "A series of posts, Volume 1"
          },
          {
            "id": "6",
            "title": "A series of posts, Volume 6"
          },
          {
            "id": "9",
            "title": "A series of posts, Volume 9"
          },
          {
            "id": "7",
            "title": "A series of posts, Volume 7"
          }
        ],
        "nextToken": "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnRkJEdXdUK09hcnovRGhNTGxLTGdMUEFBQUI1akNDQWVJR0NTcUdTSWIzRFFFSEJxQ0NBZE13Z2dIUEFnRUFNSUlCeUFZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF6ajFodkhKU1paT1pncTRaUUNBUkNBZ2dHWnJiR1dQWGxkMDB1N0xEdGY4Z2JsbktzRjRua1VCcks3TFJLcjZBTFRMeGFwVGJZMDRqOTdKVFQyYVRwSzdzbVdtNlhWWFVCTnFIOThZTzBWZHVkdDI2RlkxMHRqMDJ2QTlyNWJTUWpTbWh6NE5UclhUMG9KZWJSQ2JJbXBlaDRSVlg0Tis0WTVCN1IwNmJQWWQzOVhsbTlUTjBkZkFYMVErVCthaXZoNE5jMk50RitxVmU3SlJ5WmpzMEFkSGduM3FWd2VrOW5oeFVVd3JlK1loUks5QkRzemdiMDlmZmFPVXpzaFZ4cVJRbC93RURlOTcrRmVJdXZNby9NZ1F6dUdNbFRyalpNR3FuYzZBRnhwa0VlZTFtR0FwVDFISElUZlluakptYklmMGUzUmcxbVlnVHVSbDh4S0trNmR0QVoraEhLVDhuNUI3VnF4bHRtSnlNUXBrZGl6KzkyL3VzNDl4OWhrMnVxSW01ZFFwMjRLNnF0dm9ZK1BpdERuQTc5djhzb0grVytYT3VuQ2NVVDY4TVZ1Wk5KYkRuSEFSSEVlaTlVNVBTelU5RGZ6d2pPdmhqWDNJMWhwdWUrWi83MDVHVjlPQUxSTGlwZWZPeTFOZFhwZTdHRDZnQW00bUJUK2c1eC9Ec3ZDbWVnSDFDVXRTdHVuU1ZFa2JpZytQRC9oMUwyRTNqSHhVQldaa28yU256WUc0cG0vV1RSWkFVZHZuQT09In0="
      }
    }
  }
  ```

5개 결과가 반환되고 다음 결과 집합을 가져오는데 사용할 수 있는 `nextToken`도 있습니다.
+ 이전 결과 집합에서 `allPost`을 포함하도록 `nextToken` 쿼리를 업데이트합니다.

  ```
  query allPost {
    allPost(
      count: 5
      nextToken: "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnRlluNktJRWl6V0ZlR3hJOVJkaStrZUFBQUI1akNDQWVJR0NTcUdTSWIzRFFFSEJxQ0NBZE13Z2dIUEFnRUFNSUlCeUFZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5cW8yUGFSZThnalFpemRCTUNBUkNBZ2dHWk1JODhUNzhIOFVUZGtpdFM2ZFluSWRyVDg4c2lkN1RjZzB2d1k3VGJTTWpSQ2U3WjY3TkUvU2I1dWNETUdDMmdmMHErSGJSL0pteGRzYzVEYnE1K3BmWEtBdU5jSENJdWNIUkJ0UHBPWVdWdCtsS2U5L1pNcWdocXhrem1RaXI1YnIvQkt6dU5hZmJCdE93NmtoM2Jna1BKM0RjWWhpMFBGbmhMVGg4TUVGSjBCcXg3RTlHR1V5N0tUS0JLZlV3RjFQZ0JRREdrNzFYQnFMK2R1S2IrVGtZZzVYMjFrc3NyQmFVTmNXZmhTeXE0ZUJHSWhqZWQ5c3VKWjBSSTc2ZnVQdlZkR3FLNENjQmxHYXhpekZnK2pKK1FneEU1SXduRTNYYU5TR0I4QUpmamR2bU1wbUk1SEdvWjlMUUswclczbG14RDRtMlBsaTNLaEVlcm9pem5zcmdINFpvcXIrN2ltRDN3QkJNd3BLbGQzNjV5Nnc4ZnMrK2FnbTFVOUlKOFFrOGd2bEgySHFROHZrZXBrMWlLdWRIQ25LaS9USnBlMk9JeEVPazVnRFlzRTRUU09HUlVJTkxYY2MvdW1WVEpBMUthV2hWTlAvdjNlSnlZQUszbWV6N2h5WHVXZ1BkTVBNWERQdTdjVnVRa3EwK3NhbGZOd2wvSUx4bHNyNDVwTEhuVFpyRWZvVlV1bXZ5S2VKY1RUU1lET05hM1NwWEd2UT09In0="
    ) {
      posts {
        id
        author
      }
      nextToken
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ 나머지 4개 게시물이 쿼리 창 오른쪽에 있는 결과 창에 나타나야 합니다. 게시물이 모두 9개이며 남은 게시물이 없기 때문에 이 결과 집합에는 `nextToken`이 없습니다. 예를 들면 다음과 같아야 합니다.

  ```
  {
    "data": {
      "allPost": {
        "posts": [
          {
            "id": "2",
            "title": "A series of posts, Volume 2"
          },
          {
            "id": "3",
            "title": "A series of posts, Volume 3"
          },
          {
            "id": "4",
            "title": "A series of posts, Volume 4"
          },
          {
            "id": "8",
            "title": "A series of posts, Volume 8"
          }
        ],
        "nextToken": null
      }
    }
  }
  ```

## allPostsByAuthor 해석기 설정(DynamoDB Query)
<a name="setting-up-the-allpostsbyauthor-resolver-ddb-query"></a>

DynamoDB에서 모든 게시물을 스캔하는 것 이외에 DynamoDB를 쿼리해 특정 작성자가 생성한 게시물을 검색할 수 있습니다. 앞서 이미 생성한 DynamoDB 테이블에는 특정 작성자가 생성한 게시물을 모두 검색하는 DynamoDB `Query` 작업에 사용할 수 있는 `author-index`라는 `GlobalSecondaryIndex`가 있습니다.
+ **스키마** 탭을 선택합니다.
+ **스키마** 창에서 다음과 같이 `Query` 형식을 수정하여 새로운 `allPostsByAuthor` 쿼리를 추가할 수 있습니다.

  ```
  type Query {
      allPostsByAuthor(author: String!, count: Int, nextToken: String): PaginatedPosts!
      allPost(count: Int, nextToken: String): PaginatedPosts!
      getPost(id: ID): Post
  }
  ```

   **참고:** 여기서는 `allPost` 쿼리에 사용한 것과 동일한 `PaginatedPosts` 형식을 사용합니다.
+ **저장**을 선택합니다.
+ 오른쪽의 **데이터 유형** 창의 **쿼리** 유형에서 새로 생성한 **allPostsByAuthor** 필드를 찾은 다음 **연결**을 선택합니다.
+ **작업 메뉴**에서 **런타임 업데이트**를 선택한 다음 **단위 해석기(VTL만 해당)**를 선택합니다.
+ **Data source name(데이터 원본 이름)**에서 **PostDynamoDBTable**을 선택합니다.
+ 다음을 **Configure the request mapping template(요청 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  {
      "version" : "2017-02-28",
      "operation" : "Query",
      "index" : "author-index",
      "query" : {
        "expression": "author = :author",
          "expressionValues" : {
            ":author" : $util.dynamodb.toDynamoDBJson($context.arguments.author)
          }
      }
      #if( ${context.arguments.count} )
          ,"limit": $util.toJson($context.arguments.count)
      #end
      #if( ${context.arguments.nextToken} )
          ,"nextToken": "${context.arguments.nextToken}"
      #end
  }
  ```

  `allPost` 해석기처럼 이 해석기에는 두 가지 선택적 인수가 있는데, `count`는 단일 호출 시 반환되는 최대 항목 수를 지정하고, `nextToken`은 다음 결과 집합을 가져오는 데 사용할 수 있습니다(`nextToken`의 값은 이전 호출에서 얻을 수 있음).
+ 다음을 **Configure the response mapping template(응답 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  {
      "posts": $utils.toJson($context.result.items)
      #if( ${context.result.nextToken} )
          ,"nextToken": $util.toJson($context.result.nextToken)
      #end
  }
  ```

   **참고:** 이는 `allPost` 해석기에서 사용한 것과 동일한 응답 매핑 템플릿입니다.
+ **저장**을 선택합니다.

`Query` 요청 매핑에 대한 자세한 내용은 [쿼리](aws-appsync-resolver-mapping-template-reference-dynamodb-query.md) 참조 문서를 참조하십시오.

### 작성자별 모든 게시물을 쿼리하는 API 직접 호출
<a name="call-the-api-to-query-all-posts-by-an-author"></a>

이제 해석기가 설정되었으며 AWS AppSync는 `author-index` 인덱스에 대해 수신 `allPostsByAuthor` 변형을 DynamoDB`Query` 작업으로 변환하는 방법을 알고 있습니다. 이제 테이블을 쿼리해 특정 작성자별로 게시물을 모두 검색할 수 있습니다.

지금까지 사용한 모든 게시물은 작성자가 동일하기 때문에 검색하기 전에 추가 게시물로 테이블을 채우겠습니다.
+ **Queries** 탭을 선택합니다.
+ **쿼리** 창에 다음 변형을 붙여 넣습니다.

  ```
  mutation addPost {
    post1: addPost(id:10 author: "Nadia" title: "The cutest dog in the world" content: "So cute. So very, very cute." url: "https://aws.amazon.com/appsync/" ) { author, title }
    post2: addPost(id:11 author: "Nadia" title: "Did you know...?" content: "AppSync works offline?" url: "https://aws.amazon.com/appsync/" ) { author, title }
    post3: addPost(id:12 author: "Steve" title: "I like GraphQL" content: "It's great" url: "https://aws.amazon.com/appsync/" ) { author, title }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.

이제 `Nadia`가 작성한 게시물을 모두 반환하도록 테이블을 쿼리하겠습니다.
+ **쿼리** 창에 다음 쿼리를 붙여 넣습니다.

  ```
  query allPostsByAuthor {
    allPostsByAuthor(author: "Nadia") {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ `Nadia`가 작성한 모든 게시물이 쿼리 창 오른쪽에 있는 결과 창에 나타나야 합니다. 예를 들면 다음과 같아야 합니다.

  ```
  {
    "data": {
      "allPostsByAuthor": {
        "posts": [
          {
            "id": "10",
            "title": "The cutest dog in the world"
          },
          {
            "id": "11",
            "title": "Did you know...?"
          }
        ],
        "nextToken": null
      }
    }
  }
  ```

`Query`에 대한 페이지 매김은 `Scan`을 수행할 때와 동일하게 작동합니다. 예를 들어, `AUTHORNAME`의 모든 게시물을 살펴보겠습니다. 이 경우 한 번에 5개 결과를 반환합니다.
+ **쿼리** 창에 다음 쿼리를 붙여 넣습니다.

  ```
  query allPostsByAuthor {
    allPostsByAuthor(
      author: "AUTHORNAME"
      count: 5
    ) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ `AUTHORNAME`가 작성한 모든 게시물이 쿼리 창 오른쪽에 있는 결과 창에 나타나야 합니다. 예를 들면 다음과 같아야 합니다.

  ```
  {
    "data": {
      "allPostsByAuthor": {
        "posts": [
          {
            "id": "6",
            "title": "A series of posts, Volume 6"
          },
          {
            "id": "4",
            "title": "A series of posts, Volume 4"
          },
          {
            "id": "2",
            "title": "A series of posts, Volume 2"
          },
          {
            "id": "7",
            "title": "A series of posts, Volume 7"
          },
          {
            "id": "1",
            "title": "A series of posts, Volume 1"
          }
        ],
        "nextToken": "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnSExqRnVhVUR3ZUhEZ2QzNGJ2QlFuY0FBQUNqekNDQW9zR0NTcUdTSWIzRFFFSEJxQ0NBbnd3Z2dKNEFnRUFNSUlDY1FZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5Qkg4Yk1obW9LVEFTZHM3SUNBUkNBZ2dKQ3dISzZKNlJuN3pyYUVKY1pWNWxhSkNtZW1KZ0F5N1dhZkc2UEdTNHpNQzJycTkwZHFJTFV6Z25wck9Gd3pMS3VOQ2JvUXc3VDI5eCtnVExIbGg4S3BqbzB1YjZHQ3FwcDhvNDVmMG9JbDlmdS9JdjNXcFNNSXFKTXZ1MEVGVWs1VzJQaW5jZGlUaVRtZFdYWlU1bkV2NkgyRFBRQWZYYlNnSmlHSHFLbmJZTUZZM0FTdmRIL0hQaVZBb1RCMk1YZkg0eGJOVTdEbjZtRFNhb2QwbzdHZHJEWDNtODQ1UXBQUVNyUFhHemY0WDkyajhIdlBCSWE4Smcrb0RxbHozUVQ5N2FXUXdYWWU2S0h4emI1ejRITXdEdXEyRDRkYzhoMi9CbW10MzRMelVGUVIyaExSZGRaZ0xkdzF5cHJZdFZwY3dEc1d4UURBTzdOcjV2ZEp4VVR2TVhmODBRSnp1REhXREpTVlJLdDJwWmlpaXhXeGRwRmNod1BzQ3d2aVBqMGwrcWFFWU1jMXNQbENkVkFGem43VXJrSThWbS8wWHlwR2xZb3BSL2FkV0xVekgrbGMrYno1ZEM2SnVLVXdtY1EyRXlZeDZiS0Izbi9YdUViWGdFeU5PMWZTdE1rRlhyWmpvMVpzdlYyUFRjMzMrdEs0ZDhkNkZrdjh5VVR6WHhJRkxIaVNsOUx6VVdtT3BCaWhrTFBCT09jcXkyOHh1UmkzOEM3UFRqMmN6c3RkOUo1VUY0azBJdUdEbVZzM2xjdWg1SEJjYThIeXM2aEpvOG1HbFpMNWN6R2s5bi8vRE1EbDY3RlJraG5QNFNhSDBpZGI5VFEvMERLeFRBTUdhcWpPaEl5ekVqd2ZDQVJleFdlbldyOGlPVkhScDhGM25WZVdvbFRGK002N0xpdi9XNGJXdDk0VEg3b0laUU5lYmZYKzVOKy9Td25Hb1dyMTlWK0pEb2lIRVFLZ1cwMWVuYjZKUXo5Slh2Tm95ZzF3RnJPVmxGc2xwNlRHa1BlN2Rnd2IrWT0ifQ=="
      }
    }
  }
  ```
+ 다음과 같이 이전 쿼리에서 반환된 값으로 `nextToken` 인수를 업데이트합니다.

  ```
  query allPostsByAuthor {
    allPostsByAuthor(
      author: "AUTHORNAME"
      count: 5
      nextToken: "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnSExqRnVhVUR3ZUhEZ2QzNGJ2QlFuY0FBQUNqekNDQW9zR0NTcUdTSWIzRFFFSEJxQ0NBbnd3Z2dKNEFnRUFNSUlDY1FZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5Qkg4Yk1obW9LVEFTZHM3SUNBUkNBZ2dKQ3dISzZKNlJuN3pyYUVKY1pWNWxhSkNtZW1KZ0F5N1dhZkc2UEdTNHpNQzJycTkwZHFJTFV6Z25wck9Gd3pMS3VOQ2JvUXc3VDI5eCtnVExIbGg4S3BqbzB1YjZHQ3FwcDhvNDVmMG9JbDlmdS9JdjNXcFNNSXFKTXZ1MEVGVWs1VzJQaW5jZGlUaVRtZFdYWlU1bkV2NkgyRFBRQWZYYlNnSmlHSHFLbmJZTUZZM0FTdmRIL0hQaVZBb1RCMk1YZkg0eGJOVTdEbjZtRFNhb2QwbzdHZHJEWDNtODQ1UXBQUVNyUFhHemY0WDkyajhIdlBCSWE4Smcrb0RxbHozUVQ5N2FXUXdYWWU2S0h4emI1ejRITXdEdXEyRDRkYzhoMi9CbW10MzRMelVGUVIyaExSZGRaZ0xkdzF5cHJZdFZwY3dEc1d4UURBTzdOcjV2ZEp4VVR2TVhmODBRSnp1REhXREpTVlJLdDJwWmlpaXhXeGRwRmNod1BzQ3d2aVBqMGwrcWFFWU1jMXNQbENkVkFGem43VXJrSThWbS8wWHlwR2xZb3BSL2FkV0xVekgrbGMrYno1ZEM2SnVLVXdtY1EyRXlZeDZiS0Izbi9YdUViWGdFeU5PMWZTdE1rRlhyWmpvMVpzdlYyUFRjMzMrdEs0ZDhkNkZrdjh5VVR6WHhJRkxIaVNsOUx6VVdtT3BCaWhrTFBCT09jcXkyOHh1UmkzOEM3UFRqMmN6c3RkOUo1VUY0azBJdUdEbVZzM2xjdWg1SEJjYThIeXM2aEpvOG1HbFpMNWN6R2s5bi8vRE1EbDY3RlJraG5QNFNhSDBpZGI5VFEvMERLeFRBTUdhcWpPaEl5ekVqd2ZDQVJleFdlbldyOGlPVkhScDhGM25WZVdvbFRGK002N0xpdi9XNGJXdDk0VEg3b0laUU5lYmZYKzVOKy9Td25Hb1dyMTlWK0pEb2lIRVFLZ1cwMWVuYjZKUXo5Slh2Tm95ZzF3RnJPVmxGc2xwNlRHa1BlN2Rnd2IrWT0ifQ=="
    ) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ `AUTHORNAME`가 작성한 잔여 게시물이 쿼리 창 오른쪽에 있는 결과 창에 나타나야 합니다. 예를 들면 다음과 같아야 합니다.

  ```
  {
    "data": {
      "allPostsByAuthor": {
        "posts": [
          {
            "id": "8",
            "title": "A series of posts, Volume 8"
          },
          {
            "id": "5",
            "title": "A series of posts, Volume 5"
          },
          {
            "id": "3",
            "title": "A series of posts, Volume 3"
          },
          {
            "id": "9",
            "title": "A series of posts, Volume 9"
          }
        ],
        "nextToken": null
      }
    }
  }
  ```

## 집합 사용
<a name="using-sets"></a>

지금까지 `Post` 형식은 플랫 키/값 객체였습니다. 세트, 목록 및 맵과 같은 AWS AppSyncDynamoDB 해석기를 사용하여 복잡한 객체를 모델링할 수도 있습니다.

태그를 포함하도록 `Post` 형식을 업데이트해 보겠습니다. 게시물에는 태그가 0개 이상 있을 수 있는데, 태그는 DynamoDB에 문자열 집합으로 저장됩니다. 또한 태그를 추가 및 제거하는 몇 가지 변형과 특정 태그가 지정된 게시물을 스캔하는 새 쿼리를 설정해 볼 것입니다.
+ **스키마** 탭을 선택합니다.
+ **스키마** 창에서 다음과 같이 `Post` 형식을 수정하여 새로운 `tags` 필드를 추가할 수 있습니다.

  ```
  type Post {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int!
    downs: Int!
    version: Int!
    tags: [String!]
  }
  ```
+ **스키마** 창에서 다음과 같이 `Query` 형식을 수정하여 새로운 `allPostsByTag` 쿼리를 추가할 수 있습니다.

  ```
  type Query {
    allPostsByTag(tag: String!, count: Int, nextToken: String): PaginatedPosts!
    allPostsByAuthor(author: String!, count: Int, nextToken: String): PaginatedPosts!
    allPost(count: Int, nextToken: String): PaginatedPosts!
    getPost(id: ID): Post
  }
  ```
+ **스키마** 창에서 다음과 같이 `Mutation` 유형을 수정하여 새로운 `addTag` 및 `removeTag` 뮤테이션을 추가할 수 있습니다.

  ```
  type Mutation {
    addTag(id: ID!, tag: String!): Post
    removeTag(id: ID!, tag: String!): Post
    deletePost(id: ID!, expectedVersion: Int): Post
    upvotePost(id: ID!): Post
    downvotePost(id: ID!): Post
    updatePost(
      id: ID!,
      author: String,
      title: String,
      content: String,
      url: String,
      expectedVersion: Int!
    ): Post
    addPost(
      author: String!,
      title: String!,
      content: String!,
      url: String!
    ): Post!
  }
  ```
+ **저장**을 선택합니다.
+ 오른쪽의 **데이터 유형** 창의 **쿼리** 유형에서 새로 생성한 **allPostsByTag** 필드를 찾은 다음 **연결**을 선택합니다.
+ **Data source name(데이터 원본 이름)**에서 **PostDynamoDBTable**을 선택합니다.
+ 다음을 **Configure the request mapping template(요청 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  {
      "version" : "2017-02-28",
      "operation" : "Scan",
      "filter": {
        "expression": "contains (tags, :tag)",
          "expressionValues": {
            ":tag": $util.dynamodb.toDynamoDBJson($context.arguments.tag)
          }
      }
      #if( ${context.arguments.count} )
          ,"limit": $util.toJson($context.arguments.count)
      #end
      #if( ${context.arguments.nextToken} )
          ,"nextToken": $util.toJson($context.arguments.nextToken)
      #end
  }
  ```
+ 다음을 **Configure the response mapping template(응답 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  {
      "posts": $utils.toJson($context.result.items)
      #if( ${context.result.nextToken} )
          ,"nextToken": $util.toJson($context.result.nextToken)
      #end
  }
  ```
+ **저장**을 선택합니다.
+ 오른쪽의 **데이터 유형** 창의 **뮤테이션** 유형에서 새로 생성한 **addTag** 필드를 찾은 다음 **연결**을 선택합니다.
+ **Data source name(데이터 원본 이름)**에서 **PostDynamoDBTable**을 선택합니다.
+ 다음을 **Configure the request mapping template(요청 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "ADD tags :tags, version :plusOne",
          "expressionValues" : {
              ":tags" : { "SS": [ $util.toJson($context.arguments.tag) ] },
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ 다음을 **Configure the response mapping template(응답 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  $utils.toJson($context.result)
  ```
+ **저장**을 선택합니다.
+ 오른쪽의 **데이터 유형** 창의 **뮤테이션** 유형에서 새로 생성한 **removeTag** 필드를 찾은 다음 **연결**을 선택합니다.
+ **Data source name(데이터 원본 이름)**에서 **PostDynamoDBTable**을 선택합니다.
+ 다음을 **Configure the request mapping template(요청 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "DELETE tags :tags ADD version :plusOne",
          "expressionValues" : {
              ":tags" : { "SS": [ $util.toJson($context.arguments.tag) ] },
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ 다음을 **Configure the response mapping template(응답 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  $utils.toJson($context.result)
  ```
+ **저장**을 선택합니다.

### 태그를 사용하는 API 직접 호출
<a name="call-the-api-to-work-with-tags"></a>

이제 해석기를 설정했으므로 AWS AppSync는 들어오는 , `addTag` `removeTag`및 `allPostsByTag` 요청을 DynamoDB`UpdateItem` 및 `Scan` 작업으로 변환하는 방법을 알고 있습니다.

실행해 보기 위해 이전에 생성한 게시물 중 선택해 보겠습니다. 예를 들어, `Nadia`에서 작성한 게시물을 사용해 보겠습니다.
+ **Queries** 탭을 선택합니다.
+ **쿼리** 창에 다음 쿼리를 붙여 넣습니다.

  ```
  query allPostsByAuthor {
    allPostsByAuthor(
      author: "Nadia"
    ) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ Nadia의 모든 게시물이 쿼리 창 오른쪽에 있는 결과 창에 나타나야 합니다. 예를 들면 다음과 같아야 합니다.

  ```
  {
    "data": {
      "allPostsByAuthor": {
        "posts": [
          {
            "id": "10",
            "title": "The cutest dog in the world"
          },
          {
            "id": "11",
            "title": "Did you known...?"
          }
        ],
        "nextToken": null
      }
    }
  }
  ```
+ 제목이 `"The cutest dog in the world"`인 게시물을 사용하겠습니다. 나중에 사용할 것이기 때문에 이 게시물의 `id`를 적어 둡니다.

`dog` 태그를 추가합니다.
+ **쿼리** 창에 다음 변형을 붙여 넣습니다. 또한 앞서 적어둔 값을 갖도록 `id` 인수를 업데이트해야 합니다.

  ```
  mutation addTag {
    addTag(id:10 tag: "dog") {
      id
      title
      tags
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ 새 태그로 게시물이 업데이트됩니다.

  ```
  {
    "data": {
      "addTag": {
        "id": "10",
        "title": "The cutest dog in the world",
        "tags": [
          "dog"
        ]
      }
    }
  }
  ```

태그를 다음과 같이 더 추가할 수 있습니다.
+ `tag` 인수를 `puppy`로 변경하도록 변형을 업데이트합니다.

  ```
  mutation addTag {
    addTag(id:10 tag: "puppy") {
      id
      title
      tags
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ 새 태그로 게시물이 업데이트됩니다.

  ```
  {
    "data": {
      "addTag": {
        "id": "10",
        "title": "The cutest dog in the world",
        "tags": [
          "dog",
          "puppy"
        ]
      }
    }
  }
  ```

태그를 삭제할 수도 있습니다.
+ **쿼리** 창에 다음 변형을 붙여 넣습니다. 또한 앞서 적어둔 값을 갖도록 `id` 인수를 업데이트해야 합니다.

  ```
  mutation removeTag {
    removeTag(id:10 tag: "puppy") {
      id
      title
      tags
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ 게시물이 업데이트되고 `puppy` 태그가 삭제됩니다.

  ```
  {
    "data": {
      "addTag": {
        "id": "10",
        "title": "The cutest dog in the world",
        "tags": [
          "dog"
        ]
      }
    }
  }
  ```

또한 태그가 지정된 게시물을 모두 검색할 수도 있습니다.
+ **쿼리** 창에 다음 쿼리를 붙여 넣습니다.

  ```
  query allPostsByTag {
    allPostsByTag(tag: "dog") {
      posts {
        id
        title
        tags
      }
      nextToken
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ `dog` 태그가 지정된 게시물이 다음과 같이 모두 반환됩니다.

  ```
  {
    "data": {
      "allPostsByTag": {
        "posts": [
          {
            "id": "10",
            "title": "The cutest dog in the world",
            "tags": [
              "dog",
              "puppy"
            ]
          }
        ],
        "nextToken": null
      }
    }
  }
  ```

## 목록 및 맵 사용
<a name="using-lists-and-maps"></a>

DynamoDB 집합을 사용하는 것 이외에 DynamoDB 목록 및 맵을 사용하여 단일 객체 내에서 복잡한 데이터를 모델링할 수 있습니다.

게시물에 주석을 추가하는 기능을 추가해 보겠습니다. 이 기능은 DynamoDB의 `Post` 객체에 대한 맵 객체 목록으로 모델링됩니다.

 **참고:** 실제 애플리케이션에서는 설명을 고유의 테이블에 모델링하게 될 것입니다. 이 자습서에서는 설명을 `Post` 테이블에 추가하겠습니다.
+ **스키마** 탭을 선택합니다.
+ **스키마** 창에서 다음과 같이 새로운 `Comment` 형식을 추가합니다.

  ```
  type Comment {
      author: String!
      comment: String!
  }
  ```
+ **스키마** 창에서 다음과 같이 `Post` 형식을 수정하여 새로운 `comments` 필드를 추가할 수 있습니다.

  ```
  type Post {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int!
    downs: Int!
    version: Int!
    tags: [String!]
    comments: [Comment!]
  }
  ```
+ **스키마** 창에서 다음과 같이 `Mutation` 형식을 수정하여 새로운 `addComment` 변형을 추가할 수 있습니다.

  ```
  type Mutation {
    addComment(id: ID!, author: String!, comment: String!): Post
    addTag(id: ID!, tag: String!): Post
    removeTag(id: ID!, tag: String!): Post
    deletePost(id: ID!, expectedVersion: Int): Post
    upvotePost(id: ID!): Post
    downvotePost(id: ID!): Post
    updatePost(
      id: ID!,
      author: String,
      title: String,
      content: String,
      url: String,
      expectedVersion: Int!
    ): Post
    addPost(
      author: String!,
      title: String!,
      content: String!,
      url: String!
    ): Post!
  }
  ```
+ **저장**을 선택합니다.
+ 오른쪽의 **데이터 유형** 창의 **뮤테이션** 유형에서 새로 생성한 **addComment** 필드를 찾은 다음 **연결**을 선택합니다.
+ **Data source name(데이터 원본 이름)**에서 **PostDynamoDBTable**을 선택합니다.
+ 다음을 **Configure the request mapping template(요청 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  {
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
      "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
    },
    "update" : {
      "expression" : "SET comments = list_append(if_not_exists(comments, :emptyList), :newComment) ADD version :plusOne",
      "expressionValues" : {
        ":emptyList": { "L" : [] },
        ":newComment" : { "L" : [
          { "M": {
            "author": $util.dynamodb.toDynamoDBJson($context.arguments.author),
            "comment": $util.dynamodb.toDynamoDBJson($context.arguments.comment)
            }
          }
        ] },
        ":plusOne" : $util.dynamodb.toDynamoDBJson(1)
      }
    }
  }
  ```

  이 업데이트 표현식은 새 주석이 포함된 목록을 기존 `comments` 목록에 추가합니다. 목록이 아직 없으면 생성됩니다.
+ 다음을 **Configure the response mapping template(응답 매핑 템플릿 구성)**에 붙여 넣습니다.

  ```
  $utils.toJson($context.result)
  ```
+ **저장**을 선택합니다.

### 설명 추가를 위한 API 직접 호출
<a name="call-the-api-to-add-a-comment"></a>

이제 해석기를 설정했으므로 AWS AppSync는 수신 `addComment` 요청을 DynamoDB`UpdateItem` 작업으로 변환하는 방법을 알고 있습니다.

태그를 추가한 것과 동일한 게시물에 주석을 추가해 보겠습니다.
+ **Queries** 탭을 선택합니다.
+ **쿼리** 창에 다음 쿼리를 붙여 넣습니다.

  ```
  mutation addComment {
    addComment(
      id:10
      author: "Steve"
      comment: "Such a cute dog."
    ) {
      id
      comments {
        author
        comment
      }
    }
  }
  ```
+ **Execute query(쿼리 실행)**(주황색 재생 버튼)를 누릅니다.
+ Nadia의 모든 게시물이 쿼리 창 오른쪽에 있는 결과 창에 나타나야 합니다. 예를 들면 다음과 같아야 합니다.

  ```
  {
    "data": {
      "addComment": {
        "id": "10",
        "comments": [
          {
            "author": "Steve",
            "comment": "Such a cute dog."
          }
        ]
      }
    }
  }
  ```

이 요청을 여러 번 실행하면 목록에 주석이 여러 개 추가됩니다.

## 결론
<a name="conclusion"></a>

이 자습서에서는 AWS AppSync 및 GraphQL을 사용하여 DynamoDB의 Post 객체를 조작할 수 있는 API를 구축했습니다. 자세한 내용은 [해석기 매핑 템플릿 참조](resolver-mapping-template-reference.md#aws-appsync-resolver-mapping-template-reference) 단원을 참조하십시오.

정리를 위해 콘솔에서 AppSync GraphQL API를 삭제할 수 있습니다.

이 자습서에서 생성한 DynamoDB 테이블과 IAM 역할을 삭제하려면 다음을 실행하여 `AWSAppSyncTutorialForAmazonDynamoDB` 스택을 삭제하거나 CloudFormation 콘솔을 방문하여 스택을 삭제할 수 있습니다.

```
aws cloudformation delete-stack \
    --stack-name AWSAppSyncTutorialForAmazonDynamoDB
```