Use Cases

Claude Code × AWS CloudFormation/CDK 완전 가이드 | 인프라를 코드로 자동 생성

Claude Code로 AWS Infrastructure as Code를 빠르게. CloudFormation 템플릿, CDK TypeScript 스택, 멀티 스택 설계를 위한 실동 코드 — Masa의 실제 프로덕션 경험 기반.

“인프라 설정할 때마다 AWS 콘솔을 클릭하는 게 너무 귀찮다” — 이런 생각, 한 번쯤 해보셨죠? CloudFormation과 CDK를 사용하면 인프라를 코드로 재현 가능하게 만들 수 있지만, 그 템플릿을 작성하는 데 시간이 걸립니다.

저는 이 사이트의 인프라(Cloudflare Pages + Workers + D1)를 Claude Code로 관리하고 있고, AWS 비즈니스 인프라 설계에서도 CloudFormation/CDK 생성을 Claude Code에 맡기고 있습니다. 수동으로 작성하던 시절의 1/5 시간에 템플릿이 완성되게 됐습니다.


CloudFormation vs CDK: 어느 쪽을 선택할까?

CloudFormation: JSON/YAML로 인프라를 기술하는 AWS 네이티브 서비스
CDK (Cloud Development Kit): TypeScript/Python 등으로 인프라를 기술하고
                              CloudFormation으로 변환하는 프레임워크
비교CloudFormationCDK
언어JSON / YAMLTypeScript, Python, Java 등
타입 안전성없음우수 (TypeScript라면 완전 타입 안전)
재사용성낮음 (복사-붙여넣기)높음 (클래스·함수로 추상화)
학습 비용낮음중간
Claude Code 호환성우수최우수

신규 프로젝트라면 CDK TypeScript를 강력히 추천합니다. Claude Code와의 궁합이 탁월하고, 타입 자동완성과 결합해 품질 높은 코드가 생성됩니다.


Step 1: 기존 AWS 리소스를 CloudFormation으로 코드화

기존 AWS 환경을 나중에 코드화하고 싶을 때 사용할 수 있는 패턴입니다.

claude -p "
다음 AWS 구성을 CloudFormation 템플릿(YAML)으로 만들어줘.

[현재 구성]
- VPC: 10.0.0.0/16, 퍼블릭 서브넷 x2, 프라이빗 서브넷 x2
- EC2: t3.medium, Amazon Linux 2023, 퍼블릭 서브넷에 배치
- RDS: MySQL 8.0, db.t3.micro, 프라이빗 서브넷에 배치
- ALB: HTTPS (ACM 인증서), EC2로 포워드
- 보안 그룹: ALB에서 443, EC2에서 RDS 3306만 허용

[요구사항]
- 환경(dev/staging/prod)을 파라미터로 전환 가능
- 리소스 이름에 환경 이름을 프리픽스로 붙이기
- CloudFormation Output에 ALB의 DNS 이름 출력
"

생성된 CloudFormation 템플릿 (발췌):

AWSTemplateFormatVersion: "2010-09-09"
Description: "Web Application Infrastructure"

Parameters:
  Environment:
    Type: String
    AllowedValues: [dev, staging, prod]
    Default: dev
  DBPassword:
    Type: String
    NoEcho: true  # 비밀번호는 마스크 처리

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub "${Environment}-vpc"

  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: !Select [0, !GetAZs ""]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub "${Environment}-public-1"

  # ALB 보안 그룹
  ALBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: ALB Security Group
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0

Outputs:
  ALBDnsName:
    Value: !GetAtt ApplicationLoadBalancer.DNSName
    Export:
      Name: !Sub "${Environment}-alb-dns"

Step 2: CDK로 서버리스 구성 구축

CDK는 TypeScript로 작성할 수 있어 로직 재사용이 간단합니다.

# CDK 프로젝트 초기화
mkdir my-infra && cd my-infra
npx cdk init app --language typescript

claude -p "
lib/my-infra-stack.ts에 다음 서버리스 구성을 CDK TypeScript로 구현해줘.

