1. AWS CDK 개요
🤔 CDK가 뭔가요?
비유로 이해하기: CDK는 프로그래밍 언어로 인프라를 만드는 도구입니다.
- 기존: YAML/JSON으로 인프라 정의 (CloudFormation)
- CDK: TypeScript, Python, Java 등으로 인프라 정의!
- 익숙한 언어로 AWS 리소스를 코드처럼 작성
CDK = Cloud Development Kit
┌─────────────────────────────────────────────────────────────────┐
│ CDK의 역할 │
│ │
│ 기존 방식 (CloudFormation YAML): │
│ ┌────────────────────────────────────────────────────┐ │
│ │ AWSTemplateFormatVersion: '2010-09-09' │ │
│ │ Resources: │ │
│ │ MyBucket: │ │
│ │ Type: AWS::S3::Bucket │ │
│ │ Properties: │ │
│ │ BucketName: my-bucket │ │
│ │ ...50줄 더... │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ CDK 방식 (TypeScript): │
│ ┌────────────────────────────────────────────────────┐ │
│ │ const bucket = new s3.Bucket(this, 'MyBucket', { │ │
│ │ bucketName: 'my-bucket' │ │
│ │ }); │ │
│ └────────────────────────────────────────────────────┘ │
│ │ │
│ ↓ cdk synth │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ CloudFormation Template (자동 생성!) │ │
│ └────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘CDK 특징
| 특징 | 설명 | 초보자 설명 |
|---|---|---|
| 프로그래밍 언어 | TypeScript, Python, Java, .NET | 익숙한 언어 사용! |
| Constructs | 고수준 컴포넌트 | 레고 블록처럼 조립 |
| CloudFormation 변환 | 코드 → CF 템플릿 | 자동으로 YAML 생성 |
| 인프라 + 앱 코드 | 함께 배포 가능 | Lambda, ECS 배포 편리 |
지원 언어
| 언어 | 지원 |
|---|---|
| TypeScript | ✅ (가장 인기) |
| JavaScript | ✅ |
| Python | ✅ |
| Java | ✅ |
| .NET (C#) | ✅ |
| Go | ✅ |
2. CDK 동작 방식
CDK 워크플로우
┌─────────────────────────────────────────────────────────────────┐
│ CDK 워크플로우 │
│ │
│ 1. CDK 코드 작성 │
│ ┌─────────────────────────────────────────┐ │
│ │ // lib/my-stack.ts │ │
│ │ export class MyStack extends Stack { │ │
│ │ constructor(...) { │ │
│ │ const bucket = new s3.Bucket(...); │ │
│ │ const lambda = new lambda.Function │ │
│ │ } │ │
│ │ } │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ↓ cdk synth │
│ │
│ 2. CloudFormation 템플릿 생성 │
│ ┌─────────────────────────────────────────┐ │
│ │ cdk.out/MyStack.template.json │ │
│ │ (자동 생성된 CloudFormation 템플릿) │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ↓ cdk deploy │
│ │
│ 3. CloudFormation 배포 │
│ ┌─────────────────────────────────────────┐ │
│ │ AWS CloudFormation │ │
│ │ → S3 Bucket 생성 │ │
│ │ → Lambda Function 생성 │ │
│ │ → IAM Role 생성 │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘CDK vs CloudFormation vs SAM
| 항목 | CloudFormation | SAM | CDK |
|---|---|---|---|
| 언어 | YAML/JSON | YAML/JSON | 프로그래밍 언어 |
| 범위 | 모든 AWS | 서버리스 중심 | 모든 AWS |
| 추상화 | 낮음 | 중간 | 높음 |
| 재사용 | 중첩 스택 | - | Constructs |
| IDE 지원 | 제한적 | 제한적 | 자동완성, 타입체크 |
3. CDK vs SAM
🤔 언제 뭘 써야 하나요?
비교표
| 항목 | SAM | CDK |
|---|---|---|
| 주요 용도 | 서버리스 (Lambda) | 모든 AWS 서비스 |
| 작성 방식 | 선언형 (YAML) | 명령형 (코드) |
| 학습 곡선 | 낮음 | 중간 |
| 유연성 | 제한적 | 매우 높음 |
| 타입 안전성 | ❌ | ✅ (TypeScript) |
| IDE 지원 | 제한적 | 강력함 |
선택 가이드
┌─────────────────────────────────────────────────────────────────┐
│ SAM vs CDK 선택 │
│ │
│ Q1. 서버리스 앱만 만드나요? (Lambda + API Gateway) │
│ ├─ Yes → SAM (빠르게 시작) │
│ └─ No → CDK │
│ │
│ Q2. 복잡한 인프라가 필요한가요? (VPC, RDS, ECS...) │
│ ├─ Yes → CDK │
│ └─ No → SAM │
│ │
│ Q3. 프로그래밍 언어로 인프라를 관리하고 싶나요? │
│ ├─ Yes → CDK │
│ └─ No → SAM 또는 CloudFormation │
│ │
│ Q4. 타입 안전성과 IDE 자동완성이 중요한가요? │
│ ├─ Yes → CDK (TypeScript) │
│ └─ No → 둘 다 OK │
│ │
└─────────────────────────────────────────────────────────────────┘CDK + SAM 함께 사용
CDK로 만든 앱을 SAM CLI로 로컬 테스트 가능!
┌─────────────────────────────────────────────────────────────────┐
│ CDK + SAM 조합 │
│ │
│ 1. CDK 앱 작성 │
│ ┌─────────────────────────────────────────┐ │
│ │ const fn = new lambda.Function(...) │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ↓ cdk synth │
│ │
│ 2. CloudFormation 템플릿 생성 │
│ ┌─────────────────────────────────────────┐ │
│ │ cdk.out/MyCDKStack.template.json │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ↓ SAM CLI │
│ │
│ 3. 로컬 테스트 │
│ ┌─────────────────────────────────────────┐ │
│ │ sam local invoke \ │ │
│ │ -t cdk.out/MyCDKStack.template.json \│ │
│ │ myFunction │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘4. CDK Constructs
🤔 Construct가 뭔가요?
비유로 이해하기: 레고 블록과 같습니다.
- L1 블록: 가장 기본 블록 (1x1 브릭)
- L2 블록: 미리 조립된 블록 (자동차 바퀴)
- L3 블록: 완성된 세트 (자동차 전체)
Construct 레벨
┌─────────────────────────────────────────────────────────────────┐
│ Construct Levels │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ L3 Constructs (Patterns) │ │
│ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ │
│ │ • 여러 리소스의 패턴 │ │
│ │ • 예: LambdaRestApi, ApplicationLoadBalancedFargateService│ │
│ │ • 가장 높은 추상화 수준 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↑ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ L2 Constructs (High-level) │ │
│ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ │
│ │ • AWS 리소스의 고수준 표현 │ │
│ │ • 편리한 기본값 제공 │ │
│ │ • 예: s3.Bucket, lambda.Function │ │
│ │ • 메서드 제공: bucket.addLifeCycleRule() │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↑ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ L1 Constructs (CFN Resources) │ │
│ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ │
│ │ • CloudFormation 리소스와 1:1 매핑 │ │
│ │ • 모든 속성 직접 설정 필요 │ │
│ │ • 이름이 Cfn으로 시작: CfnBucket, CfnFunction │ │
│ │ • 가장 낮은 추상화 수준 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘L1, L2, L3 코드 비교
L1 Construct (CFN Resources)
// L1: CfnBucket (CloudFormation 리소스 직접 사용)
// 모든 속성을 직접 설정해야 함
import { CfnBucket } from "aws-cdk-lib/aws-s3";
const bucket = new CfnBucket(this, "MyBucket", {
bucketName: "my-bucket",
versioningConfiguration: {
status: "Enabled",
},
publicAccessBlockConfiguration: {
blockPublicAcls: true,
blockPublicPolicy: true,
ignorePublicAcls: true,
restrictPublicBuckets: true,
},
});L2 Construct (High-level)
// L2: Bucket (편리한 기본값 + 메서드)
// 훨씬 간단하고, 보안 설정이 기본으로 적용됨!
import * as s3 from "aws-cdk-lib/aws-s3";
const bucket = new s3.Bucket(this, "MyBucket", {
bucketName: "my-bucket",
versioned: true, // 간단한 플래그
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, // 편의 상수
});
// 편리한 메서드 제공!
bucket.addLifecycleRule({
expiration: Duration.days(90),
});L3 Construct (Patterns)
// L3: LambdaRestApi (API Gateway + Lambda 한 번에!)
// 복잡한 아키텍처를 한 줄로!
import * as apigateway from "aws-cdk-lib/aws-apigateway";
const api = new apigateway.LambdaRestApi(this, "MyApi", {
handler: myLambdaFunction,
// API Gateway, Lambda 권한, 통합 등이 모두 자동 설정!
});
// 또는 Fargate 패턴
import * as ecsPatterns from "aws-cdk-lib/aws-ecs-patterns";
const fargate = new ecsPatterns.ApplicationLoadBalancedFargateService(this, "MyService", {
taskImageOptions: {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
},
// ALB + Fargate + ECS Cluster + IAM Role 등 모두 자동!
});Construct 레벨 비교
| 레벨 | 이름 형식 | 추상화 | 설정 | 사용 시기 |
|---|---|---|---|---|
| L1 | CfnXxx | 낮음 | 모든 속성 직접 | CF에 없는 속성 필요 시 |
| L2 | Xxx | 중간 | 편리한 기본값 | 일반적인 경우 |
| L3 | XxxPattern | 높음 | 최소 설정 | 표준 아키텍처 |
Construct Hub
AWS가 제공하는 Construct 외에도
3rd party, 오픈소스 Construct 사용 가능!
https://constructs.dev
예시:
- cdk-dynamo-table-viewer: DynamoDB 테이블 뷰어 UI
- cdk-watchful: 자동 CloudWatch 대시보드
- cdk-nag: 보안/컴플라이언스 검사5. CDK 주요 명령어
CDK CLI 명령어
| 명령어 | 설명 |
|---|---|
npm install -g aws-cdk | CDK CLI 전역 설치 |
cdk init app | 새 CDK 프로젝트 생성 |
cdk synth | CloudFormation 템플릿 생성 |
cdk bootstrap | CDK 배포 환경 준비 |
cdk deploy | 스택 배포 |
cdk diff | 로컬 vs 배포된 스택 비교 |
cdk destroy | 스택 삭제 |
cdk ls | 스택 목록 |
명령어 상세
1️⃣ 프로젝트 초기화
# 새 CDK 프로젝트 생성 (TypeScript)
mkdir my-cdk-app
cd my-cdk-app
cdk init app --language typescript
# 생성되는 구조
my-cdk-app/
├── bin/
│ └── my-cdk-app.ts # 엔트리 포인트
├── lib/
│ └── my-cdk-app-stack.ts # 스택 정의
├── test/
│ └── my-cdk-app.test.ts # 테스트
├── cdk.json # CDK 설정
├── package.json
└── tsconfig.json2️⃣ CloudFormation 템플릿 생성
# 템플릿 생성 (cdk.out/ 디렉토리에 저장)
cdk synth
# 출력 예시
Resources:
MyBucketF68F3FF0:
Type: AWS::S3::Bucket
Properties:
BucketName: my-bucket
...3️⃣ Bootstrap (환경 준비)
# 각 AWS 계정/리전에 처음 1회 필요!
cdk bootstrap aws://123456789012/ap-northeast-2
# CDKToolkit 스택이 생성됨:
# - S3 버킷 (아티팩트 저장)
# - IAM 역할 (배포 권한)4️⃣ 배포
# 스택 배포
cdk deploy
# 모든 스택 배포
cdk deploy --all
# 특정 스택 배포
cdk deploy MyStack
# 승인 없이 배포 (CI/CD용)
cdk deploy --require-approval never5️⃣ 차이점 확인
# 로컬 코드 vs 배포된 스택 비교
cdk diff
# 출력 예시
Stack MyStack
Resources
[~] AWS::S3::Bucket MyBucket MyBucketF68F3FF0
└─ [+] VersioningConfiguration
└─ {"Status":"Enabled"}6. CDK Bootstrapping
🤔 Bootstrapping이 뭔가요?
비유로 이해하기: CDK를 사용하기 전 기초 공사입니다.
- 건물 짓기 전에 기초를 다져야 하듯이
- CDK 배포 전에 S3 버킷, IAM Role 등을 미리 생성
Bootstrap 프로세스
┌─────────────────────────────────────────────────────────────────┐
│ CDK Bootstrap │
│ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ cdk bootstrap aws://123456789012/ap-northeast-2 │ │
│ └─────────────────────────┬─────────────────────────┘ │
│ │ │
│ ↓ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ CloudFormation Stack │ │
│ │ "CDKToolkit" │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────┐ │ │
│ │ │ S3 Bucket │ │ │
│ │ │ - 아티팩트 저장 (Lambda 코드, Assets 등) │ │ │
│ │ └────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────┐ │ │
│ │ │ IAM Roles │ │ │
│ │ │ - CloudFormation 실행 역할 │ │ │
│ │ │ - 배포 역할 │ │ │
│ │ │ - 파일 업로드 역할 │ │ │
│ │ └────────────────────────────────────────────┘ │ │
│ │ │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ ⚠️ 각 AWS 계정 + 리전 조합마다 1회 필요! │
│ │
└─────────────────────────────────────────────────────────────────┘Bootstrap이 안 되어 있으면?
Error: Policy contains a statement with one or more invalid principal
→ cdk bootstrap을 먼저 실행하세요!여러 환경 Bootstrap
# 개발 환경
cdk bootstrap aws://123456789012/ap-northeast-2
# 스테이징 환경
cdk bootstrap aws://123456789012/us-east-1
# 프로덕션 환경 (다른 계정)
cdk bootstrap aws://987654321098/ap-northeast-27. CDK Testing
🤔 CDK 앱도 테스트할 수 있나요?
비유로 이해하기: 일반 코드처럼 단위 테스트 가능!
- CDK 코드 → CloudFormation 템플릿 생성
- 템플릿의 내용을 검증
테스트 종류
| 테스트 | 설명 | 용도 |
|---|---|---|
| Fine-grained Assertions | 특정 리소스/속성 검증 | 세부 검증 |
| Snapshot Tests | 전체 템플릿 비교 | 변경 감지 |
Fine-grained Assertions (세부 검증)
// test/my-stack.test.ts
import * as cdk from "aws-cdk-lib";
import { Template, Match } from "aws-cdk-lib/assertions";
import { MyStack } from "../lib/my-stack";
describe("MyStack", () => {
test("S3 Bucket Created", () => {
// 1. 스택 생성
const app = new cdk.App();
const stack = new MyStack(app, "TestStack");
// 2. 템플릿 추출
const template = Template.fromStack(stack);
// 3. 검증: S3 버킷이 존재하는지
template.hasResourceProperties("AWS::S3::Bucket", {
BucketName: "my-bucket",
VersioningConfiguration: {
Status: "Enabled",
},
});
});
test("Lambda Function with Correct Memory", () => {
const app = new cdk.App();
const stack = new MyStack(app, "TestStack");
const template = Template.fromStack(stack);
// Lambda 함수의 메모리가 512MB인지 확인
template.hasResourceProperties("AWS::Lambda::Function", {
MemorySize: 512,
Runtime: "nodejs18.x",
});
});
test("Correct Number of Resources", () => {
const app = new cdk.App();
const stack = new MyStack(app, "TestStack");
const template = Template.fromStack(stack);
// S3 버킷이 정확히 2개인지 확인
template.resourceCountIs("AWS::S3::Bucket", 2);
});
});Snapshot Tests (스냅샷 테스트)
// test/my-stack.test.ts
import * as cdk from "aws-cdk-lib";
import { Template } from "aws-cdk-lib/assertions";
import { MyStack } from "../lib/my-stack";
test("Snapshot Test", () => {
const app = new cdk.App();
const stack = new MyStack(app, "TestStack");
const template = Template.fromStack(stack);
// 스냅샷과 비교
expect(template.toJSON()).toMatchSnapshot();
});┌─────────────────────────────────────────────────────────────────┐
│ Snapshot Test 동작 │
│ │
│ 첫 실행: │
│ ┌─────────────────────────────────────────┐ │
│ │ 현재 템플릿 → 스냅샷으로 저장 │ │
│ │ __snapshots__/my-stack.test.ts.snap │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 이후 실행: │
│ ┌─────────────────────────────────────────┐ │
│ │ 현재 템플릿 vs 저장된 스냅샷 │ │
│ │ → 다르면 테스트 실패! │ │
│ │ → 의도된 변경이면 스냅샷 업데이트 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ npm test -- -u (스냅샷 업데이트) │
│ │
└─────────────────────────────────────────────────────────────────┘Template 가져오는 방법
| 메서드 | 용도 |
|---|---|
Template.fromStack(stack) | CDK로 만든 스택 |
Template.fromString(json) | 외부 템플릿 문자열 |
8. CDK 실전 예시
S3 + Lambda + DynamoDB 예시
// lib/my-stack.ts
import * as cdk from "aws-cdk-lib";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as dynamodb from "aws-cdk-lib/aws-dynamodb";
import * as s3n from "aws-cdk-lib/aws-s3-notifications";
import { Construct } from "constructs";
export class MyStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// 1. DynamoDB 테이블
const table = new dynamodb.Table(this, "ResultsTable", {
partitionKey: { name: "id", type: dynamodb.AttributeType.STRING },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
removalPolicy: cdk.RemovalPolicy.DESTROY, // 개발용
});
// 2. Lambda 함수
const fn = new lambda.Function(this, "ProcessImageFunction", {
runtime: lambda.Runtime.NODEJS_18_X,
handler: "index.handler",
code: lambda.Code.fromAsset("lambda"), // lambda/ 폴더의 코드
environment: {
TABLE_NAME: table.tableName,
},
timeout: cdk.Duration.seconds(30),
memorySize: 512,
});
// 3. Lambda에 DynamoDB 권한 부여
table.grantWriteData(fn);
// 4. S3 버킷
const bucket = new s3.Bucket(this, "ImageBucket", {
removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: true, // 개발용
});
// 5. S3 → Lambda 트리거
bucket.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.LambdaDestination(fn));
// 6. 출력
new cdk.CfnOutput(this, "BucketName", {
value: bucket.bucketName,
});
}
}프로젝트 구조
my-cdk-app/
├── bin/
│ └── my-cdk-app.ts
├── lib/
│ └── my-stack.ts
├── lambda/ # Lambda 코드
│ ├── index.ts
│ └── package.json
├── test/
│ └── my-stack.test.ts
├── cdk.json
├── package.json
└── tsconfig.jsonL3 Construct 사용 예시 (API Gateway + Lambda)
// API Gateway + Lambda 한 번에!
import * as apigateway from "aws-cdk-lib/aws-apigateway";
export class ApiStack extends cdk.Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
const fn = new lambda.Function(this, "HelloFunction", {
runtime: lambda.Runtime.NODEJS_18_X,
handler: "index.handler",
code: lambda.Code.fromInline(`
exports.handler = async () => ({
statusCode: 200,
body: JSON.stringify({ message: 'Hello!' })
});
`),
});
// L3 Pattern: API Gateway + Lambda 통합
const api = new apigateway.LambdaRestApi(this, "HelloApi", {
handler: fn,
// API Gateway, Lambda 권한, 통합 모두 자동!
});
new cdk.CfnOutput(this, "ApiUrl", {
value: api.url,
});
}
}Fargate 서비스 예시 (L3 Pattern)
import * as ecs from "aws-cdk-lib/aws-ecs";
import * as ecsPatterns from "aws-cdk-lib/aws-ecs-patterns";
export class FargateStack extends cdk.Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
// L3 Pattern: ALB + Fargate 한 번에!
const service = new ecsPatterns.ApplicationLoadBalancedFargateService(this, "MyService", {
taskImageOptions: {
image: ecs.ContainerImage.fromRegistry("nginx"),
},
publicLoadBalancer: true,
// VPC, ECS Cluster, ALB, Target Group, Security Group 모두 자동!
});
// Auto Scaling 추가도 간단!
service.service
.autoScaleTaskCount({
minCapacity: 1,
maxCapacity: 10,
})
.scaleOnCpuUtilization("CpuScaling", {
targetUtilizationPercent: 50,
});
}
}핵심 요약
CDK vs SAM vs CloudFormation
| 항목 | CloudFormation | SAM | CDK |
|---|---|---|---|
| 언어 | YAML/JSON | YAML | 프로그래밍 언어 |
| 범위 | 모든 AWS | 서버리스 | 모든 AWS |
| 추상화 | 낮음 | 중간 | 높음 |
| 타입 안전성 | ❌ | ❌ | ✅ |
Construct 레벨
| 레벨 | 예시 | 특징 |
|---|---|---|
| L1 | CfnBucket | CF 리소스 1:1 |
| L2 | Bucket | 편리한 기본값 |
| L3 | LambdaRestApi | 완성된 패턴 |
주요 명령어
| 명령어 | 용도 |
|---|---|
cdk init | 프로젝트 생성 |
cdk synth | CF 템플릿 생성 |
cdk bootstrap | 환경 준비 (1회) |
cdk deploy | 배포 |
cdk diff | 차이 확인 |
cdk destroy | 삭제 |