Amazon Web Services 한국 블로그

AWS Amplify 신규 기능 – AWS CDK용 MySQL 및 PostgreSQL 데이터베이스 질의 지원

AWS AmplifyAWS Cloud Development Kit(AWS CDK) 를 기반으로 기존 MySQL 및 PostgreSQL 데이터베이스에 연결하고 이를 쿼리할 수 있습니다. 이 새로운 기능은 AWS 내부 또는 외부에 있는 관계형 데이터베이스를 위해 안전한 실시간 GraphQL API를 만듭니다. 이제 데이터베이스 엔드포인트와 보안 인증 정보만 있으면, 어떤 관계형 데이터베이스 작업도 수행할 수 있는 전체 API를 생성할 수 있습니다. 데이터베이스 스키마가 변경되면 명령을 실행하여 최신 테이블 스키마 변경 사항을 적용할 수 있습니다.

AWS Amplify GraphQL Transformer 버전 2를 2021년에 발표했습니다. 이 기능을 사용하면 개발자는 최소한의 클라우드 전문 지식으로도 다양한 기능을 제공하며 유연하고 확장 가능한 GraphQL 기반 앱 백엔드를 개발할 수 있습니다. 이 새 GraphQL Transformer는 처음부터 새롭게 재설계됐습니다. 확장 가능한 파이프라인 리졸버를 생성하여 GraphQL API 요청을 라우팅하고, 비즈니스 로직(예: 권한 부여)을 적용하며, Amazon DynamoDB 같은 기본 데이터 소스와 통신할 수 있습니다.

하지만 고객은 Amazon DynamoDB는 물론 Amazon RDSAmazon Aurora 데이터베이스 같은 GraphQL API용 관계형 데이터베이스 소스도 사용하기를 원했습니다. 이제 관계형 데이터베이스와 DynamoDB 데이터 소스 모두에 Amplify GraphQL API의 @model 유형을 사용할 수 있습니다. 관계형 데이터베이스 정보는 별도의 schema.sql.graphql 파일에 생성됩니다. 일반 schema.graphql 파일을 사용하여 DynamoDB가 지원되는 유형을 생성하고 관리하는 작업은 계속 하실 수 있습니다.

사용자가 Virtual Private Cloud(VPC)에 있거나 인터넷에서 공개적으로 액세스할 수 있는 MySQL 또는 PostgreSQL 데이터베이스 정보를 단순히 제공하면, AWS Amplify는 데이터베이스 테이블에 안전하게 연결하고 create, read, update 또는 delete(CRUD) 쿼리 및 변형을 노출하며 수정 가능한 GraphQL API를 자동으로 생성합니다. 프런트엔드에서 더 어울리도록 데이터 모델의 이름을 변경할 수도 있습니다. 예를 들어 이름이 ‘todos’(복수, 소문자)인 데이터베이스 테이블이 클라이언트에는 ‘ToDo’(단수, PascalCase)로 표시됩니다.

코드 한 줄로 기존 Amplify GraphQL 권한 부여 규칙을 API에 추가할 수 있으므로, 사용 사례(예: 소유자 기반 권한 부여 또는 공개적인 읽기 전용 패턴)를 원활하게 구축할 수 있습니다. 생성된 API는 AWS AppSync의 GraphQL 기능을 기반으로 구축되므로, 안전한 실시간 구독을 바로 사용할 수 있습니다. 코드 몇 줄만 작성하면 모든 데이터 모델에서 CRUD 이벤트를 구독할 수 있습니다.

AWS CDK에서 MySQL 데이터베이스로 시작하기
AWS CDK를 사용하면 프로그래밍 언어의 뛰어난 표현력을 사용하여, 클라우드에서 안정적이고 확장 가능하며 비용 효율적인 애플리케이션을 구축할 수 있습니다. 시작하려면 로컬 머신에 AWS CDK를 설치하세요.

$ npm install -g aws-cdk

다음 명령을 실행하여 올바르게 설치되었는지 확인하고 AWS CDK의 버전 번호를 인쇄합니다.

$ cdk –version

그런 다음 앱의 새 디렉토리를 생성합니다.

$ mkdir amplify-api-cdk
$ cd amplify-api-cdk

cdk init 명령을 사용하여 CDK 앱을 초기화합니다.

$ cdk init app --language typescript

새로운 CDK 프로젝트에 Amplify의 GraphQL API 구성을 설치합니다.

$ npm install @aws-amplify/graphql-api-construct

CDK 프로젝트에서 기본 스택 파일을 엽니다(대부분의 경우 lib/<your-project-name>-stack.ts에 있음). 파일 맨 위에서 필요한 구성을 가져옵니다.

import {
    AmplifyGraphqlApi,
    AmplifyGraphqlDefinition
} from '@aws-amplify/graphql-api-construct';

MySQL 데이터베이스에서 다음 SQL 명령문을 실행하여 새 관계형 데이터베이스 API를 위한 GraphQL 스키마를 생성합니다. 열 헤더를 포함한 결과를 .csv 파일로 출력하는 것을 확인하고 <database-name>을 데이터베이스 이름, 스키마 이름 또는 둘 다로 대체합니다.