[구성]
- API Gateway (REST API) + Lambda (Node.js 20)
- DynamoDB 테이블 (PAY_PER_REQUEST)
- S3 버킷 (프라이빗, 버저닝 활성화)
- CloudFront (S3 오리진, 커스텀 도메인 지원)
- Lambda 환경 변수에 DynamoDB 테이블 이름과 S3 버킷 이름 전달
- Lambda에 최소 권한 IAM 역할 자동 부여

각 리소스에 적절한 태그를 부여하고,
프로덕션 환경(NODE_ENV=production)에서는 삭제 보호를 활성화할 것.
"

생성된 CDK 코드:

// lib/my-infra-stack.ts
import * as cdk from "aws-cdk-lib";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as apigateway from "aws-cdk-lib/aws-apigateway";
import * as dynamodb from "aws-cdk-lib/aws-dynamodb";
import * as s3 from "aws-cdk-lib/aws-s3";
import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
import * as origins from "aws-cdk-lib/aws-cloudfront-origins";

export class MyInfraStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const isProd = process.env.NODE_ENV === "production";

    // DynamoDB 테이블
    const table = new dynamodb.Table(this, "AppTable", {
      partitionKey: { name: "PK", type: dynamodb.AttributeType.STRING },
      sortKey: { name: "SK", type: dynamodb.AttributeType.STRING },
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
      pointInTimeRecovery: true,
      deletionProtection: isProd,
      removalPolicy: isProd ? cdk.RemovalPolicy.RETAIN : cdk.RemovalPolicy.DESTROY,
    });

    // S3 버킷
    const bucket = new s3.Bucket(this, "AssetsBucket", {
      versioned: true,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      encryption: s3.BucketEncryption.S3_MANAGED,
      deletionProtection: isProd,
      removalPolicy: isProd ? cdk.RemovalPolicy.RETAIN : cdk.RemovalPolicy.DESTROY,
    });

    // Lambda 함수
    const apiLambda = new lambda.Function(this, "ApiLambda", {
      runtime: lambda.Runtime.NODEJS_20_X,
      handler: "index.handler",
      code: lambda.Code.fromAsset("src/lambda"),
      environment: {
        TABLE_NAME: table.tableName,
        BUCKET_NAME: bucket.bucketName,
        NODE_ENV: process.env.NODE_ENV ?? "development",
      },
      timeout: cdk.Duration.seconds(30),
      memorySize: 512,
    });

    // Lambda에 최소 권한 부여
    table.grantReadWriteData(apiLambda);
    bucket.grantReadWrite(apiLambda);

    // API Gateway
    const api = new apigateway.RestApi(this, "AppApi", {
      restApiName: "MyApp API",
      defaultCorsPreflightOptions: {
        allowOrigins: apigateway.Cors.ALL_ORIGINS,
        allowMethods: apigateway.Cors.ALL_METHODS,
      },
    });

    api.root.addProxy({
      defaultIntegration: new apigateway.LambdaIntegration(apiLambda),
    });

    // CloudFront
    const distribution = new cloudfront.Distribution(this, "Distribution", {
      defaultBehavior: {
        origin: new origins.S3Origin(bucket),
        viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
      },
    });

    // 출력
    new cdk.CfnOutput(this, "ApiUrl", { value: api.url });
    new cdk.CfnOutput(this, "CloudFrontUrl", { value: distribution.distributionDomainName });
  }
}

Step 3: 기존 스택 업데이트와 차이 확인

# 배포 전에 차이 확인
npx cdk diff

# Claude Code에게 차이를 설명받기
claude -p "
cdk diff 출력을 읽고, 이번 변경 내용을 한국어로 설명해줘.
특히 보안 그룹·IAM 정책 변경은 자세히 설명할 것.

$(npx cdk diff 2>&1)
"

Step 4: 멀티 스택 구성 설계

프로덕션 규모의 인프라는 스택을 분할해 관리합니다.

claude -p "
다음 멀티 스택 구성을 CDK TypeScript로 설계해줘.

[스택 분할 방침]
- NetworkStack: VPC, 서브넷, 보안 그룹
- DatabaseStack: RDS, ElastiCache (NetworkStack에 의존)
- ApplicationStack: ECS, ALB, AutoScaling (NetworkStack, DatabaseStack에 의존)
- MonitoringStack: CloudWatch 대시보드, 알람, SNS 알림

