AWS CDK: Stack

김기현·2026년 1월 30일

AWS

목록 보기
28/44

AWS CDK에서 Stack은 CloudFormation의 Stack과 1:1로 매핑되는 배포 단위입니다.

1. Stack의 위치: App -Stage - Stack - Construct

  • App: 전체 CDK 애플리케이션의 루트(Root)
  • Stage: (선택 사항) 개발, 테스트, 운영 환경을 구분하는 논리적 단위
  • Stack: 배포의 최소 단위. 리소스들을 묶어 하나의 단위로 AWS에 전달한다.
  • Construct: Stack 내부를 구성하는 개별의 리소스(S3, EC2, Lambda 등)

2. Stack의 주요 특징

① 배포 단위 (Unit of Deployment)

CDK 명령어로 cdk deploy를 실행할 때 대상이 되는 것은 항상 Stack이다. 특정 리소스 하나만 배포하는 것이 아니라 그 리소스가 속한 Stack 전체가 하나의 트랜잭션으로 배포된다.

② 리전 및 계정 설정 (Environment)

하나의 Stack은 하나의 AWS 계정과 리전에 귀속된다.

  • 만약 서울(ap-northeast-2)과 버지니아(us-east-1)에 각각 리소스를 배포해야 한다면, 최소 2개의 Stack이 필요하다.

③ 리소스 제한

AWS CloudFormation의 제약을 그대로 따른다. 하나의 Stack에는 최대 500개의 리소스만 포함할 수 있다. (대규모 프로젝트에서 Stack 분할이 필요한 이유이다.)


3. Stack 간 데이터 전달 (Cross-Stack References)

서로 다른 Stack끼리 데이터를 주고받을 때 두 가지 방식이 있다.

  1. 직접 참조 (Automatic Exports)
    • Stack A에서 생성한 리소스를 Stack B에서 생성자 인자로 넘길 때 발생한다.
    • 주의: Stack B가 Stack A의 값을 쓰고 있으면 Stack A를 수정하거나 삭제할 때 의존성 에러가 발생하여 관리가 까다로워질 수 있다.
  2. 파라미터 스토어 사용: 의존성을 끊기 위해 Stack A에서 값을 SSM Parameter Store에 저장하고 Stack B에서 이를 읽어오는 방식을 실무에서 선호한다.

4. 실무 Stack 설계 전략

✅ 서비스 논리 단위로 분리

모든 것을 한 Stack에 담지 마세요. 보통 다음과 같이 분리합니다.

  • Network Stack: VPC, Subnet, IGW (자주 변하지 않음)
  • Data Stack: RDS, ElastiCache, S3 (데이터 보존이 중요함)
  • App Stack: ECS, Lambda, API Gateway (자주 업데이트됨

✅ 상태 유무에 따른 분리 (Stateful vs Stateless)

  • Stateful: DB, S3 같이 데이터가 담긴 리소스. (실수로 삭제되면 재앙)
  • Stateless: Lambda, API Gateway 같이 언제든 지우고 다시 만들어도 되는 리소스.
  • 이 둘을 분리하면 배포 위험을 크게 줄일 수 있습니다.

✅ 환경별 매개변수화

동일한 Stack 클래스를 사용하여 개발(Dev)과 운영(Prod) 환경을 다르게 구성한다.

public class MyCdkApp {
    public static void main(final String[] args) {
        software.amazon.awscdk.App app = new software.amazon.awscdk.App();

        // [환경별 매개변수화] 
        // 계정 ID와 리전 정보를 객체로 정의하여 환경을 분리합니다.
        Environment devEnv = Environment.builder()
                .account("123456789012")
                .region("ap-northeast-2")
                .build();

        Environment prodEnv = Environment.builder()
                .account("987654321098")
                .region("us-east-1")
                .build();

        // 개발 환경 배포
        NetworkStack devNetwork = new NetworkStack(app, "DevNetworkStack", 
            StackProps.builder().env(devEnv).build());
            
        new AppStack(app, "DevAppStack", 
            StackProps.builder().env(devEnv).build(), devNetwork.getVpc());

        // 운영 환경 배포 (다른 사양 적용 가능)
        new NetworkStack(app, "ProdNetworkStack", 
            StackProps.builder().env(prodEnv).build());

        app.synth();
    }
}

/**
 * 네트워크 인프라 스택 (네트워크 정보를 Export하는 역할)
 */
public class NetworkStack extends Stack {
    private final IVpc vpc;

    public NetworkStack(final Construct scope, final String id, final StackProps props) {
        super(scope, id, props);

        // VPC 생성 (L2 Construct)
        this.vpc = Vpc.Builder.create(this, "MyVPC")
                .maxAzs(2)
                .build();

        // [CloudFormation Export 방식]
        // 다른 스택에서 참조할 수 있도록 VPC ID를 Export합니다.
        // 배포 시 CloudFormation의 'Outputs' 탭에 나타납니다.
        CfnOutput.Builder.create(this, "VpcIdOutput")
                .value(this.vpc.getVpcId())
                .exportName("MySharedVpcId") // 이것이 Export 이름이 됩니다.
                .build();
    }

    public IVpc getVpc() {
        return this.vpc;
    }
}

/**
 * 애플리케이션 스택 (네트워크 정보를 Import하여 사용하는 역할)
 */
public class AppStack extends Stack {
    public AppStack(final Construct scope, final String id, final StackProps props, IVpc vpc) {
        super(scope, id, props);

        // 1. 직접 객체를 넘겨받는 방식 (CDK가 내부적으로 ImportValue를 생성)
        System.out.println("참조된 VPC ID: " + vpc.getVpcId());

        // 2. 만약 다른 프로젝트의 스택이라면 아래와 같이 이름을 지정해 가져옵니다.
        // IVpc importedVpc = Vpc.fromLookup(this, "ImportedVPC", VpcLookupOptions.builder()
        //         .vpcId(Fn.importValue("MySharedVpcId"))
        //         .build());
    }
}

5. 핵심 체크리스트

  • Termination Protection: 운영 환경 Stack에는 실수로 삭제되지 않도록 terminationProtection: true 설정을 했는가?
  • Idempotency: Stack을 여러 번 배포해도 동일한 결과가 나오는가? (Hard-coding 금지)
  • Tagging: 리소스 추적을 위해 Stack 수준에서 태그(Project, Stage 등)를 일괄 적용했는가?
  • Diff 확인: 배포 전 cdk diff를 통해 변경 사항을 확인하는 습관을 가졌는가?
profile
백엔드 개발자를 목표로 공부하는 대학생

0개의 댓글