BLOG

AWS AppSync 오프라인 레퍼런스 아키텍처 – Amplify DataStore로 구동
작성일: 2020-06-01

현대적인 모바일 및 웹 응용 프로그램은 즐겁고 원활한 사용자 경험을 제공하도록 제작되었습니다. 사용자들은 더욱 많은 요구사항을 갖게 되고 모바일 앱의 훌륭한 사용자 경험을 기대합니다. 소셜 미디어 앱에서 좋아요 버튼을 클릭하는 특정 순간에 인터넷 연결이 끊어질 수 있지만 애플리케이션이 재시도를 통해 연결이 끊어지지 않았던 것처럼 보이게 할 것을 기대합니다. 이러한 종류의 사용자 경험을 제공하기 위해 frontend개발자는 전통적으로 로컬 캐시에 데이터를 저장, 자동으로 연결을 재시도, 지수 백오프를 재시도, 데이터를 클라우드에 동기화, 안전한 충돌 해결과 병합하기 위해 복잡한 메커니즘을 구현해야 했습니다. 그러한 경험을 구현하는 것은 쉽지 않으며 개발자들은 이러한 힘든 일에 반복적으로 시간을 투자하기보다 새로운 기능을 개발하는 데 시간을 투자하길 원합니다.

 

AWS AppSync 는 모바일 및 웹 애플리케이션을 위한 GraphQL 서버리스 백엔드입니다. 내장 컴퓨팅 레이어가 포함된 유연하고 안정적인 API를 제공하여 쿼리, 변형 및 구독을 실행하여 모든 애플리케이션에 대한 하나 이상의 데이터 소스의 데이터에 안전하게 액세스, 조작 및 결합할 수 있습니다. 클라이언트 장치에서 Amplify DataStore 는 오프라인, 실시간 및 온라인 시나리오에 대한 추가 코드를 작성하지 않고도 공유 및 분산 데이터를 활용할 수 있는 친숙한 프로그래밍 모델을 제공하므로 local-only 데이터를 사용하는 것만큼이나 쉽게 분산된 다중 사용자 데이터를 쉽게 사용할 수 있습니다.

 

오늘은 frontend의 Amplify DataStore와 React Native를, 백엔드의 Amazon DynamoDB와 AWS AppSync를 사용하여 내장 오프라인 및 클라우드 동기화 기능을 갖춘 최신 모바일 애플리케이션을 구축하는 방법을 보여주는 참조 애플리케이션과 아키텍처를 공유해드리겠습니다.

 

참조 아키텍처

 

 

Amplify DataStore는 모바일 장치 또는 웹 브라우저 내에서 실행되며 개발자가 상호 작용할 수 있는 API를 제공하는 클라이언트입니다. 데이터를 로컬에 저장하고 GraphQL 쿼리, 변형 및 구독을 통해 백그라운드에서 클라우드로 자동 동기화하는 영구 저장소 역할을 합니다. 동기화, 충돌 감지, 버전 관리 및 저널링은 백그라운드에서 Sync Engine 및 AWS AppSync에 의해 자동으로 처리되는 반면 DataStore 클라이언트와의 모든 상호 작용은 항상 로컬 데이터와 반대됩니다. 클라우드에 대한 데이터 동기화는 DataStore의 옵션 기능이며, 필요한 경우 AWS 계정 없이 로컬 전용 독립형 모드에서 사용할 수 있습니다.

 

또 다른 중요한 특징은 개발자가 Apollo 또는 Relay와 같은 GraphQL 라이브러리에 대해 익숙하지 않아도 괜찮다는 것입니다. 표준 함수 호출을 사용하여 DataStore API를 호출하기 만하면 되며 클라이언트는 이러한 작업을 GraphQL 쿼리, 돌연변이 및 비하인드 구독으로 자동 변환하여 AWS AppSync API 엔드 포인트와 상호 작용합니다. DataStore에서 다음 API를 사용하여 데이터를 쉽게 조작할 수 있습니다.

 

  • 저장 – 항목을 만들거나 업데이트합니다.
  • 쿼리 – ID 또는 조건자 필터를 사용하여 항목을 가져옵니다. 페이지 매김을 지원합니다.
  • D는 elete – ID 또는 술어 필터를 사용하여 항목을 삭제합니다.
  • 관찰 – AppSync 실시간 기능을 사용하여 모델의 변경 사항을 수신하는 기능을 구독 하세요.
  • 지우기 – 로컬 데이터를 지우려면 사용자를 로그 아웃 할 때 유용한 로컬 저장소에서 모든 데이터를 삭제하십시오.

 

