3주 쉬었다가 다시 잡았다. 이번엔 AWS 이관이다.
Supabase는 커넥션 풀러를 거치는 URL과, 마이그레이션용 다이렉트 URL이 따로 있다. DATABASE_URL만 쓰면 prisma migrate가 풀러 뒤에서 막힌다.
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DIRECT_URL")
}
DIRECT_URL을 따로 추가해서 마이그레이션은 다이렉트로, 런타임 쿼리는 풀러로 나눴다.
S3 버킷 이름을 그냥 jobs-prod로 박아뒀더니 리전 전체에서 유일해야 하는 S3 네이밍 규칙에 걸렸다. 계정 ID를 붙이는 걸로 바꿨다.
data "aws_caller_identity" "current" {}
module "s3" {
source = "../../modules/s3-bucket"
bucket_name = "jobs-prod-${data.aws_caller_identity.current.account_id}"
}
SQS, IAM 모듈도 이 시점에 같이 올렸다. .tfstate는 아직 로컬 보관이라 .gitignore에 추가해뒀다.
script, tts, upload 워커를 각각 serverless.yml로 뜯었다. serverless-esbuild로 번들링하고, SQS 이벤트 소스를 붙이는 구조다.
functions:
handler:
handler: src/handler.handler
timeout: 60
memorySize: 512
events:
- sqs:
arn: arn:aws:sqs:ap-northeast-2:682251233572:prod-script-queue
batchSize: 1
functionResponseType: ReportBatchItemFailures
api는 @fastify/aws-lambda로 감싸서 API Gateway HTTP API 뒤에 붙였다. 이 과정에서 InternalKeyGuard가 DI 컨테이너에서 죽었다. esbuild가 emitDecoratorMetadata를 지원 안 해서 Reflector 타입 정보가 번들 시점에 날아간 거였다.
constructor(@Inject(Reflector) private readonly reflector: Reflector) {}
타입 추론 대신 @Inject 데코레이터로 명시했다. esbuild 기반 배포에서 NestJS 쓸 때 흔히 걸리는 함정이다.
로컬에서는 멀쩡했는데 Lambda에 올리면 Prisma가 쿼리 엔진을 못 찾았다. Lambda 런타임은 rhel-openssl-3.0.x 바이너리가 필요한데 기본 생성 타겟엔 없었다.
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "rhel-openssl-3.0.x"]
}
바이너리를 생성해도 esbuild 번들에는 자동으로 안 실린다. serverless.yml에 패키징 패턴을 직접 추가했다.
package:
individually: true
patterns:
- 'libquery_engine-rhel-openssl-3.0.x.so.node'
이거 하나 빠뜨려서 배포 후에 PrismaClientInitializationError를 몇 번 봤다. 4개 워커 전부 같은 패턴을 반복해야 했다.