AI 던전 게임
모듈 1: 모노레포 설정
섹션 제목: “모듈 1: 모노레포 설정”먼저 새로운 모노레포를 생성합니다. 원하는 디렉토리에서 다음 명령어를 실행하세요:
npx create-nx-workspace@21.6.3 dungeon-adventure --pm=pnpm --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgents
npx create-nx-workspace@21.6.3 dungeon-adventure --pm=yarn --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgents
npx create-nx-workspace@21.6.3 dungeon-adventure --pm=npm --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgents
npx create-nx-workspace@21.6.3 dungeon-adventure --pm=bun --preset=@aws/nx-plugin --iacProvider=CDK --ci=skip --aiAgents
이 명령은 dungeon-adventure
디렉토리 내에 NX 모노레포를 설정하며, 이후 vscode에서 열 수 있습니다. 결과는 다음과 같습니다:
디렉터리.nx/
- …
디렉터리.vscode/
- …
디렉터리node_modules/
- …
디렉터리packages/ 서브 프로젝트가 위치할 곳
- …
- .gitignore
- .npmrc
- .prettierignore
- .prettierrc
- nx.json Nx CLI 및 모노레포 기본값 구성
- package.json 모든 노드 의존성 정의
- pnpm-lock.yaml 또는 bun.lock, yarn.lock, package-lock.json (패키지 매니저에 따라 다름)
- pnpm-workspace.yaml (pnpm 사용 시)
- README.md
- tsconfig.base.json 모든 노드 기반 서브 프로젝트가 상속함
- tsconfig.json
- aws-nx-plugin.config.mts AWS용 Nx 플러그인 구성
이제 @aws/nx-plugin
을 사용하여 다양한 서브 프로젝트를 생성할 준비가 되었습니다.
게임 API
섹션 제목: “게임 API”먼저 Game API를 생성합니다. 다음 단계에 따라 GameApi
라는 tRPC API를 생성하세요:
- 설치 Nx Console VSCode Plugin 아직 설치하지 않았다면
- VSCode에서 Nx 콘솔 열기
- 클릭
Generate (UI)
"Common Nx Commands" 섹션에서 - 검색
@aws/nx-plugin - ts#trpc-api
- 필수 매개변수 입력
- name: GameApi
- 클릭
Generate
pnpm nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive
yarn nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive
npx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive
bunx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive
어떤 파일이 변경될지 확인하기 위해 드라이 런을 수행할 수도 있습니다
pnpm nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-run
yarn nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-run
bunx nx g @aws/nx-plugin:ts#trpc-api --name=GameApi --no-interactive --dry-run
파일 트리에 새로운 파일들이 생성된 것을 확인할 수 있습니다.
ts#trpc-api로 생성된 파일
ts#trpc-api
제너레이터로 생성된 모든 파일 목록입니다. 파일 트리에서 강조된 주요 파일들을 살펴보겠습니다:
디렉터리packages/
디렉터리common/
디렉터리constructs/
디렉터리src/
디렉터리app/ 애플리케이션 특화 CDK 컨스트럭트
디렉터리apis/
- game-api.ts tRPC API를 생성하는 CDK 컨스트럭트
- index.ts
- …
- index.ts
디렉터리core/ 일반적 CDK 컨스트럭트
디렉터리api/
- rest-api.ts API Gateway Rest API 기본 컨스트럭트
- trpc-utils.ts trpc API CDK 컨스트럭트 유틸리티
- utils.ts API 컨스트럭트 유틸리티
- index.ts
- runtime-config.ts
- index.ts
- project.json
- …
디렉터리types/ 공유 타입
디렉터리src/
- index.ts
- runtime-config.ts CDK와 웹사이트 모두에서 사용되는 인터페이스 정의
- project.json
- …
디렉터리game-api/ tRPC API
디렉터리src/
디렉터리client/ 머신 간 통신용 클라이언트
- index.ts
- sigv4.ts
디렉터리middleware/ Powertools 계측
- error.ts
- index.ts
- logger.ts
- metrics.ts
- tracer.ts
디렉터리schema/ API 입력/출력 정의
- echo.ts
디렉터리procedures/ API 프로시저/라우트 구현
- echo.ts
- index.ts
- init.ts 컨텍스트 및 미들웨어 설정
- local-server.ts 로컬 tRPC 서버 실행용
- router.ts 모든 프로시저를 정의하는 람다 핸들러 진입점
- project.json
- …
- eslint.config.mjs
- vitest.workspace.ts
주요 파일 몇 가지를 살펴보겠습니다:
import { awsLambdaRequestHandler, CreateAWSLambdaContextOptions,} from '@trpc/server/adapters/aws-lambda';import { echo } from './procedures/echo.js';import { t } from './init.js';import { APIGatewayProxyEvent } from 'aws-lambda';
export const router = t.router;
export const appRouter = router({ echo,});
export const handler = awsLambdaRequestHandler({ router: appRouter, createContext: ( ctx: CreateAWSLambdaContextOptions<APIGatewayProxyEvent>, ) => ctx, responseMeta: () => ({ headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': '*', }, }),});
export type AppRouter = typeof appRouter;
라우터는 tRPC API의 진입점이며 모든 API 메서드를 선언하는 곳입니다. 위 코드에서 echo
메서드가 구현된 ./procedures/echo.ts
파일을 확인할 수 있습니다.
import { publicProcedure } from '../init.js';import { EchoInputSchema, EchoOutputSchema,} from '../schema/echo.js';
export const echo = publicProcedure .input(EchoInputSchema) .output(EchoOutputSchema) .query((opts) => ({ result: opts.input.message }));
이 파일은 echo
메서드의 구현체로, 입력 및 출력 데이터 구조를 강력하게 타입화합니다.
import { z } from 'zod';
export const EchoInputSchema = z.object({ message: z.string(),});
export type IEchoInput = z.TypeOf<typeof EchoInputSchema>;
export const EchoOutputSchema = z.object({ result: z.string(),});
export type IEchoOutput = z.TypeOf<typeof EchoOutputSchema>;
모든 tRPC 스키마 정의는 Zod를 사용하여 정의되며, z.TypeOf
구문을 통해 TypeScript 타입으로 내보내집니다.
import { Construct } from 'constructs';import * as url from 'url';import { Code, Runtime, Function, FunctionProps, Tracing,} from 'aws-cdk-lib/aws-lambda';import { AuthorizationType, Cors, LambdaIntegration,} from 'aws-cdk-lib/aws-apigateway';import { Duration, Stack } from 'aws-cdk-lib';import { PolicyDocument, PolicyStatement, Effect, AccountPrincipal, AnyPrincipal,} from 'aws-cdk-lib/aws-iam';import { IntegrationBuilder, RestApiIntegration,} from '../../core/api/utils.js';import { RestApi } from '../../core/api/rest-api.js';import { Procedures, routerToOperations } from '../../core/api/trpc-utils.js';import { AppRouter, appRouter } from ':dungeon-adventure/game-api';
// API 작업 이름을 위한 문자열 유니온 타입type Operations = Procedures<AppRouter>;
/** * GameApi 컨스트럭트 생성 속성 * * @template TIntegrations - 작업 이름과 통합 매핑 */export interface GameApiProps< TIntegrations extends Record<Operations, RestApiIntegration>,> { /** * 작업 이름과 API Gateway 통합 매핑 */ integrations: TIntegrations;}
/** * GameApi 전용 AWS API Gateway REST API 생성 및 구성 CDK 컨스트럭트 * @template TIntegrations - 작업 이름과 통합 매핑 */export class GameApi< TIntegrations extends Record<Operations, RestApiIntegration>,> extends RestApi<Operations, TIntegrations> { /** * 모든 작업에 대한 기본 통합 생성 (각 작업을 개별 람다 함수로 구현) * * @param scope - CDK 컨스트럭트 스코프 * @returns 기본 람다 통합이 포함된 IntegrationBuilder */ public static defaultIntegrations = (scope: Construct) => { return IntegrationBuilder.rest({ operations: routerToOperations(appRouter), defaultIntegrationOptions: { runtime: Runtime.NODEJS_LATEST, handler: 'index.handler', code: Code.fromAsset( url.fileURLToPath( new URL( '../../../../../../dist/packages/game-api/bundle', import.meta.url, ), ), ), timeout: Duration.seconds(30), tracing: Tracing.ACTIVE, environment: { AWS_CONNECTION_REUSE_ENABLED: '1', }, } satisfies FunctionProps, buildDefaultIntegration: (op, props: FunctionProps) => { const handler = new Function(scope, `GameApi${op}Handler`, props); return { handler, integration: new LambdaIntegration(handler) }; }, }); };
constructor( scope: Construct, id: string, props: GameApiProps<TIntegrations>, ) { super(scope, id, { apiName: 'GameApi', defaultMethodOptions: { authorizationType: AuthorizationType.IAM, }, defaultCorsPreflightOptions: { allowOrigins: Cors.ALL_ORIGINS, allowMethods: Cors.ALL_METHODS, }, policy: new PolicyDocument({ statements: [ // 배포 계정의 AWS 자격 증명으로 API 호출 허용 new PolicyStatement({ effect: Effect.ALLOW, principals: [new AccountPrincipal(Stack.of(scope).account)], actions: ['execute-api:Invoke'], resources: ['execute-api:/*'], }), // 브라우저의 사전 요청 허용을 위해 OPTIONS 개방 new PolicyStatement({ effect: Effect.ALLOW, principals: [new AnyPrincipal()], actions: ['execute-api:Invoke'], resources: ['execute-api:/*/OPTIONS/*'], }), ], }), operations: routerToOperations(appRouter), ...props, }); }}
이 CDK 컨스트럭트는 GameApi를 정의합니다. defaultIntegrations
메서드는 tRPC API의 각 프로시저에 대해 람다 함수를 자동 생성하며, 빌드 타임에 번들링된 API 구현을 가리킵니다. 이는 cdk synth
시점에 번들링이 발생하지 않음을 의미합니다(NodeJsFunction와 달리).
스토리 API
섹션 제목: “스토리 API”이제 Fast API인 StoryApi
를 생성합니다. 다음 단계를 따르세요:
- 설치 Nx Console VSCode Plugin 아직 설치하지 않았다면
- VSCode에서 Nx 콘솔 열기
- 클릭
Generate (UI)
"Common Nx Commands" 섹션에서 - 검색
@aws/nx-plugin - py#fast-api
- 필수 매개변수 입력
- name: StoryApi
- moduleName: story_api
- 클릭
Generate
pnpm nx g @aws/nx-plugin:py#fast-api --name=StoryApi --moduleName=story_api --no-interactive
yarn nx g @aws/nx-plugin:py#fast-api --name=StoryApi --moduleName=story_api --no-interactive
npx nx g @aws/nx-plugin:py#fast-api --name=StoryApi --moduleName=story_api --no-interactive
bunx nx g @aws/nx-plugin:py#fast-api --name=StoryApi --moduleName=story_api --no-interactive
어떤 파일이 변경될지 확인하기 위해 드라이 런을 수행할 수도 있습니다
pnpm nx g @aws/nx-plugin:py#fast-api --name=StoryApi --moduleName=story_api --no-interactive --dry-run
yarn nx g @aws/nx-plugin:py#fast-api --name=StoryApi --moduleName=story_api --no-interactive --dry-run
npx nx g @aws/nx-plugin:py#fast-api --name=StoryApi --moduleName=story_api --no-interactive --dry-run
bunx nx g @aws/nx-plugin:py#fast-api --name=StoryApi --moduleName=story_api --no-interactive --dry-run
파일 트리에 새로운 파일들이 생성된 것을 확인할 수 있습니다.
py#fast-api로 생성된 파일
py#fast-api
제너레이터로 생성된 주요 파일 목록입니다:
디렉터리.venv/ 모노레포 단일 가상 환경
- …
디렉터리packages/
디렉터리common/
디렉터리constructs/
디렉터리src/
디렉터리app/ 애플리케이션 특화 CDK 컨스트럭트
디렉터리apis/
- story-api.ts Fast API 생성용 CDK 컨스트럭트
- index.ts 새 story-api 내보내기로 업데이트
- project.json story_api 빌드 의존성 추가
디렉터리types/ 공유 타입
디렉터리src/
- runtime-config.ts StoryApi 추가로 업데이트
디렉터리story_api/
디렉터리story_api/ Python 모듈
- init.py Powertools, FastAPI 및 미들웨어 설정
- main.py 모든 라우트를 포함하는 람다 진입점
디렉터리tests/
- …
- .python-version
- project.json
- pyproject.toml
- project.json
- .python-version 고정된 uv Python 버전
- pyproject.toml
- uv.lock
import { Construct } from 'constructs';import * as url from 'url';import { Code, Runtime, Function, FunctionProps, Tracing,} from 'aws-cdk-lib/aws-lambda';import { AuthorizationType, Cors, LambdaIntegration,} from 'aws-cdk-lib/aws-apigateway';import { Duration, Stack } from 'aws-cdk-lib';import { PolicyDocument, PolicyStatement, Effect, AccountPrincipal, AnyPrincipal,} from 'aws-cdk-lib/aws-iam';import { IntegrationBuilder, RestApiIntegration,} from '../../core/api/utils.js';import { RestApi } from '../../core/api/rest-api.js';import { OPERATION_DETAILS, Operations,} from '../../generated/story-api/metadata.gen.js';
/** * StoryApi 컨스트럭트 생성 속성 * * @template TIntegrations - 작업 이름과 통합 매핑 */export interface StoryApiProps< TIntegrations extends Record<Operations, RestApiIntegration>,> { /** * 작업 이름과 API Gateway 통합 매핑 */ integrations: TIntegrations;}
/** * StoryApi 전용 AWS API Gateway REST API 생성 및 구성 CDK 컨스트럭트 * @template TIntegrations - 작업 이름과 통합 매핑 */export class StoryApi< TIntegrations extends Record<Operations, RestApiIntegration>,> extends RestApi<Operations, TIntegrations> { /** * 모든 작업에 대한 기본 통합 생성 (각 작업을 개별 람다 함수로 구현) * * @param scope - CDK 컨스트럭트 스코프 * @returns 기본 람다 통합이 포함된 IntegrationBuilder */ public static defaultIntegrations = (scope: Construct) => { return IntegrationBuilder.rest({ operations: OPERATION_DETAILS, defaultIntegrationOptions: { runtime: Runtime.PYTHON_3_12, handler: 'story_api.main.handler', code: Code.fromAsset( url.fileURLToPath( new URL( '../../../../../../dist/packages/story_api/bundle-x86', import.meta.url, ), ), ), timeout: Duration.seconds(30), tracing: Tracing.ACTIVE, environment: { AWS_CONNECTION_REUSE_ENABLED: '1', }, } satisfies FunctionProps, buildDefaultIntegration: (op, props: FunctionProps) => { const handler = new Function(scope, `StoryApi${op}Handler`, props); return { handler, integration: new LambdaIntegration(handler) }; }, }); };
constructor( scope: Construct, id: string, props: StoryApiProps<TIntegrations>, ) { super(scope, id, { apiName: 'StoryApi', defaultMethodOptions: { authorizationType: AuthorizationType.IAM, }, defaultCorsPreflightOptions: { allowOrigins: Cors.ALL_ORIGINS, allowMethods: Cors.ALL_METHODS, }, policy: new PolicyDocument({ statements: [ // 배포 계정의 AWS 자격 증명으로 API 호출 허용 new PolicyStatement({ effect: Effect.ALLOW, principals: [new AccountPrincipal(Stack.of(scope).account)], actions: ['execute-api:Invoke'], resources: ['execute-api:/*'], }), // 브라우저의 사전 요청 허용을 위해 OPTIONS 개방 new PolicyStatement({ effect: Effect.ALLOW, principals: [new AnyPrincipal()], actions: ['execute-api:Invoke'], resources: ['execute-api:/*/OPTIONS/*'], }), ], }), operations: OPERATION_DETAILS, ...props, }); }}
이 CDK 컨스트럭트는 StoryApi를 정의합니다. defaultIntegrations
메서드는 FastAPI의 각 작업에 대해 람다 함수를 자동 생성하며, 빌드 타임에 번들링된 API 구현을 가리킵니다.
from .init import app, lambda_handler, tracer
handler = lambda_handler
@app.get("/")@tracer.capture_methoddef read_root(): return {"Hello": "World"}
이곳에 모든 API 메서드가 정의됩니다. GET /
라우트에 매핑된 read_root
메서드가 있습니다. 타입 안전성을 위해 Pydantic을 사용할 수 있습니다.
게임 UI: 웹사이트
섹션 제목: “게임 UI: 웹사이트”이제 게임과 상호작용할 수 있는 UI를 생성합니다. 다음 단계에 따라 GameUI
웹사이트를 생성하세요:
- 설치 Nx Console VSCode Plugin 아직 설치하지 않았다면
- VSCode에서 Nx 콘솔 열기
- 클릭
Generate (UI)
"Common Nx Commands" 섹션에서 - 검색
@aws/nx-plugin - ts#react-website
- 필수 매개변수 입력
- name: GameUI
- 클릭
Generate
pnpm nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive
yarn nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive
npx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive
bunx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive
어떤 파일이 변경될지 확인하기 위해 드라이 런을 수행할 수도 있습니다
pnpm nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-run
yarn nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-run
bunx nx g @aws/nx-plugin:ts#react-website --name=GameUI --no-interactive --dry-run
파일 트리에 새로운 파일들이 생성된 것을 확인할 수 있습니다.
ts#react-website로 생성된 파일
ts#react-website
제너레이터로 생성된 주요 파일 목록입니다:
디렉터리packages/
디렉터리common/
디렉터리constructs/
디렉터리src/
디렉터리app/ 애플리케이션 특화 CDK 컨스트럭트
디렉터리static-websites/
- game-ui.ts Game UI 생성용 CDK 컨스트럭트
디렉터리core/
- static-website.ts 일반적 정적 웹사이트 컨스트럭트
디렉터리game-ui/
디렉터리public/
- …
디렉터리src/
디렉터리components/
디렉터리AppLayout/
- index.ts 전체 페이지 레이아웃: 헤더, 푸터, 사이드바 등
- navitems.ts 사이드바 네비게이션 항목
디렉터리hooks/
- useAppLayout.tsx 알림, 페이지 스타일 등을 동적으로 설정
디렉터리routes/ @tanstack/react-router 파일 기반 라우트
- index.tsx 루트 ’/’ 페이지가 ‘/welcome’으로 리다이렉트
- __root.tsx 모든 페이지의 기본 컴포넌트
디렉터리welcome/
- index.tsx
- config.ts
- main.tsx React 진입점
- routeTree.gen.ts @tanstack/react-router에 의해 자동 업데이트
- styles.css
- index.html
- project.json
- vite.config.ts
- …
import * as url from 'url';import { Construct } from 'constructs';import { StaticWebsite } from '../../core/index.js';
export class GameUI extends StaticWebsite { constructor(scope: Construct, id: string) { super(scope, id, { websiteName: 'GameUI', websiteFilePath: url.fileURLToPath( new URL( '../../../../../../dist/packages/game-ui/bundle', import.meta.url, ), ), }); }}
이 CDK 컨스트럭트는 GameUI를 정의합니다. Vite 기반 UI의 번들 경로가 이미 구성되어 있습니다. 이는 build
타임에 번들링이 game-ui
프로젝트의 빌드 대상 내에서 수행됨을 의미합니다.
import React from 'react';import { createRoot } from 'react-dom/client';import { I18nProvider } from '@cloudscape-design/components/i18n';import messages from '@cloudscape-design/components/i18n/messages/all.en';import { RouterProvider, createRouter } from '@tanstack/react-router';import { routeTree } from './routeTree.gen';
import '@cloudscape-design/global-styles/index.css';
const router = createRouter({ routeTree });
// 타입 안전성을 위해 라우터 인스턴스 등록declare module '@tanstack/react-router' { interface Register { router: typeof router; }}
const root = document.getElementById('root');root && createRoot(root).render( <React.StrictMode> <I18nProvider locale="en" messages={[messages]}> <RouterProvider router={router} /> </I18nProvider> </React.StrictMode>, );
이것은 React가 마운트되는 진입점입니다. 파일 기반 라우팅
구성을 사용합니다. 개발 서버가 실행 중인 동안 routes
폴더 내에 파일을 생성하면 @tanstack/react-router
가 보일러플레이트를 자동 생성하고 routeTree.gen.ts
파일을 업데이트합니다. 자세한 내용은 @tanstack/react-router
문서를 참조하세요.
import { ContentLayout, Header, SpaceBetween, Container,} from '@cloudscape-design/components';import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/welcome/')({ component: RouteComponent,});
function RouteComponent() { return ( <ContentLayout header={<Header>Welcome</Header>}> <SpaceBetween size="l"> <Container>Welcome to your new Cloudscape website!</Container> </SpaceBetween> </ContentLayout> );}
/welcome
라우트로 이동할 때 렌더링될 컴포넌트입니다. @tanstack/react-router
가 라우트를 관리합니다.
게임 UI: 인증
섹션 제목: “게임 UI: 인증”이제 Amazon Cognito를 통해 인증이 필요한 Game UI를 구성합니다. 다음 단계를 따르세요:
- 설치 Nx Console VSCode Plugin 아직 설치하지 않았다면
- VSCode에서 Nx 콘솔 열기
- 클릭
Generate (UI)
"Common Nx Commands" 섹션에서 - 검색
@aws/nx-plugin - ts#react-website#auth
- 필수 매개변수 입력
- cognitoDomain: game-ui
- project: @dungeon-adventure/game-ui
- allowSignup: true
- 클릭
Generate
pnpm nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive
yarn nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive
npx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive
bunx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive
어떤 파일이 변경될지 확인하기 위해 드라이 런을 수행할 수도 있습니다
pnpm nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-run
yarn nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-run
bunx nx g @aws/nx-plugin:ts#react-website#auth --cognitoDomain=game-ui --project=@dungeon-adventure/game-ui --allowSignup=true --no-interactive --dry-run
파일 트리에 새 파일이 생성되거나 변경된 것을 확인할 수 있습니다.
ts#react-website#auth로 생성/변경된 파일
ts#react-website#auth
제너레이터로 생성/변경된 주요 파일 목록입니다:
디렉터리packages/
디렉터리common/
디렉터리constructs/
디렉터리src/
디렉터리core/
- user-identity.ts 사용자/ID 풀 생성 CDK 컨스트럭트
디렉터리types/
디렉터리src/
- runtime-config.ts cognitoProps 추가로 업데이트
디렉터리game-ui/
디렉터리src/
디렉터리components/
디렉터리AppLayout/
- index.tsx 로그인 사용자/로그아웃 헤더 추가
디렉터리CognitoAuth/
- index.ts Cognito 로그인 관리
디렉터리RuntimeConfig/
- index.tsx
runtime-config.json
가져와 컨텍스트로 제공
- index.tsx
디렉터리hooks/
- useRuntimeConfig.tsx
- main.tsx Cognito 추가로 업데이트
import CognitoAuth from './components/CognitoAuth';import RuntimeConfigProvider from './components/RuntimeConfig';import React from 'react';import { createRoot } from 'react-dom/client';import { I18nProvider } from '@cloudscape-design/components/i18n';import messages from '@cloudscape-design/components/i18n/messages/all.en';import { RouterProvider, createRouter } from '@tanstack/react-router';import { routeTree } from './routeTree.gen';import '@cloudscape-design/global-styles/index.css';const router = createRouter({ routeTree });// 타입 안전성을 위해 라우터 인스턴스 등록declare module '@tanstack/react-router' { interface Register { router: typeof router; }}const root = document.getElementById('root');root && createRoot(root).render( <React.StrictMode> <I18nProvider locale="en" messages={[messages]}> <RuntimeConfigProvider> <CognitoAuth> <RouterProvider router={router} /> </CognitoAuth> </RuntimeConfigProvider> </I18nProvider> </React.StrictMode>, );
RuntimeConfigProvider
와 CognitoAuth
컴포넌트가 AST 변환을 통해 main.tsx
에 추가되었습니다. 이는 CognitoAuth
컴포넌트가 runtime-config.json
을 가져와 올바른 백엔드 호출을 할 수 있도록 합니다.
게임 UI: Story API 연결
섹션 제목: “게임 UI: Story API 연결”이전에 생성한 Story API에 Game UI를 연결합니다:
- 설치 Nx Console VSCode Plugin 아직 설치하지 않았다면
- VSCode에서 Nx 콘솔 열기
- 클릭
Generate (UI)
"Common Nx Commands" 섹션에서 - 검색
@aws/nx-plugin - api-connection
- 필수 매개변수 입력
- sourceProject: @dungeon-adventure/game-ui
- targetProject: dungeon_adventure.story_api
- 클릭
Generate
pnpm nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=dungeon_adventure.story_api --no-interactive
yarn nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=dungeon_adventure.story_api --no-interactive
npx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=dungeon_adventure.story_api --no-interactive
bunx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=dungeon_adventure.story_api --no-interactive
어떤 파일이 변경될지 확인하기 위해 드라이 런을 수행할 수도 있습니다
pnpm nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=dungeon_adventure.story_api --no-interactive --dry-run
yarn nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=dungeon_adventure.story_api --no-interactive --dry-run
npx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=dungeon_adventure.story_api --no-interactive --dry-run
bunx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=dungeon_adventure.story_api --no-interactive --dry-run
파일 트리에 새 파일이 생성되거나 변경된 것을 확인할 수 있습니다.
UI -> FastAPI api-connection으로 생성/변경된 파일
api-connection
제너레이터로 생성/변경된 주요 파일 목록입니다:
디렉터리packages/
디렉터리game-ui/
디렉터리src/
디렉터리hooks/
- useSigV4.tsx StoryApi 요청 서명용
- useStoryApiClient.tsx StoryApi 클라이언트 생성 훅
- useStoryApi.tsx TanStack Query를 사용한 StoryApi 상호작용 훅
디렉터리components/
- QueryClientProvider.tsx TanStack Query 클라이언트 제공자
- StoryApiProvider.tsx StoryApi TanStack Query 훅 제공자
- main.tsx QueryClientProvider 및 StoryApiProvider 추가
- .gitignore 생성된 클라이언트 파일 무시
- project.json OpenAPI 훅 생성 대상 추가
- …
디렉터리story_api/
디렉터리scripts/
- generate_open_api.py
- project.json openapi.json 파일 생성으로 업데이트
import { StoryApi } from '../generated/story-api/client.gen';import { useSigV4 } from './useSigV4';import { useRuntimeConfig } from './useRuntimeConfig';import { useMemo } from 'react';
export const useStoryApi = (): StoryApi => { const runtimeConfig = useRuntimeConfig(); const apiUrl = runtimeConfig.apis.StoryApi; const sigv4Client = useSigV4(); return useMemo( () => new StoryApi({ url: apiUrl, fetch: sigv4Client, }), [apiUrl, sigv4Client], );};
이 훅은 StoryApi
에 인증된 API 요청을 하는 데 사용됩니다. 빌드 시 생성된 StoryApi
를 사용하므로 코드를 빌드하기 전까지 IDE에서 오류가 표시됩니다. 자세한 내용은 React to FastAPI 가이드를 참조하세요.
import { createContext, FC, PropsWithChildren, useMemo } from 'react';import { useStoryApiClient } from '../hooks/useStoryApiClient';import { StoryApiOptionsProxy } from '../generated/story-api/options-proxy.gen';
export const StoryApiContext = createContext<StoryApiOptionsProxy | undefined>( undefined,);
export const StoryApiProvider: FC<PropsWithChildren> = ({ children }) => { const client = useStoryApiClient(); const optionsProxy = useMemo( () => new StoryApiOptionsProxy({ client }), [client], );
return ( <StoryApiContext.Provider value={optionsProxy}> {children} </StoryApiContext.Provider> );};
export default StoryApiProvider;
이 제공자 컴포넌트는 useStoryApiClient
훅을 사용하고 StoryApiOptionsProxy
를 인스턴스화합니다. 이 프록시는 TanStack Query 훅 옵션을 구성하는 데 사용됩니다. useStoryApi
훅을 사용하여 이 옵션 프록시에 접근할 수 있습니다.
게임 UI: Game API 연결
섹션 제목: “게임 UI: Game API 연결”이전에 생성한 Game API에 Game UI를 연결합니다:
- 설치 Nx Console VSCode Plugin 아직 설치하지 않았다면
- VSCode에서 Nx 콘솔 열기
- 클릭
Generate (UI)
"Common Nx Commands" 섹션에서 - 검색
@aws/nx-plugin - api-connection
- 필수 매개변수 입력
- sourceProject: @dungeon-adventure/game-ui
- targetProject: @dungeon-adventure/game-api
- 클릭
Generate
pnpm nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive
yarn nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive
npx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive
bunx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive
어떤 파일이 변경될지 확인하기 위해 드라이 런을 수행할 수도 있습니다
pnpm nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-run
yarn nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-run
npx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-run
bunx nx g @aws/nx-plugin:api-connection --sourceProject=@dungeon-adventure/game-ui --targetProject=@dungeon-adventure/game-api --no-interactive --dry-run
파일 트리에 새 파일이 생성되거나 변경된 것을 확인할 수 있습니다.
UI -> tRPC api-connection으로 생성/변경된 파일
api-connection
제너레이터로 생성/변경된 주요 파일 목록입니다:
디렉터리packages/
디렉터리game-ui/
디렉터리src/
디렉터리components/
- GameApiClientProvider.tsx GameAPI 클라이언트 설정
디렉터리hooks/
- useGameApi.tsx GameApi 호출 훅
- main.tsx trpc 클라이언트 제공자 주입
- package.json
import { GameApiTRCPContext } from '../components/GameApiClientProvider';
export const useGameApi = GameApiTRCPContext.useTRPC;
이 훅은 tRPC의 최신 React Query 통합을 사용하여 추가 추상화 없이 @tanstack/react-query
와 직접 상호작용합니다. 사용 예시는 tRPC 훅 사용 가이드를 참조하세요.
import GameApiClientProvider from './components/GameApiClientProvider';import QueryClientProvider from './components/QueryClientProvider';import CognitoAuth from './components/CognitoAuth';import RuntimeConfigProvider from './components/RuntimeConfig';import React from 'react';import { createRoot } from 'react-dom/client';import { I18nProvider } from '@cloudscape-design/components/i18n';import messages from '@cloudscape-design/components/i18n/messages/all.en';import { RouterProvider, createRouter } from '@tanstack/react-router';import { routeTree } from './routeTree.gen';import '@cloudscape-design/global-styles/index.css';const router = createRouter({ routeTree });// 타입 안전성을 위해 라우터 인스턴스 등록declare module '@tanstack/react-router' { interface Register { router: typeof router; }}const root = document.getElementById('root');root && createRoot(root).render( <React.StrictMode> <I18nProvider locale="en" messages={[messages]}> <RuntimeConfigProvider> <CognitoAuth> <QueryClientProvider> <GameApiClientProvider> <RouterProvider router={router} /> </GameApiClientProvider> </QueryClientProvider> </CognitoAuth> </RuntimeConfigProvider> </I18nProvider> </React.StrictMode>, );
main.tsx
파일이 AST 변환을 통해 tRPC 제공자를 주입하도록 업데이트되었습니다.
게임 UI: 인프라스트럭처
섹션 제목: “게임 UI: 인프라스트럭처”마지막으로 CDK 인프라스트럭처를 생성합니다. 다음 단계를 따르세요:
- 설치 Nx Console VSCode Plugin 아직 설치하지 않았다면
- VSCode에서 Nx 콘솔 열기
- 클릭
Generate (UI)
"Common Nx Commands" 섹션에서 - 검색
@aws/nx-plugin - ts#infra
- 필수 매개변수 입력
- name: infra
- 클릭
Generate
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
yarn nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
npx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
bunx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive
어떤 파일이 변경될지 확인하기 위해 드라이 런을 수행할 수도 있습니다
pnpm nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run
yarn nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run
npx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run
bunx nx g @aws/nx-plugin:ts#infra --name=infra --no-interactive --dry-run
파일 트리에 새 파일이 생성되거나 변경된 것을 확인할 수 있습니다.
ts#infra로 생성/변경된 파일
ts#infra
제너레이터로 생성/변경된 주요 파일 목록입니다:
디렉터리packages/
디렉터리common/
디렉터리constructs/
디렉터리src/
디렉터리core/
- checkov.ts
- index.ts
디렉터리infra
디렉터리src/
디렉터리stages/
- application-stage.ts CDK 스택 정의
디렉터리stacks/
- application-stack.ts CDK 리소스 정의
- index.ts
- main.ts 모든 스테이지 정의 진입점
- cdk.json
- project.json
- …
- package.json
- tsconfig.json 참조 추가
- tsconfig.base.json 별칭 추가
import { ApplicationStage } from './stacks/application-stage.js';import { App } from ':dungeon-adventure/common-constructs';
const app = new App();
// CLI 자격 증명을 사용한 샌드박스 환경 배포new ApplicationStage(app, 'dungeon-adventure-infra-sandbox', { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION, },});
app.synth();
이것은 CDK 애플리케이션의 진입점입니다.
import * as cdk from 'aws-cdk-lib';import { Construct } from 'constructs';
export class ApplicationStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props);
// 스택 정의 코드 }}
이곳에 던전 어드벤처 게임을 구축하기 위한 CDK 컨스트럭트를 인스턴스화합니다.
인프라스트럭처 업데이트
섹션 제목: “인프라스트럭처 업데이트”생성된 컨스트럭트를 인스턴스화하도록 packages/infra/src/stacks/application-stack.ts
를 업데이트합니다:
import { GameApi, GameUI, StoryApi, UserIdentity,} from ':dungeon-adventure/common-constructs';import { Stack, StackProps } from 'aws-cdk-lib';import { Construct } from 'constructs';
export class ApplicationStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props);
// The code that defines your stack goes here const userIdentity = new UserIdentity(this, 'UserIdentity');
const gameApi = new GameApi(this, 'GameApi', { integrations: GameApi.defaultIntegrations(this).build(), }); const storyApi = new StoryApi(this, 'StoryApi', { integrations: StoryApi.defaultIntegrations(this).build(), });
// grant our authenticated role access to invoke our APIs [storyApi, gameApi].forEach((api) => api.grantInvokeAccess(userIdentity.identityPool.authenticatedRole), );
// Ensure this is instantiated last so our runtime-config.json can be automatically configured new GameUI(this, 'GameUI'); }}
두 API에 기본 통합을 제공합니다. 기본적으로 각 API 작업은 개별 람다 함수에 매핑됩니다.
코드 빌드
섹션 제목: “코드 빌드”Nx 명령어
단일 vs 다중 대상
섹션 제목: “단일 vs 다중 대상”run-many
명령은 여러 서브프로젝트에서 대상을 실행합니다(--all
은 모두 대상). 종속성이 올바른 순서로 실행되도록 합니다.
단일 프로젝트 대상에 대해 빌드(또는 기타 작업)를 실행할 수도 있습니다. 예를 들어 @dungeon-adventure/infra
프로젝트를 빌드하려면:
pnpm nx run @dungeon-adventure/infra:build
yarn nx run @dungeon-adventure/infra:build
npx nx run @dungeon-adventure/infra:build
bunx nx run @dungeon-adventure/infra:build
종속성 시각화
섹션 제목: “종속성 시각화”종속성을 시각화하려면:
pnpm nx graph
yarn nx graph
npx nx graph
bunx nx graph

Nx는 캐싱을 사용하여 이전 빌드 아티팩트를 재사용합니다. 캐시 없이 빌드하려면 --skip-nx-cache
인수를 추가하세요:
pnpm nx run @dungeon-adventure/infra:build --skip-nx-cache
yarn nx run @dungeon-adventure/infra:build --skip-nx-cache
npx nx run @dungeon-adventure/infra:build --skip-nx-cache
bunx nx run @dungeon-adventure/infra:build --skip-nx-cache
캐시를 지우려면:
pnpm nx reset
yarn nx reset
npx nx reset
bunx nx reset
pnpm nx run-many --target build --all
yarn nx run-many --target build --all
npx nx run-many --target build --all
bunx nx run-many --target build --all
다음과 같은 프롬프트가 표시됩니다:
NX The workspace is out of sync
[@nx/js:typescript-sync]: Some TypeScript configuration files are missing project references to the projects they depend on or contain outdated project references.
This will result in an error in CI.
? Would you like to sync the identified changes to get your workspace up to date? …Yes, sync the changes and run the tasksNo, run the tasks without syncing the changes
이 메시지는 NX가 자동으로 업데이트할 수 있는 파일을 감지했음을 나타냅니다. Yes, sync the changes and run the tasks 옵션을 선택하여 진행하세요. 누락된 TypeScript 참조가 자동으로 추가되어 IDE 오류가 해결됩니다.
모든 빌드 아티팩트는 모노레포 루트의 dist/
폴더 내에 생성됩니다. 파일 트리를 정리하려면 dist/
폴더를 삭제하면 됩니다.
축하합니다! 던전 어드벤처 게임의 핵심을 구현할 준비가 완료되었습니다. 🎉🎉🎉