콘텐츠로 이동

tRPC API를 관계형 데이터베이스에 연결

connection 생성기는 tRPC API관계형 데이터베이스 프로젝트에 연결하여, 프로시저 컨텍스트에서 Prisma 클라이언트를 사용할 수 있게 하는 타입 안전 tRPC 미들웨어 플러그인을 생성합니다.

이 생성기를 사용하기 전에 다음이 필요합니다:

  1. ts#trpc-api 프로젝트
  2. ts#rdb 프로젝트
  1. 설치 Nx Console VSCode Plugin 아직 설치하지 않았다면
  2. VSCode에서 Nx 콘솔 열기
  3. 클릭 Generate (UI) "Common Nx Commands" 섹션에서
  4. 검색 @aws/nx-plugin - connection
  5. 필수 매개변수 입력
    • 클릭 Generate

    tRPC API 프로젝트를 소스로, 관계형 데이터베이스 프로젝트를 대상으로 선택하세요.

    매개변수 타입 기본값 설명
    sourceProject 필수 string - 소스 프로젝트
    targetProject 필수 string - 연결할 대상 프로젝트
    sourceComponent string - 연결을 시작할 소스 컴포넌트 (컴포넌트 이름, 소스 프로젝트 루트 기준 상대 경로, 또는 generator id). 프로젝트를 소스로 명시적으로 선택하려면 '.'을 사용하세요.
    targetComponent string - 연결할 대상 컴포넌트 (컴포넌트 이름, 대상 프로젝트 루트 기준 상대 경로, 또는 generator id). 프로젝트를 대상으로 명시적으로 선택하려면 '.'을 사용하세요.

    생성기는 tRPC API 프로젝트에 미들웨어 파일을 생성합니다:

    • 디렉터리packages/api/src
      • 디렉터리middleware
        • <db-name>.ts 프로시저 컨텍스트에서 Prisma 클라이언트를 노출하는 tRPC 플러그인

    또한 tRPC API의 serve-local 타겟을 업데이트하여 로컬에서 실행할 때 데이터베이스를 자동으로 시작하도록 합니다.

    생성된 플러그인을 tRPC 라우터에 추가하여 이를 사용하는 모든 프로시저가 데이터베이스에 액세스할 수 있도록 합니다:

    packages/api/src/router.ts
    import { t } from './init.js';
    import { createMyDbPlugin } from './middleware/my-db.js';
    export const authenticatedProcedure = t.procedure
    .concat(createMyDbPlugin());

    프로시저에서 데이터베이스 액세스

    섹션 제목: “프로시저에서 데이터베이스 액세스”

    플러그인은 IMyDbContext를 프로시저 컨텍스트에 병합하여 myDb를 선택적 속성으로 사용할 수 있게 합니다:

    packages/api/src/procedures/users.ts
    import { z } from 'zod';
    import { authenticatedProcedure } from '../router.js';
    export const listUsers = authenticatedProcedure
    .output(z.array(z.object({ id: z.string(), name: z.string() })))
    .query(async ({ ctx }) => {
    // ctx.myDb는 Prisma 클라이언트입니다 — Awaited<ReturnType<typeof getPrisma>>로 타입 지정됨
    return await ctx.myDb!.user.findMany();
    });

    대상 데이터베이스가 MySQL 엔진을 사용하는 경우, 생성된 미들웨어는 opts.next()$disconnect()를 호출하는 try/finally 블록으로 래핑합니다:

    packages/api/src/middleware/my-db.ts
    return t.procedure.use(async (opts) => {
    const myDb = await getPrisma();
    try {
    return await opts.next({ ctx: { ...opts.ctx, myDb } });
    } finally {
    await myDb.$disconnect();
    }
    });

    이는 쿼리 후 MySQL 어댑터가 Node.js 이벤트 루프를 열어두는 문제를 해결하며, 그렇지 않으면 Lambda가 스트리밍 응답을 플러시하지 못하게 됩니다. finally에서 연결을 해제하면 이벤트 루프가 해제되어 응답이 완료될 수 있습니다. 자세한 내용은 MySQL: API Gateway 스트리밍 모드를 참조하세요.

    PostgreSQL은 이것이 필요하지 않습니다 — 어댑터가 allowExitOnIdle: true로 구성된 연결 풀을 사용합니다.

    다른 대상으로 생성기를 다시 실행하여 추가 데이터베이스를 연결할 수 있습니다. 각 데이터베이스는 자체 플러그인과 컨텍스트 인터페이스를 갖습니다:

    packages/api/src/router.ts
    export const dbProcedure = t.procedure
    .concat(createPostgresDbPlugin())
    .concat(createMySqlDbPlugin());

    런타임에 API가 데이터베이스에 연결할 수 있도록 하려면, API Lambda 함수를 데이터베이스와 동일한 VPC에 배포하고 네트워크 및 IAM 액세스 권한을 부여해야 합니다.

    애플리케이션 스택에서 API를 데이터베이스와 동일한 VPC에 배포한 다음, allowDefaultPortFromgrantConnect를 호출하여 네트워크 경로를 열고 각 Lambda 핸들러에 IAM rds-db:connect 권한을 부여합니다:

    packages/infra/src/stacks/application-stack.ts
    import { MyDatabase } from ':my-scope/common-constructs';
    const db = new MyDatabase(this, 'Db', { vpc, ... });
    const api = new MyApi(this, 'Api', {
    integrations: MyApi.defaultIntegrations(this)
    .withDefaultOptions({
    vpc,
    vpcSubnets: { subnetType: SubnetType.PRIVATE_WITH_EGRESS },
    })
    .build(),
    });
    Object.entries(api.integrations).forEach(([operation, integration]) => {
    db.allowDefaultPortFrom(integration.handler, `Allow ${operation} to connect to the database`);
    db.grantConnect(integration.handler);
    });

    API Lambda 함수를 프라이빗 격리 서브넷이 아닌 외부 연결이 가능한 프라이빗 서브넷에 배포하세요. 런타임에 getPrisma()는 AWS AppConfig에서 데이터베이스 연결 세부 정보를 가져오는데, 이는 아웃바운드 인터넷 액세스가 필요한 퍼블릭 AWS 서비스 엔드포인트입니다.

    생성기는 tRPC API의 serve-local 타겟이 데이터베이스의 serve-local 타겟에 의존하도록 구성하므로 다음을 실행하면:

    Terminal window
    pnpm nx serve-local <api-project-name>

    API와 함께 로컬 데이터베이스가 자동으로 시작됩니다.