SELECT
  INFORMATION_SCHEMA.COLUMNS.TABLE_NAME,
  INFORMATION_SCHEMA.COLUMNS.COLUMN_NAME,
  INFORMATION_SCHEMA.COLUMNS.COLUMN_DEFAULT,
  INFORMATION_SCHEMA.COLUMNS.ORDINAL_POSITION,
  INFORMATION_SCHEMA.COLUMNS.DATA_TYPE,
  INFORMATION_SCHEMA.COLUMNS.COLUMN_TYPE,
  INFORMATION_SCHEMA.COLUMNS.IS_NULLABLE,
  INFORMATION_SCHEMA.COLUMNS.CHARACTER_MAXIMUM_LENGTH,
  INFORMATION_SCHEMA.STATISTICS.INDEX_NAME,
  INFORMATION_SCHEMA.STATISTICS.NON_UNIQUE,
  INFORMATION_SCHEMA.STATISTICS.SEQ_IN_INDEX,
  INFORMATION_SCHEMA.STATISTICS.NULLABLE
      FROM INFORMATION_SCHEMA.COLUMNS
      LEFT JOIN INFORMATION_SCHEMA.STATISTICS ON INFORMATION_SCHEMA.COLUMNS.TABLE_NAME=INFORMATION_SCHEMA.STATISTICS.TABLE_NAME AND INFORMATION_SCHEMA.COLUMNS.COLUMN_NAME=INFORMATION_SCHEMA.STATISTICS.COLUMN_NAME
      WHERE INFORMATION_SCHEMA.COLUMNS.TABLE_SCHEMA = '<database-name>';

다음 명령을 실행하여 <path-schema.csv>를 이전 단계에서 생성한 .csv 파일 경로로 대체합니다.

$ npx @aws-amplify/cli api generate-schema \
    --sql-schema <path-to-schema.csv> \
    --engine-type mysql –out lib/schema.sql.graphql

schema.sql.graphql 파일을 열면 MySQL 데이터베이스 스키마에서 가져온 데이터 모델을 볼 수 있습니다.

input AMPLIFY {
     engine: String = "mysql"
     globalAuthRule: AuthRule = {allow: public}
}

type Meals @model {
     id: Int! @primaryKey
     name: String!
}

type Restaurants @model {
     restaurant_id: Int! @primaryKey
     address: String!
     city: String!
     name: String!
     phone_number: String!
     postal_code: String!
     ...
}

아직 하지 않았다면 AWS Systems Manager 콘솔Parameter Store로 이동하여 hostname/url, database name, port, usernamepassword 같은 데이터베이스 연결 세부 정보의 파라미터를 만듭니다. 이러한 파라미터는 다음 단계에서 Amplify가 데이터베이스에 연결하고 이에 대한 GraphQL 쿼리 또는 변이를 수행하는 데 필요합니다.

기본 스택 클래스에 다음 코드를 추가하여 새 GraphQL API를 정의합니다. dbConnectionConfg 옵션을 이전 단계에서 만든 파라미터 경로로 대체합니다.

new AmplifyGraphqlApi(this, "MyAmplifyGraphQLApi", {
  apiName: "MySQLApi",
  definition: AmplifyGraphqlDefinition.fromFilesAndStrategy(
    [path.join(__dirname, "schema.sql.graphql")],
    {
      name: "MyAmplifyGraphQLSchema",
      dbType: "MYSQL",
      dbConnectionConfig: {
        hostnameSsmPath: "/amplify-cdk-app/hostname",
        portSsmPath: "/amplify-cdk-app/port",
        databaseNameSsmPath: "/amplify-cdk-app/database",
        usernameSsmPath: "/amplify-cdk-app/username",
        passwordSsmPath: "/amplify-cdk-app/password",
      },
    }
  ),
  authorizationModes: { apiKeyConfig: { expires: cdk.Duration.days(7) } },
  translationBehavior: { sandboxModeEnabled: true },
});

이 구성은 인터넷에서 데이터베이스를 액세스할 수 있다고 가정합니다. 또한 기본 인증 모드는 AWS AppSync용 Api Key로 설정되며, 샌드박스 모드가 활성화되어 모든 모델에서 공개 액세스할 수 있습니다. 보다 세분화된 권한 부여 규칙을 추가하기 전에 API를 테스트할 때 유용합니다.

마지막으로, AWS 클라우드에 GraphQL API를 배포합니다.

$ cdk deploy

이제 AWS AppSync 콘솔로 이동하여 생성된 GraphQL API를 확인할 수 있습니다.

프로젝트와 Queries 메뉴를 선택합니다. 새로 생성된 GraphQL API가 getMeals처럼 하나의 항목을 가져오거나 listRestaurants처럼 모든 항목을 나열하는 MySQL 데이터베이스 테이블과 호환되는 것을 확인할 수 있습니다.