개발자는 Amplify CLI의 모델 생성 도구를 사용하여 데이터 모델을 표준 GraphQL 스키마로 정의하여 CLI를 자동으로 모델을 기본 코드로 생성하고 GraphQL문 (쿼리, 돌연변이 및 구독)을 생성하므로 직접 코딩할 필요가 없습니다. 또한 push Amplify CLI 의 명령을 사용하여 개발자는 DynamoDB 테이블 및 GraphQL 리졸버 세트를 사용하여 AppSync API를 자동으로 프로비저닝하여 정의된 스키마를 기반으로 데이터에 대해 CRUD 작업을 수행할 수 있습니다. 이 모든 것은 한 줄의 백엔드 코드를 작성하지 않고도 가능합니다.

 

 

샘플 애플리케이션

위에서 설명한 모든 서비스를 보여주는 모바일 애플리케이션으로 최신 서버리스 POS (Point of Sale) 구현해보겠습니다. 판매를 처리하고 사소한 인터넷 끊김이 발생해도 계속 작동해야 하는 중요한 비즈니스 응용 프로그램보다 오프라인 기능이 더 나은 사용 사례에는 무엇이 있을까요? 상점 직원은 인터넷 연결 상태에 대한 걱정 없이 거래를 처리하고 고객에게 서비스를 제공해야 할 것입니다.

 

Point of Sale 앱의 커피 숍 테마를 선택 했으므로 모바일 애플리케이션을 사용하는 바리스타의 사용자 경험을 살펴 보겠습니다. 바리스타가 고객으로부터 주문을 받을 때 장바구니에 제품을 추가할 수 있는 홈 화면에서 시작해보겠습니다.

 

 

고객이 결제할 준비가 되면 바리스타는 주문 항목이 품목, 수량 및 세금과 함께 표시되는 결제 화면으로 이동합니다. Checkout 버튼을 누르면 DataStore를 사용하여 주문을 저장하고 홈 화면으로 돌아갑니다.

 

마지막으로, 사용자는 날짜 별로 그룹화 된 주문 목록을 찾아볼 수 있습니다. POS (Point of Sale) 애플리케이션 사용자가 새로 주문을 하는 순간 목록이 생성되므로 데이터에 대한 실시간 변경 사항을 구독하여 유용하게 사용할 수 있습니다. 목록을 수동으로 새로 고칠 필요가 없으며, DataStore는 비동기적으로 새로운 주문을 관찰하고 검색하며 주문 목록이 항상 최신 상태가 되도록 앱 로컬 스토리지를 자동으로 업데이트합니다.

 

이 응용 프로그램은 빠르게 쇼핑 카트에 제품을 추가하며, 빠르게 반응하고, 네트워크를 사용할 수 없는 경우에도 사용자 인터페이스에 지연이 없으므로 원활한 사용자 경험을 제공합니다. 이는 UI가 Amplify DataStore와 상호 작용하기 때문에 가능합니다. Amplify DataStore는 항상 로컬로 데이터에 액세스하고 네트워크 연결이 있을 때마다 백그라운드에서 클라우드 변경 사항을 동기화합니다. 이것은 복잡한 계산 요구 사항이 없는 간단한 응용 프로그램이지만, 가장 중요한 점은 Amplify DataStore를 사용하여 안정적이고 효율적인 데이터 처리를 통해 아주 쉽게 즐겁고 반응이 뛰어난 사용자 경험을 구축할 수 있다는 것입니다.

 

 

코드

코드들은 모두 GraphQL 스키마 정의로 시작합니다. 개발자는 응용 프로그램 데이터 모델을 graphql.schema파일로 정의 하고 Amplify는 로컬 데이터 저장소에 필요한 모든 구문을 모국어로 생성합니다. POS (Point of Sale) 애플리케이션의 스키마를 살펴 보겠습니다.

 

