

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# 계층 2 구문
<a name="layer-2"></a>

[AWS CDK 오픈 소스 리포지토리](https://github.com/aws/aws-cdk)는 주로 [TypeScript](https://www.typescriptlang.org/) 프로그래밍 언어를 사용하여 작성되며 다양한 패키지와 모듈로 구성됩니다. `aws-cdk-lib`라는 기본 패키지 라이브러리는 대략적으로 AWS 서비스당 하나의 패키지로 분할되지만, 항상 그렇지는 않습니다. 앞서 설명한 대로 L1 구문은 빌드 프로세스 중에 자동으로 생성됩니다. 그렇다면 리포지토리 내부를 살펴볼 때 표시되는 모든 코드는 무엇인가요? 이는 L1 구문의 추상화에 해당하는 [L2 구문](https://docs.aws.amazon.com/cdk/v2/guide/constructs.html#constructs_using)입니다.

패키지에는 TypeScript 유형, 열거형 및 인터페이스의 컬렉션과 더 많은 기능을 추가하는 헬퍼 클래스가 포함됩니다. 이러한 항목은 모두 L2 구문도 지원합니다. 모든 L2 구문은 인스턴스화할 때 생성자에서 해당 L1 구문을 직접 호출하고, 그 결과 생성된 L1 구문은 다음과 같이 계층 2에서 액세스할 수 있습니다.

```
const role = new Bucket(this, "amzn-s3-demo-bucket", {/*...BucketProps*/});
const cfnBucket = role.node.defaultChild;
```

L2 구문은 기본 속성, 편의 메서드 및 기타 간결한 구문(syntactic sugar)을 사용하여 L1 구문에 적용합니다. 이 방식에서는 CloudFormation에서 리소스를 직접 프로비저닝하는 데 필요한 많은 반복과 상세 정보가 제거됩니다.

모든 L2 구문은 해당 L1 구문을 후드 아래에 빌드합니다. 그러나 L2 구문은 실제로 L1 구문을 확장하지 않습니다. L1 및 L2 구문 모두 [Construct](https://docs.aws.amazon.com/cdk/api/v2/docs/constructs.Construct.html)라는 특수 클래스를 상속합니다. AWS CDK의 버전 1에서는 `Construct` 클래스가 개발 기트에 빌드되지만, 버전 2에서는 별도의 [독립형 패키지](https://www.npmjs.com/package/constructs)에 해당합니다. 그래서 [Cloud Development Kit for Terraform(CDKTF)](https://developer.hashicorp.com/terraform/cdktf)과 같은 다른 패키지가 이를 종속성으로 포함할 수 있습니다. `Construct` 클래스를 상속하는 모든 클래스는 L1, L2 또는 L3 구문입니다. 다음 표와 같이 L2 구문은 이 클래스를 직접 확장하는 반면, L1 구문은 `CfnResource`라는 클래스를 확장합니다.


| 
| 
| **L1 상속 트리** | **L2 상속 트리** | 
| --- |--- |
| *L1 구문**→ 클래스 *[https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.CfnResource.html](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.CfnResource.html)*→→ 추상 클래스 *[https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.CfnRefElement.html](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.CfnRefElement.html)*→→→ 추상 클래스 *[https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.CfnElement.html](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.CfnElement.html)*→→→→ 클래스 *[https://docs.aws.amazon.com/cdk/api/v2/docs/constructs.Construct.html](https://docs.aws.amazon.com/cdk/api/v2/docs/constructs.Construct.html) | *L2 구문**→ 클래스 *[https://docs.aws.amazon.com/cdk/api/v2/docs/constructs.Construct.html](https://docs.aws.amazon.com/cdk/api/v2/docs/constructs.Construct.html) | 

L1 및 L2 구문이 모두 `Construct` 클래스를 상속하는 경우 L2 구문이 L1을 확장하지 않는 이유는 무엇인가요? `Construct` 클래스와 계층 1 사이의 클래스는 CloudFormation 리소스의 미러 이미지로 L1 구문을 제자리에서 잠급니다. 여기에는 `_toCloudFormation`과 같은 추상 메서드(다운스트림 클래스가 포함해야 하는 **필수** 메서드)가 포함되며, 이는 구문에서 CloudFormation 구문을 직접 강제 출력합니다. L2 구문은 해당 클래스를 건너뛰고 `Construct` 클래스를 직접 확장합니다. 이 경우 해당 생성자 내에서 별도로 빌드하여 L1 구문에 필요한 대부분의 코드를 추상화하는 유연성을 활용할 수 있습니다.

이전 섹션에서는 CloudFormation 템플릿의 S3 버킷과 L1 구문으로 렌더링된 동일한 S3 버킷을 항목별로 비교했습니다. 이 비교를 통해 속성과 구문은 거의 동일했지만, L1 구문은 CloudFormation 구문에 비해 3\$14줄만 저장하는 것으로 나타났습니다. 이제 L1 구문을 동일한 S3 버킷의 L2 구문과 비교해 보겠습니다.


| 
| 
| **S3 버킷에 대한 L1 구문** | **S3 버킷에 대한 L2 구문** | 
| --- |--- |
| <pre>new CfnBucket(this, "amzns3demobucket", {<br />  bucketName: "amzn-s3-demo-bucket",<br />  bucketEncryption: {<br />    serverSideEncryptionConfiguration: [<br />      {<br />        serverSideEncryptionByDefault: {<br />          sseAlgorithm: "AES256"<br />        }<br />      }<br />    ]<br />  },<br />  metricsConfigurations: [<br />    {<br />      id: "myConfig"<br />    }<br />  ],<br />  ownershipControls: {<br />    rules: [<br />      {<br />        objectOwnership: "BucketOwnerPreferred"<br />      }<br />    ]<br />  },<br />  publicAccessBlockConfiguration: {<br />    blockPublicAcls: true,<br />    blockPublicPolicy: true,<br />    ignorePublicAcls: true,<br />    restrictPublicBuckets: true<br />  },<br />  versioningConfiguration: {<br />    status: "Enabled"<br />  }<br />});</pre> | <pre>new Bucket(this, "amzns3demobucket", {<br />  bucketName: "amzn-s3-demo-bucket",<br />  encryption: BucketEncryption.S3_MANAGED,<br />  metrics: [<br />    {<br />      id: "myConfig"<br />    },<br />  ],<br />  objectOwnership: ObjectOwnership.BUCKET_OWNER_PREFERRED,<br />  blockPublicAccess: BlockPublicAccess.BLOCK_ALL,<br />  versioned: true<br />});</pre> | 

보시다시피 L2 구문은 L1 구문 크기의 절반 미만입니다. L2 구문은 이러한 통합을 위해 많은 기술을 사용합니다. 이러한 기법 중 일부는 단일 L2 구문에 적용되지만, 재사용성을 위해 자체 클래스로 분리되도록 여러 구문에서 재사용할 수 있는 기법도 있습니다. L2 구문은 다음 섹션에서 설명한 대로 여러 가지 방법으로 CloudFormation 구문을 통합합니다.

## 기본 속성
<a name="l2-default-properties"></a>

리소스를 프로비저닝하기 위해 코드를 통합하는 가장 간단한 방법은 가장 일반적인 속성 설정을 기본값으로 설정하는 것입니다. AWS CDK는 강력한 프로그래밍 언어에 액세스할 수 있고 CloudFormation은 액세스할 수 없으므로, 이러한 기본값은 종종 조건부로 적용됩니다. AWS CDK 코드에서 여러 줄의 CloudFormation 구성을 제거할 수 있는 경우가 있습니다. 해당 설정은 구문에 전달되는 다른 속성의 값에서 CloudFormation 구성을 추론할 수 있기 때문입니다.

## 구조, 유형 및 인터페이스
<a name="l2-structs"></a>

AWS CDK는 여러 프로그래밍 언어로 제공되지만 기본적으로 TypeScript로 작성되므로 언어 유형 시스템을 사용하여 L2 구문을 구성하는 유형을 정의합니다. 이 가이드에서는 해당 유형 시스템을 자세히 살펴보지 않습니다. 자세한 내용은 [TypeScript 설명서](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html)를 참조하세요. 요약하자면 TypeScript `type`은 특정 변수가 보유한 데이터의 종류를 설명합니다. 이는 `string`과 같은 기본 데이터 또는 `object`와 같은 보다 복잡한 데이터일 수 있습니다. TypeScript `interface`는 TypeScript 객체 유형을 표현하는 또 다른 방법이며, `struct`는 인터페이스의 또 다른 이름입니다.

TypeScript는 *구조체*라는 용어를 사용하지 않지만 [AWS CDK API 참조](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-construct-library.html)를 보면 구조체가 실제로 코드 내 다른 TypeScript 인터페이스임을 알 수 있습니다. API 참조는 특정 인터페이스도 인터페이스로 참조합니다. 구조체와 인터페이스가 동일하다면 AWS CDK 설명서에서 구조체와 인터페이스를 구분하는 이유는 무엇인가요?

AWS CDK에서 *구조체*라고 하는 요소는 L2 구문에서 사용하는 객체를 나타내는 인터페이스입니다. 여기에는 인스턴스화 중에 L2 구문에 전달되는 속성 인수의 객체 유형(예: S3 버킷 구문의 경우 `BucketProps`, DynamoDB 테이불 구문의 경우 `TableProps`)과 AWS CDK에서 사용되는 기타 TypeScript 인터페이스가 포함됩니다. 즉, AWS CDK 내 TypeScript 인터페이스이고 이름 앞에 `I` 접두사가 붙지 않은 경우 AWS CDK에서는 이를 *구조체*라고 합니다.

반대로, AWS CDK에서는 일반 객체를 특정 구문이나 헬퍼 클래스의 적절한 표현으로 간주해야 하는 기본 요소를 나타내기 위해 *인터페이스*라는 용어를 사용합니다. 즉, 인터페이스는 L2 구문의 퍼블릭 속성이 어떠해야 하는지 설명합니다. 모든 AWS CDK 인터페이스 이름은 `I` 문자 접두사가 추가된 기존 구문 또는 헬퍼 클래스의 이름입니다. 모든 L2 구문은 `Construct` 클래스를 확장하지만 대응하는 인터페이스도 구현합니다. 따라서 L2 구문 `Bucket`은 `IBucket` 인터페이스를 구현합니다.

## 정적 메서드
<a name="l2-static-methods"></a>

L2 구문의 모든 인스턴스는 해당 인터페이스의 인스턴스이기도 하지만, 반대의 경우에는 해당되지 않습니다. 이는 구조체를 보면서 필요한 데이터 유형을 확인할 때 중요합니다. 구조체에 데이터 유형 `IBucket`이 필요한 `bucket` 속성이 있는 경우 L2 `Bucket`의 인스턴스 또는 `IBucket` 인터페이스에 나열된 속성이 포함된 객체를 전달할 수 있습니다. 둘 중 하나가 작동합니다. 그러나 해당 `bucket` 속성이 L2 `Bucket`에 대해 직접 호출되는 경우 해당 필드에서 `Bucket` 인스턴스만 전달할 수 있습니다.

기존 리소스를 스택으로 가져올 때 이러한 구분이 매우 중요합니다. 스택에서 기본적인 리소스에 대해 L2 구문을 생성할 수 있지만 스택 외부에서 생성된 리소스를 참조해야 하는 경우 해당 L2 구문의 인터페이스를 사용해야 합니다. L2 구문을 생성하면 해당 스택 내 아직 없는 경우 새 리소스가 생성되기 때문입니다. 기존 리소스에 대한 참조는 해당 L2 구문의 인터페이스를 준수하는 일반 객체여야 합니다.

실제로 더 쉽게 수행하기 위해 대부분의 L2 구문에는 해당 L2 구문의 인터페이스를 반환하는 정적 메서드 세트가 연결되어 있습니다. 이러한 정적 메서드는 일반적으로 `from` 단어로 시작합니다. 이러한 메서드에 전달되는 처음 두 인수는 표준 L2 구문에 필요한 `scope` 및 `id` 인수와 동일합니다. 그러나 세 번째 인수는 `props`가 아니라 인터페이스를 정의하는 속성의 작은 하위 세트(또는 경우에 따라 하나의 속성)입니다. 이러한 이유로 L2 구문을 전달할 때 대부분의 경우 인터페이스의 요소만 필요합니다. 그러면 가능한 경우 가져온 리소스도 사용할 수 있습니다.

```
// Example of referencing an external S3 bucket
const preExistingBucket = Bucket.fromBucketName(this, "external-bucket", "name-of-bucket-that-already-exists");
```

그러나 인터페이스에 크게 의존해서는 안 됩니다. 인터페이스는 L2 구문 성능을 더욱 강화하는 헬퍼 메서드와 같은 많은 속성을 제공하지 않으므로 반드시 필요한 경우에만 리소스를 가져오고 인터페이스를 직접 사용해야 합니다.

## 도우미 메서드
<a name="l2-helper-methods"></a>

L2 구문은 단순한 객체가 아닌 프로그래밍 방식의 클래스이므로 인스턴스화된 후 리소스 구성을 조작할 수 있는 클래스 메서드를 노출할 수 있습니다. 이에 대한 좋은 예는 AWS Identity and Access Management(IAM) L2 [Role](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_iam.Role.html) 구문입니다. 다음 코드 조각에서는 L2 `Role` 구문을 사용하여 동일한 IAM 역할을 생성하는 두 가지 방법을 보여줍니다.

헬퍼 메서드가 없는 경우:

```
const role = new Role(this, "my-iam-role", {
   assumedBy: new FederatedPrincipal('my-identity-provider.com'),
   managedPolicies: [
      ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess")
   ],
   inlinePolicies: {
      lambdaPolicy: new PolicyDocument({
         statements: [
            new PolicyStatement({
               effect: Effect.ALLOW,
               actions: [ 'lambda:UpdateFunctionCode' ],
               resources: [ 'arn:aws:lambda:us-east-1:123456789012:function:my-function' ]
            })
         ]
      })
   }
});
```

헬퍼 메서드가 있는 경우:

```
const role = new Role(this, "my-iam-role", {
   assumedBy: new FederatedPrincipal('my-identity-provider.com')
});
   
role.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess"));
role.attachInlinePolicy(new Policy(this, "lambda-policy", {
   policyName: "lambdaPolicy",
   statements: [
      new PolicyStatement({
         effect: Effect.ALLOW,
         actions: [ 'lambda:UpdateFunctionCode' ],
         resources: [ 'arn:aws:lambda:us-east-1:123456789012:function:my-function' ]
      })
   ]
 }));
```

인스턴스화된 후 인스턴스 메서드를 사용하여 리소스 구성을 조작하는 기능을 통해 L2 구문은 이전 계층에 비해 많은 추가 유연성을 제공합니다. 또한 L1 구문은 일부 리소스 메서드(예: `addPropertyOverride`)도 상속하지만 계층 2까지는 해당 리소스 및 해당 속성에 맞게 특별히 설계된 메서드를 얻을 수 없습니다.

## Enums
<a name="l2-enums"></a>

CloudFormation 구문을 사용하려면 리소스를 올바르게 프로비저닝하기 위해 종종 많은 세부 정보를 지정해야 합니다. 그러나 몇 개의 구성으로도 대부분의 사용 사례를 지원할 수 있습니다. 일련의 열거형 값을 사용하여 이러한 구성을 표현하면 필요한 코드 양을 크게 줄일 수 있습니다.

예를 들어 이 섹션 앞부분에 나온 S3 버킷 L2 코드 예제에서는 CloudFormation 템플릿의 `bucketEncryption` 속성을 사용하여 사용할 암호화 알고리즘의 이름을 포함한 모든 세부 정보를 제공해야 합니다. AWS CDK에서는 대신 `BucketEncryption` 열거형을 제공합니다. 여기에서는 가장 일반적인 5가지 형태의 버킷 암호화를 사용하고 단일 변수 이름을 사용하여 각각을 표현할 수 있습니다.

열거형에서 다루지 않는 엣지 사례는 어떤가요? L2 구문의 목표 중 하나는 계층 1 리소스를 프로비저닝하는 태스크를 단순화하는 것이므로, 덜 일반적으로 사용되는 특정 엣지 사례는 계층 2에서 지원되지 않을 수 있습니다. 이러한 엣지 사례를 지원하기 위해 AWS CDK에서는 [addPropertyOverride](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.CfnResource.html#addwbrpropertywbroverridepropertypath-value) 메서드를 사용하여 기본 CloudFormation 리소스 속성을 직접 조작할 수 있습니다. 속성 재정의에 대한 자세한 내용은 이 가이드의 [모범 사례](best-practices.md) 섹션과 AWS CDK 설명서의 [Abstractions and escape hatches](https://docs.aws.amazon.com/cdk/v2/guide/cfn_layer.html)를 참조하세요.

## 헬퍼 클래스
<a name="l2-helper-classes"></a>

때로는 열거형을 통해 지정된 사용 사례에 맞게 리소스를 구성하는 데 필요한 프로그래밍 로직을 수행하지 못할 수 있습니다. 이러한 상황에서 AWS CDK는 대신 헬퍼 클래스를 제공하곤 합니다. 열거형은 일련의 키 값 페어를 제공하는 간단한 객체인 반면, 헬퍼 클래스는 TypeScript 클래스의 전체 기능을 제공합니다. 헬퍼 클래스는 여전히 정적 속성을 노출하여 열거형과 같이 작동할 수 있지만, 이러한 속성은 헬퍼 클래스 생성자 또는 헬퍼 메서드에서 조건부 로직을 통해 내부적으로 해당 값을 설정할 수 있습니다.

따라서 `BucketEncryption` 열거형은 S3 버킷에서 암호화 알고리즘을 설정하는 데 필요한 코드 양을 줄일 수 있지만, 선택할 수 있는 값이 너무 많기 때문에 기간을 설정하는 데 동일한 전략이 작동하지 않습니다. 각 값에 열거형을 생성하는 작업은 이점보다 단점이 더 많습니다. 이러한 이유로 [ObjectLockRetention](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.ObjectLockRetention.html) 클래스로 표시되는 S3 버킷의 기본 S3 Object Lock 구성 설정에 헬퍼 클래스가 사용됩니다. `ObjectLockRetention`에는 두 가지 정적 메서드(규정 준수 보존을 위한 메서드 및 거버넌스 보존을 위한 메서드)가 있습니다. 두 메서드 모두 [Duration 헬퍼 클래스](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Duration.html)의 인스턴스를 인수로 사용하여 잠금을 구성해야 하는 시간을 표시합니다.

또 다른 예로 AWS Lambda 헬퍼 클래스 [Runtime](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.Runtime.html)이 있습니다. 얼핏 보면 이 클래스와 연결된 정적 속성을 열거형에서 처리할 수 있는 것처럼 보입니다. 그러나 자세히 보면 각 속성 값은 `Runtime` 클래스 자체의 인스턴스를 나타내므로, 클래스 생성자에서 수행된 로직은 열거형 내에서 달성할 수 없습니다.