콘텐츠로 이동

제너레이터 기여하기

@aws/nx-plugin에 기여할 새로운 제너레이터를 만들어 봅시다. 우리의 목표는 tRPC API를 위한 새로운 프로시저를 생성하는 것입니다.

먼저 플러그인을 클론해 봅시다:

Terminal window
git clone git@github.com:awslabs/nx-plugin-for-aws.git

다음으로 설치 및 빌드를 진행합니다:

Terminal window
cd nx-plugin-for-aws
pnpm i
pnpm nx run-many --target build --all

packages/nx-plugin/src/trpc/procedure에 새로운 제너레이터를 만들어 봅시다.

새로운 제너레이터를 빠르게 스캐폴딩할 수 있도록 제너레이터를 생성하는 제너레이터를 제공합니다! 다음과 같이 이 제너레이터를 실행할 수 있습니다:

Terminal window
pnpm nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adds a procedure to a tRPC API
어떤 파일이 변경될지 확인하기 위해 드라이 런을 수행할 수도 있습니다
Terminal window
pnpm nx g @aws/nx-plugin:ts#nx-generator --pluginProject=@aws/nx-plugin --name=ts#trpc-api#procedure --directory=trpc/procedure --description=Adds a procedure to a tRPC API --dry-run

다음 파일들이 생성된 것을 확인할 수 있습니다:

  • 디렉터리packages/nx-plugin/src/trpc/procedure
    • schema.json 제너레이터의 입력을 정의합니다
    • schema.d.ts 스키마와 일치하는 TypeScript 인터페이스
    • generator.ts Nx가 제너레이터로 실행하는 함수
    • generator.spec.ts 제너레이터를 위한 테스트
  • 디렉터리docs/src/content/docs/guides/
    • trpc-procedure.mdx 제너레이터를 위한 문서
  • packages/nx-plugin/generators.json 제너레이터를 포함하도록 업데이트됨

제너레이터에 필요한 속성을 추가하기 위해 스키마를 업데이트해 봅시다:

{
"$schema": "https://json-schema.org/schema",
"$id": "tRPCProcedure",
"title": "Adds a procedure to a tRPC API",
"type": "object",
"properties": {
"project": {
"type": "string",
"description": "tRPC API project",
"x-prompt": "Select the tRPC API project to add the procedure to",
"x-dropdown": "projects",
"x-priority": "important"
},
"procedure": {
"description": "The name of the new procedure",
"type": "string",
"x-prompt": "What would you like to call your new procedure?",
"x-priority": "important",
},
"type": {
"description": "The type of procedure to generate",
"type": "string",
"x-prompt": "What type of procedure would you like to generate?",
"x-priority": "important",
"default": "query",
"enum": ["query", "mutation"]
}
},
"required": ["project", "procedure"]
}

제너레이터가 이미 packages/nx-plugin/generators.json에 연결되어 있는 것을 확인할 수 있습니다:

...
"generators": {
...
"ts#trpc-api#procedure": {
"factory": "./src/trpc/procedure/generator",
"schema": "./src/trpc/procedure/schema.json",
"description": "Adds a procedure to a tRPC API"
}
},
...

tRPC API에 프로시저를 추가하려면 두 가지 작업을 수행해야 합니다:

  1. 새로운 프로시저를 위한 TypeScript 파일 생성
  2. 라우터에 프로시저 추가

새로운 프로시저를 위한 TypeScript 파일을 생성하기 위해 generateFiles라는 유틸리티를 사용합니다. 이를 사용하면 사용자가 선택한 옵션을 기반으로 변수를 사용하여 제너레이터에서 렌더링할 수 있는 EJS 템플릿을 정의할 수 있습니다.

먼저 packages/nx-plugin/src/trpc/procedure/files/procedures/__procedureNameKebabCase__.ts.template에 템플릿을 정의합니다:

files/procedures/__procedureNameKebabCase__.ts.template
import { publicProcedure } from '../init.js';
import { z } from 'zod';
export const <%- procedureNameCamelCase %> = publicProcedure
.input(z.object({
// TODO: define input
}))
.output(z.object({
// TODO: define output
}))
.<%- procedureType %>(async ({ input, ctx }) => {
// TODO: implement!
return {};
});

템플릿에서 세 가지 변수를 참조했습니다:

  • procedureNameCamelCase
  • procedureNameKebabCase
  • procedureType

