

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

# 에서 버전이 지정된 데이터 소스에 Delta Sync 작업 사용 AWS AppSync
<a name="tutorial-delta-sync"></a>

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

 AWS AppSync의 클라이언트 애플리케이션은 GraphQL 응답을 모바일/웹 애플리케이션의 디스크에 로컬로 캐싱하여 데이터를 저장합니다. 버전이 지정된 데이터 원본 및 `Sync` 작업을 통해 고객은 해석기 하나로 동기화 프로세스를 수행할 수 있습니다. 따라서 클라이언트가 수 많은 레코드가 있을 수 있는 기본 쿼리 하나의 결과로 로컬 캐시를 하이드레이션한 다음 마지막 쿼리 이후 변경된 데이터만 수신할 수 있습니다(*델타 업데이트*). 클라이언트가 최초 요청을 사용한 캐시의 기본 하이드레이션과 다른 요청의 증분 업데이트를 수행하도록 해 클라이언트 애플리케이션에서 백엔드로 컴퓨팅을 이전할 수 있습니다. 이는 온라인과 오프라인 상태 간에 자주 전환하는 클라이언트 애플리케이션에 훨씬 더 효율적입니다.

Delta Sync를 구현하기 위해 `Sync` 쿼리는 버전이 지정된 데이터 원본에 대한 `Sync` 작업을 사용합니다. AWS AppSync 변형이 버전이 지정된 데이터 소스의 항목을 변경하면 해당 변경에 대한 레코드도 *Delta* 테이블에 저장됩니다. 다른 버전이 지정된 데이터 소스에 대해 다른 *Delta* 테이블(예: 유형당 하나, 도메인 영역당 하나)을 사용하거나 API에 대해 단일 *Delta* 테이블을 사용하도록 선택할 수 있습니다. AWS AppSync는 기본 키의 충돌을 방지하기 위해 여러 APIs에 대해 단일 *Delta* 테이블을 사용하지 않도록 권장합니다.

또한 Delta Sync 클라이언트는 구독을 인수로 수신할 수 있고, 그런 다음 오프라인-온라인 전환 간에 구독 다시 연결 및 쓰기를 조정합니다. Delta Sync는 지수 백오프, 여러 네트워크 오류 시나리오 간에 지터를 사용한 재시도 및 대기열에 이벤트 저장을 비롯하여 구독을 자동으로 재개해 이를 수행합니다. 그런 다음 대기열의 모든 이벤트를 병합하여 최종적으로 구독을 정상적으로 처리하기 전에 적절한 델타 또는 기본 쿼리가 실행됩니다.

