콘텐츠로 이동

Blog

AWS PDK에서 마이그레이션하기

이 가이드는 AWS PDK 프로젝트를 Nx Plugin for AWS로 마이그레이션하는 예제를 안내하고, 이 주제에 대한 일반적인 지침을 제공합니다.

Nx Plugin for AWS로 마이그레이션하면 PDK 대비 다음과 같은 이점을 얻을 수 있습니다:

  • 더 빠른 빌드
  • 더 쉬운 사용 (UI 및 CLI)
  • 바이브 코딩 친화적 (MCP 서버를 사용해보세요!)
  • 더 현대적인 기술
  • 로컬 API 및 웹사이트 개발
  • 더 많은 제어 (사용 사례에 맞게 제공된 파일 수정 가능)
  • 그리고 더 많은 것들!

예제 마이그레이션: 쇼핑 목록 애플리케이션

섹션 제목: “예제 마이그레이션: 쇼핑 목록 애플리케이션”

이 가이드에서는 PDK 튜토리얼의 쇼핑 목록 애플리케이션을 마이그레이션 대상 프로젝트로 사용합니다. 직접 따라하고 싶다면 해당 튜토리얼의 단계를 따라 대상 프로젝트를 생성하세요.

쇼핑 목록 애플리케이션은 다음 PDK 프로젝트 타입으로 구성됩니다:

  • MonorepoTsProject
  • TypeSafeApiProject
  • CloudscapeReactTsWebsiteProject
  • InfrastructureTsProject

시작하려면 새 프로젝트를 위한 새 워크스페이스를 생성합니다. 제자리 마이그레이션보다 더 극단적이지만, 이 접근 방식은 가장 깔끔한 최종 결과를 제공합니다. Nx 워크스페이스를 생성하는 것은 PDK의 MonorepoTsProject를 사용하는 것과 동등합니다:

Terminal window
pnpm create @aws/nx-workspace@1.0.0-rc.47 shopping-list --iac=cdk

이 명령이 생성하는 shopping-list 디렉토리를 선호하는 IDE에서 엽니다.

쇼핑 리스트 애플리케이션에서 사용된 TypeSafeApiProject는 다음을 활용했습니다:

  • 모델링 언어로 Smithy
  • 작업 구현을 위한 TypeScript
  • React 웹사이트와 통합하기 위한 TypeScript 훅 생성

따라서 동등한 기능을 제공하기 위해 ts#smithy-api 제너레이터를 사용할 수 있습니다.

frameworksmithy로 설정하여 ts#api 제너레이터를 실행하여 packages/api에 API 프로젝트를 설정합니다:

Terminal window
pnpm nx g @aws/nx-plugin:ts#api --name=api --framework=smithy --namespace=com.aws --auth=iam --no-interactive
어떤 파일이 변경될지 확인하기 위해 드라이 런을 수행할 수도 있습니다
Terminal window
pnpm nx g @aws/nx-plugin:ts#api --name=api --framework=smithy --namespace=com.aws --auth=iam --no-interactive --dry-run

이렇게 하면 model 프로젝트와 backend 프로젝트가 생성됩니다. model 프로젝트에는 Smithy 모델이 포함되고, backend에는 서버 구현이 포함됩니다.

백엔드는 Smithy Server Generator for TypeScript를 사용합니다. 아래에서 이에 대해 더 자세히 살펴보겠습니다.

