FastAPI
FastAPI는 Python으로 API를 구축하기 위한 프레임워크입니다.
FastAPI 생성기는 AWS CDK 또는 Terraform 인프라 설정이 포함된 새로운 FastAPI를 생성합니다. 생성된 백엔드는 서버리스 배포를 위해 AWS Lambda를 사용하며 AWS API Gateway API를 통해 노출됩니다. AWS Lambda Powertools를 설정하여 로깅, AWS X-Ray 추적, Cloudwatch 메트릭을 포함한 관측 가능성을 제공합니다.
사용 방법
섹션 제목: “사용 방법”FastAPI 생성
섹션 제목: “FastAPI 생성”새로운 FastAPI를 두 가지 방법으로 생성할 수 있습니다:
- 설치 Nx Console VSCode Plugin 아직 설치하지 않았다면
- VSCode에서 Nx 콘솔 열기
- 클릭
Generate (UI)
"Common Nx Commands" 섹션에서 - 검색
@aws/nx-plugin - py#fast-api
- 필수 매개변수 입력
- 클릭
Generate
pnpm nx g @aws/nx-plugin:py#fast-api
yarn nx g @aws/nx-plugin:py#fast-api
npx nx g @aws/nx-plugin:py#fast-api
bunx nx g @aws/nx-plugin:py#fast-api
어떤 파일이 변경될지 확인하기 위해 드라이 런을 수행할 수도 있습니다
pnpm nx g @aws/nx-plugin:py#fast-api --dry-run
yarn nx g @aws/nx-plugin:py#fast-api --dry-run
npx nx g @aws/nx-plugin:py#fast-api --dry-run
bunx nx g @aws/nx-plugin:py#fast-api --dry-run
매개변수 | 타입 | 기본값 | 설명 |
---|---|---|---|
name 필수 | string | - | Name of the API project to generate |
computeType | string | ServerlessApiGatewayRestApi | The type of compute to use to deploy this API. Choose between ServerlessApiGatewayRestApi (default) or ServerlessApiGatewayHttpApi. |
auth | string | IAM | The method used to authenticate with your API. Choose between IAM (default), Cognito or None. |
directory | string | packages | The directory to store the application in. |
iacProvider | string | CDK | The preferred IaC provider |
moduleName | string | - | Python module name |
생성기 출력
섹션 제목: “생성기 출력”생성기는 <directory>/<api-name>
디렉토리에 다음 프로젝트 구조를 생성합니다:
- project.json 프로젝트 구성 및 빌드 대상
- pyproject.toml Python 프로젝트 구성 및 의존성
디렉터리<module_name>
- __init__.py 모듈 초기화
- init.py FastAPI 앱 설정 및 powertools 미들웨어 구성
- main.py API 구현
디렉터리scripts
- generate_open_api.py FastAPI 앱에서 OpenAPI 스키마 생성 스크립트
인프라
섹션 제목: “인프라”이 생성기는 선택한 iacProvider
기반으로 인프라를 코드 형태로 제공하므로, packages/common
디렉터리에 관련 CDK 구축 요소 또는 Terraform 모듈을 포함하는 프로젝트를 생성합니다.
공통 인프라스트럭처 코드 프로젝트의 구조는 다음과 같습니다:
디렉터리packages/common/constructs
디렉터리src
디렉터리app/ 특정 프로젝트/생성기에 종속적인 인프라를 위한 구축 요소
- …
디렉터리core/
app
내 구축 요소에서 재사용되는 일반적 구축 요소- …
- index.ts
app
의 구축 요소를 익스포트하는 진입점
- project.json 프로젝트 빌드 대상 및 구성
디렉터리packages/common/terraform
디렉터리src
디렉터리app/ 특정 프로젝트/생성기 전용 Terraform 모듈
- …
디렉터리core/
app
내 모듈에서 재사용되는 일반적 모듈- …
- project.json 프로젝트 빌드 대상 및 구성
API 배포를 위해 다음 파일들이 생성됩니다:
디렉터리packages/common/constructs/src
디렉터리app
디렉터리apis
- <project-name>.ts API를 배포하기 위한 CDK construct
디렉터리core
디렉터리api
- http-api.ts HTTP API 배포를 위한 CDK construct (HTTP API 배포를 선택한 경우)
- rest-api.ts REST API 배포를 위한 CDK construct (REST API 배포를 선택한 경우)
- utils.ts API constructs를 위한 유틸리티
디렉터리packages/common/terraform/src
디렉터리app
디렉터리apis
디렉터리<project-name>
- <project-name>.tf API를 배포하기 위한 모듈
디렉터리core
디렉터리api
디렉터리http-api
- http-api.tf HTTP API 배포를 위한 모듈 (HTTP API 배포를 선택한 경우)
디렉터리rest-api
- rest-api.tf REST API 배포를 위한 모듈 (REST API 배포를 선택한 경우)
FastAPI 구현
섹션 제목: “FastAPI 구현”주요 API 구현은 main.py
에 있습니다. 여기서 API 경로와 구현을 정의합니다. 예시:
from .init import app, tracerfrom pydantic import BaseModel
class Item(BaseModel): name: str
@app.get("/items/{item_id}")def get_item(item_id: int) -> Item: return Item(name=...)
@app.post("/items")def create_item(item: Item): return ...
생성기는 자동으로 여러 기능을 설정합니다:
- 관측 가능성을 위한 AWS Lambda Powertools 통합
- 에러 핸들링 미들웨어
- 요청/응답 상관 관계
- 메트릭 수집
- Mangum을 사용한 AWS Lambda 핸들러
AWS Lambda Powertools를 이용한 관측 가능성
섹션 제목: “AWS Lambda Powertools를 이용한 관측 가능성”생성기는 AWS Lambda Powertools를 사용해 구조화된 로깅을 구성합니다. 라우트 핸들러에서 로거에 접근할 수 있습니다:
from .init import app, logger
@app.get("/items/{item_id}")def read_item(item_id: int): logger.info("Fetching item", extra={"item_id": item_id}) return {"item_id": item_id}
로거는 자동으로 다음을 포함합니다:
- 요청 추적을 위한 상관 ID
- 요청 경로 및 메서드
- Lambda 컨텍스트 정보
- 콜드 스타트 표시기
AWS X-Ray 추적이 자동으로 구성됩니다. 추적에 커스텀 서브세그먼트를 추가할 수 있습니다:
from .init import app, tracer
@app.get("/items/{item_id}")@tracer.capture_methoddef read_item(item_id: int): # 새로운 서브세그먼트 생성 with tracer.provider.in_subsegment("fetch-item-details"): # 로직 구현 return {"item_id": item_id}
메트릭
섹션 제목: “메트릭”CloudWatch 메트릭이 각 요청에 대해 자동으로 수집됩니다. 커스텀 메트릭을 추가할 수 있습니다:
from .init import app, metricsfrom aws_lambda_powertools.metrics import MetricUnit
@app.get("/items/{item_id}")def read_item(item_id: int): metrics.add_metric(name="ItemViewed", unit=MetricUnit.Count, value=1) return {"item_id": item_id}
기본 메트릭 포함 사항:
- 요청 수
- 성공/실패 수
- 콜드 스타트 메트릭
- 경로별 메트릭
에러 핸들링
섹션 제목: “에러 핸들링”생성기는 포괄적인 에러 핸들링을 포함합니다:
from fastapi import HTTPException
@app.get("/items/{item_id}")def read_item(item_id: int): if item_id < 0: raise HTTPException(status_code=400, detail="Item ID must be positive") return {"item_id": item_id}
처리되지 않은 예외는 미들웨어에 의해 포착되어:
- 스택 트레이스와 함께 전체 예외 기록
- 실패 메트릭 기록
- 클라이언트에 안전한 500 응답 반환
- 상관 ID 유지
스트리밍
섹션 제목: “스트리밍”FastAPI에서는 StreamingResponse
응답 타입을 사용해 호출자에게 스트리밍 응답을 전송할 수 있습니다.
인프라 변경
섹션 제목: “인프라 변경”AWS API Gateway는 스트리밍 응답을 지원하지 않으므로 FastAPI를 스트리밍을 지원하는 플랫폼에 배포해야 합니다. 가장 간단한 옵션은 AWS Lambda Function URL을 사용하는 것입니다.
이를 위해 생성된 common/constructs/src/app/apis/<name>-api.ts
구성을 Function URL을 배포하는 구성으로 교체할 수 있습니다.
스트리밍 FunctionURL 구성 예시
import { Duration, Stack, CfnOutput } from 'aws-cdk-lib';import { IGrantable, Grant } from 'aws-cdk-lib/aws-iam';import { Runtime, Code, Tracing, LayerVersion, FunctionUrlAuthType, InvokeMode, Function,} from 'aws-cdk-lib/aws-lambda';import { Construct } from 'constructs';import url from 'url';import { RuntimeConfig } from '../../core/runtime-config.js';
export class MyApi extends Construct { public readonly handler: Function;
constructor(scope: Construct, id: string) { super(scope, id);
this.handler = new Function(this, 'Handler', { runtime: Runtime.PYTHON_3_12, handler: 'run.sh', code: Code.fromAsset( url.fileURLToPath( new URL( '../../../../../../dist/packages/my_api/bundle', import.meta.url, ), ), ), timeout: Duration.seconds(30), tracing: Tracing.ACTIVE, environment: { AWS_CONNECTION_REUSE_ENABLED: '1', }, });
const stack = Stack.of(this); this.handler.addLayers( LayerVersion.fromLayerVersionArn( this, 'LWALayer', `arn:aws:lambda:${stack.region}:753240598075:layer:LambdaAdapterLayerX86:24`, ), ); this.handler.addEnvironment('PORT', '8000'); this.handler.addEnvironment('AWS_LWA_INVOKE_MODE', 'response_stream'); this.handler.addEnvironment('AWS_LAMBDA_EXEC_WRAPPER', '/opt/bootstrap'); const functionUrl = this.handler.addFunctionUrl({ authType: FunctionUrlAuthType.AWS_IAM, invokeMode: InvokeMode.RESPONSE_STREAM, cors: { allowedOrigins: ['*'], allowedHeaders: [ 'authorization', 'content-type', 'x-amz-content-sha256', 'x-amz-date', 'x-amz-security-token', ], }, });
new CfnOutput(this, 'MyApiUrl', { value: functionUrl.url });
// 클라이언트 디스커버리를 위해 런타임 구성에 API URL 등록 RuntimeConfig.ensure(this).config.apis = { ...RuntimeConfig.ensure(this).config.apis!, MyApi: functionUrl.url, }; }
public grantInvokeAccess(grantee: IGrantable) { Grant.addToPrincipal({ grantee, actions: ['lambda:InvokeFunctionUrl'], resourceArns: [this.handler.functionArn], conditions: { StringEquals: { 'lambda:FunctionUrlAuthType': 'AWS_IAM', }, }, }); }}
Terraform을 사용하는 경우 생성된 API Gateway 인프라를 스트리밍 응답을 지원하는 Lambda Function URL로 교체할 수 있습니다.
스트리밍 Lambda Function URL 구성 예시
# 현재 AWS 컨텍스트를 위한 데이터 소스data "aws_caller_identity" "current" {}data "aws_region" "current" {}
# FastAPI 스트리밍을 위한 Lambda 함수resource "aws_lambda_function" "my_api_handler" { filename = "../../../../../../dist/packages/my_api/bundle.zip" function_name = "my-api-handler" role = aws_iam_role.lambda_execution_role.arn handler = "run.sh" runtime = "python3.12" timeout = 30 source_code_hash = filebase64sha256("../../../../../../dist/packages/my_api/bundle.zip")
# X-Ray 추적 활성화 tracing_config { mode = "Active" }
# Lambda Web Adapter 환경 변수 environment { variables = { AWS_CONNECTION_REUSE_ENABLED = "1" PORT = "8000" AWS_LWA_INVOKE_MODE = "response_stream" AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap" } }
# Lambda Web Adapter 레이어 추가 layers = [ "arn:aws:lambda:${data.aws_region.current.name}:753240598075:layer:LambdaAdapterLayerX86:24" ]
depends_on = [ aws_iam_role_policy_attachment.lambda_logs, aws_cloudwatch_log_group.lambda_logs, ]}
# Lambda 함수를 위한 CloudWatch 로그 그룹resource "aws_cloudwatch_log_group" "lambda_logs" { name = "/aws/lambda/my-api-handler" retention_in_days = 14}
# Lambda 실행을 위한 IAM 역할resource "aws_iam_role" "lambda_execution_role" { name = "my-api-lambda-execution-role"
assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "lambda.amazonaws.com" } } ] })}
# 기본 실행 정책 연결resource "aws_iam_role_policy_attachment" "lambda_logs" { role = aws_iam_role.lambda_execution_role.name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"}
# X-Ray 추적 정책 연결resource "aws_iam_role_policy_attachment" "lambda_xray" { role = aws_iam_role.lambda_execution_role.name policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"}
# 스트리밍 지원 Lambda Function URLresource "aws_lambda_function_url" "my_api_url" { function_name = aws_lambda_function.my_api_handler.function_name authorization_type = "AWS_IAM" invoke_mode = "RESPONSE_STREAM"
cors { allow_credentials = false allow_origins = ["*"] allow_methods = ["*"] allow_headers = [ "authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token" ] expose_headers = ["date", "keep-alive"] max_age = 86400 }}
# Function URL 출력output "my_api_url" { description = "스트리밍 FastAPI Lambda Function URL" value = aws_lambda_function_url.my_api_url.function_url}
# 선택 사항: 런타임 구성을 위한 SSM 파라미터 생성resource "aws_ssm_parameter" "my_api_url" { name = "/runtime-config/apis/MyApi" type = "String" value = aws_lambda_function_url.my_api_url.function_url
tags = { Environment = "production" Service = "my-api" }}
# Function URL 호출 권한을 위한 IAM 정책resource "aws_iam_policy" "my_api_invoke_policy" { name = "my-api-invoke-policy" description = "스트리밍 FastAPI Lambda Function URL 호출 권한 정책"
policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = "lambda:InvokeFunctionUrl" Resource = aws_lambda_function.my_api_handler.arn Condition = { StringEquals = { "lambda:FunctionUrlAuthType" = "AWS_IAM" } } } ] })}
# 예시: 정책을 역할에 연결 (필요에 따라 주석 해제 및 수정)# resource "aws_iam_role_policy_attachment" "my_api_invoke_access" {# role = var.authenticated_role_name# policy_arn = aws_iam_policy.my_api_invoke_policy.arn# }
인프라를 스트리밍을 지원하도록 업데이트한 후 FastAPI에서 스트리밍 API를 구현할 수 있습니다. API는 다음을 수행해야 합니다:
StreamingResponse
반환- 각 응답 청크의 반환 타입 선언
- API Connection 사용을 계획하는 경우 OpenAPI 벤더 확장
x-streaming: true
추가
예를 들어 API에서 일련의 JSON 객체를 스트리밍하려면 다음과 같이 구현할 수 있습니다:
from pydantic import BaseModelfrom fastapi.responses import StreamingResponse
class Chunk(BaseModel): message: str timestamp: datetime
async def stream_chunks(): for i in range(0, 100): yield Chunk(message=f"This is chunk {i}", timestamp=datetime.now())
@app.get("/stream", openapi_extra={'x-streaming': True})def my_stream() -> Chunk: return StreamingResponse(stream_chunks(), media_type="application/json")
스트리밍 응답을 소비하려면 API Connection Generator를 사용하여 스트리밍 청크를 반복하는 타입 세이프 메서드를 활용할 수 있습니다.
FastAPI 배포
섹션 제목: “FastAPI 배포”FastAPI 생성기는 선택한 iacProvider
에 따라 CDK 또는 Terraform 인프라 코드를 생성합니다. 이를 사용해 FastAPI를 배포할 수 있습니다.
common/constructs
폴더에 API 배포를 위한 CDK 구성이 있습니다. CDK 애플리케이션에서 사용할 수 있습니다:
import { MyApi } from ':my-scope/common-constructs';
export class ExampleStack extends Stack { constructor(scope: Construct, id: string) { // 스택에 API 추가 const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(), }); }}
이 설정은 다음을 구성합니다:
- FastAPI 애플리케이션의 각 작업을 위한 AWS Lambda 함수
- 함수 트리거로 API Gateway HTTP/REST API
- IAM 역할 및 권한
- CloudWatch 로그 그룹
- X-Ray 추적 구성
- CloudWatch 메트릭 네임스페이스
common/terraform
폴더에 API 배포를 위한 Terraform 모듈이 있습니다. Terraform 구성에서 사용할 수 있습니다:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# Lambda 함수 환경 변수 env = { ENVIRONMENT = var.environment LOG_LEVEL = "INFO" }
# 추가 IAM 정책 (필요 시) additional_iam_policy_statements = [ # API에 필요한 추가 권한 ]
tags = local.common_tags}
이 설정은 다음을 구성합니다:
- 모든 FastAPI 경로를 제공하는 AWS Lambda 함수
- 함수 트리거로 API Gateway HTTP/REST API
- IAM 역할 및 권한
- CloudWatch 로그 그룹
- X-Ray 추적 구성
- CORS 구성
Terraform 모듈은 사용 가능한 여러 출력을 제공합니다:
# API 엔드포인트 접근output "api_url" { value = module.my_api.stage_invoke_url}
# Lambda 함수 상세 정보output "lambda_function_name" { value = module.my_api.lambda_function_name}
# 추가 권한 부여를 위한 IAM 역할output "lambda_execution_role_arn" { value = module.my_api.lambda_execution_role_arn}
모듈에 변수를 전달해 CORS 설정을 커스터마이즈할 수 있습니다:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# 커스텀 CORS 구성 cors_allow_origins = ["https://myapp.com", "https://staging.myapp.com"] cors_allow_methods = ["GET", "POST", "PUT", "DELETE"] cors_allow_headers = [ "authorization", "content-type", "x-custom-header" ]
tags = local.common_tags}
REST/HTTP API CDK 구문은 각 작업에 대한 통합을 정의하기 위한 타입 안전 인터페이스를 제공하도록 구성됩니다.
기본 통합
섹션 제목: “기본 통합”정적 defaultIntegrations
를 사용하여 각 작업에 대해 개별 AWS Lambda 함수를 정의하는 기본 패턴을 활용할 수 있습니다:
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(),});
Terraform 모듈은 단일 Lambda 함수를 사용하는 라우터 패턴을 자동으로 적용합니다. 추가 구성이 필요하지 않습니다:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# 모듈은 모든 API 작업을 처리하는 단일 Lambda 함수를 자동 생성합니다 tags = local.common_tags}
통합 접근
섹션 제목: “통합 접근”API 구문의 integrations
속성을 통해 타입 안전 방식으로 기본 AWS Lambda 함수에 접근할 수 있습니다. 예를 들어 API에 sayHello
작업이 정의된 경우 이 함수에 권한을 추가하려면 다음과 같이 할 수 있습니다:
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this).build(),});
// sayHello는 API에 정의된 작업으로 타입이 지정됩니다api.integrations.sayHello.handler.addToRolePolicy(new PolicyStatement({ effect: Effect.ALLOW, actions: [...], resources: [...],}));
Terraform 라우터 패턴에서는 단일 Lambda 함수만 존재합니다. 모듈 출력을 통해 접근할 수 있습니다:
# 단일 Lambda 함수에 추가 권한 부여resource "aws_iam_role_policy" "additional_permissions" { name = "additional-api-permissions" role = module.my_api.lambda_execution_role_name
policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "s3:GetObject", "s3:PutObject" ] Resource = "arn:aws:s3:::my-bucket/*" } ] })}
기본 옵션 사용자 정의
섹션 제목: “기본 옵션 사용자 정의”withDefaultOptions
메서드를 사용하여 기본 통합 생성 시 사용되는 옵션을 사용자 정의할 수 있습니다. 예를 들어 모든 Lambda 함수를 VPC에 배치하려면:
const vpc = new Vpc(this, 'Vpc', ...);
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withDefaultOptions({ vpc, }) .build(),});
VPC 구성과 같은 옵션을 사용자 정의하려면 생성된 Terraform 모듈을 수정해야 합니다. 모든 Lambda 함수에 VPC 지원을 추가하려면:
# VPC 변수 추가variable "vpc_subnet_ids" { description = "Lambda 함수용 VPC 서브넷 ID 목록" type = list(string) default = []}
variable "vpc_security_group_ids" { description = "Lambda 함수용 VPC 보안 그룹 ID 목록" type = list(string) default = []}
# Lambda 함수 리소스 업데이트resource "aws_lambda_function" "api_lambda" { # ... 기존 구성 ...
# VPC 구성 추가 vpc_config { subnet_ids = var.vpc_subnet_ids security_group_ids = var.vpc_security_group_ids }}
다음과 같이 VPC 구성으로 모듈을 사용합니다:
module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# VPC 구성 vpc_subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id] vpc_security_group_ids = [aws_security_group.lambda_sg.id]
tags = local.common_tags}
통합 재정의
섹션 제목: “통합 재정의”withOverrides
메서드를 사용하여 특정 작업에 대한 통합을 재정의할 수 있습니다. 각 재정의는 HTTP 또는 REST API에 적합한 CDK 통합 구문으로 타입이 지정된 integration
속성을 지정해야 합니다. withOverrides
메서드도 타입 안전합니다. 예를 들어 getDocumentation
API를 외부 웹사이트 호스팅 문서로 재정의하려면:
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withOverrides({ getDocumentation: { integration: new HttpIntegration('https://example.com/documentation'), }, }) .build(),});
api.integrations.getDocumentation
을 통해 접근 시 재정의된 통합에는 더 이상 handler
속성이 없습니다.
추가 속성을 통합에 추가하면 다른 유형의 통합을 추상화하면서도 타입 안전성을 유지할 수 있습니다. 예를 들어 REST API용 S3 통합을 생성한 후 특정 작업에 대한 버킷을 참조하려면:
const storageBucket = new Bucket(this, 'Bucket', { ... });
const apiGatewayRole = new Role(this, 'ApiGatewayS3Role', { assumedBy: new ServicePrincipal('apigateway.amazonaws.com'),});
storageBucket.grantRead(apiGatewayRole);
const api = new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withOverrides({ getFile: { bucket: storageBucket, integration: new AwsIntegration({ service: 's3', integrationHttpMethod: 'GET', path: `${storageBucket.bucketName}/{fileName}`, options: { credentialsRole: apiGatewayRole, requestParameters: { 'integration.request.path.fileName': 'method.request.querystring.fileName', }, integrationResponses: [{ statusCode: '200' }], }, }), options: { requestParameters: { 'method.request.querystring.fileName': true, }, methodResponses: [{ statusCode: '200', }], } }, }) .build(),});
// 이후 다른 파일에서 정의한 bucket 속성에 타입 안전 방식으로 접근 가능api.integrations.getFile.bucket.grantRead(...);
인증자 재정의
섹션 제목: “인증자 재정의”통합에 options
를 제공하여 Cognito 인증과 같은 특정 메서드 옵션을 재정의할 수 있습니다. 예를 들어 getDocumentation
작업에 Cognito 인증을 사용하려면:
new MyApi(this, 'MyApi', { integrations: MyApi.defaultIntegrations(this) .withOverrides({ getDocumentation: { integration: new HttpIntegration('https://example.com/documentation'), options: { authorizer: new CognitoUserPoolsAuthorizer(...) // REST용 또는 HttpUserPoolAuthorizer (HTTP API) } }, }) .build(),});
명시적 통합
섹션 제목: “명시적 통합”기본 통합 대신 각 작업에 직접 통합을 제공할 수 있습니다. 이는 각 작업이 다른 유형의 통합을 사용해야 하거나 새 작업 추가 시 타입 오류를 수신하려는 경우 유용합니다:
new MyApi(this, 'MyApi', { integrations: { sayHello: { integration: new LambdaIntegration(...), }, getDocumentation: { integration: new HttpIntegration(...), }, },});
Terraform에서 작업별 명시적 통합을 사용하려면 기본 프록시 통합을 작업별 특정 통합으로 교체하도록 생성된 앱별 모듈을 수정해야 합니다.
packages/common/terraform/src/app/apis/my-api/my-api.tf
편집:
- 기본 프록시 경로 제거 (예:
resource "aws_apigatewayv2_route" "proxy_routes"
) - 단일 Lambda 함수를 작업별 개별 함수로 교체
- 동일 ZIP 번들 재사용하여 작업별 특정 통합 및 경로 생성:
# 기본 단일 Lambda 함수 제거 resource "aws_lambda_function" "api_lambda" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApiHandler" role = aws_iam_role.lambda_execution_role.arn handler = "index.handler" runtime = "nodejs22.x" timeout = 30 # ... 나머지 구성 }
# 기본 프록시 통합 제거 resource "aws_apigatewayv2_integration" "lambda_integration" { api_id = module.http_api.api_id integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.api_lambda.invoke_arn # ... 나머지 구성 }
# 기본 프록시 경로 제거 resource "aws_apigatewayv2_route" "proxy_routes" { for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]) api_id = module.http_api.api_id route_key = "${each.key} /{proxy+}" target = "integrations/${aws_apigatewayv2_integration.lambda_integration.id}" # ... 나머지 구성 }
# 동일 번들 사용 작업별 Lambda 함수 추가 resource "aws_lambda_function" "say_hello_handler" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApi-SayHello" role = aws_iam_role.lambda_execution_role.arn handler = "sayHello.handler" # 이 작업용 특정 핸들러 runtime = "nodejs22.x" timeout = 30 source_code_hash = data.archive_file.lambda_zip.output_base64sha256
tracing_config { mode = "Active" }
environment { variables = merge({ AWS_CONNECTION_REUSE_ENABLED = "1" }, var.env) }
tags = var.tags }
resource "aws_lambda_function" "get_documentation_handler" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApi-GetDocumentation" role = aws_iam_role.lambda_execution_role.arn handler = "getDocumentation.handler" # 이 작업용 특정 핸들러 runtime = "nodejs22.x" timeout = 30 source_code_hash = data.archive_file.lambda_zip.output_base64sha256
tracing_config { mode = "Active" }
environment { variables = merge({ AWS_CONNECTION_REUSE_ENABLED = "1" }, var.env) }
tags = var.tags }
# 작업별 특정 통합 추가 resource "aws_apigatewayv2_integration" "say_hello_integration" { api_id = module.http_api.api_id integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.say_hello_handler.invoke_arn payload_format_version = "2.0" timeout_milliseconds = 30000 }
resource "aws_apigatewayv2_integration" "get_documentation_integration" { api_id = module.http_api.api_id integration_type = "HTTP_PROXY" integration_uri = "https://example.com/documentation" integration_method = "GET" }
# 작업별 특정 경로 추가 resource "aws_apigatewayv2_route" "say_hello_route" { api_id = module.http_api.api_id route_key = "POST /sayHello" target = "integrations/${aws_apigatewayv2_integration.say_hello_integration.id}" authorization_type = "AWS_IAM" }
resource "aws_apigatewayv2_route" "get_documentation_route" { api_id = module.http_api.api_id route_key = "GET /documentation" target = "integrations/${aws_apigatewayv2_integration.get_documentation_integration.id}" authorization_type = "NONE" }
# 각 함수에 Lambda 권한 추가 resource "aws_lambda_permission" "say_hello_permission" { statement_id = "AllowExecutionFromAPIGateway-SayHello" action = "lambda:InvokeFunction" function_name = aws_lambda_function.say_hello_handler.function_name principal = "apigateway.amazonaws.com" source_arn = "${module.http_api.api_execution_arn}/*/*" }
resource "aws_lambda_permission" "get_documentation_permission" { statement_id = "AllowExecutionFromAPIGateway-GetDocumentation" action = "lambda:InvokeFunction" function_name = aws_lambda_function.get_documentation_handler.function_name principal = "apigateway.amazonaws.com" source_arn = "${module.http_api.api_execution_arn}/*/*" }
# 기본 단일 Lambda 함수 제거 resource "aws_lambda_function" "api_lambda" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApiHandler" role = aws_iam_role.lambda_execution_role.arn handler = "index.handler" runtime = "nodejs22.x" timeout = 30 # ... 나머지 구성 }
# 기본 프록시 통합 제거 resource "aws_apigatewayv2_integration" "lambda_integration" { api_id = module.http_api.api_id integration_type = "AWS_PROXY" integration_uri = aws_lambda_function.api_lambda.invoke_arn # ... 나머지 구성 }
# 기본 프록시 경로 제거 resource "aws_apigatewayv2_route" "proxy_routes" { for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]) api_id = module.http_api.api_id route_key = "${each.key} /{proxy+}" target = "integrations/${aws_apigatewayv2_integration.lambda_integration.id}" # ... 나머지 구성 }
# 동일 번들 사용 작업별 Lambda 함수 추가 resource "aws_lambda_function" "say_hello_handler" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApi-SayHello" role = aws_iam_role.lambda_execution_role.arn handler = "sayHello.handler" # 이 작업용 특정 핸들러 runtime = "nodejs22.x" timeout = 30 source_code_hash = data.archive_file.lambda_zip.output_base64sha256
tracing_config { mode = "Active" }
environment { variables = merge({ AWS_CONNECTION_REUSE_ENABLED = "1" }, var.env) }
tags = var.tags }
resource "aws_lambda_function" "get_documentation_handler" { filename = data.archive_file.lambda_zip.output_path function_name = "MyApi-GetDocumentation" role = aws_iam_role.lambda_execution_role.arn handler = "getDocumentation.handler" # 이 작업용 특정 핸들러 runtime = "nodejs22.x" timeout = 30 source_code_hash = data.archive_file.lambda_zip.output_base64sha256
tracing_config { mode = "Active" }
environment { variables = merge({ AWS_CONNECTION_REUSE_ENABLED = "1" }, var.env) }
tags = var.tags }
# 작업별 리소스 및 메서드 추가 resource "aws_api_gateway_resource" "say_hello_resource" { rest_api_id = module.rest_api.api_id parent_id = module.rest_api.api_root_resource_id path_part = "sayHello" }
resource "aws_api_gateway_method" "say_hello_method" { rest_api_id = module.rest_api.api_id resource_id = aws_api_gateway_resource.say_hello_resource.id http_method = "POST" authorization = "AWS_IAM" }
resource "aws_api_gateway_integration" "say_hello_integration" { rest_api_id = module.rest_api.api_id resource_id = aws_api_gateway_resource.say_hello_resource.id http_method = aws_api_gateway_method.say_hello_method.http_method
integration_http_method = "POST" type = "AWS_PROXY" uri = aws_lambda_function.say_hello_handler.invoke_arn }
resource "aws_api_gateway_resource" "get_documentation_resource" { rest_api_id = module.rest_api.api_id parent_id = module.rest_api.api_root_resource_id path_part = "documentation" }
resource "aws_api_gateway_method" "get_documentation_method" { rest_api_id = module.rest_api.api_id resource_id = aws_api_gateway_resource.get_documentation_resource.id http_method = "GET" authorization = "NONE" }
resource "aws_api_gateway_integration" "get_documentation_integration" { rest_api_id = module.rest_api.api_id resource_id = aws_api_gateway_resource.get_documentation_resource.id http_method = aws_api_gateway_method.get_documentation_method.http_method
integration_http_method = "GET" type = "HTTP" uri = "https://example.com/documentation" }
# 새 통합에 종속성 업데이트~ resource "aws_api_gateway_deployment" "api_deployment" { rest_api_id = module.rest_api.api_id
depends_on = [ aws_api_gateway_integration.lambda_integration, aws_api_gateway_integration.say_hello_integration, aws_api_gateway_integration.get_documentation_integration, ]
lifecycle { create_before_destroy = true }
triggers = { redeployment = sha1(jsonencode([ aws_api_gateway_integration.say_hello_integration, aws_api_gateway_integration.get_documentation_integration, ])) } }
# 각 함수에 Lambda 권한 추가 resource "aws_lambda_permission" "say_hello_permission" { statement_id = "AllowExecutionFromAPIGateway-SayHello" action = "lambda:InvokeFunction" function_name = aws_lambda_function.say_hello_handler.function_name principal = "apigateway.amazonaws.com" source_arn = "${module.rest_api.api_execution_arn}/*/*" }
resource "aws_lambda_permission" "get_documentation_permission" { statement_id = "AllowExecutionFromAPIGateway-GetDocumentation" action = "lambda:InvokeFunction" function_name = aws_lambda_function.get_documentation_handler.function_name principal = "apigateway.amazonaws.com" source_arn = "${module.rest_api.api_execution_arn}/*/*" }
라우터 패턴
섹션 제목: “라우터 패턴”모든 API 요청을 처리하는 단일 Lambda 함수를 배포하려면 defaultIntegrations
메서드를 수정하여 작업당 하나가 아닌 단일 함수를 생성할 수 있습니다:
export class MyApi<...> extends ... {
public static defaultIntegrations = (scope: Construct) => { const router = new Function(scope, 'RouterHandler', { ... }); return IntegrationBuilder.rest({ ... defaultIntegrationOptions: {}, buildDefaultIntegration: (op) => { return { // 모든 통합에서 동일한 라우터 Lambda 핸들러 참조 integration: new LambdaIntegration(router), }; }, }); };}
router
함수를 메서드 내부에서 생성하는 대신 defaultIntegrations
의 매개변수로 정의하는 것과 같은 다른 방식으로 코드를 수정할 수도 있습니다.
Terraform 모듈은 기본적으로 라우터 패턴을 사용합니다. 이는 기본적이며 유일하게 지원되는 접근 방식입니다. 생성된 모듈은 모든 API 작업을 처리하는 단일 Lambda 함수를 생성합니다.
기본 모듈을 인스턴스화하여 라우터 패턴을 얻을 수 있습니다:
# 기본 라우터 패턴 - 모든 작업을 처리하는 단일 Lambda 함수module "my_api" { source = "../../common/terraform/src/app/apis/my-api"
# 단일 Lambda 함수가 모든 작업을 자동으로 처리 tags = local.common_tags}
코드 생성
섹션 제목: “코드 생성”FastAPI 작업은 Python으로 정의되고 CDK 인프라는 TypeScript로 작성되므로, 통합을 위한 타입 세이프 인터페이스를 제공하기 위해 메타데이터를 CDK 구성에 전달하는 코드 생성을 도입합니다.
타입 세이프 코드 생성을 위해 common/constructs
의 project.json
에 generate:<ApiName>-metadata
대상이 추가됩니다. 이는 packages/common/constructs/src/generated/my-api/metadata.gen.ts
와 같은 파일을 생성하며, 빌드 시 생성되므로 버전 관리에서 제외됩니다.
접근 권한 부여 (IAM 전용)
섹션 제목: “접근 권한 부여 (IAM 전용)”IAM
인증을 선택한 경우 grantInvokeAccess
메서드를 사용해 API 접근 권한을 부여할 수 있습니다:
api.grantInvokeAccess(myIdentityPool.authenticatedRole);
# API 호출을 허용하는 IAM 정책 생성resource "aws_iam_policy" "api_invoke_policy" { name = "MyApiInvokePolicy" description = "FastAPI 호출 허용 정책"
policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = "execute-api:Invoke" Resource = "${module.my_api.api_execution_arn}/*/*" } ] })}
# 정책을 IAM 역할에 연결 (예: 인증된 사용자)resource "aws_iam_role_policy_attachment" "api_invoke_access" { role = aws_iam_role.authenticated_user_role.name policy_arn = aws_iam_policy.api_invoke_policy.arn}
# 기존 역할에 정책 연결resource "aws_iam_role_policy_attachment" "api_invoke_access_existing" { role = "MyExistingRole" policy_arn = aws_iam_policy.api_invoke_policy.arn}
API 모듈의 주요 출력 값:
module.my_api.api_execution_arn
- execute-api:Invoke 권한 부여용module.my_api.api_arn
- API Gateway ARNmodule.my_api.lambda_function_arn
- Lambda 함수 ARN
로컬 개발
섹션 제목: “로컬 개발”생성기는 다음 명령으로 실행할 수 있는 로컬 개발 서버를 구성합니다:
pnpm nx run my-api:serve
yarn nx run my-api:serve
npx nx run my-api:serve
bunx nx run my-api:serve
이 명령은 다음 기능이 포함된 로컬 FastAPI 개발 서버를 시작합니다:
- 코드 변경 시 자동 리로드
/docs
또는/redoc
에서 대화형 API 문서/openapi.json
에서 OpenAPI 스키마
FastAPI 호출
섹션 제목: “FastAPI 호출”React 웹사이트에서 API를 호출하려면 api-connection
생성기를 사용할 수 있습니다.