ALLSSU

AWS Amplify Gen 2 사용기. Gen 1과의 비교

AWS Amplify Gen 2의 코드 중심(Code-first) 접근 방식과 클라우드 샌드박스를 통한 빠른 로컬개발

2024년 2월 29일 목 00:27

Amplify Gen 2 Docs (https://docs.amplify.aws/gen2/)

2023년 11월 AWS Amplify의 차세대 버전인 Amplify Gen 2가 발표되었다. Amplify Gen 2는 아직 Preview 단계지만, CDK와 Amplify를 굉장히 좋아하는 팬심으로 사용해 보고😍 기존 Amplify와 비교하면서 어떤 변화와 장단점들이 있는지 알아봤다.

로컬 개발 방식의 변화 🚀 : CLI & environment vs Sandbox

Gen 1: 환경 기반의 로컬 개발

Gen 1에서는 Amplify CLI를 사용하여 AWS 리소스를 생성하고, 이를 로컬 프로젝트와 연동하여 개발을 진행한다. 이 과정에서 개발자는 각 Environment 환경을 직접 생성해서 관리해야 하고, 로컬에서의 변경 사항을 실제 클라우드 환경에 반영하기 위해 amplify push 명령어를 사용해야 한다.

Gen 2: 개발자별 샌드박스를 통한 로컬 개발

Amplify-gen2 Sandbox

Image Source: https://docs.amplify.aws/gen2/deploy-and-host/sandbox-environments/setup/

Amplify Gen 2는 실시간으로 변경되는(로컬에서 인프라 코드 변경을 감지하는) 클라우드 샌드박스를 도입하여 로컬 개발 경험을 개선했다.

개발자는 이제 코드를 작성하고, 이를 로컬에서 바로 테스트할 수 있는 샌드박스 환경을 사용할 수 있다. 개발자마다 클라우드 리소스가 실시간으로 생성되는 방식인데, 각자만의 클라우드 리소스가 마련되기 때문에 마치 로컬 백엔드 리소스가 생성된 것처럼 테스트하고 개발할 수 있게 해준다. 따라서, 개발 초기 단계에서의 빠른 반복 작업과 테스팅이 가능해지고, 협업 시 유용해졌다.

[Sandbox] Running successfulDeployment event handlers
[Sandbox] Watching for file changes...
npx amplify sandbox를 입력하면 코드를 감시하고, 인프라 변경이 있으면 즉시 클라우드 샌드박스 환경을 업데이트한다.

Sandbox가 생성되는 터미널 모습

샌드박스가 생성되는 터미널 모습. 변경 시 CDK를 통해 실시간으로 클라우드 샌드박스가 업데이트된다.

인프라 생성 방식의 변화 💻 : CLI vs Code-first

Gen 1: CLI로 인프라를 생성

모든 AWS 리소스는 터미널에 입력한 명령어를 통해 생성하고 변경된다. 초기 설정으로는 React나 Vue 프로젝트를 생성하고 amplify init 명령어를 통해서 프론트엔드 프로젝트에 Amplify를 세팅한다. 이후 인증, API 등 필요한 기능에 따라 CLI를 통해 AWS 리소스와 연동한다.

예를 들어, amplify add auth 명령어를 입력해서 인증 기능을 추가한다고 하면 어떻게 추가할 건지 물어보는 방식인데, 터미널에서 설정이 모두 끝나게 된다.

amplify add auth
? Do you want to use the default authentication and security configuration? Default configuration
? How do you want users to be able to sign in? Username
? Do you want to configure advanced settings?  No, I am done.

Gen 2: 코드 우선(Code-first)의 접근 방식

TypeScript를 사용해 한 프로젝트에서 프론트엔드와 백엔드 정의를 직접 작성할 수 있다. 이 부분이 Gen 2에서 가장 큰 변화이자 장점이라고 생각하는데, 아래와 같은 코드를 정해진 폴더 구조에 맞게 작성하고 로컬용 샌드박스나 프로덕션으로 배포하면 된다.

// amplify/auth/resource.ts
import { defineAuth, defineFunction } from "@aws-amplify/backend";

/**
 * @title Amplify Gen 2 인증 예제
 * @description defineAuth를 통해서 Amplify의 인증 기능이 생성된다.
 * */
export const auth = defineAuth({
  loginWith: {
    email: {
      verificationEmailSubject: "Welcome! Verify your email!",
    },
  },
  triggers: {
    preSignUp: defineFunction({
      entry: "./post-confirmation-handler.ts",
    }),
  },
});

API(Appsync) 개발 경험의 변화 ⚡️

Gen 1: GraphQL 스키마 모델링 위주

스키마 작성 및 GraphQL API를 개발한다고 가정하면, CLI로 Appsync 인프라를 생성하고 개발자가 직접 GraphQL 스키마를 모델링해서 수정하게 된다. 이때, @model 지시문을 통해 DynamoDB 테이블을 생성하게 할 수 있고, @hasMany, @belongsTo와 같은 지시문으로 테이블 간의 관계를 만든다. amplify push -y 명령어를 통해 배포하면 모델에 맞는 테이블과 GraphQL 리졸버가 자동으로 생성되는 구조다.

type Todo @model {
  id: ID!
  name: String!
  description: String
}

Gen 2: 타입스크립트에서 Amplify Gen 2의 모델링 방식과 API 사용

GraphQL 스키마를 정의할 필요 없다. 타입스크립트로 모델을 정의하고, Amplify에서 알아서 GraphQL 스키마와 리졸버를 생성해준다. 이를 통해 리액트, Vue 등 프론트엔드 코드에서 타입스크립트로 생성 된 모델을 타입으로 연동하고, API를 사용할 수 있다.

Amplify Gen 2의 모델 생성

// amplify/data/resource.ts
import { a, defineData, type ClientSchema } from "@aws-amplify/backend";

const schema = a.schema({
  Todo: a
    .model({
      name: a.string().required(),
      description: a.string(),
    })
    .authorization([a.allow.public()]),
});

export type Schema = ClientSchema<typeof schema>;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: "userPool",
  },
});