Amplify DataStore를 포함한 클라이언트 구성 옵션에 대한 문서는 [Amplify Framework 웹사이트](https://aws-amplify.github.io/)를 참조하십시오. 이 문서에서는 최적의 데이터 액세스를 위해 Delta Sync 클라이언트와 작동하도록 버전이 지정된 DynamoDB 데이터 원본과 `Sync` 작업을 설정하는 방법을 설명합니다.

## 원클릭 설치
<a name="one-click-setup"></a>

모든 해석기가 구성되고 필요한 AWS 리소스가 있는 GraphQL 엔드포인트를 AWS AppSync에서 자동으로 설정하려면 다음 AWS CloudFormation 템플릿을 사용합니다.

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

이 스택은 계정에 다음 리소스를 생성합니다.
+ DynamoDB 테이블 2개(기본 및 델타)
+ API 키가 있는 1 AWS AppSync API
+ DynamoDB 테이블에 대한 정책이 연결된 IAM 역할 1개

클라이언트가 오프라인 상태였을 때 누락된 이벤트 저널로 작동하는 두 번째 테이블로 동기화 쿼리를 분할하는 데 테이블 2개가 사용됩니다. 델타 테이블에 대해 쿼리를 효율적으로 유지하기 위해 [Amazon DynamoDB TTL](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html)을 사용하면 필요에 따라 이벤트를 자동으로 정리됩니다. TTL 시간은 데이터 원본의 필요에 맞게 구성할 수 있습니다(1시간, 1일 등).

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

Delta Sync를 시연하기 위해 샘플 애플리케이션은 DynamoDB의 *Base* 및 *Delta* 테이블이 지원하는 *Posts* 스키마를 생성합니다. AWS AppSync는 두 테이블 모두에 변형을 자동으로 기록합니다. 동기화 쿼리는 *기본* 또는 *델타* 테이블에서 레코드를 적절하게 가져오고 재연결 로직에서 클라이언트가 이러한 테이블을 어떻게 활용하는지 보여주도록 단일 구독이 정의됩니다.

```
input CreatePostInput {
    author: String!
    title: String!
    content: String!
    url: String
    ups: Int
    downs: Int
    _version: Int
}

interface Connection {
  nextToken: String
  startedAt: AWSTimestamp!
}

type Mutation {
    createPost(input: CreatePostInput!): Post
    updatePost(input: UpdatePostInput!): Post
    deletePost(input: DeletePostInput!): Post
}

type Post {
    id: ID!
    author: String!
    title: String!
    content: String!
    url: AWSURL
    ups: Int
    downs: Int
    _version: Int
    _deleted: Boolean
    _lastChangedAt: AWSTimestamp!
}

type PostConnection implements Connection {
    items: [Post!]!
    nextToken: String
    startedAt: AWSTimestamp!
}

type Query {
    getPost(id: ID!): Post
    syncPosts(limit: Int, nextToken: String, lastSync: AWSTimestamp): PostConnection!
}

type Subscription {
    onCreatePost: Post
        @aws_subscribe(mutations: ["createPost"])
    onUpdatePost: Post
        @aws_subscribe(mutations: ["updatePost"])
    onDeletePost: Post
        @aws_subscribe(mutations: ["deletePost"])
}

input DeletePostInput {
    id: ID!
    _version: Int!
}

input UpdatePostInput {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int
    downs: Int
    _version: Int!
}

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

GraphQL 스키마가 표준이긴 하지만 계속 진행하기 전에 첫째, 모든 변형이 자동으로 먼저 *기본* 테이블에 기록한 다음 *델타* 테이블에 기록합니다. *기본* 테이블은 상태에 대한 중앙 출처인 반면에 *델타* 테이블은 저널입니다. `lastSync: AWSTimestamp`를 전달하지 않으면 `syncPosts` 쿼리가 *기본* 테이블에 대해 실행되어 캐시를 하이드레이트하고, 클라이언트가 *델타* 테이블에서 구성한 TTL 시간보다 오랫동안 오프라인 상태일 때 에지 케이스에 대한 *글로벌 캐치업 프로세스*로 주기적으로 실행됩니다. `lastSync: AWSTimestamp`를 전달하지 않으면 `syncPosts` 쿼리는 *델타* 테이블에 대해 실행되고 클라이언트가 마지막 오프라인 상태였던 이후 변경된 이벤트를 검색하는 데 사용됩니다. Amplify 클라이언트는 자동으로 `lastSync: AWSTimestamp` 값을 전달하고 디스크에 적절하게 계속 씁니다.

*Post*의 *\$1deleted* 필드는 **삭제** 작업에 사용됩니다. 클라이언트가 오프라인 상태이고 레코드가 *기본* 테이블에서 제거된 경우 이 속성은 클라이언트에 동기화를 수행하여 로컬 캐시에서 항목을 제거하라고 알립니다. 클라이언트가 장기간 오프라인 상태로 유지되고 Delta Sync 쿼리를 사용하여 클라이언트가 이 값을 검색하기 전에 항목이 제거되면 (클라이언트에서 구성 가능한) 기본 쿼리의 전역 캐치업 이벤트가 실행되어 캐시에서 항목을 제거합니다. 존재하는 항목을 삭제하는 동기화 쿼리를 실행할 때 값만 반환하기 때문에 이 필드는 선택 사항으로 표시됩니다.

## 변형
<a name="mutations"></a>

모든 변형에 대해 AWS AppSync는 *기본* 테이블에서 표준 Create/Update/Delete 작업을 수행하고 *델타* 테이블에 변경 사항을 자동으로 기록합니다. 데이터 원본에서 `DeltaSyncTableTTL` 값을 수정하여 레코드를 보관할 시간을 줄이거나 늘릴 수 있습니다. 데이터의 속도가 빠른 조직의 경우 이 시간을 짧게 유지하는 것이 합리적일 수 있습니다. 또는 클라이언트가 장기간 오프라인 상태인 경우에는 이 시간을 늘리는 것이 좋을 수 있습니다.

## 동기화 쿼리
<a name="sync-queries"></a>

*기본 쿼리*는 `lastSync` 값이 지정되지 않은 DynamoDB 동기화 작업입니다. 기본 쿼리는 시작 시 그 이후에는 정기적으로만 실행되기 때문에 이는 다수의 조직에 해당하는 내용입니다.

*델타 쿼리*는 `lastSync` 값이 지정된 DynamoDB 동기화 작업입니다. (기본 쿼리 주기가 실행되지 않는 한) *델타 쿼리*는 클라이언트의 상태가 오프라인에서 다시 온라인으로 전환될 때마다 실행됩니다. 클라이언트는 데이터를 동기화하기 위해 마지막으로 쿼리를 실행한 시간을 자동으로 추적합니다.

델타 쿼리가 실행되면 쿼리의 해석기에서 `ds_pk` 및 `ds_sk`를 사용하여 클라이언트가 마지막으로 동기화를 수행한 이후 변경된 레코드에 대해서만 쿼리합니다. 클라이언트는 적절한 GraphQL 응답을 저장합니다.

동기화 쿼리 실행에 대한 자세한 내용은 [동기화 작업 설명서](aws-appsync-conflict-detection-and-sync-sync-operations.md)를 참조하십시오.

## 예제
<a name="example"></a>

먼저 `createPost` 변형을 호출하여 항목을 만듭니다.

```
mutation create {
  createPost(input: {author: "Nadia", title: "My First Post", content: "Hello World"}) {
    id
    author
    title
    content
    _version
    _lastChangedAt
    _deleted
  }
}
```

이 변형의 반환 값은 다음과 같습니다.

```
{
  "data": {
    "createPost": {
      "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b",
      "author": "Nadia",
      "title": "My First Post",
      "content": "Hello World",
      "_version": 1,
      "_lastChangedAt": 1574469356331,
      "_deleted": null
    }
  }
}
```

*기본* 테이블의 내용을 검사하면 다음과 같은 레코드를 볼 수 있습니다.

```
{
  "_lastChangedAt": {
    "N": "1574469356331"
  },
  "_version": {
    "N": "1"
  },
  "author": {
    "S": "Nadia"
  },
  "content": {
    "S": "Hello World"
  },
  "id": {
    "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b"
  },
  "title": {
    "S": "My First Post"
  }
}
```

*델타* 테이블의 내용을 검사하면 다음과 같은 레코드를 볼 수 있습니다.

```
{
  "_lastChangedAt": {
    "N": "1574469356331"
  },
  "_ttl": {
    "N": "1574472956"
  },
  "_version": {
    "N": "1"
  },
  "author": {
    "S": "Nadia"
  },
  "content": {
    "S": "Hello World"
  },
  "ds_pk": {
    "S": "AppSync-delta-sync-post:2019-11-23"
  },
  "ds_sk": {
    "S": "00:35:56.331:81d36bbb-1579-4efe-92b8-2e3f679f628b:1"
  },
  "id": {
    "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b"
  },
  "title": {
    "S": "My First Post"
  }
}
```

이제 `syncPosts` 쿼리와 같이 클라이언트가 로컬 데이터 저장소를 하이드레이트하기 위해 실행하는 *기본* 쿼리를 시뮬레이션할 수 있습니다:

```
query baseQuery {
  syncPosts(limit: 100, lastSync: null, nextToken: null) {
    items {
      id
      author
      title
      content
      _version
      _lastChangedAt
    }
    startedAt
    nextToken
  }
}
```

이 *기본* 쿼리의 반환 값은 다음과 같습니다.

```
{
  "data": {
    "syncPosts": {
      "items": [
        {
          "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b",
          "author": "Nadia",
          "title": "My First Post",
          "content": "Hello World",
          "_version": 1,
          "_lastChangedAt": 1574469356331
        }
      ],
      "startedAt": 1574469602238,
      "nextToken": null
    }
  }
}
```

나중에 `startedAt` 값을 저장하여 *델타* 쿼리를 시뮬레이션하겠지만 먼저 테이블을 변경해야 합니다. `updatePost` 변형을 사용하여 기존 게시물을 수정하겠습니다.

```
mutation updatePost {
  updatePost(input: {id: "81d36bbb-1579-4efe-92b8-2e3f679f628b", _version: 1, title: "Actually this is my Second Post"}) {
    id
    author
    title
    content
    _version
    _lastChangedAt
    _deleted
  }
}
```

이 변형의 반환 값은 다음과 같습니다.

```
{
  "data": {
    "updatePost": {
      "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b",
      "author": "Nadia",
      "title": "Actually this is my Second Post",
      "content": "Hello World",
      "_version": 2,
      "_lastChangedAt": 1574469851417,
      "_deleted": null
    }
  }
}
```

이제 *기본* 테이블의 내용을 검사하면 업데이트된 항목을 볼 수 있습니다.

```
{
  "_lastChangedAt": {
    "N": "1574469851417"
  },
  "_version": {
    "N": "2"
  },
  "author": {
    "S": "Nadia"
  },
  "content": {
    "S": "Hello World"
  },
  "id": {
    "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b"
  },
  "title": {
    "S": "Actually this is my Second Post"
  }
}
```

이제 *델타* 테이블의 내용을 검사하면 두 개의 레코드를 볼 수 있습니다.

1. 항목이 생성되었을 때의 레코드

1. 항목이 업데이트되었을 때에 대한 레코드.

새 항목은 다음과 같습니다.

```
{
  "_lastChangedAt": {
    "N": "1574469851417"
  },
  "_ttl": {
    "N": "1574473451"
  },
  "_version": {
    "N": "2"
  },
  "author": {
    "S": "Nadia"
  },
  "content": {
    "S": "Hello World"
  },
  "ds_pk": {
    "S": "AppSync-delta-sync-post:2019-11-23"
  },
  "ds_sk": {
    "S": "00:44:11.417:81d36bbb-1579-4efe-92b8-2e3f679f628b:2"
  },
  "id": {
    "S": "81d36bbb-1579-4efe-92b8-2e3f679f628b"
  },
  "title": {
    "S": "Actually this is my Second Post"
  }
}
```

이제 *델타* 쿼리를 시뮬레이션하여 클라이언트가 오프라인 상태일 때 발생한 수정 사항을 검색할 수 있습니다. *Base* 쿼리에서 반환된 `startedAt` 값을 사용하여 요청해 보겠습니다.

```
query delta {
  syncPosts(limit: 100, lastSync: 1574469602238, nextToken: null) {
    items {
      id
      author
      title
      content
      _version
    }
    startedAt
    nextToken
  }
}
```

이 *델타* 쿼리의 반환 값은 다음과 같습니다.

```
{
  "data": {
    "syncPosts": {
      "items": [
        {
          "id": "81d36bbb-1579-4efe-92b8-2e3f679f628b",
          "author": "Nadia",
          "title": "Actually this is my Second Post",
          "content": "Hello World",
          "_version": 2
        }
      ],
      "startedAt": 1574470400808,
      "nextToken": null
    }
  }
}
```