내가 직접 AWS 배포 도구를 만든 이유
Source: Dev.to
번역을 진행하려면 번역하고자 하는 전체 텍스트를 제공해 주세요. 텍스트를 주시면 요청하신 대로 한국어로 번역해 드리겠습니다.
My Journey to Building a Faster Serverless Deployment Tool
저는 12년 이상 다양한 언어와 팀, 수십 개 프로젝트를 경험한 소프트웨어 엔지니어입니다. 제가 하는 일을 사랑하고, 깔끔한 도구로 실제 문제를 해결하는 데 매력을 느낍니다.
The Problem
- Serverless on AWS – 저는 Serverless Framework와 AWS CDK를 사용해 서버리스 애플리케이션을 구축해 온 경험이 있습니다.
- Friction – 아이디어를 떠올리고 클라우드에 배포하기까지의 간극이 생각보다 크게 느껴졌습니다.
- CloudFormation bottlenecks – 배포마다 CloudFormation이 차이점(diff)을 계산하고, 계획을 세우고, 변경을 적용하는 데 기다려야 했습니다. 한 줄짜리 코드 수정에도 마찬가지였습니다. 민첩함이 핵심인 서버리스 환경에서는 마치 닻을 내린 듯한 느낌이었습니다.
Inspiration from Other Clouds
- Google Cloud / Firebase – Firebase의 개발자 경험에 감명받았습니다: 코드에 함수를 정의하고, CLI 한 줄로 실행하면 모든 것이 배포됩니다.
- Why AWS still wins – AWS Lambda는 더 간단해 보였습니다(컨테이너 빌드 없이, 빠른 콜드 스타트) 그리고 “모든 곳에 컨테이너” 접근 방식보다 제 사고 모델에 더 잘 맞았습니다.
The Idea
“Firebase의 code‑as‑config 모델을 직접 AWS SDK 호출과 결합해 CloudFormation을 완전히 건너뛰자.”
Lambda 핸들러를 작성하고, 그 코드 옆에 바로 설정을 선언한 뒤, 하나의 명령으로 모든 것을 배포할 수 있다면—CloudFormation 템플릿도, 스택 정의도, 번들러 설정도, IAM 보일러플레이트도 없이—원하던 빠른 피드백 루프를 얻을 수 있을 것입니다.
Core Challenges
- State reconciliation – CloudFormation이 하는 일을 재현: 현재 AWS 상태와 원하는 상태를 비교하고 차이를 동기화하되, 직접 SDK 호출만 사용합니다.
- Type‑safe orchestration – API 호출, 오류 처리, 동시성을 관리하면서 코드베이스가 통제 불능으로 흐트러지는 것을 방지합니다.
The Three Libraries That Made It Possible
| Library | 왜 도움이 되었는가 |
|---|---|
| ts‑morph | TypeScript AST를 분석해 핸들러 코드에서 인프라 정보를 직접 추출할 수 있게 해줍니다(별도의 YAML/JSON 필요 없음). |
| Effect‑TS | 함수형 효과 시스템을 제공해 API 호출을 조율하고, 오류를 처리하며, 강력한 타입 안전성을 갖춘 동시성 관리를 가능하게 합니다. |
| Typed AWS SDK wrapper (my own) | AWS SDK 소스의 JSDoc을 기반으로 Effect‑랩핑된 SDK 호출을 생성하고, 완전한 타입 지정 오류 처리를 제공합니다. |
이 도구들을 활용해 effortless‑aws를 만들었습니다.
Design Goals
- Single‑object configuration – 모든 핸들러는 하나의 옵션 객체(
path,method,handler logic,dependencies,parameters등)를 받는 단일 함수 호출입니다. 커리드 함수나 빌더 체인이 없습니다. - Context & DI via Effect‑TS – 핸들러는 Lambda 레벨에서 컨텍스트 팩터리를 선언하고, 이는 모든 요청 핸들러에 컴파일 타임 보장을 통해 주입됩니다.
- Type‑checked dependencies – 다른 리소스(예: DynamoDB 테이블)가 필요한 핸들러는
deps필드만 추가하면 시스템이 자동으로 연결해 줍니다.
Minimal Handler Example
import { defineHttp } from "effortless-aws";
export const hello = defineHttp({
method: "GET",
path: "/hello",
onRequest: async ({ req }) => ({
status: 200,
body: { message: "Hello World!" },
}),
});
그것이 바로 Lambda 함수, API‑Gateway 라우트, IAM 역할을 모두 한 파일에 담은 모습입니다.
eff deploy를 실행하면 바로 라이브됩니다.
Comparison: CDK vs. Effortless‑AWS
CDK – Creating a Simple HTTP Endpoint with a DynamoDB Table
// CDK: stack definition (separate file)
const table = new dynamodb.Table(this, "Orders", {
partitionKey: { name: "id", type: dynamodb.AttributeType.STRING },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
});
const fn = new nodejs.NodejsFunction(this, "GetOrders", {
entry: "src/handlers/get-orders.ts",
runtime: lambda.Runtime.NODEJS_20_X,
environment: { TABLE_NAME: table.tableName },
bundling: { /* … */ },
});
- Multiple files – Stack definition, function definition, and bundling config are all separate.
- CloudFormation – Deployments wait for diff/plan/execute cycles.
- Boilerplate – IAM roles, permissions, and resource references must be wired manually.
Effortless‑AWS – Same Outcome in One Place
import { defineHttp } from "effortless-aws";
export const getOrders = defineHttp({
method: "GET",
path: "/orders",
deps: {
table: {
name: "Orders",
partitionKey: "id",
billingMode: "PAY_PER_REQUEST",
},
},
onRequest: async ({ deps }) => {
const items = await deps.table.scan().items();
return { status: 200, body: items };
},
});
- Single file – Handler, API route, DynamoDB table, and IAM permissions are declared together.
- No CloudFormation – Direct SDK calls make deployment near‑instant.
- Zero boilerplate – The framework infers IAM policies and wiring from the
depsobject.
Takeaways
- Skipping CloudFormation for serverless workloads can shave minutes (or even seconds) off the feedback loop.
- A code‑as‑config approach, powered by TypeScript AST analysis and a strong effect system, gives you the developer experience of Firebase on AWS.
- With proper type‑level guarantees, you can keep the flexibility of raw SDK calls without the chaos of manual wiring.
That’s the story of why I finally snapped and built my own deployment tool – effortless‑aws – and how it solves the pain points I faced with Serverless Framework, CDK, and CloudFormation.
CDK를 사용한 인프라 (비교용)
fy: true, sourceMap: true },
});
table.grantReadData(fn);
const api = new apigateway.HttpApi(this, "Api");
api.addRoutes({
path: "/orders",
methods: [apigateway.HttpMethod.GET],
integration: new HttpLambdaIntegration("GetOrdersIntegration", fn),
});
그것은 단지 인프라일 뿐입니다. 핸들러 파일, 스택 연결, 앱 진입점, 그리고 CloudFormation을 사용한 cdk deploy가 아직 필요합니다.
effortless‑aws 로 동일하게
“한 파일. 한 명령.”
// That's it. One file.
import { defineHttp, defineTable } from "effortless-aws";
export const orders = defineTable({
pk: "id",
});
export const getOrders = defineHttp({
method: "GET",
path: "/orders",
deps: { orders },
onRequest: async ({ deps }) => {
const items = await deps.orders.scan();
return { status: 200, body: items };
},
});
실행:
eff deploy
완료. 테이블, 함수, 라우트, IAM 권한이 모두 몇 초 만에 생성됩니다.
Source: …
eff deploy 작동 방식
eff deploy는 네 단계를 거칩니다. 이 단계들을 조율하는 것이 프로젝트에서 가장 어려운 부분이었는데, 각 단계가 다음 단계에 연결되고 오류가 어디서든 발생할 수 있으며, 리소스들이 서로 의존하기 때문입니다. Effect‑TS 덕분에 이를 관리할 수 있었으며, 전체 파이프라인을 구성 가능하게 만들고, 각 단계는 독립적으로 사고할 수 있는 Effect가 됩니다.
1. 코드 분석 (ts‑morph)
ts‑morph는 TypeScript 소스를 읽어defineHttp/defineTable호출을 모두 추출합니다 — 메서드, 경로, 의존성, 파라미터, 정적 파일 — 모두 AST에서 직접 가져옵니다.- 이는 별도의 YAML/JSON 설정 파일을 대체하며, 코드 자체가 진실의 출처가 됩니다.
2. 번들링 (esbuild)
esbuild는 각 핸들러를 단일 ESM 파일로 컴파일합니다.- 동일한
node_modules가 모든 Lambda ZIP에 중복 포함되는 것을 방지하기 위해, 공유 의존성은 Lambda Layer에 배치합니다 (프로젝트 전체에 하나의 레이어). - 각 핸들러는 깔끔한 단일 JS 파일로 번들링되고, 레이어가 런타임에
node_modules를 제공합니다. - 현재까지 pnpm 프로젝트와도 안정적으로 동작합니다.
3. 상태 비교
- 도구는 AWS에 이미 존재하는 리소스를 확인하고, 코드가 선언한 내용과 비교합니다.
- 차이점만 적용됩니다 — 본질적으로 CloudFormation이 하는 일과 비슷하지만, 프로비저닝 엔진 오버헤드가 없습니다.
- 가장 큰 개념적 난관은 적절한 비교 granularity를 찾고, 멱등성을 보장하는 업데이트를 구현하는 것이었습니다.
4. 리소스 프로비저닝 (AWS SDK + Effect‑TS)
- 직접적인 AWS SDK 호출을 통해 리소스(Lambda 함수, DynamoDB 테이블, API Gateway 라우트, IAM 정책)를 생성, 업데이트 또는 재구성합니다.
- 모든 AWS 호출은 타입이 지정된 Effect 래퍼를 통해 이루어지므로, 가능한 모든 오류가 사전에 알려집니다.
- 함수가 이미 존재하면 → 로그를 남기고 계속 진행.
- 내부 AWS 오류가 발생하면 → 배포를 중단.
프로젝트 상태
- effortless‑aws는 현재 알파 단계입니다.
- 이미 프로덕션에서 사용되고 있습니다:
- 문서 웹사이트.
- Lambda 함수, DynamoDB 테이블, 스트림 및 트리거를 사용하는 개인 프로젝트.
- 모든 것이 배포되고 실행되지만, 아직 다듬어야 할 부분이 남아 있으며 활발히 개발 중입니다.
로드맵
- 새로운 핸들러 (예: S3 정적 웹사이트, EventBridge).
- 개선된 diff 알고리즘.
- 더 나은 TypeScript 사용성.
설치 및 리소스
npm install effortless-aws
- GitHub:
- 웹사이트 및 문서:
프로젝트 구축에 사용된 도구들
| 도구 | 목적 |
|---|---|
| Effect‑TS | TypeScript용 타입이 지정된 함수형 프로그래밍 |
| ts‑morph | TypeScript AST 분석 및 조작 |
| esbuild | 빠른 JavaScript/TypeScript 번들러 |
| AWS SDK for JavaScript (v3) | 공식 AWS SDK |
| Typed AWS SDK wrapper | AWS SDK에 대한 타입이 지정된 오류를 포함한 Effect 래퍼 |