Amplify Gen 2의 API 사용

// components/TodoList.tsx
"use client";

import { useState, useEffect } from "react";
import { generateClient } from "aws-amplify/data";
import type { Schema } from "@/amplify/data/resource";

// generate your data client using the Schema from your backend
const client = generateClient<Schema>();

export default function TodoList() {
  const [todos, setTodos] = useState<Schema["Todo"][]>([]);

  async function listTodos() {
    // fetch all todos
    const { data } = await client.models.Todo.list();
    setTodos(data);
  }

  useEffect(() => {
    listTodos();
  }, []);

  return (
    <div>
      <h1>Todos</h1>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>{todo.content}</li>
        ))}
      </ul>
    </div>
  );
}
코드로 생성한 샌드박스의 백엔드 모델을 같은 프로젝트에서 연동한다.

Gen 2의 제한, 단점 😵‍💫

자동으로 생성되는 리소스명의 문제

자동으로 생성되는 리소스명에 대한 제약. Amplify Gen 2 이슈 링크 (https://github.com/aws-amplify/amplify-backend/issues/907)

Amplify를 사용하면서 커스텀 리소스를 위해 CDK를 사용할 수 있는데, 간단한 Cognito 트리거 함수를 만들어도 Amplify에서 생성한 리소스를 자동으로 연결할 수 없는 문제가 생긴다. CDK로 모든 리소스를 생성하면 객체 인스턴스와 프로퍼티들로 이어 붙이면 되는 부분이지만, Amplify로 생성한 코드들은 CDK 레벨에서 사용할 수 없는 문제가 발생하게 된다.

연결할 수 있다고 하더라도 약간의 트릭 또는 해킹이 필요하고, 기능으로서 연동할 방법은 현재까지 보이진 않는다. Amplify Gen 2의 경우 배포하면 CDK로 배포되는데, 이렇게 생성한 리소스를 CDK 레벨에서 사용 가능하다면 조금 더 강력한 도구가 될 것 같다.

기능의 부재

아직 Preview 단계고, 이제 막 시작하는 프로젝트라서 기존에 지원했던 스토리지, Lambda, AL/ML 등 기존 Amplify에서 통합했던 기능들이 없다. 없던 기능들은 CDK로 만들어서 이어 붙이면 되긴 하는데, 그래도 꼭 초기 단계에 있으면 좋겠다고 생각한 기능이 OpenSearch Service 통합이다.

Gen 1에서는 GraphQL(AWS Appsync)을 사용할 때 DynamoDB로 테이블이 생성되는데, DynamoDB의 약점인 검색과 정렬을 효율적으로 할 수 있도록 GraphQL 스키마에 @searchable 지시자를 추가하면 Amazon OpenSearch Service가 생성된다.

그에 반해 Gen 2에서는 모델에서 OpenSearch Service를 연동할 방법이 아직은 없다. Amplify로 DynamoDB를 생성하면 키 설계에 대한 제약이 생기는데, 검색과 정렬을 위해 OpenSearch Service를 직접 만들어서 DynamoDB와 붙여야 하는 것이 제약으로 생각되었다.

정리 🤔

  • GraphQL 자체를 래핑해서 추상화한 건 좋은데, Amplify 락인을 위해 표준과 너무 멀어지게 만든 느낌.
  • 샌드박스로 인해 기존 Amplify보다 훨씬 개발 속도가 빠르다. 토이프로젝트나 MVP 만들기에 좋다고 생각된다.
  • 출시된 기능들은 기존보다 편하고 합리적이다. 그러나 전체적인 기능이 아직 부족하다.
  • AWS는 앞으로도 CDK 위주의 통합을 지속할 것 같다.