이제 Smithy API 프로젝트의 기본 구조가 있으므로 모델을 마이그레이션할 수 있습니다:

  1. packages/api/model/src에서 생성된 예제 Smithy 파일을 삭제합니다

  2. PDK 프로젝트의 packages/api/model/src/main/smithy 디렉토리에서 모델을 새 프로젝트의 packages/api/model/src 디렉토리로 복사합니다.

  3. smithy-build.json에서 서비스 이름과 네임스페이스를 PDK 애플리케이션과 일치하도록 업데이트합니다:

    smithy-build.json
    "plugins": {
    "openapi": {
    "service": "com.aws#MyApi",
    ...
  4. main.smithy의 서비스를 업데이트하여 Smithy TypeScript Server SDK를 사용할 때 필요한 ValidationException 오류를 추가합니다.

    main.smithy
    use smithy.framework#ValidationException
    /// My Shopping List API
    @restJson1
    service MyApi {
    version: "1.0"
    operations: [
    GetShoppingLists
    PutShoppingList
    DeleteShoppingList
    ]
    errors: [
    BadRequestError
    NotAuthorizedError
    InternalFailureError
    ValidationException
    ]
    }
  5. packages/api/model/srcextensions.smithy 파일을 추가하여 생성된 클라이언트에 페이지네이션 정보를 제공하는 트레이트를 정의합니다:

    extensions.smithy
    $version: "2"
    namespace com.aws
    use smithy.openapi#specificationExtension
    @trait
    @specificationExtension(as: "x-cursor")
    structure cursor {
    inputToken: String
    enabled: Boolean
    }
  6. get-shopping-lists.smithyGetShoppingLists 작업에 새로운 @cursor 트레이트를 추가합니다:

    operations/get-shopping-lists.smithy
    @readonly
    @http(method: "GET", uri: "/shopping-list")
    @paginated(inputToken: "nextToken", outputToken: "nextToken", pageSize: "pageSize", items: "shoppingLists")
    @cursor(inputToken: "nextToken")
    @handler(language: "typescript")
    operation GetShoppingLists {
    input := with [PaginatedInputMixin] {
    @httpQuery("shoppingListId")
    shoppingListId: ShoppingListId
    }

    Nx Plugin for AWS에서 제공하는 클라이언트 제너레이터(api-connection 제너레이터를 통해)를 사용하는 경우 모든 @paginated 작업은 @cursor도 사용해야 합니다.

  7. 마지막으로 모든 작업에서 @handler 트레이트를 제거합니다. 이는 Nx Plugin for AWS에서 지원되지 않습니다. ts#smithy-api를 사용하면 이 트레이트에 의해 생성되는 자동 생성 람다 함수 CDK 구성 및 번들링 타겟이 필요하지 않습니다. 모든 람다 함수에 대해 단일 번들을 사용하기 때문입니다.

이 시점에서 빌드를 실행하여 모델 변경 사항을 확인하고 작업할 생성된 서버 코드가 있는지 확인해 보겠습니다. 백엔드 프로젝트(@shopping-list/api)에서 일부 실패가 발생할 수 있지만 다음에 해결하겠습니다.

Terminal window
pnpm nx run-many --target build

api/backend 프로젝트는 Type Safe API의 api/handlers/typescript 프로젝트와 다소 동등하다고 볼 수 있습니다.

Type Safe API와 ts#smithy-api 제너레이터의 주요 차이점 중 하나는 핸들러가 Type Safe API의 자체 생성된 핸들러 래퍼(api/generated/typescript/runtime 프로젝트에 있음) 대신 Smithy Server Generator for TypeScript를 사용하여 구현된다는 것입니다.

쇼핑 리스트 애플리케이션의 람다 핸들러는 @aws-sdk/client-dynamodb 패키지에 의존하므로 @shopping-list/api 프로젝트에 설치해 보겠습니다:

Terminal window
pnpm add @aws-sdk/client-dynamodb --filter api

그런 다음 PDK 프로젝트의 handlers/src/dynamo-client.ts 파일을 backend/src/operations로 복사하여 핸들러에서 사용할 수 있도록 합니다.

ts#smithy-api 제너레이터는 예제 Echo 작업을 스캐폴딩합니다. 모델에서 이를 제거했으므로 backend/src/operations/echo.ts의 해당 핸들러를 삭제합니다. 아래의 service.ts에서 마이그레이션된 작업을 등록하겠습니다.

핸들러를 마이그레이션하려면 다음 일반 단계를 따를 수 있습니다:

  1. PDK 프로젝트의 packages/api/handlers/typescript/src 디렉토리에서 핸들러를 새 프로젝트의 packages/api/backend/src/operations 디렉토리로 복사합니다.

  2. my-api-typescript-runtime 임포트를 제거하고 대신 생성된 TypeScript Server SDK에서 작업 타입과 ServiceContext를 임포트합니다. 예를 들어:

    import {
    deleteShoppingListHandler,
    DeleteShoppingListChainedHandlerFunction,
    INTERCEPTORS,
    Response,
    LoggingInterceptor,
    } from 'myapi-typescript-runtime';
    import { DeleteShoppingList as DeleteShoppingListOperation } from '../generated/ssdk/index.js';
    import { ServiceContext } from '../context.js';
  3. 핸들러 래퍼 내보내기를 삭제합니다

    export const handler = deleteShoppingListHandler(
    ...INTERCEPTORS,
    deleteShoppingList,
    );
  4. SSDK를 사용하도록 작업 핸들러의 시그니처를 업데이트합니다:

    export const deleteShoppingList: DeleteShoppingListChainedHandlerFunction = async (request) => {
    export const DeleteShoppingList: DeleteShoppingListOperation<ServiceContext> = async (input, ctx) => {
  5. LoggingInterceptor 사용을 ctx.logger로 교체합니다. (메트릭 및 추적 인터셉터에도 적용됩니다):

    LoggingInterceptor.getLogger(request).info('...');
    ctx.logger.info('...');
  6. 입력 매개변수에 대한 참조를 업데이트합니다. SSDK는 Smithy 모델과 정확히 일치하는 타입을 제공하므로(경로/쿼리/헤더 매개변수를 본문 매개변수와 별도로 그룹화하는 대신) 입력 참조를 적절히 업데이트합니다:

    const shoppingListId = request.input.requestParameters.shoppingListId;
    const shoppingListId = input.shoppingListId;
  7. Response 사용을 제거합니다. SSDK에서는 대신 일반 객체를 반환합니다.

    return Response.success({ shoppingListId });
    return { shoppingListId };

    또한 더 이상 Response를 throw하거나 반환하지 않고 대신 SSDK의 생성된 오류를 throw합니다:

    throw Response.badRequest({ message: 'oh no' });
    return Response.badRequest({ message: 'oh no' });
    import { BadRequestError } from '../generated/ssdk/index.js';
    throw new BadRequestError({ message: 'oh no' });
  8. ESM 구문을 사용하도록 임포트를 업데이트합니다. 즉, 상대 임포트에 .js 확장자를 추가합니다.

  9. service.ts에 작업을 추가합니다

    service.ts
    import { ServiceContext } from './context.js';
    import { MyApiService } from './generated/ssdk/index.js';
    import { DeleteShoppingList } from './operations/delete-shopping-list.js';
    import { GetShoppingLists } from './operations/get-shopping-lists.js';
    import { PutShoppingList } from './operations/put-shopping-list.js';
    // Register operations to the service here
    export const Service: MyApiService<ServiceContext> = {
    PutShoppingList,
    GetShoppingLists,
    DeleteShoppingList,
    };
튜토리얼의 세 가지 쇼핑 리스트 작업에 대한 전체 이전/이후 예제를 보려면 여기를 클릭하세요

처음에 Smithy API 프로젝트를 api라는 이름으로 생성했는데, 이는 PDK 프로젝트와의 일관성을 위해 packages/api에 추가하기 위함이었습니다. 이제 Smithy API가 service Api 대신 service MyApi를 정의하므로 getApiServiceHandler의 모든 인스턴스를 getMyApiServiceHandler로 업데이트해야 합니다.

handler.ts를 다음과 같이 변경합니다:

packages/api/backend/src/handler.ts
import { getApiServiceHandler } from './generated/ssdk/index.js';
import { getMyApiServiceHandler } from './generated/ssdk/index.js';
process.env.POWERTOOLS_METRICS_NAMESPACE = 'Api';
process.env.POWERTOOLS_SERVICE_NAME = 'Api';
const tracer = new Tracer();
const logger = new Logger();
const metrics = new Metrics();
const serviceHandler = getApiServiceHandler(Service);
const serviceHandler = getMyApiServiceHandler(Service);

그리고 local-server.ts도 변경합니다:

packages/api/backend/src/local-server.ts
import { getApiServiceHandler } from './generated/ssdk/index.js';
import { getMyApiServiceHandler } from './generated/ssdk/index.js';
const PORT = 3001;
const tracer = new Tracer();
const logger = new Logger();
const metrics = new Metrics();
const serviceHandler = getApiServiceHandler(Service);
const serviceHandler = getMyApiServiceHandler(Service);

또한 packages/api/backend/project.json을 업데이트하고 metadata.apiNamemy-api로 업데이트합니다:

packages/api/backend/project.json
"metadata": {
"generator": "ts#smithy-api",
"apiName": "api",
"apiName": "my-api",
"auth": "iam",
"modelProject": "@shopping-list/api-model",
"ports": [3001]
},

이제 프로젝트를 빌드하여 지금까지의 마이그레이션이 제대로 작동하는지 확인할 수 있습니다:

Terminal window
pnpm nx run-many --target build

쇼핑 리스트 애플리케이션에서 사용된 CloudscapeReactTsWebsiteProject는 CloudScape와 Cognito 인증이 내장된 React 웹사이트를 구성했습니다.

이 프로젝트 타입은 현재 더 이상 사용되지 않는 create-react-app을 활용했습니다. 이 가이드에서 웹사이트를 마이그레이션하기 위해 보다 현대적이고 지원되는 기술, 즉 Vite를 사용하는 ts#website 생성기를 사용할 것입니다.

마이그레이션의 일환으로, PDK에서 구성된 React Router에서 웹사이트 라우팅에 추가적인 타입 안전성을 제공하는 TanStack Router로 이동할 것입니다.

packages/website에 웹사이트 프로젝트를 설정하기 위해 frameworkreact로 설정하여 ts#website 생성기를 실행합니다. 쇼핑 리스트 애플리케이션은 CloudScape 컴포넌트로 구축되었으므로 uxcloudscape로 설정합니다(기본값은 shadcn입니다):

Terminal window
pnpm nx g @aws/nx-plugin:ts#website --name=website --framework=react --ux=cloudscape --no-interactive
어떤 파일이 변경될지 확인하기 위해 드라이 런을 수행할 수도 있습니다
Terminal window
pnpm nx g @aws/nx-plugin:ts#website --name=website --framework=react --ux=cloudscape --no-interactive --dry-run

위의 React 웹사이트 생성기는 CloudscapeReactTsWebsiteProject처럼 기본적으로 cognito 인증을 번들로 제공하지 않으며, 대신 ts#website#auth 생성기를 통해 명시적으로 추가됩니다.

Terminal window
pnpm nx g @aws/nx-plugin:ts#website#auth --project=website --cognitoDomain=shopping-list --no-interactive
어떤 파일이 변경될지 확인하기 위해 드라이 런을 수행할 수도 있습니다
Terminal window
pnpm nx g @aws/nx-plugin:ts#website#auth --project=website --cognitoDomain=shopping-list --no-interactive --dry-run

이것은 사용자가 Cognito 호스팅 UI를 사용하여 로그인하도록 적절한 리디렉션을 관리하는 React 컴포넌트를 추가합니다. 또한 UserIdentity라는 packages/common/constructs에 Cognito 리소스를 배포하기 위한 CDK 구성을 추가합니다.

PDK에서는 제공된 Projen 프로젝트를 서로 전달하여 통합 코드가 제공되도록 트리거할 수 있었습니다. 이것은 쇼핑 리스트 애플리케이션에서 웹사이트가 API와 통합할 수 있도록 구성하는 데 사용되었습니다.

Nx Plugin for AWS에서는 API 통합이 connection 생성기를 통해 지원됩니다. 다음으로, 웹사이트가 Smithy API를 호출할 수 있도록 이 생성기를 사용합니다:

Terminal window
pnpm nx g @aws/nx-plugin:connection --sourceProject=website --targetProject=api --no-interactive
어떤 파일이 변경될지 확인하기 위해 드라이 런을 수행할 수도 있습니다
Terminal window
pnpm nx g @aws/nx-plugin:connection --sourceProject=website --targetProject=api --no-interactive --dry-run

이것은 웹사이트가 생성된 TypeScript 클라이언트를 통해 API를 호출하는 데 필요한 클라이언트 프로바이더와 빌드 타겟을 생성합니다.

CloudscapeReactTsWebsiteProject는 쇼핑 리스트 애플리케이션에서 사용되는 @aws-northstar/ui에 대한 의존성을 자동으로 포함했으므로, @shopping-list/website 프로젝트에 추가합니다:

Terminal window
pnpm add @aws-northstar/ui --filter website

@aws-northstar/ui는 Vite가 해결할 수 없는 webpack 전용 import를 사용하는 ace-builds에 의존하는 코드 에디터 컴포넌트를 번들로 제공합니다. 쇼핑 리스트 애플리케이션은 이 컴포넌트를 사용하지 않으므로, packages/website/vite.config.mts의 기존 build 옵션 내 external 구성에 추가하여 번들에서 제외합니다:

packages/website/vite.config.mts
build: {
outDir: '../../dist/packages/website/bundle',
emptyOutDir: true,
reportCompressedSize: true,
commonjsOptions: {
transformMixedEsModules: true,
},
rollupOptions: {
external: ['ace-builds/webpack-resolver'],
},
},

쇼핑 리스트 애플리케이션에는 CreateItem이라는 하나의 컴포넌트와 ShoppingListShoppingLists라는 두 개의 페이지가 있습니다. TanStack Router와 Nx Plugin for AWS TypeScript 클라이언트 코드 생성기를 사용하므로 몇 가지 조정을 하면서 이들을 새 웹사이트로 마이그레이션하겠습니다.

  1. PDK 프로젝트의 packages/website/src/components/CreateItem/index.tsx를 새 프로젝트의 정확히 동일한 위치로 복사합니다.

  2. packages/website/src/pages/ShoppingLists/index.tsxpackages/website/src/routes/index.tsx로 복사합니다. ShoppingLists가 홈 페이지이고 TanStack 라우터와 함께 파일 기반 라우팅을 사용하기 때문입니다.

  3. packages/website/src/pages/ShoppingList/index.tsxpackages/website/src/routes/$shoppingListId.tsx로 복사합니다. ShoppingList/:shoppingListId 라우트에 표시하려는 페이지이기 때문입니다.

이제 IDE에서 일부 빌드 오류가 표시될 것입니다. 새 프레임워크에 맞추기 위해 몇 가지 추가 변경이 필요하며, 아래에 설명되어 있습니다.

React Router에서 TanStack Router로 마이그레이션

섹션 제목: “React Router에서 TanStack Router로 마이그레이션”

파일 기반 라우팅을 사용하고 있으므로, 웹사이트 로컬 개발 서버를 사용하여 라우트 구성을 자동으로 생성할 수 있습니다.

로컬 웹사이트 서버를 시작해 봅시다:

Terminal window
pnpm nx dev website

일부 오류가 표시되지만, 로컬 웹사이트 서버는 포트 4200에서 시작되고 로컬 Smithy API 서버는 포트 3001에서 시작됩니다.

TanStack Router로 마이그레이션하려면 routes/index.tsxroutes/$shoppingListId.tsx 모두에서 아래 단계를 따르세요:

  1. 각 라우트를 등록하기 위해 createFileRoute를 추가합니다:

    import { createFileRoute } from "@tanstack/react-router";
    ...
    export default ShoppingLists;
    export const Route = createFileRoute('/')({
    component: ShoppingLists,
    });

    파일을 저장하면 createFileRoute 호출의 타입 오류가 사라진 것을 알 수 있습니다.

  2. useNavigate 훅을 교체합니다.

    import를 업데이트합니다:

    import { useNavigate } from 'react-router-dom';
    import { useNavigate } from '@tanstack/react-router';

    타입 안전 라우트를 전달하도록 navigate 메서드(useNavigate에서 반환됨) 호출을 업데이트합니다:

    navigate(`/${cell.shoppingListId}`);
    navigate({
    to: '/$shoppingListId',
    params: { shoppingListId: cell.shoppingListId },
    });
  3. useParams 훅을 교체합니다.

    import를 제거합니다:

    import { useParams } from 'react-router-dom';

    위에서 생성한 Route에서 제공하는 훅으로 useParams 호출을 업데이트합니다. 이제 타입 안전합니다!

    const { shoppingListId } = useParams();
    const { shoppingListId } = Route.useParams();

라우트 파일이 PDK 프로젝트만큼 파일 트리에 깊게 중첩되어 있지 않으므로, routes/index.tsxroutes/$shoppingListId.tsx 모두에서 CreateItem에 대한 import를 수정해야 합니다:

import CreateItem from "../../components/CreateItem";
import CreateItem from "../components/CreateItem";

AppLayoutContext도 새 프로젝트에서 약간 다른 위치에 제공됩니다:

import { AppLayoutContext } from "../../layouts/App";
import { AppLayoutContext } from "../components/AppLayout";

새로 생성된 TypeScript 클라이언트 사용으로 마이그레이션

섹션 제목: “새로 생성된 TypeScript 클라이언트 사용으로 마이그레이션”

이제 거의 다 왔습니다! 다음으로, Type Safe API에 비해 몇 가지 개선 사항이 있는 Nx Plugin for AWS에서 제공하는 TypeScript 클라이언트를 사용하도록 마이그레이션해야 합니다. 이를 위해 아래 단계를 따르세요

  1. 예를 들어, 이전 것 대신 새로 생성된 클라이언트와 타입을 import합니다:

    import {
    ShoppingList,
    usePutShoppingList,
    useDeleteShoppingList,
    useGetShoppingLists,
    } from "myapi-typescript-react-query-hooks";
    import { ShoppingList } from "../generated/my-api/types.gen";
    import { useMyApi } from "../hooks/useMyApi";
    import { useInfiniteQuery, useMutation } from "@tanstack/react-query";

    routes/$shoppingListId.tsxShoppingList 타입을 _ShoppingList로 import합니다 - 해당 파일에서도 동일하게 하되, types.gen에서 import합니다.

    생성된 클라이언트는 훅 래퍼가 아닌 TanStack 쿼리 훅에 대한 옵션을 생성하는 메서드를 제공하므로, @tanstack/react-query에서 직접 관련 훅을 import합니다.

  2. 예를 들어, 새로운 TanStack Query 훅을 인스턴스화합니다:

    const getShoppingLists = useGetShoppingLists({ pageSize: PAGE_SIZE });
    const putShoppingList = usePutShoppingList();
    const deleteShoppingList = useDeleteShoppingList();
    const api = useMyApi();
    const getShoppingLists = useInfiniteQuery(
    api.getShoppingLists.infiniteQueryOptions(
    { pageSize: PAGE_SIZE },
    { getNextPageParam: (p) => p.nextToken },
    ),
    );
    const putShoppingList = useMutation(api.putShoppingList.mutationOptions());
    const deleteShoppingList = useMutation(
    api.deleteShoppingList.mutationOptions(),
    );
  3. 요청 본문에 매개변수를 받는 작업 호출에 대한 래퍼 <operation>RequestContent를 제거합니다:

    await putShoppingList.mutateAsync({
    putShoppingListRequestContent: {
    name: item,
    },
    });

TanStack Query v4에서 v5로 마이그레이션

섹션 제목: “TanStack Query v4에서 v5로 마이그레이션”

TanStack Query v4(PDK에서 사용)와 connection 생성기가 추가한 v5 간의 차이로 인해 수정해야 할 몇 가지 오류가 남아 있습니다:

  1. 예를 들어, mutation에 대해 isLoadingisPending으로 교체합니다:

    putShoppingList.isLoading
    putShoppingList.isPending
  2. 쇼핑 리스트 애플리케이션은 TanStack Query v4의 타입을 기대하는 @aws-northstar/uiInfiniteQueryTable을 사용했습니다. 이것은 실제로 v5의 무한 쿼리와 작동하므로 타입 오류를 억제할 수 있습니다:

    <InfiniteQueryTable
    query={getShoppingLists}
    query={getShoppingLists as any}

이제 http://localhost:4200/에서 로컬 웹사이트를 방문할 수 있습니다

모든 것이 마이그레이션되었으므로 웹사이트가 로드되어야 합니다! 쇼핑 리스트 애플리케이션이 API, 웹사이트 및 Identity 외에 의존하는 유일한 인프라는 DynamoDB 테이블이므로 - 리전 내에 shopping_list라는 이름의 DynamoDB 테이블이 있고 이에 액세스할 수 있는 로컬 AWS 자격 증명이 있다면 웹사이트가 완전히 작동할 것입니다!

그렇지 않아도 괜찮습니다. 다음으로 인프라를 마이그레이션하겠습니다.

튜토리얼의 두 쇼핑 리스트 페이지에 대한 전체 이전/이후 예제를 보려면 여기를 클릭하세요

쇼핑 리스트 애플리케이션을 위해 마이그레이션해야 하는 마지막 프로젝트는 InfrastructureTsProject입니다. 이것은 TypeScript CDK 프로젝트이며, Nx Plugin for AWS에서 이에 해당하는 것은 ts#infra 생성기입니다.

Projen 프로젝트뿐만 아니라 PDK는 이러한 프로젝트가 의존하는 CDK 구성 요소도 제공했습니다. Nx Plugin for AWS에서 생성된 구성 요소를 사용하여 이러한 CDK 구성 요소에서도 쇼핑 리스트 애플리케이션을 마이그레이션할 것입니다.

TypeScript CDK 인프라스트럭처 프로젝트 생성

섹션 제목: “TypeScript CDK 인프라스트럭처 프로젝트 생성”

ts#infra 생성기를 실행하여 packages/infra에 인프라스트럭처 프로젝트를 설정합니다:

Terminal window
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
어떤 파일이 변경될지 확인하기 위해 드라이 런을 수행할 수도 있습니다
Terminal window
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run

CDK 인프라스트럭처 마이그레이션

섹션 제목: “CDK 인프라스트럭처 마이그레이션”

PDK 쇼핑 리스트 애플리케이션은 CDK 애플리케이션 스택 내에서 다음 구성 요소를 인스턴스화했습니다:

  • 쇼핑 리스트를 저장하는 DynamoDB 테이블을 위한 DatabaseConstruct
  • PDK에서 직접 가져온 Cognito 리소스를 위한 UserIdentity
  • Smithy API를 배포하기 위한 MyApi, PDK의 TypeSafeRestApi CDK 구성 요소를 기반으로 타입 안전 통합과 함께 생성된 TypeScript CDK 구성 요소를 사용했습니다.
  • PDK의 StaticWebsite CDK 구성 요소를 래핑하여 웹사이트를 배포하기 위한 Website

다음으로, 이들 각각을 새 프로젝트로 마이그레이션할 것입니다.

PDK 쇼핑 리스트 애플리케이션의 packages/infra/src/stacks/application-stack.ts를 새 프로젝트의 정확히 동일한 위치로 복사합니다. 아래에서 해결할 몇 가지 TypeScript 오류가 표시될 것입니다.

PDK 쇼핑 리스트 애플리케이션에는 packages/src/constructs/database.tsDatabase 구성 요소가 있었습니다. 이것을 새 프로젝트의 정확히 동일한 위치로 복사합니다.

Nx Plugin for AWS는 PDK Nag보다 조금 더 엄격한 보안 테스트를 위해 Checkov를 사용하므로 일부 억제를 추가해야 합니다:

constructs/database.ts
import { suppressRules } from '@shopping-list/common-constructs';
...
suppressRules(
this.shoppingListTable,
['CKV_AWS_28', 'CKV_AWS_119'],
'Backup and KMS key not required for this project',
);

application-stack.ts에서 DatabaseConstruct의 import를 ESM 구문을 사용하도록 업데이트합니다:

stacks/application-stack.ts
import { DatabaseConstruct } from '../constructs/database';
import { DatabaseConstruct } from '../constructs/database.js';
UserIdentity 구성 요소 마이그레이션
섹션 제목: “UserIdentity 구성 요소 마이그레이션”

UserIdentity 구성 요소는 일반적으로 import를 조정하여 변경 없이 교체할 수 있습니다.

import { UserIdentity } from "@aws/pdk/identity";
import { UserIdentity } from '@shopping-list/common-constructs';
...
const userIdentity = new UserIdentity(this, `${id}UserIdentity`);

새로운 UserIdentity 구성 요소에서 사용하는 기본 구성 요소는 aws-cdk-lib에서 직접 제공되는 반면, PDK는 @aws-cdk/aws-cognito-identitypool-alpha를 사용했습니다.

PDK 쇼핑 리스트 애플리케이션에는 Smithy 모델에서 Type Safe API가 생성한 CDK 구성 요소를 인스턴스화하는 constructs/apis/myapi.ts의 구성 요소가 있었습니다.

이 구성 요소뿐만 아니라 PDK 프로젝트가 @handler 트레이트를 사용했기 때문에 생성된 람다 함수 CDK 구성 요소도 생성되었습니다.

Type Safe API와 마찬가지로 Nx Plugin for AWS는 Smithy 모델을 기반으로 통합에 대한 타입 안전성을 제공하지만 훨씬 더 간단하고 유연한 방식으로 달성됩니다. 빌드 시 전체 CDK 구성 요소를 생성하는 대신 최소한의 “메타데이터”만 생성되며, packages/common/constructs/src/app/apis/api.ts가 이를 일반적인 방식으로 사용합니다. 구성 요소 사용 방법에 대한 자세한 내용은 ts#smithy-api 생성기 가이드에서 확인할 수 있습니다.

다음 단계를 따르세요:

  1. application-stack.ts에서 Api 구성 요소 인스턴스화

    stacks/application-stack.ts
    import { MyApi } from "../constructs/apis/myapi";
    import { Api } from '@shopping-list/common-constructs';
    ...
    const myapi = new MyApi(this, "MyApi", {
    databaseConstruct,
    userIdentity,
    });
    const api = new Api(this, 'MyApi', {
    integrations: Api.defaultIntegrations(this).build(),
    });

    여기서 Api.defaultIntegrations(this).build()를 사용하는 것을 주목하세요 - 기본 동작은 API의 각 작업에 대해 람다 함수를 생성하는 것이며, 이는 myapi.ts에서 가졌던 것과 동일한 동작입니다.

  2. 람다 함수가 DynamoDB 테이블에 액세스할 수 있도록 권한 부여

    PDK 쇼핑 리스트 애플리케이션에서는 DatabaseConsructMyApi에 전달되었고, 생성된 각 함수 구성 요소에 관련 권한을 추가하는 것을 관리했습니다. Api 구성 요소의 타입 안전 integrations 속성에 액세스하여 application-stack.ts 파일에서 직접 이 작업을 수행할 것입니다:

    stacks/application-stack.ts
    // Grant our lambda functions scoped access to call Dynamo
    databaseConstruct.shoppingListTable.grantReadData(
    api.integrations.getShoppingLists.handler,
    );
    [
    api.integrations.putShoppingList.handler,
    api.integrations.deleteShoppingList.handler,
    ].forEach((f) => databaseConstruct.shoppingListTable.grantWriteData(f));
  3. 인증된 사용자가 API를 호출할 수 있도록 권한 부여

    PDK 애플리케이션의 myapi.ts 내에서 인증된 사용자에게도 API를 호출할 수 있는 IAM 권한이 부여되었습니다. application-stack.ts에서 동등한 작업을 수행할 것입니다:

    stacks/application-stack.ts
    api.grantInvokeAccess(userIdentity.identityPool.authenticatedRole);
웹사이트 구성 요소 마이그레이션
섹션 제목: “웹사이트 구성 요소 마이그레이션”

마지막으로, PDK 쇼핑 리스트 애플리케이션의 packages/infra/src/constructs/websites/website.ts와 동등한 packages/common/constructs/src/app/static-websites/website.tsWebsite 구성 요소를 application-stack.ts에 추가합니다.

import { Website } from "../constructs/websites/website";
import { Website } from '@shopping-list/common-constructs';
...
new Website(this, "Website", {
userIdentity,
myapi,
});
new Website(this, 'Website');

웹사이트에 identity나 API를 전달하지 않는 것을 주목하세요 - 런타임 구성은 Nx Plugin for AWS에서 제공하는 각 구성 요소 내에서 관리되며, UserIdentityApi는 필요한 값을 등록하고 Website는 정적 웹사이트의 /runtime-config.json에 배포하는 것을 관리합니다.

이제 코드베이스의 모든 관련 부분을 새 프로젝트로 마이그레이션했으므로 프로젝트를 빌드해 보겠습니다.

Terminal window
pnpm nx run-many --target build

이제 완전히 마이그레이션된 코드베이스를 얻었으므로 배포를 살펴볼 수 있습니다. 이 시점에서 두 가지 경로를 선택할 수 있습니다.

가장 간단한 접근 방식은 이것을 완전히 새로운 애플리케이션으로 취급하는 것입니다. 즉, 새로운 DynamoDB 테이블과 Cognito User Pool로 “처음부터 시작”하여 모든 사용자와 쇼핑 목록을 잃게 됩니다. 이 접근 방식의 경우 다음과 같이 하면 됩니다:

  1. shopping_list라는 이름의 DynamoDB 테이블을 삭제합니다

  2. 새 애플리케이션을 배포합니다:

    Terminal window
    pnpm nx deploy infra shopping-list-infra-sandbox/*

🎉 완료되었습니다! 🎉

중단 없이 기존 상태 저장 리소스 마이그레이션 (더 복잡함)

섹션 제목: “중단 없이 기존 상태 저장 리소스 마이그레이션 (더 복잡함)”

실제로는 고객에게 다운타임 없이 기존 AWS 리소스를 새 코드베이스에서 관리하도록 마이그레이션하고 싶을 가능성이 높습니다.

쇼핑 목록 애플리케이션의 경우, 우리가 관심 있는 상태 저장 리소스는 사용자의 쇼핑 목록을 포함하는 DynamoDB 테이블과 등록된 모든 사용자의 세부 정보를 포함하는 User Pool입니다. 우리의 상위 수준 계획은 이 두 가지 핵심 리소스를 유지하고 새 스택에서 관리되도록 이동한 다음, DNS를 업데이트하여 새 웹사이트(및 고객에게 노출된 경우 API)를 가리키도록 하는 것입니다.

  1. 유지하려는 기존 리소스를 참조하도록 새 애플리케이션을 업데이트합니다.

    쇼핑 목록 애플리케이션의 경우 DynamoDB 테이블에 대해 이렇게 합니다

    constructs/database.ts
    this.shoppingListTable = new Table(this, 'ShoppingList', {
    ...
    this.shoppingListTable = Table.fromTableName(
    this,
    'ShoppingList',
    'shopping_list',
    );

    그리고 Cognito User Pool의 경우

    packages/common/constructs/src/core/user-identity.ts
    this.userPool = this.createUserPool();
    this.userPool = UserPool.fromUserPoolId(
    this,
    'UserPool',
    '<your-user-pool-id>',
    );
  2. 새 애플리케이션을 빌드하고 배포합니다:

    Terminal window
    pnpm nx run-many --target build
    Terminal window
    pnpm nx deploy infra shopping-list-infra-sandbox/*

    이제 기존 리소스를 참조하는 새 애플리케이션이 구축되었지만 아직 트래픽을 받지 않습니다.

  3. 새 애플리케이션이 예상대로 작동하는지 확인하기 위해 전체 통합 테스트를 수행합니다. 쇼핑 목록 애플리케이션의 경우 웹사이트를 로드하고 로그인하여 쇼핑 목록을 생성, 보기, 편집 및 삭제할 수 있는지 확인합니다.

  4. 새 애플리케이션에서 기존 리소스를 참조하는 변경 사항을 되돌리되 아직 배포하지 마십시오.

    constructs/database.ts
    this.shoppingListTable = new Table(this, 'ShoppingList', {
    ...
    this.shoppingListTable = Table.fromTableName(
    this,
    'ShoppingList',
    'shopping_list',
    );

    그리고 Cognito User Pool의 경우

    packages/common/constructs/src/core/user-identity.ts
    this.userPool = this.createUserPool();
    this.userPool = UserPool.fromUserPoolId(
    this,
    'UserPool',
    '<your-user-pool-id>',
    );

    그런 다음 빌드를 실행합니다

    Terminal window
    pnpm nx run-many --target build
  5. 새 애플리케이션의 packages/infra 폴더에서 cdk import를 사용하여 가져오라는 메시지가 표시될 리소스를 확인합니다.

    New Application
    cd packages/infra
    pnpm exec cdk import shopping-list-infra-sandbox/Application --force

    엔터를 눌러 프롬프트를 진행합니다. 리소스가 다른 스택에서 관리되기 때문에 가져오기가 실패합니다 - 이것은 예상된 것이며, 유지해야 할 리소스를 확인하기 위해 이 단계를 수행했습니다. 다음과 같은 출력이 표시됩니다:

    Terminal window
    shopping-list-infra-sandbox/Application/ApplicationUserIdentity/UserPool/smsRole/Resource (AWS::IAM::Role): enter RoleName (empty to skip)
    shopping-list-infra-sandbox/Application/ApplicationUserIdentity/UserPool/Resource (AWS::Cognito::UserPool): enter UserPoolId (empty to skip)
    shopping-list-infra-sandbox/Application/Database/ShoppingList/Resource (AWS::DynamoDB::Table): import with TableName=shopping_list (y/n) y

    이것은 실제로 새 스택으로 가져와야 할 리소스가 3개 있다는 것을 알려줍니다.

  6. 이전 단계에서 발견한 리소스에 대해 RemovalPolicyRETAIN으로 설정하도록 이전 PDK 프로젝트를 업데이트합니다. 이 글을 작성하는 시점에서 이것은 User Pool과 DynamoDB 테이블 모두의 기본값이지만, 위에서 발견한 SMS Role에 대해서는 업데이트해야 합니다:

    application-stack.ts
    const userIdentity = new UserIdentity(this, `${id}UserIdentity`, {
    userPool,
    });
    const smsRole = userIdentity.userPool.node.findAll().filter(
    c => CfnResource.isCfnResource(c) &&
    c.node.path.includes('/smsRole/'))[0] as CfnResource;
    smsRole.applyRemovalPolicy(RemovalPolicy.RETAIN);
  7. 제거 정책이 적용되도록 PDK 프로젝트를 배포합니다

    PDK Application
    cd packages/infra
    npx projen deploy
  8. CloudFormation 콘솔을 살펴보고 위의 cdk import 단계에서 요청받은 값을 기록합니다

    1. User Pool ID, 예: us-west-2_XXXXX
    2. SMS Role Name, 예: infra-sandbox-UserIdentityUserPoolsmsRoleXXXXXX
  9. 리소스를 생성하는 대신 기존 리소스를 참조하도록 PDK 프로젝트를 업데이트합니다

    constructs/database.ts
    this.shoppingListTable = new Table(this, 'ShoppingList', {
    ...
    this.shoppingListTable = Table.fromTableName(
    this,
    'ShoppingList',
    'shopping_list',
    );

    그리고 Cognito User Pool의 경우

    application-stack.ts
    const userPool = UserPool.fromUserPoolId(
    this,
    'UserPool',
    '<your-user-pool-id>',
    );
    const userIdentity = new UserIdentity(this, `${id}UserIdentity`, {
    // PDK construct accepts UserPool not IUserPool, but this still works!
    userPool: userPool as any,
    });
  10. PDK 프로젝트를 다시 배포합니다. 이렇게 하면 리소스가 더 이상 PDK 프로젝트의 CloudFormation 스택에서 관리되지 않습니다.

    PDK Application
    cd packages/infra
    npx projen deploy
  11. 이제 리소스가 관리되지 않으므로 새 애플리케이션에서 cdk import를 실행하여 실제로 가져오기를 수행할 수 있습니다:

    New Application
    cd packages/infra
    pnpm exec cdk import shopping-list-infra-sandbox/Application --force

    메시지가 표시되면 값을 입력하면 가져오기가 성공적으로 완료됩니다.

  12. 이러한 기존 리소스(이제 새 스택에서 관리됨)에 대한 변경 사항이 적용되도록 새 애플리케이션을 다시 배포합니다:

    Terminal window
    pnpm nx deploy infra shopping-list-infra-sandbox/*
  13. 새 애플리케이션에 대한 전체 테스트를 다시 수행합니다

  14. 새 웹사이트(및 필요한 경우 API)를 가리키도록 DNS 레코드를 업데이트합니다.

    Route53 가중치 기반 라우팅을 사용하는 점진적 접근 방식을 권장합니다. 이를 통해 처음에는 일부 요청만 새 애플리케이션으로 전달됩니다. 메트릭을 모니터링하면서 이전 PDK 애플리케이션으로 트래픽이 전송되지 않을 때까지 새 애플리케이션의 가중치를 늘릴 수 있습니다.

    DNS가 없고 웹사이트와 API에 자동 생성된 도메인을 사용한 경우 항상 요청을 프록시하는 방법을 고려할 수 있습니다(예: CloudFront HTTP origin 또는 API Gateway HTTP integration(s)를 통해).

  15. PDK 애플리케이션 메트릭을 모니터링하여 트래픽이 없는지 확인하고 마지막으로 이전 CloudFormation 스택을 삭제합니다:

    Terminal window
    cd packages/infra
    npx projen destroy

꽤 복잡했지만 사용자를 새 애플리케이션으로 원활하게 마이그레이션하는 데 성공했습니다! 🎉🎉🎉

이제 PDK에 비해 Nx Plugin for AWS의 새로운 이점을 얻게 되었습니다:

  • 더 빠른 빌드
  • 로컬 API 개발 지원
  • 바이브 코딩 친화적인 코드베이스 (MCP 서버를 사용해 보세요!)
  • 더 직관적인 타입 안전 클라이언트/서버 코드
  • 그리고 더 많은 것들!

이 섹션은 위의 예제 마이그레이션에서 다루지 않은 PDK 기능에 대한 지침을 제공합니다.

PDK에서 이동할 때 일반적인 규칙으로, PDK Monorepo와의 유사성을 고려하여 Nx Workspace로 모든 프로젝트를 시작하는 것을 권장합니다. 또한 새로운 타입을 구축하는 기본 요소로 우리의 제너레이터를 사용하는 것을 권장합니다.

Terminal window
pnpm create @aws/nx-workspace my-project

CDK Graph는 연결된 CDK 리소스의 그래프를 구축하며, 두 가지 플러그인을 제공했습니다:

CDK Graph Diagram Plugin은 CDK 인프라에서 AWS 아키텍처 다이어그램을 생성합니다.

유사한 결정론적 접근 방식을 위한 실행 가능한 대안으로는 CDK-Dia가 있습니다.

생성형 AI의 발전으로 많은 기반 모델이 CDK 인프라에서 고품질 다이어그램을 생성할 수 있게 되었습니다. AWS Diagram MCP Server를 사용해 보시기를 권장합니다. 자세한 안내는 이 블로그 게시물을 확인하세요.

CDK Graph Threat Composer Plugin은 CDK 코드에서 시작용 Threat Composer 위협 모델을 생성합니다.

이 플러그인은 예제 위협이 포함된 기본 위협 모델을 필터링하고, 스택에서 사용하는 리소스를 기반으로 필터링하는 방식으로 작동했습니다.

이러한 특정 예제 위협에 관심이 있다면 기본 위협 모델을 복사하고 필터링하거나, 기반 모델이 유사한 모델을 생성하는 데 도움이 되는 컨텍스트로 사용할 수 있습니다.

AWS Arch는 위의 CDK Graph에 대한 CloudFormation 리소스와 관련 아키텍처 아이콘 간의 매핑을 제공했습니다.

아이콘 관련 리소스는 AWS Architecture Icons 페이지를 참조하세요. Diagrams도 코드로 다이어그램을 구축하는 방법을 제공합니다.

이를 직접 사용하고 있었다면 프로젝트를 포크하여 소유권을 가져가는 것을 고려해보세요!

PDK는 CDK 인프라 프로젝트를 설정하고 일부 CDK Pipelines 리소스를 래핑한 CDK 구성 요소를 사용하는 PDKPipelineProject를 제공했습니다.

이를 마이그레이션하려면 CDK Pipelines 구성 요소를 직접 사용할 수 있습니다. 그러나 실제로는 GitHub actions 또는 GitLab CI/CD와 같은 것을 사용하는 것이 더 간단할 수 있습니다. 여기서 CDK Stages를 정의하고 적절한 스테이지에 대한 배포 명령을 직접 실행합니다.

PDK NagCDK Nag를 래핑하며, 프로토타입 구축에 특화된 규칙 세트를 제공합니다.

PDK Nag에서 마이그레이션하려면 CDK Nag를 직접 사용하세요. 동일한 규칙 세트가 필요한 경우 여기 문서를 따라 자체 “팩”을 생성할 수 있습니다.

Type Safe API에서 가장 일반적으로 사용되는 컴포넌트는 위의 예제 마이그레이션에서 다루고 있지만, 다른 기능들도 있으며 아래에 마이그레이션 세부 정보가 나와 있습니다.

Nx Plugin for AWS는 Smithy로 모델링된 API를 지원하지만 OpenAPI로 직접 모델링된 API는 지원하지 않습니다. ts#smithy-api 생성기가 좋은 시작점이며 이를 수정할 수 있습니다. Smithy 대신 model 프로젝트의 src 폴더에 OpenAPI 사양을 정의하고, NPM에서 사용할 수 없는 경우 클라이언트/서버용 원하는 코드 생성 도구를 사용하도록 build.Dockerfile을 수정할 수 있습니다. 원하는 도구가 NPM에 있는 경우 Nx 워크스페이스에 개발 의존성으로 설치하고 Nx 빌드 타겟으로 직접 호출할 수 있습니다.

OpenAPI로 모델링된 타입 안전 백엔드의 경우 OpenAPI Generator Server Generators 중 하나를 사용하는 것을 고려할 수 있습니다. 이들은 AWS Lambda용으로 직접 생성하지 않지만 AWS Lambda Web Adapter를 사용하여 많은 경우 격차를 해소할 수 있습니다.

TypeScript 클라이언트의 경우 ts#website 생성기connection 생성기를 예제 ts#api(frameworksmithy로 설정)와 함께 사용하여 클라이언트가 생성되고 웹사이트와 통합되는 방법을 확인할 수 있습니다. 이는 open-api#ts-client 또는 open-api#ts-hooks 생성기를 호출하여 클라이언트를 생성하는 빌드 타겟을 구성합니다. OpenAPI 사양을 가리키도록 하여 이러한 생성기를 직접 사용할 수 있습니다.

다른 언어의 경우 OpenAPI Generator의 생성기 중 필요에 맞는 것이 있는지 확인할 수도 있습니다.

ts#nx-generator 생성기를 사용하여 맞춤형 생성기를 구축할 수도 있습니다. OpenAPI에서 코드를 생성하는 방법에 대한 자세한 내용은 해당 생성기의 문서를 참조하세요. Nx Plugin for AWS의 템플릿을 시작점으로 사용할 수 있습니다. 더 많은 영감을 얻기 위해 PDK 코드베이스의 템플릿을 참조할 수도 있습니다. 단, 템플릿이 작동하는 데이터 구조가 Nx Plugin for AWS와 약간 다르다는 점에 유의하세요.

TypeSpec의 경우 위의 OpenAPI 섹션도 적용됩니다. ts#smithy-api를 생성하는 것으로 시작하고, Nx 워크스페이스에 TypeSpec 컴파일러와 OpenAPI 패키지를 설치한 다음, 모델 프로젝트의 compile 타겟을 대신 tsp compile을 실행하도록 업데이트하여 dist 디렉토리에 OpenAPI 사양을 출력하도록 할 수 있습니다.

권장되는 접근 방식은 TypeSpec HTTP Server generator for JavaScript를 사용하여 서버 코드를 생성하는 것입니다. 이는 TypeSpec 모델에서 직접 작동하기 때문입니다.

AWS Lambda에서 생성된 서버를 실행하려면 AWS Lambda Web Adapter를 사용할 수 있습니다.

위의 OpenAPI 옵션 중 하나를 사용할 수도 있습니다.

TypeSpec은 Type Safe API가 지원하는 세 가지 언어 모두에 대한 자체 코드 생성기를 가지고 있습니다:

TypeSpec은 OpenAPI로 컴파일할 수 있으므로 위의 OpenAPI 섹션도 적용됩니다.

위의 예제 마이그레이션은 ts#smithy-api 생성기를 사용하도록 마이그레이션하는 방법을 설명합니다. 이 섹션에서는 Python 및 Java 백엔드와 클라이언트에 대한 옵션을 다룹니다.

Smithy code generator for Java. 이는 Java 서버 생성기와 AWS Lambda에서 생성된 Java 서버를 실행하기 위한 어댑터를 가지고 있습니다.

Smithy는 Python용 서버 생성기가 없으므로 OpenAPI를 통해 진행해야 합니다. 잠재적인 옵션에 대해서는 OpenAPI로 모델링된 API에 관한 위의 섹션을 참조하세요.

Smithy code generator for Java. 이는 Java 클라이언트 생성기를 가지고 있습니다.

Python 클라이언트의 경우 Smithy Python을 확인할 수 있습니다.

TypeScript의 경우 Smithy TypeScript를 확인하거나, OpenAPI를 통해 진행하는 ts#smithy-api에서 취한 것과 동일한 접근 방식을 사용하세요(TanStack Query 훅을 통해 tRPC, FastAPI 및 Smithy API 간의 일관성을 제공하기 때문에 이 방식을 선택했습니다).

Type Safe API는 여러 Smithy 기반 API에서 재사용할 수 있는 Smithy 모델을 포함하는 프로젝트를 구성하는 SmithyShapeLibraryProject라는 Projen 프로젝트 유형을 제공했습니다.

이에 해당하는 것은 typeshapes로 설정한 smithy#project 생성기입니다:

Terminal window
pnpm nx g @aws/nx-plugin:smithy#project --name=my-shapes --type=shapes
어떤 파일이 변경될지 확인하기 위해 드라이 런을 수행할 수도 있습니다
Terminal window
pnpm nx g @aws/nx-plugin:smithy#project --name=my-shapes --type=shapes --dry-run

SmithyShapeLibraryProject의 shape를 생성된 프로젝트의 src 폴더로 이동한 다음, API 모델의 의존성으로 라이브러리를 연결하는 방법에 대해서는 Smithy 프로젝트 가이드를 참조하세요.

Type Safe API는 다음과 같은 기본 인터셉터를 제공했습니다:

  • Powertools for AWS Lambda를 사용한 로깅, 추적 및 메트릭 인터셉터
  • 포착되지 않은 예외를 처리하기 위한 Try-catch 인터셉터
  • CORS 헤더를 반환하기 위한 CORS 인터셉터

ts#smithy-api 생성기는 Middy를 사용하여 Powertools for AWS Lambda로 로깅, 추적 및 메트릭을 계측합니다. Try-catch 인터셉터의 동작은 Smithy TypeScript SSDK에 내장되어 있으며, CORS 헤더는 handler.ts에 추가됩니다.

모든 언어에서 로깅, 추적 및 메트릭 인터셉터의 경우 Powertools for AWS Lambda를 직접 사용하세요.

사용자 정의 인터셉터를 마이그레이션하려면 다음 라이브러리를 사용하는 것이 좋습니다:

Type Safe API는 Redocly CLI를 사용한 문서 생성을 제공했습니다. 위와 같이 마이그레이션한 후 기존 프로젝트에 이를 추가하는 것은 매우 쉽습니다.

  1. Redocly CLI 설치

    Terminal window
    pnpm add -Dw @redocly/cli
  2. redocly build-docs를 사용하여 model 프로젝트에 문서 생성 타겟을 추가합니다. 예를 들어:

    model/project.json
    {
    ...
    "documentation": {
    "cache": true,
    "outputs": ["{workspaceRoot}/dist/{projectRoot}/documentation"],
    "executor": "nx:run-commands",
    "options": {
    "command": "redocly build-docs dist/packages/api/model/build/openapi/openapi.json --output=dist/packages/api/model/documentation/index.html",
    "cwd": "{workspaceRoot}"
    },
    "dependsOn": ["compile"]
    }
    }

OpenAPI Generator documentation generators를 고려할 수도 있습니다.

Type Safe API는 생성된 인프라 패키지 내에서 mock을 생성했습니다.

JSON 스키마를 기반으로 mock 데이터를 생성할 수 있는 JSON Schema Faker로 이동할 수 있습니다. 이는 OpenAPI 사양에서 직접 작동할 수 있으며, model 프로젝트 빌드의 일부로 실행할 수 있는 CLI가 있습니다.

CDK 인프라를 업데이트하여 JSON Schema Faker가 출력한 JSON 파일을 읽고, 생성된 metadata.gen.ts(ts#smithy-api 생성기를 사용했다고 가정)를 기반으로 통합에 대한 적절한 API Gateway MockIntegration을 반환할 수 있습니다.

Type Safe API는 백엔드에서 다양한 언어를 혼합하여 API를 구현하는 것을 지원했습니다. 이는 CDK에서 API 구성을 인스턴스화할 때 통합에 “재정의”를 제공하여 달성할 수도 있습니다:

application-stack.ts
const pythonLambdaHandler = new Function(this, 'PythonImplementation', {
runtime: Runtime.PYTHON_3_12,
...
});
new MyApi(this, 'MyApi', {
integrations: Api.defaultIntegrations(this)
.withOverrides({
echo: {
integration: new LambdaIntegration(pythonLambdaHandler),
handler: pythonLambdaHandler,
},
})
.build(),
});

ts#smithy-api와 TypeScript Server SDK를 사용하는 경우 서비스가 컴파일되도록 서비스/라우터를 “스텁”해야 합니다. 예를 들어:

service.ts
export const Service: ApiService<ServiceContext> = {
...
Echo: () => { throw new Error(`Not Implemented`); },
};

Type Safe API는 SpecRestApi 구성을 내부적으로 사용했기 때문에 OpenAPI 사양을 기반으로 요청 본문에 대한 네이티브 API Gateway 유효성 검사를 추가했습니다.

ts#smithy-api 생성기를 사용하면 유효성 검사는 Server SDK 자체에서 수행됩니다. 이는 대부분의 서버 생성기에서 동일합니다.

네이티브 API Gateway 유효성 검사를 구현하려면 packages/common/constructs/src/core/api/rest-api.ts를 수정하여 OpenAPI 사양에서 각 작업의 요청 본문에 대한 관련 JSON 스키마를 읽도록 할 수 있습니다.

안타깝게도 API Gateway 및 Lambda를 사용하는 Type Safe API의 websocket API에 대한 간단한 마이그레이션 경로는 없습니다. 그러나 이 가이드의 이 섹션은 최소한 몇 가지 아이디어를 제공하는 것을 목표로 합니다.

비동기 API를 처리하도록 설계되었으므로 OpenAPI 또는 TypeSpec 대신 AsyncAPI를 사용하여 API를 모델링하는 것을 고려하세요. AsyncAPI NodeJS Template은 예를 들어 ECS에서 호스팅할 수 있는 Node websocket 백엔드를 생성할 수 있습니다.

인프라를 위해 AppSync Events를 고려하고 Powertools를 사용할 수도 있습니다. 이 블로그 게시물을 읽어볼 가치가 있습니다!

또 다른 옵션은 AppSync에서 websocket과 함께 GraphQL API를 사용하는 것입니다. 이에 대해 +1을 할 수 있는 GitHub 이슈가 있습니다! 자세한 내용과 샘플 프로젝트 링크는 AppSync 개발자 가이드를 참조하세요.

Type Safe API와 동일한 벤더 확장을 해석하는 자체 코드 생성기를 구축하는 것도 고려할 수 있습니다. 사용자 정의 OpenAPI 기반 코드 생성기 구축에 대한 자세한 내용은 OpenAPI로 모델링된 API 섹션을 참조하세요. Type Safe API가 API Gateway Websocket API Lambda 핸들러에 사용하는 템플릿은 여기에서, 클라이언트는 여기에서 찾을 수 있습니다.

tRPC를 사용하려면 ts#trpc-api 생성기를 사용하도록 마이그레이션하는 것도 고려할 수 있습니다. 이 글을 쓰는 시점에는 아직 구독/스트리밍을 지원하지 않지만 필요한 경우 이를 추적하는 GitHub 이슈에 +1을 추가하세요.

Smithy는 프로토콜에 구애받지 않지만 아직 Websocket 프로토콜을 지원하지 않습니다. 지원을 추적하는 GitHub 이슈를 참조하세요.

PDK는 Python과 Java로 작성된 CDK 인프라를 지원했습니다. 현재 Nx Plugin for AWS에서는 이를 지원하지 않습니다.

권장되는 방법은 CDK 인프라를 TypeScript로 마이그레이션하거나, 우리의 제너레이터를 사용하고 공통 구성 요소 패키지를 원하는 언어로 마이그레이션하는 것입니다. Kiro CLI와 같은 생성형 AI를 사용하여 이러한 종류의 마이그레이션을 가속화할 수 있습니다. AI 에이전트가 합성된 CloudFormation 템플릿이 동일해질 때까지 마이그레이션을 반복하도록 할 수 있습니다.

Python 또는 Java로 생성된 Type Safe API의 인프라에도 동일하게 적용됩니다 - 공통 구성 요소 패키지에서 일반 rest-api.ts 구성 요소를 변환하고, 대상 언어에 대한 간단한 메타데이터 제너레이터를 직접 구현할 수 있습니다(OpenAPI로 모델링된 API 섹션 참조).

py#project 제너레이터를 사용하여 CDK 코드를 추가할 기본 Python 프로젝트를 생성할 수 있습니다(cdk.json 파일을 이동하고 관련 타겟을 추가). Java 프로젝트의 경우 Nx의 @nx/gradle 플러그인을 사용하거나, Maven의 경우 @jnxplus/nx-maven을 사용할 수 있습니다.

PDK는 Projen 위에 구축되었습니다. Projen과 Nx Generators는 상당히 근본적인 차이가 있어서 기술적으로는 결합이 가능하지만 안티 패턴일 가능성이 높습니다. Projen은 프로젝트 파일을 코드로 관리하여 직접 수정할 수 없도록 하는 반면, Nx generators는 프로젝트 파일을 한 번 생성한 후 코드를 자유롭게 수정할 수 있습니다.

Projen을 계속 사용하고 싶다면, 원하는 Projen 프로젝트 타입을 직접 구현할 수 있습니다. Nx Plugin for AWS의 패턴을 따르려면, 우리의 generators를 실행하거나 GitHub에서 소스 코드를 검토하여 원하는 프로젝트 타입이 어떻게 구성되는지 확인하고, Projen의 primitives를 사용하여 관련 부분을 구현할 수 있습니다.

Nx Plugin for AWS MCP Server 소개

급속도로 진화하는 소프트웨어 개발 환경에서 AI 어시스턴트는 우리의 코딩 여정에서 가치 있는 협력자가 되었습니다. 많은 개발자들이 우리가 애정을 담아 “바이브 코딩”이라고 부르는 것, 즉 인간의 창의성과 AI 지원 간의 협업적 춤을 받아들였습니다. 모든 새로운 관행과 마찬가지로, 이는 흥미로운 이점과 주목할 만한 과제를 모두 가지고 있습니다. 이 글에서는 AWS 제품 및 서비스로 작업할 때 AI 지원 개발 경험을 향상시키는 Nx Plugin for AWS MCP Server를 소개합니다.

바이브 코딩은 AI 어시스턴트와 협력하여 소프트웨어를 구축하는 관행으로, 많은 조직이 소프트웨어 개발에 접근하는 방식을 변화시켰습니다. 구축하고자 하는 것을 설명하면 AI 어시스턴트가 코드와 테스트를 작성하고, 빌드 명령을 실행하며, 크고 작은 작업을 완료하기 위해 협력적으로 반복하면서 비전을 실현하는 데 도움을 줍니다.

이러한 협업적 접근 방식은 개발 주기를 크게 가속화했습니다. 이전에는 수동으로 작성하는 데 몇 시간이 걸렸을 복잡한 구현을 종종 몇 분 만에 완료할 수 있습니다.

이점에도 불구하고 바이브 코딩에는 흐름을 방해하고 좌절로 이어질 수 있는 함정이 있습니다. AI 도구는 프로젝트 전체에서 일관되지 않은 패턴을 생성할 수 있으며, 이는 나중에 유지보수 문제를 야기할 수 있습니다. 구체적인 지침이 없으면 AI는 숙련된 개발자가 자연스럽게 통합할 중요한 AWS 관련 모범 사례나 보안 고려 사항을 놓칠 수 있습니다.

명확한 프로젝트 구조가 없으면 AI 지원 코드는 무질서해지고 유지보수하기 어려워질 수 있습니다. AI는 이미 확립된 솔루션이 있는 문제에 대해 사용자 정의 구현을 만들어 불필요하게 바퀴를 재발명할 수 있습니다.

이러한 과제는 기술 부채, 보안 취약점, 그리고 특히 단일 프레임워크의 범위 내에서만이 아니라 다양한 상호 연결된 AWS 서비스로 작업할 때 좌절로 이어질 수 있습니다.

Nx Plugin for AWS는 Nx 모노레포 도구를 사용하여 AWS 애플리케이션을 구축하기 위한 구조화된 기반을 제공합니다. 빈 캔버스로 시작하는 대신, 플러그인은 프로젝트 구성을 위한 일관된 프레임워크를 제공합니다.

플러그인은 일반적인 프로젝트 유형을 위한 제너레이터를 통해 일관된 프로젝트 스캐폴딩을 보장하여 코드베이스 전체의 구조적 무결성을 유지합니다. AWS 모범 사례를 따르는 사전 구성된 템플릿을 통합하여 개발자가 일반적인 함정과 보안 문제를 피할 수 있도록 돕습니다. 통합된 도구는 AWS 애플리케이션을 빌드, 테스트 및 배포하기 위한 내장 명령을 제공하고 로컬 개발 서버를 통해 개발 워크플로를 간소화합니다. 또한 복잡한 프로젝트를 위한 Nx의 강력한 종속성 관리를 활용하여 모노레포 관리를 단순화합니다.

이러한 구조를 제공함으로써 Nx Plugin for AWS는 AI 어시스턴트에게 작업할 명확한 구조를 제공합니다. 처음부터 패턴을 발명하는 대신 AI 어시스턴트는 확립된 규칙을 따를 수 있어 더 일관되고 유지보수 가능한 코드베이스로 이어집니다.

Model Context Protocol(MCP)은 AI 어시스턴트가 외부 도구 및 리소스와 상호 작용할 수 있도록 하는 개방형 표준입니다. Nx Plugin for AWS MCP 서버는 Nx Plugin for AWS에 대한 전문 지식으로 AI 어시스턴트의 기능을 확장합니다.

MCP 서버는 AWS 개발에 특화된 모범 사례, 사용 가능한 프로젝트 구조 및 구현 패턴에 대한 컨텍스트 정보를 제공합니다. AI 도구가 워크스페이스를 생성하고 제너레이터를 실행하여 일반적인 프로젝트 유형을 스캐폴딩할 수 있도록 합니다. 이러한 컨텍스트 인식은 AI가 확립된 패턴에 부합하고 일반적인 함정을 피하는 더 많은 정보에 기반한 제안을 하는 데 도움이 됩니다.

모범 사례에 부합하지 않거나 존재하지 않는 기능을 참조할 수 있는 코드를 생성하는 대신, AI 어시스턴트는 MCP 서버를 활용하여 프로젝트의 기반을 마련할 수 있습니다. 그 결과 프로젝트의 핵심 구성 요소에 대한 견고한 기반으로 시작하고 AI를 사용하여 비즈니스 로직을 채울 수 있는 더 결정론적이고 신뢰할 수 있는 개발 경험이 됩니다.

더 많은 구조와 신뢰성을 갖춘 AI 지원 AWS 개발을 탐색하는 데 관심이 있다면 Nx Plugin for AWS MCP Server를 사용해 보세요. 다음 MCP Server 구성으로 선호하는 AI 어시스턴트(Kiro, Kiro CLI, Cline, Claude Code 등)에서 설정할 수 있습니다:

{
"mcpServers": {
"nx-plugin-for-aws": {
"command": "npx",
"args": ["-y", "@aws/nx-plugin-mcp"]
}
}
}

자세한 지침은 AI로 빌드하기 가이드를 참조하세요.

@aws/nx-plugin에 오신 것을 환영합니다

드디어 출시되었습니다! 🚀

Nx Plugin for AWS는 AWS에서 풀스택 애플리케이션을 빌드하고 배포하는 프로세스를 단순화하는 툴킷을 제공하는 Nx 플러그인입니다. 개발자에게 애플리케이션과 IaC 코드 모두에 대한 사전 구성된 템플릿을 제공하여 설정 및 구성에 소요되는 시간을 크게 줄여줍니다. 이 플러그인은 커스터마이징의 유연성을 유지하면서 AWS 서비스 통합의 복잡성을 처리합니다.

사용자는 사용 가능한 제너레이터 목록에서 원하는 컴포넌트를 선택하고, 구성 옵션을 제공하면 @aws/nx-plugin이 필요한 스타터 코드를 생성합니다. 이 툴킷에는 API, 웹사이트, 인프라를 생성할 수 있는 여러 제너레이터가 있으며, 타입 안전 클라이언트를 사용하여 프론트엔드를 백엔드에 통합하는 것(AST 변환을 통한 기존 파일 업데이트 포함!)과 같은 더 정교한 작업도 수행할 수 있습니다.

generator

자세히 알아보려면 플러그인의 주요 컴포넌트를 모두 다루고 사용 방법에 대한 좋은 감을 제공하는 Dungeon Adventure 튜토리얼을 시작해 보세요.

여러분의 피드백을 듣고 싶습니다. 토론을 게시하거나 이슈를 등록하여 여러분의 생각과 다음에 보고 싶은 것을 알려주시기 바랍니다!

사용해 보세요!