예를 들어 address, city, name, phone_number 등의 필드가 있는 항목을 선택하면 새 GraphQL 쿼리를 확인할 수 있습니다. 실행 버튼을 선택하면 MySQL 데이터베이스에서 쿼리 결과를 확인할 수 있습니다.

MySQL 데이터베이스를 쿼리하면 동일한 결과를 확인할 수 있습니다.

데이터베이스를 위해 GraphQL 스키마를 사용자 지정하는 방법
SQL에 사용자 지정 쿼리나 변형을 추가하려면, 생성된 schema.sql.graphql 파일을 열고 the :<variable> 표기법을 사용하는 파라미터에 있는 @sql(statement: "") 패스를 사용해야 합니다.

type Query {
     listRestaurantsInState(state: String): Restaurants @sql("SELECT * FROM Restaurants WHERE state = :state;”)
}

SQL 쿼리가 길고 복잡하다면 customSqlStatements 구성 옵션에서 SQL 명령문을 참조할 수 있습니다. 참조 값은 SQL 명령문에 매핑된 속성 이름과 일치해야 합니다. 다음 예시에서는 customSqlStatementssearchPosts 속성을 참조합니다.

type Query {
      searchPosts(searchTerm: String): [Post]
      @sql(reference: "searchPosts")
}

API 정의에서 SQL 명령문을 매핑하는 방법은 다음과 같습니다.

new AmplifyGraphqlApi(this, "MyAmplifyGraphQLApi", { 
    apiName: "MySQLApi",
    definition: AmplifyGraphqlDefinition.fromFilesAndStrategy( [path.join(__dirname, "schema.sql.graphql")],
    {
        name: "MyAmplifyGraphQLSchema",
        dbType: "MYSQL",
        dbConnectionConfig: {
        //	...ssmPaths,
     }, customSqlStatements: {
        searchPosts: // property name matches the reference value in schema.sql.graphql 
        "SELECT * FROM posts WHERE content LIKE CONCAT('%', :searchTerm, '%');",
     },
    }
  ),
//...
});

SQL 명령문은 스키마에 인라인으로 정의된 것처럼 실행됩니다. 파라미터 사용, 유효한 SQL 구문 확인, 반환 유형 일치에도 동일한 규칙이 적용됩니다. 참조 파일을 사용하면 스키마를 깔끔하게 유지하고 다른 필드에서 SQL 명령문을 재사용할 수 있습니다. 참조 파일은 길고 복잡한 SQL 쿼리에 사용하는 것이 가장 좋습니다.

@refersTo 지시문을 사용하여 필드 및 모델 이름을 변경할 수도 있습니다. @refersTo 지시문을 제공하지 않으면, AWS Amplify는 모델 이름과 필드 이름이 데이터베이스 테이블 및 열 이름과 정확하게 일치한다고 가정합니다.

type Todo @model @refersTo(name: "todos") {
     content: String
     done: Boolean
}

두 데이터베이스 테이블 간에 관계를 생성하고 싶다면 @hasOne@hasMany 지시문을 사용하여 1:1 또는 1:M 관계를 만들어야 합니다. @belongsTo 지시문을 사용하여 관계 부모와의 양방향 관계를 다시 생성하세요. 예를 들어 레스토랑과 해당 레스토랑 식사 메뉴 간의 1:M 관계를 만들 수 있습니다.

type Meals @model {
     id: Int! @primaryKey
     name: String!
     menus: [Restaurants] @hasMany(references: ["restaurant_id"])
}

type Restaurants @model {
     restaurant_id: Int! @primaryKey
     address: String!
     city: String!
     name: String!
     phone_number: String!
     postal_code: String!
     meals: Meals @belongsTo(references: ["restaurant_id"])
     ...
}

DB 인스턴스에서 GraphQL 스키마 또는 데이터베이스 스키마를 변경할 때마다 변경 내용을 클라우드에 배포해야 합니다.

DB 인스턴스에서 GraphQL 스키마 또는 데이터베이스 스키마를 변경하는 경우, SQL 스크립트를 다시 실행하고 이 가이드에서 앞서 언급한 .csv로 내보내기 단계를 실행하여 schema.sql.graphql 파일을 다시 생성하고 변경 내용을 클라우드에 배포해야 합니다.

$ cdk deploy

자세한 내용은 AWS Amplify 설명서의 API를 기존 MySQL 또는 PostgreSQL 데이터베이스에 연결 항목을 참조하세요.

정식 출시
AWS Amplify에 대한 관계형 데이터베이스 지원이 이제 Amazon VPC 내부나 AWS 클라우드 외부에서도 호스팅되는 모든 MySQL 및 PostgreSQL 데이터베이스에 적용됩니다.

실제로 적용해 보시고 AWS Amplify용 AWS re:Post, Amplify GraphQL API의 GitHub 리포지토리 또는 AWS Support 담당자를 통해 피드백을 알려 주세요.

Channy

추신: 샘플 코드를 작성해 주신 AWS 수석 제품 관리자 René Huangtian Brandel에게 특별히 감사드립니다.