스택 간 의존 관계와 CfnOutput / Fn.importValue 사용법도 포함해서
"
// bin/app.ts
import * as cdk from "aws-cdk-lib";
import { NetworkStack } from "../lib/network-stack";
import { DatabaseStack } from "../lib/database-stack";
import { ApplicationStack } from "../lib/application-stack";
import { MonitoringStack } from "../lib/monitoring-stack";

const app = new cdk.App();
const env = { account: process.env.CDK_ACCOUNT, region: "ap-northeast-2" };

const network = new NetworkStack(app, "NetworkStack", { env });
const database = new DatabaseStack(app, "DatabaseStack", { env, vpc: network.vpc });
const application = new ApplicationStack(app, "ApplicationStack", {
  env,
  vpc: network.vpc,
  database: database.cluster,
});
new MonitoringStack(app, "MonitoringStack", {
  env,
  alb: application.alb,
  database: database.cluster,
});

Step 5: CloudFormation/CDK 디버깅을 Claude Code에 맡기기

배포 실패 시 오류도 Claude Code가 해결해줍니다.

claude -p "
CDK deploy가 다음 오류로 실패하고 있어.
원인과 수정 방법을 알려줘:

$(npx cdk deploy 2>&1 | tail -30)
"

자주 발생하는 실패 패턴:

  • ROLLBACK_COMPLETE: 이전 배포가 실패해 스택이 망가진 상태 → cdk destroy 후 재배포
  • UPDATE_ROLLBACK_FAILED: 수동 변경과 충돌 → AWS Console에서 수동 해결
  • Resource already exists: 기존 리소스와 이름 충돌 → 리소스 이름 변경 또는 import

주의해야 할 함정 4가지

1. cdk destroy로 삭제 보호를 우회하는 실수

개발 환경에서 removalPolicy: DESTROY로 설정하지 않으면 cdk destroy 후에도 리소스가 남습니다. 프로덕션에서는 RETAIN으로 설정해 실수로 인한 삭제를 방지하세요.

2. CloudFormation 드리프트 감지를 무시하는 실수

AWS 콘솔에서 수동으로 리소스를 변경하면 CloudFormation과 실제 상태가 어긋납니다(드리프트). 월 1회 CloudFormation 콘솔에서 드리프트 감지를 실행하세요.

3. 시크릿을 템플릿에 직접 쓰는 실수

# 위험: 비밀번호가 템플릿에 남음
DBPassword: "mysecretpassword"

# 안전: Secrets Manager 또는 Parameter Store에서 가져오기
DBPassword: !Sub "{{resolve:secretsmanager:prod/db-password}}"

4. CDK 버전 고정을 소홀히 하는 실수

package.json의 CDK 버전을 팀 내에서 맞추지 않으면 차이가 발생합니다. package-lock.json을 커밋해 버전을 통일하세요.


정리

작업Claude Code의 역할
CloudFormation 생성요구사항을 전달하기만 하면 완성된 YAML
CDK 구현TypeScript로 타입 안전한 인프라 코드
스택 설계멀티 스택의 의존 관계 설계
차이 설명cdk diff 출력을 한국어로 해설
디버깅오류 로그에서 원인과 수정 방법 제시

인프라 코드화는 “해야 하는데 계속 미루게 되는” 작업입니다. Claude Code를 사용하면 “이런 AWS 구성이 필요하다”고 말하기만 해도 템플릿이 완성되므로, IaC 도입의 문턱이 극적으로 낮아집니다.

관련 글

참고 자료

#claude-code #aws #cloudformation #cdk #iac #typescript

Claude Code 워크플로우를 한 단계 업그레이드하세요

지금 바로 Claude Code에 복사해 쓸 수 있는 검증된 프롬프트 템플릿 50선.

무료 제공

무료 PDF: 5분 완성 Claude Code 치트시트

이메일 주소만 등록하시면 A4 한 장짜리 치트시트 PDF를 즉시 보내드립니다.

개인정보는 엄격하게 관리하며 스팸은 보내지 않습니다.

Masa

이 글을 작성한 사람

Masa

Claude Code를 적극 활용하는 엔지니어. 10개 언어, 2,000페이지 이상의 테크 미디어 claudecode-lab.com을 운영 중.