type Order @model {

  id: ID!

  total: Float

  subtotal: Float

  tax: Float

  createdAt: String!

  lineItems: [LineItem] @connection(name: “OrderLineItems”)

}

 

 type LineItem @model {

  id: ID!

  qty: Int

  order: Order @connection(name: “OrderLineItems”)

  product: Product @connection

  description: String

  price: Float

  total: Float

}

 

 type Product @model {

  id: ID

  sku: String

  name: String

  price: Float

  image: String

}

 

@model및 @connection지침을 살펴보세요. Amplify abstractions는 개발자가 데이터를 다루기 위한 DynamoDB테이블과 GraphQL resolvers 을 포함한 응용 프로그램의 백엔드를 신속하게 만들 수 있도록 합니다. 특히 @connection지시어는 “has one” , “has many” and “belongs to”와 같은 모델 간의 관계를 정의하는 데 사용됩니다. 이 경우, 우리의 모델은 Order이 많은 LineItems을 가지며 LineItem는  Order와 Product에 속한다고 정의합니다.

 

Checkout 기능을 살펴 보겠습니다:

 

async function submitOrder(order) {

    const now = new Date().toISOString();

    // Save order header

    const newOrder = await DataStore.save(

        new Order({

            total: order.total,

            subtotal: order.subtotal,

            tax: order.tax,

            createdAt: now,

        })

    );

 

     // Save each lineItem

    const promises = order.lineItems.map(lineItem => {

        return DataStore.save(

            new LineItem({

                qty: lineItem.qty,

                description: lineItem.description,

                price: lineItem.price,

                total: lineItem.total,

                order: newOrder, // associate to order

                product: lineItem.product, // associate to product

            })

        );

    });

     await Promise.all(promises);

}

 

 

정말 간단하죠? Amplify DataStore를 사용하지 않고 인터넷을 사용할 수 없는 경우 오류 처리 로직을 구현하고 나중에 클라우드와 동기화하기 위해 로컬로 데이터를 저장하기 위한 코드는 위의 코드보다 훨씬 복잡할 것입니다.

 

 

충돌 해결

여러 클라이언트가 backend로 동시에 트랜잭션을 보내는 경우 클라이언트가 같은 항목을 동시에 변경하려고 시도할 가능성이 있습니다. 이는 간단하게 충돌을 해결하는 또 다른 AppSync 기능입니다.

 

충돌 해결 외에도 판매 시점 애플리케이션에서 구체적인 예를 살펴 보겠습니다. 두 명의 바리스타가 제품 카탈로그를 업데이트하여 제품 속성을 변경하려고 합니다. 그 중 한 명은 제품 가격을 업데이트하고 다른 하나는 제품 이미지를 업데이트합니다. 이렇게 함으로써 이 둘이 같은 제품을 동시에 업데이트하게 됩니다.

 

 

AppSync는 두 업데이트가 모두 성공하도록 하여 충돌을 정상적으로 해결합니다. 이 전략을 Automerge 라고 하며 DynamoDB 데이터 소스에서 충돌 해결이 활성화 된 경우의 기본값입니다. 본질적으로 GraphQL 유형 및 필드 정보를 사용하여 업데이트를 검사하고 테이블에 작성된 현재 항목과 비교합니다.

 

데이터베이스에 최신으로 작성된 항목이 수신 레코드에 대한 버전 확인을 사용하는 Optimistic Concurrency 을 사용하여 전체 오브젝트에 버전 확인을 적용하도록 이 전략을 변경할 수 있습니다. 또는 충돌 해결 전략을 더욱 세밀하게 제어하려는 개발자의 경우 업데이트 병합 또는 거부 시 충돌에 대한 사용자 지정 비즈니스 논리를 정의하는 Lambda 함수를 사용할 수 있습니다.

이 기능에 대한 자세한 내용은 공식 문서를 참조하세요.

 

 

델타 동기화 테이블