따라서 이러한 변수들을 generateFiles에 전달해야 하며, 파일을 생성할 디렉토리, 즉 사용자가 제너레이터의 입력으로 선택한 tRPC 프로젝트의 소스 파일 위치(즉, sourceRoot)도 전달해야 합니다. 이는 프로젝트 구성에서 추출할 수 있습니다.

제너레이터를 업데이트해 봅시다:

procedure/generator.ts
import {
generateFiles,
joinPathFragments,
readProjectConfiguration,
type Tree,
} from '@nx/devkit';
import type { TrpcProcedureSchema } from './schema.js';
import { formatFilesInSubtree } from '../../utils/format';
import camelCase from 'lodash.camelcase';
import kebabCase from 'lodash.kebabcase';
export const trpcProcedureGenerator = async (
tree: Tree,
options: TrpcProcedureSchema,
) => {
const projectConfig = readProjectConfiguration(tree, options.project);
const procedureNameCamelCase = camelCase(options.procedure);
const procedureNameKebabCase = kebabCase(options.procedure);
generateFiles(
tree,
joinPathFragments(import.meta.dirname, 'files'),
projectConfig.sourceRoot,
{
procedureNameCamelCase,
procedureNameKebabCase,
procedureType: options.type,
},
);
await formatFilesInSubtree(tree);
};
export default trpcProcedureGenerator;

다음으로 제너레이터가 새로운 프로시저를 라우터에 연결하도록 하려고 합니다. 이는 사용자의 소스 코드를 읽고 업데이트하는 것을 의미합니다!

소스 코드를 선언적으로 검색하고 변환하기 위해 GritQL을 사용합니다. addDestructuredImport 헬퍼는 명명된 import를 추가하고, applyGritQL은 GritQL 패턴을 적용하여 라우터의 객체 리터럴에 프로시저를 추가합니다.

procedure/generator.ts
import {
generateFiles,
joinPathFragments,
readProjectConfiguration,
type Tree,
} from '@nx/devkit';
import type { TrpcProcedureSchema } from './schema.js';
import { formatFilesInSubtree } from '../../utils/format';
import camelCase from 'lodash.camelcase';
import kebabCase from 'lodash.kebabcase';
import { addDestructuredImport, applyGritQL } from '../../utils/ast';
export const trpcProcedureGenerator = async (
tree: Tree,
options: TrpcProcedureSchema,
) => {
const projectConfig = readProjectConfiguration(tree, options.project);
const procedureNameCamelCase = camelCase(options.procedure);
const procedureNameKebabCase = kebabCase(options.procedure);
generateFiles(
tree,
joinPathFragments(import.meta.dirname, 'files'),
projectConfig.sourceRoot,
{
procedureNameCamelCase,
procedureNameKebabCase,
procedureType: options.type,
},
);
const routerPath = joinPathFragments(projectConfig.sourceRoot, 'router.ts');
await addDestructuredImport(
tree,
routerPath,
[procedureNameCamelCase],
`./procedures/${procedureNameKebabCase}.js`,
);
await applyGritQL(
tree,
routerPath,
`\`router({ $props })\` => \`router({ $props, ${procedureNameCamelCase} })\` where { $props <: not contains \`${procedureNameCamelCase}\` }`,
);
await formatFilesInSubtree(tree);
};
export default trpcProcedureGenerator;

이제 제너레이터를 구현했으므로 던전 어드벤처 프로젝트에서 테스트할 수 있도록 컴파일해 봅시다.

Terminal window
pnpm nx compile @aws/nx-plugin

제너레이터를 테스트하기 위해 로컬 Nx Plugin for AWS를 기존 코드베이스에 연결합니다.

tRPC API가 있는 테스트 프로젝트 생성

섹션 제목: “tRPC API가 있는 테스트 프로젝트 생성”

별도의 디렉토리에서 새로운 테스트 워크스페이스를 생성합니다:

Terminal window
pnpm create @aws/nx-workspace trpc-generator-test

다음으로 프로시저를 추가할 tRPC API를 생성해 봅시다:

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

코드베이스에서 로컬 @aws/nx-plugin을 연결해 봅시다:

Terminal window
cd path/to/trpc-generator-test
pnpm link path/to/nx-plugin-for-aws/dist/packages/nx-plugin

새로운 제너레이터를 시도해 봅시다:

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

성공하면 새로운 프로시저가 생성되고 router.ts의 라우터에 프로시저가 추가되어야 합니다.