AppSync mutation 이 버전이 지정된 항목을 변경하면 해당 변경 기록이 증분 업데이트에 최적화 된 Delta DynamoDB 테이블에 저장됩니다. 이 테이블의 목적은 클라이언트가 많은 레코드를 가질 수 있는 하나의 기본 쿼리의 결과로 로컬 스토리지를 수화하고 마지막 쿼리 이후 변경된 데이터만 수신하도록 하는 것입니다(델타 업데이트 ). 델타 동기화 테이블은 AWS 계정에 표시되며 Amplify DataStore 및 AppSync 리졸버가 자동으로 업데이트하므로 개발자가 입력하지 않아도 됩니다.

 

POS (Point of Sale) 응용 프로그램의 의미는 무엇일까요? 사용자가 애플리케이션을 처음 열면 DataStore Orders는 lastSync값을 지정하지 않고 AppSync에 쿼리를 수행하여 가져오므로 GraphQL resolver는 기본 Orders테이블에서 데이터를 검색합니다.

 

 

DataStore lastSync는 backend에서 주문을 fetch하기 위한 후속 호출에 대한 값을 자동으로 추적하고 포함합니다. 이를 델타 쿼리 라고 하며 델타 동기화 테이블을 사용하여 해당 클라이언트에서 마지막 동기화 이후 변경된 데이터만 가져옵니다. 델타 동기화 테이블은 타임 스탬프를 기반으로 하는 쿼리를 위해 의도적으로 설계되었으므로 기본 테이블로 이동하는 것보다 효율적입니다.

 

델타 동기화 테이블은 모든 변형에 대해 AppSync resolver가 최신 상태로 유지하는 journal을 제공합니다. 테이블의 항목은 DynamoDB TTL 속성 을 사용하여 주기적으로 제거 되어 이 테이블이 오래된 데이터를 누적하지 못하게 합니다. 이를 통해 클라이언트는 오프라인에서 온라인 상태로 전환할 때 데이터를 효율적으로 가져올 수 있습니다. “Soft deletes”는 DynamoDB 데이터 소스가 제공하는 AppSync의 Delta Sync 구현에서 제공하는 또 다른 흥미로운 기능입니다. 즉 삭제된 항목은 구성 가능한 시간 동안 삭제 표시 플래그와 함께 기본 테이블에 유지됩니다. 이를 통해 개발자는 항목이 영구적으로 삭제되기 전에 BaseTableTTL 속성을 제거하여 우발적인 삭제를 처리 할 수 있는 ” Undelete “기능을 구현할 수 있습니다.

 

DeltaSync 테이블 작동 방식에 대한 자세한 내용은 공식 설명서를 참조하세요.

 

 

결론

Frontend 개발자는 AWS AppSync 및 Amplify DataStore를 사용하여 익숙하고 간단한 프로그래밍 모델을 사용하여 몇 줄의 코드만 작성함으로써 즐겁고 빠른 응답 사용자 경험을 제공하는 최신 애플리케이션을 구축할 수 있습니다. AppSync는 확장성이 뛰어난 DynamoDB 테이블에 데이터를 저장하는 관리되는 GraphQL API 엔드 포인트를 제공하여 backend를 강화하고 Amplify DataStore는 강력한 데이터 처리 및 동기화 엔진을 제공하여 클라이언트 애플리케이션을 강화합니다.

 

GitHub 리포지토리지에서 구축한 POS 애플리케이션 코드를 살펴보고 이를 자신의 React Native 모바일 애플리케이션을 시작하기 위한 예제로 사용하세요: https://github.com/aws-samples/aws-appsync-refarch-offline. 몇 분만에 AWS 계정의 모든 backend 서비스뿐만 아니라 모바일 frontend를 직접 사용해 볼 수 있습니다.

 

원문URL :  https://aws.amazon.com/ko/blogs/mobile/aws-appsync-offline-reference-architecture/

** 메가존 클라우드 TechBlog는 AWS BLOG 영문 게재 글 중에서 한국 사용자들에게 유용한 정보 및 콘텐츠를 우선적으로 번역하여 내부 엔지니어 검수를 받아서, 정기적으로 게재하고 있습니다. 추가로 번역 및 게재를 희망하는 글에 대해서 관리자에게 메일 또는 SNS 페이지에 댓글을 남겨주시면, 우선적으로 번역해서 전달해드리도록 하겠습니다.