여기까지 도달했고 Nx 제너레이터를 실험할 시간이 더 있다면, 프로시저 제너레이터에 추가할 기능에 대한 몇 가지 제안 사항이 있습니다:

다음을 통해 중첩된 라우터를 지원하도록 제너레이터를 업데이트해 보세요:

  • procedure 입력에 대해 점 표기법 허용 (예: games.query)
  • 역순 점 표기법을 기반으로 한 이름으로 프로시저 생성 (예: queryGames)
  • 적절한 중첩 라우터 추가 (또는 이미 존재하는 경우 업데이트!)

제너레이터는 tRPC API가 아닌 project를 사용자가 선택하는 것과 같은 잠재적인 문제를 방어해야 합니다. 이에 대한 예제는 connection 제너레이터를 참조하세요.

제너레이터를 위한 단위 테스트를 작성하세요. 이들은 구현하기가 상당히 간단하며, 대부분 다음과 같은 일반적인 흐름을 따릅니다:

  1. createTreeUsingTsSolutionSetup()을 사용하여 빈 워크스페이스 트리 생성
  2. 트리에 이미 존재해야 하는 파일 추가 (예: tRPC 백엔드를 위한 project.jsonsrc/router.ts)
  3. 테스트 중인 제너레이터 실행
  4. 예상되는 변경 사항이 트리에 적용되었는지 검증

새로운 워크스페이스에서 제너레이터를 실행하고 모든 것이 빌드되는지 확인하는 “스모크 테스트” 모음이 있습니다. 최소한 새로운 제너레이터는 스모크 테스트에서 실행되도록 제너레이터 매트릭스에 모두 추가되어야 합니다:

  • e2e/src/smoke-tests/generator-matrix.ts — 사용자가 하는 것처럼 CLI를 통해 각 제너레이터를 한 번에 하나씩 실행합니다.
  • packages/nx-plugin/src/internal/test-matrix/generator.ts — 버전 간 마이그레이션을 테스트하기 위해 다른 모든 제너레이터를 구성하는 숨겨진 제너레이터입니다.

제너레이터 매트릭스는 제너레이터를 실행하고 워크스페이스를 빌드하기만 하며 인프라를 인스턴스화하지는 않습니다. 인프라를 배포하는 제너레이터의 경우 배포 e2e 테스트(e2e/src/smoke-tests/cdk-deploy.spec.tsterraform-deploy.spec.ts)를 확장하여 실제로 리소스를 배포(cdk deploy / terraform apply를 통해)한 다음, 배포된 리소스를 호출하고 예상대로 동작하는지 확인하는 어설션을 deploy-invocations.ts에 추가하는 것을 고려하세요.

제너레이터가 로컬 개발 서버를 제공하는 경우 dev 타겟을 시작하고 실행 중인 서버를 실행하는 로컬 개발 e2e 테스트(e2e/src/smoke-tests/local-dev.spec.ts)에 추가하는 것을 고려하세요.

기여를 가속화하기 위한 일반 지침

섹션 제목: “기여를 가속화하기 위한 일반 지침”

이 섹션에는 Nx Plugin for AWS 작업 시 도움이 될 수 있는 몇 가지 일반 지침이 포함되어 있습니다.

실제 프로젝트에서 역방향으로 작업

섹션 제목: “실제 프로젝트에서 역방향으로 작업”

새로운 제너레이터를 빌드하거나 기존 제너레이터에 기능/수정 사항을 추가하는 유용한 방법은 _먼저 실제로 빌드하는 것_입니다. 이렇게 하면 아이디어를 검증하고 필요한 기능을 달성하기 위해 빠르게 반복할 수 있습니다. 원하는 결과에 도달한 후 제너레이터를 업데이트할 수 있습니다.

실제로 이 프로세스는 다음과 같을 수 있습니다:

  1. 새로운 워크스페이스 생성

    Terminal window
    pnpm create @aws/nx-workspace my-project
  2. 새로운 제너레이터/기능/수정의 전제 조건이 될 수 있는 제너레이터 실행

  3. 변경 사항 커밋 (git commit)

  4. 원하는 변경 사항을 만들고 필요에 따라 테스트

  5. 변경 사항의 git diff를 사용하여 Nx Plugin for AWS에 어떤 변경 사항을 적용해야 하는지 파악

  6. 제너레이터가 필요한 변경 사항을 제공하는지 확인하기 위해 최종 엔드 투 엔드 테스트 수행 (@aws/nx-plugin 연결)