

버전 4(V4) AWS SDK for .NET 가 릴리스되었습니다.

변경 사항 해제 및 애플리케이션 마이그레이션에 대한 자세한 내용은 [마이그레이션 주제를](https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/net-dg-v4.html) 참조하세요.

 [https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/net-dg-v4.html](https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/net-dg-v4.html)

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

# 에서 AWS 서비스 호출 AWS SDK for .NET
<a name="working-with-aws-services"></a>

다음 섹션에는를 사용하여 AWS 서비스를 AWS SDK for .NET 사용하는 방법을 보여주는 예제, 자습서, 작업 및 가이드가 포함되어 있습니다. 이러한 예제와 자습서는 AWS SDK for .NET 에서 제공하는 API를 기반으로 합니다. API에서 사용할 수 있는 클래스와 메서드를 확인하려면 [AWS SDK for .NET API 참조](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/)를 참조하세요.

를 처음 사용하는 경우 먼저 [간단한 애플리케이션 생성](quick-start.md) 주제를 확인할 AWS SDK for .NET수 있습니다. SDK에 대한 소개를 제공합니다.

[AWS 코드 예제 리포지토리](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv4) 및 GitHub의 [awslabs 리포지토리](https://github.com/awslabs/aws-sdk-net-samples)에서 더 많은 코드 예제를 찾을 수 있습니다.

시작하기 전에 [환경을 설정하고](net-dg-config.md) [프로젝트를 구성](configuring-the-sdk.md)했는지 확인합니다. 또한 [SDK 사용](net-dg-sdk-features.md)의 정보를 검토합니다.

**Topics**
+ [가이드 코드 예제](tutorials-examples.md)
+ [AWS Lambda](aws-lambda.md)
+ [개요 수준 라이브러리 및 프레임워크](high-level-libraries.md)
+ [기타 서비스 및 구성](other-apis-intro.md)

# 에 대한 가이드 코드 예제 AWS SDK for .NET
<a name="tutorials-examples"></a>

다음 섹션에는 코드 예제가 포함되어 있으며 예제에 대한 지침을 제공합니다. 이를 통해를 사용하여 AWS 서비스를 AWS SDK for .NET 사용하는 방법을 배울 수 있습니다. 추가 코드 예제는 [코드 예제](csharp_code_examples.md) 섹션을 참조하세요.

**참고**  
의 V3와 관련된 코드 예제 AWS SDK for .NET 도 사용할 수 있습니다. 이를 찾으려면 AWS SDK for .NET (V3) 개발자 안내서의 [지침이 포함된 코드 예제](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/tutorials-examples.html)와 [코드 예제](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/csharp_code_examples.html)를 참조하세요. 그러나 SDK의 V3-specific 코드 예제를 사용하는 경우의 정보에 따라 조정해야 할 수 있습니다[버전 4로 마이그레이션](net-dg-v4.md). V4 

를 처음 사용하는 경우 먼저 [간단한 애플리케이션 생성](quick-start.md) 주제를 확인할 AWS SDK for .NET수 있습니다. SDK에 대한 소개를 제공합니다.

시작하기 전에 [환경을 설정하고](net-dg-config.md) [프로젝트를 구성](configuring-the-sdk.md)했는지 확인합니다. 또한 [SDK 사용](net-dg-sdk-features.md)의 정보를 검토합니다.

**Topics**
+ [CloudFormation](cloudformation-apis-intro.md)
+ [Amazon Cognito](cognito-apis-intro.md)
+ [DynamoDB](dynamodb-intro.md)
+ [Amazon EC2](ec2-apis-intro.md)
+ [IAM](iam-apis-intro.md)
+ [Amazon S3](s3-apis-intro.md)
+ [Amazon SNS](sns-apis-intro.md)
+ [Amazon SQS](sqs-apis-intro.md)

# 를 CloudFormation 사용하여 액세스 AWS SDK for .NET
<a name="cloudformation-apis-intro"></a>

는 AWS 인프라 배포를 예측 가능하고 반복적으로 생성하고 프로비저닝[AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/)하는를 AWS SDK for .NET 지원합니다.

## API
<a name="w2aac19c15c15b5"></a>

는 CloudFormation 클라이언트를 위한 APIs AWS SDK for .NET 제공합니다. APIs 사용하면 템플릿 및 스택과 같은 CloudFormation 기능을 사용할 수 있습니다. 이 섹션에는 이러한 API로 작업할 때 따를 수 있는 패턴을 보여주는 몇 가지 예제가 포함되어 있습니다. 전체 API 세트를 보려면 [AWS SDK for .NET API 참조](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/)를 참조하세요(그리고 "Amazon.CloudFormation"으로 스크롤하세요).

 AWS CloudFormation APIs는 [AWSSDK.CloudFormation](https://www.nuget.org/packages/AWSSDK.CloudFormation/) 패키지에서 제공됩니다.

## 사전 조건
<a name="w2aac19c15c15b7"></a>

시작하기 전에 [환경을 설정하고](net-dg-config.md) [프로젝트를 구성](configuring-the-sdk.md)했는지 확인합니다. 또한 [SDK 사용](net-dg-sdk-features.md)의 정보를 검토합니다.

## 주제
<a name="w2aac19c15c15b9"></a>

**Topics**
+ [

## API
](#w2aac19c15c15b5)
+ [

## 사전 조건
](#w2aac19c15c15b7)
+ [

## 주제
](#w2aac19c15c15b9)
+ [AWS 리소스 나열](cfn-list-resources.md)

# 를 사용하여 AWS 리소스 나열 AWS CloudFormation
<a name="cfn-list-resources"></a>

이 예제에서는를 사용하여 CloudFormation 스택의 리소스를 나열 AWS SDK for .NET 하는 방법을 보여줍니다. 이 예제에서는 하위 수준 API를 사용합니다. 애플리케이션에 인수가 없지만 단순히 사용자 보안 인증으로 액세스할 수 있는 모든 스택에 대한 정보를 수집한 다음 해당 스택에 대한 정보를 표시합니다.

## SDK 레퍼런스
<a name="w2aac19c15c15c13b5b1"></a>

NuGet 패키지:
+ [AWSSDK.CloudFormation](https://www.nuget.org/packages/AWSSDK.CloudFormation/)

프로그래밍 요소:
+ 네임스페이스 [Amazon.CloudFormation](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/CloudFormation/NCloudFormation.html)

  클래스 [AmazonCloudFormationClient](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/CloudFormation/TCloudFormationClient.html)
+ 네임스페이스 [Amazon.CloudFormation.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/CloudFormation/NCloudFormationModel.html)

  클래스 [ICloudFormationPaginatorFactory.DescribeStacks](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/CloudFormation/MICloudFormationPaginatorFactoryDescribeStacksDescribeStacksRequest.html)

  클래스 [DescribeStackResourcesRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/CloudFormation/TDescribeStackResourcesRequest.html)

  클래스 [DescribeStackResourcesResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/CloudFormation/TDescribeStackResourcesResponse.html)

  클래스 [Stack](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/CloudFormation/TStack.html)

  클래스 [StackResource](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/CloudFormation/TStackResource.html)

  클래스 [Tag](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/CloudFormation/TTag.html)

```
using Amazon.CloudFormation;
using Amazon.CloudFormation.Model;
using Amazon.Runtime;

namespace CloudFormationActions;

public static class HelloCloudFormation
{
    public static IAmazonCloudFormation _amazonCloudFormation;

    static async Task Main(string[] args)
    {
        // Create the CloudFormation client
        _amazonCloudFormation = new AmazonCloudFormationClient();
        Console.WriteLine($"\nIn Region: {_amazonCloudFormation.Config.RegionEndpoint}");

        // List the resources for each stack
        await ListResources();
    }

    /// <summary>
    /// Method to list stack resources and other information.
    /// </summary>
    /// <returns>True if successful.</returns>
    public static async Task<bool> ListResources()
    {
        try
        {
            Console.WriteLine("Getting CloudFormation stack information...");

            // Get all stacks using the stack paginator.
            var paginatorForDescribeStacks =
                _amazonCloudFormation.Paginators.DescribeStacks(
                    new DescribeStacksRequest());
            await foreach (Stack stack in paginatorForDescribeStacks.Stacks)
            {
                // Basic information for each stack
                Console.WriteLine("\n------------------------------------------------");
                Console.WriteLine($"\nStack: {stack.StackName}");
                Console.WriteLine($"  Status: {stack.StackStatus.Value}");
                Console.WriteLine($"  Created: {stack.CreationTime}");

                // The tags of each stack (etc.)
                if (stack.Tags.Count > 0)
                {
                    Console.WriteLine("  Tags:");
                    foreach (Tag tag in stack.Tags)
                        Console.WriteLine($"    {tag.Key}, {tag.Value}");
                }

                // The resources of each stack
                DescribeStackResourcesResponse responseDescribeResources =
                    await _amazonCloudFormation.DescribeStackResourcesAsync(
                        new DescribeStackResourcesRequest
                        {
                            StackName = stack.StackName
                        });
                if (responseDescribeResources.StackResources.Count > 0)
                {
                    Console.WriteLine("  Resources:");
                    foreach (StackResource resource in responseDescribeResources
                                 .StackResources)
                        Console.WriteLine(
                            $"    {resource.LogicalResourceId}: {resource.ResourceStatus}");
                }
            }

            Console.WriteLine("\n------------------------------------------------");
            return true;
        }
        catch (AmazonCloudFormationException ex)
        {
            Console.WriteLine("Unable to get stack information:\n" + ex.Message);
            return false;
        }
        catch (AmazonServiceException ex)
        {
            if (ex.Message.Contains("Unable to get IAM security credentials"))
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine("If you are usnig SSO, be sure to install" +
                                  " the AWSSDK.SSO and AWSSDK.SSOOIDC packages.");
            }
            else
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
            }

            return false;
        }
        catch (ArgumentNullException ex)
        {
            if (ex.Message.Contains("Options property cannot be empty: ClientName"))
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine("If you are using SSO, have you logged in?");
            }
            else
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
            }

            return false;
        }
    }
```

# Amazon Cognito로 사용자 인증
<a name="cognito-apis-intro"></a>

**참고**  
이 주제의 정보는 .NET Framework 및 AWS SDK for .NET 버전 3.3 이하를 기반으로 하는 프로젝트에만 해당됩니다.

Amazon Cognito 자격 증명을 사용하면 사용자를 위한 고유한 자격 증명을 생성하고 Amazon S3 또는 Amazon DynamoDB와 같은 AWS 리소스에 대한 보안 액세스를 위해 사용자를 인증할 수 있습니다. Amazon Cognito Identity는 Amazon, Facebook, Twitter/Digits, Google 또는 OpenID Connect 호환 공급자와 같은 퍼블릭 ID 공급자와 인증되지 않은 자격 증명을 지원합니다. 또한 Amazon Cognito는 [개발자 인증 자격 증명](https://aws.amazon.com/blogs/mobile/amazon-cognito-announcing-developer-authenticated-identities/)을 지원하여 Amazon Cognito Sync를 사용하여 사용자 데이터를 동기화하고 AWS 리소스에 액세스하면서도 자체 백엔드 인증 프로세스를 사용하여 사용자를 등록하고 인증할 수 있게 합니다.

[Amazon Cognito](https://aws.amazon.com/cognito/)에 대한 자세한 내용은 [Amazon Cognito 개발자 안내서](https://docs.aws.amazon.com/cognito/latest/developerguide/)를 참조하세요.

다음 코드 예시는 Amazon Cognito 자격 증명을 쉽게 사용하는 방법을 보여줍니다. [자격 증명 공급자](cognito-creds-provider.md) 예에서는 사용자 자격 증명을 생성하고 인증하는 방법을 보여줍니다. [CognitoAuthentication 확장 라이브러리](cognito-authentication-extension.md) 예제에서는 CognitoAuthentication 확장 라이브러리를 사용하여 Amazon Cognito 사용자 풀을 인증하는 방법을 보여줍니다.

**Topics**
+ [자격 증명 공급자](cognito-creds-provider.md)
+ [CognitoAuthentication 확장 라이브러리](cognito-authentication-extension.md)

# Amazon Cognito 보안 인증 공급자
<a name="cognito-creds-provider"></a>

**참고**  
이 주제의 정보는 .NET Framework 및 AWS SDK for .NET 버전 3.3 이하를 기반으로 하는 프로젝트에만 해당됩니다.

 `Amazon.CognitoIdentity.CognitoAWSCredentials`[AWSSDK.CognitoIdentity](https://www.nuget.org/packages/AWSSDK.CognitoIdentity/) NuGet 패키지에 있는는 Amazon Cognito 및 AWS Security Token Service (AWS STS)를 사용하여 AWS 호출할 자격 증명을 검색하는 자격 증명 객체입니다.

`CognitoAWSCredentials` 설정의 첫 번째 단계는 "자격 증명 풀"을 생성하는 것입니다. (자격 증명 풀은 계정에 관련된 사용자 자격 증명 정보의 저장소입니다. 정보는 클라이언트 플랫폼, 디바이스 및 운영 체제 간에 가져올 수 있어 사용자가 스마트폰에서 앱을 사용하다가 태블릿으로 전환하면 앱 정보가 해당 사용자에게 계속 제공됩니다. Amazon Cognito 콘솔에서 새로운 자격 증명 풀을 생성할 수 있습니다. 콘솔을 사용 중인 경우, 콘솔에서 사용자에게 필요한 다음과 같은 다른 정보 또한 제공합니다.
+ 계정 번호- A 12자리 숫자. 예: 123456789012 (사용자 계정에 고유한 숫자)
+ 인증되지 않은 역할 ARN- 인증되지 않은 사용자가 맡을 역할 예를 들어, 이 역할은 데이터에 대한 읽기 전용 권한을 제공할 수 있습니다.
+ 인증된 역할 ARN- 인증된 사용자가 맡을 역할 이 역할은 데이터에 대한 더 광범위한 권한을 제공할 수 있습니다.

## CognitoAWSCredentials 설정
<a name="set-up-cognitoawscredentials"></a>

다음 코드 예제에서는 인증되지 않은 사용자로 Amazon S3를 호출하는 데 사용할 수 있도록 `CognitoAWSCredentials`를 설정하는 방법을 보여줍니다. 이렇게 하면 사용자를 인증하기 위해 필요한 최소한의 데이터만으로도 호출할 수 있습니다. 사용자 권한은 역할에 의해 제어되므로 필요한 액세스를 구성할 수 있습니다.

```
CognitoAWSCredentials credentials = new CognitoAWSCredentials(
    accountId,        // Account number
    identityPoolId,   // Identity pool ID
    unAuthRoleArn,    // Role for unauthenticated users
    null,             // Role for authenticated users, not set
    region);
using (var s3Client = new AmazonS3Client(credentials))
{
    s3Client.ListBuckets();
}
```

## 인증되지 않은 사용자 AWS 로 사용
<a name="use-aws-as-an-unauthenticated-user"></a>

다음 코드 예제에서는 미인증 사용자 AWS 로 사용을 시작한 다음 Facebook을 통해 인증하고 자격 증명을 업데이트하여 Facebook 자격 증명을 사용하는 방법을 보여줍니다. 이 접근 방식을 사용하여 인증된 역할을 통해 인증된 사용자에게 다른 자격 증명을 부여할 수 있습니다. 예를 들어, 사용자가 익명으로 콘텐츠를 볼 수 있도록 허용하되 하나 이상의 구성된 공급자로 로그온한 경우에 게시할 수 있도록 허용하는 스마트폰 애플리케이션이 있을 수도 있습니다.

```
CognitoAWSCredentials credentials = new CognitoAWSCredentials(
    accountId, identityPoolId,
    unAuthRoleArn,    // Role for unauthenticated users
    authRoleArn,      // Role for authenticated users
    region);
using (var s3Client = new AmazonS3Client(credentials))
{
    // Initial use will be unauthenticated
    s3Client.ListBuckets();

    // Authenticate user through Facebook
    string facebookToken = GetFacebookAuthToken();

    // Add Facebook login to credentials. This clears the current AWS credentials
    // and retrieves new AWS credentials using the authenticated role.
    credentials.AddLogin("graph.facebook.com", facebookAccessToken);

    // This call is performed with the authenticated role and credentials
    s3Client.ListBuckets();
}
```

`CognitoAWSCredentials` 객체는 AWS SDK for .NET의 일부인 `AmazonCognitoSyncClient`와 함께 사용하는 경우 훨씬 더 많은 기능을 제공합니다. `AmazonCognitoSyncClient`와 `CognitoAWSCredentials`를 모두 사용 중인 경우 `IdentityPoolId`로 호출할 때 `IdentityId` 및 `AmazonCognitoSyncClient` 속성을 지정하지 않아도 됩니다. 이러한 속성은 `CognitoAWSCredentials`에서 자동으로 채워집니다. 다음 코드 예에서는 이를 보여주고, 뿐만 아니라 `IdentityId`에 대한 `CognitoAWSCredentials`가 변경될 때마다 이를 알려주는 이벤트도 보여줍니다. `IdentityId`는 인증되지 않은 사용자에서 인증된 사용자로 변경될 때와 같은 일부 경우에 변경될 수 있습니다.

```
CognitoAWSCredentials credentials = GetCognitoAWSCredentials();

// Log identity changes
credentials.IdentityChangedEvent += (sender, args) =>
{
    Console.WriteLine("Identity changed: [{0}] => [{1}]", args.OldIdentityId, args.NewIdentityId);
};

using (var syncClient = new AmazonCognitoSyncClient(credentials))
{
    var result = syncClient.ListRecords(new ListRecordsRequest
    {
        DatasetName = datasetName
        // No need to specify these properties
        //IdentityId = "...",
        //IdentityPoolId = "..."        
    });
}
```

# Amazon CognitoAuthentication 확장 라이브러리 예
<a name="cognito-authentication-extension"></a>

**참고**  
이 주제의 정보는 .NET Framework 및 AWS SDK for .NET 버전 3.3 이하를 기반으로 하는 프로젝트에만 해당됩니다.

[Amazon.Extensions.CognitoAuthentication](https://www.nuget.org/packages/Amazon.Extensions.CognitoAuthentication/) NuGet 패키지에 있는 CognitoAuthentication 확장 라이브러리는 .NET Core 및 Xamarin 개발자를 위한 Amazon Cognito 사용자 풀의 인증 프로세스를 간소화합니다. 이 라이브러리는 사용자 인증 API 직접 호출을 생성하고 전송하기 위해 Amazon Cognito Identity Provider API를 기반으로 하여 구축됩니다.

## CognitoAuthentication 확장 라이브러리 사용
<a name="using-the-cognitoauthentication-extension-library"></a>

Amazon Cognito에는 표준 인증 흐름에서 SRP(Secure Remote Password)를 통해 사용자 이름과 암호를 확인할 수 있는 기본 제공 `AuthFlow` 및 `ChallengeName` 값이 있습니다. 인증 흐름에 대한 자세한 내용은 [Amazon Cognito 사용자 풀 인증 흐름](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html)을 참조하십시오.

다음 예제에는 다음과 같은 `using` 설명문이 필요합니다.

```
// Required for all examples
using System;
using Amazon;
using Amazon.CognitoIdentity;
using Amazon.CognitoIdentityProvider;
using Amazon.Extensions.CognitoAuthentication;
using Amazon.Runtime;
// Required for the GetS3BucketsAsync example
using Amazon.S3;
using Amazon.S3.Model;
```

### 기본 사용자 인증 사용
<a name="use-basic-authentication"></a>

요청에 서명하지 않아도 되는 [AnonymousAWSCredentials](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/Runtime/TAnonymousAWSCredentials.html)를 사용하여 [AmazonCognitoIdentityProviderClient](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/CognitoIdentityProvider/TCognitoIdentityProviderClient.html)를 생성합니다. 리전을 공급할 필요가 없습니다. 리전이 제공되지 않은 경우 기본 코드가 `FallbackRegionFactory.GetRegionEndpoint()`를 호출합니다. `CognitoUserPool` 및 `CognitoUser` 객체를 생성합니다. 사용자 암호를 포함한 `StartWithSrpAuthAsync`로 `InitiateSrpAuthRequest` 메서드를 호출합니다.

```
public static async void GetCredsAsync()
{
    AmazonCognitoIdentityProviderClient provider =
        new AmazonCognitoIdentityProviderClient(new Amazon.Runtime.AnonymousAWSCredentials());
    CognitoUserPool userPool = new CognitoUserPool("poolID", "clientID", provider);
    CognitoUser user = new CognitoUser("username", "clientID", userPool, provider);
    InitiateSrpAuthRequest authRequest = new InitiateSrpAuthRequest()
    {
        Password = "userPassword"
    };

    AuthFlowResponse authResponse = await user.StartWithSrpAuthAsync(authRequest).ConfigureAwait(false);
    accessToken = authResponse.AuthenticationResult.AccessToken;

}
```

### 챌린지로 인증
<a name="authenticate-with-challenges"></a>

NewPasswordRequired 및 Multi-Factor Authentication(MFA)과 같은 챌린지로 인증 흐름을 계속하는 것 또한 간단합니다. 요구 사항은 CognitoAuthentication 객체, SRP에 대한 사용자 암호, 및 다음 챌린지(사용자에게 정보를 입력하라는 메시지를 표시한 후에 확보됨)를 위해 필요한 정보 뿐입니다. 다음 코드에서는 챌린지 유형을 확인하고 인증 흐름 동안 MFA 및 NewPasswordRequired 챌린지에 대한 적절한 응답을 얻기 위한 하나의 방법을 보여줍니다.

전과 같이 기본 인증 요청을 사용하고 `await`를 `AuthFlowResponse` 합니다. 응답이 반환된 `AuthenticationResult` 객체를 통해 받은 루프인 경우. `ChallengeName` 유형이 `NEW_PASSWORD_REQUIRED`인 경우 `RespondToNewPasswordRequiredAsync` 메서드를 호출합니다.

```
public static async void GetCredsChallengesAsync()
{
    AmazonCognitoIdentityProviderClient provider = 
        new AmazonCognitoIdentityProviderClient(new Amazon.Runtime.AnonymousAWSCredentials());
    CognitoUserPool userPool = new CognitoUserPool("poolID", "clientID", provider);
    CognitoUser user = new CognitoUser("username", "clientID", userPool, provider);
    InitiateSrpAuthRequest authRequest = new InitiateSrpAuthRequest(){
        Password = "userPassword"
    };

    AuthFlowResponse authResponse = await user.StartWithSrpAuthAsync(authRequest).ConfigureAwait(false);

    while (authResponse.AuthenticationResult == null)
    {
        if (authResponse.ChallengeName == ChallengeNameType.NEW_PASSWORD_REQUIRED)
        {
            Console.WriteLine("Enter your desired new password:");
            string newPassword = Console.ReadLine();

            authResponse = await user.RespondToNewPasswordRequiredAsync(new RespondToNewPasswordRequiredRequest()
            {
                SessionID = authResponse.SessionID,
                NewPassword = newPassword
            });
            accessToken = authResponse.AuthenticationResult.AccessToken;
        }
        else if (authResponse.ChallengeName == ChallengeNameType.SMS_MFA)
        {
            Console.WriteLine("Enter the MFA Code sent to your device:");
            string mfaCode = Console.ReadLine();

            AuthFlowResponse mfaResponse = await user.RespondToSmsMfaAuthAsync(new RespondToSmsMfaRequest()
            {
                SessionID = authResponse.SessionID,
                MfaCode = mfaCode

            }).ConfigureAwait(false);
            accessToken = authResponse.AuthenticationResult.AccessToken;
        }
        else
        {
            Console.WriteLine("Unrecognized authentication challenge.");
            accessToken = "";
            break;
        }
    }

    if (authResponse.AuthenticationResult != null)
    {
        Console.WriteLine("User successfully authenticated.");
    }
    else
    {
        Console.WriteLine("Error in authentication process.");
    }
 
}
```

### 인증 후 AWS 리소스 사용
<a name="use-aws-resources-after-authentication"></a>

사용자가 CognitoAuthentication 라이브러리를 사용하여 인증되면 다음 단계는 사용자가 적절한 AWS 리소스에 액세스할 수 있도록 허용하는 것입니다. 이렇게 하려면 Amazon Cognito 연동 자격 증명 콘솔을 통해 자격 증명 풀을 생성해야 합니다. poolID 및 clientID를 사용하여 공급자로 생성한 Amazon Cognito 사용자 풀을 지정하면, Amazon Cognito 사용자 풀 사용자가 계정에 연결된 AWS 리소스에 액세스하도록 허용할 수 있습니다. 인증되지 않은 사용자와 인증된 사용자가 서로 다른 리소스에 액세스할 수 있도록 서로 다른 역할을 지정할 수도 있습니다. 역할의 연결된 정책의 **Action** 필드에서 권한을 추가하거나 제거할 수 있는 IAM 콘솔에서 이러한 역할을 변경할 수 있습니다. 그런 다음 적절한 자격 증명 풀, 사용자 풀 및 Amazon Cognito 사용자 정보를 사용하여 다른 AWS 리소스를 호출할 수 있습니다. 다음 예제에서는 연결된 자격 증명 풀의 역할이 허용하는 다양한 Amazon S3 버킷에 액세스하여 SRP로 인증된 사용자를 보여줍니다.

```
public async void GetS3BucketsAsync()
{
    var provider = new AmazonCognitoIdentityProviderClient(new AnonymousAWSCredentials());
    CognitoUserPool userPool = new CognitoUserPool("poolID", "clientID", provider);
    CognitoUser user = new CognitoUser("username", "clientID", userPool, provider);

    string password = "userPassword";

    AuthFlowResponse context = await user.StartWithSrpAuthAsync(new InitiateSrpAuthRequest()
    {
        Password = password
    }).ConfigureAwait(false);

    CognitoAWSCredentials credentials =
        user.GetCognitoAWSCredentials("identityPoolID", RegionEndpoint.< YourIdentityPoolRegion >);

    using (var client = new AmazonS3Client(credentials))
    {
        ListBucketsResponse response =
            await client.ListBucketsAsync(new ListBucketsRequest()).ConfigureAwait(false);

        foreach (S3Bucket bucket in response.Buckets)
        {
            Console.WriteLine(bucket.BucketName);
        }
    }
}
```

## 더 많은 인증 옵션
<a name="more-authentication-options"></a>

SRP, NewPasswordRequired 및 MFA 뿐만 아니라 CognitoAuthentication 확장 라이브러리도 다음을 위해 더 쉬운 인증 흐름을 제공합니다.
+ 사용자 지정 - `StartWithCustomAuthAsync(InitiateCustomAuthRequest customRequest)`에 대한 호출로 시작합니다.
+ RefreshToken - `StartWithRefreshTokenAuthAsync(InitiateRefreshTokenAuthRequest refreshTokenRequest)`에 대한 호출로 시작합니다.
+ RefreshTokenSRP - `StartWithRefreshTokenAuthAsync(InitiateRefreshTokenAuthRequest refreshTokenRequest)`에 대한 호출로 시작합니다.
+ AdminNoSRP - `StartWithAdminNoSrpAuthAsync(InitiateAdminNoSrpAuthRequest adminAuthRequest)`에 대한 호출로 시작합니다.

원하는 흐름에 따라 적절한 메서드를 호출합니다. 그런 다음 각 메서드 호출의 `AuthFlowResponse` 객체에 제시되어 있는 챌린지로 사용자에게 프롬프트 표시를 계속합니다. 또한 MFA 챌린지에 대한 `RespondToSmsMfaAuthAsync` 및 사용자 지정 챌린지에 대한 `RespondToCustomAuthAsync`와 같은 적절한 응답 메서드를 호출합니다.

# Amazon DynamoDB NoSQL 데이터베이스 사용
<a name="dynamodb-intro"></a>

**참고**  
이 항목의 프로그래밍 모델은 .NET Framework와 .NET(Core) 모두에 제공되지만 호출 규칙은 동기식이든 비동기식이든 서로 다릅니다.

는에서 제공하는 빠른 NoSQL 데이터베이스 서비스인 Amazon DynamoDB를 AWS SDK for .NET 지원합니다 AWS. SDK는 DynamoDB와 통신하기 위해 *하위 레벨* 모델, *문서* 모델, *객체 지속성* 모델이라는 세 가지 프로그래밍 모델을 제공합니다.

다음 정보는 이러한 모델과 해당 API를 소개하고 이러한 모델과 해당 API를 어떻게, 언제 사용하는지에 대한 예제를 제공하며 AWS SDK for .NET의 추가 DynamoDB 프로그래밍 리소스에 대한 링크를 제공합니다.

## 하위 수준 모델
<a name="dynamodb-intro-apis-low-level"></a>

하위 레벨 프로그래밍 모델은 DynamoDB 서비스에 대한 직접적인 호출을 래핑합니다. [Amazon.DynamoDBv2](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/DynamoDBv2/NDynamoDBv2.html) 네임스페이스를 통해 이 모델에 액세스합니다.

세 모델 중에서 하위 수준 모델은 사용자가 코드 대부분을 작성해야 합니다. 예를 들면 .NET 데이터 형식을 동등한 DynamoDB 데이터 형식으로 전환해야 합니다. 그러나 이 모델을 사용하면 대부분의 기능에 액세스할 수 있습니다.

다음 예제에서는 하위 레벨 모델을 사용하여 DynamoDB에서 테이블을 생성하고 테이블을 수정하며 테이블에 항목을 삽입하는 방법을 보여줍니다.

### 표 생성
<a name="dynamodb-intro-apis-low-level-create-table"></a>

다음 예제에서는 `CreateTable` 클래스의 `AmazonDynamoDBClient` 메서드를 사용하여 테이블을 생성합니다. `CreateTable` 메서드에서는 필수 항목 속성 이름, 기본 키 정의, 처리 용량과 같은 특성이 저장된 `CreateTableRequest` 클래스의 인스턴스를 사용합니다. `CreateTable` 메서드는 `CreateTableResponse` 클래스의 인스턴스를 반환합니다.

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();

Console.WriteLine("Getting list of tables");
List<string> currentTables = client.ListTables().TableNames;
Console.WriteLine("Number of tables: " + currentTables.Count);
if (!currentTables.Contains("AnimalsInventory"))
{
    var request = new CreateTableRequest
    {
        TableName = "AnimalsInventory",
        AttributeDefinitions = new List<AttributeDefinition>
      {
        new AttributeDefinition
        {
          AttributeName = "Id",
          // "S" = string, "N" = number, and so on.
          AttributeType = "N"
        },
        new AttributeDefinition
        {
          AttributeName = "Type",
          AttributeType = "S"
        }
      },
        KeySchema = new List<KeySchemaElement>
      {
        new KeySchemaElement
        {
          AttributeName = "Id",
          // "HASH" = hash key, "RANGE" = range key.
          KeyType = "HASH"
        },
        new KeySchemaElement
        {
          AttributeName = "Type",
          KeyType = "RANGE"
        },
      },
        ProvisionedThroughput = new ProvisionedThroughput
        {
            ReadCapacityUnits = 10,
            WriteCapacityUnits = 5
        },
    };

    var response = client.CreateTable(request);

    Console.WriteLine("Table created with request ID: " +
      response.ResponseMetadata.RequestId);
}
```

### 테이블이 수정할 준비가 되었는지 확인
<a name="dynamodb-intro-apis-low-level-verify-table"></a>

테이블은 변경 또는 수정 전에 미리 그러한 작업을 할 준비가 되어 있어야 합니다. 다음 예제에서는 하위 레벨 모델을 사용하여 DynamoDB의 테이블이 준비되는지 확인하는 방법을 보여줍니다. 이 예제에서 확인할 대상 테이블은 `DescribeTable` 클래스의 `AmazonDynamoDBClient` 메서드를 통해 참조됩니다. 코드는 5초마다 테이블의 `TableStatus` 속성에 대한 값을 확인합니다. 상태가 `ACTIVE`로 설정되어 있으면 테이블은 수정할 준비가 된 것입니다.

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();      
var status = "";

do
{
  // Wait 5 seconds before checking (again).
  System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
        
  try
  {
    var response = client.DescribeTable(new DescribeTableRequest
    {
      TableName = "AnimalsInventory"
    });

    Console.WriteLine("Table = {0}, Status = {1}",
      response.Table.TableName,
      response.Table.TableStatus);

    status = response.Table.TableStatus;
  }
  catch (ResourceNotFoundException)
  {
    // DescribeTable is eventually consistent. So you might
    //   get resource not found. 
  }

} while (status != TableStatus.ACTIVE);
```

### 테이블에 항목 삽입
<a name="dynamodb-intro-apis-low-level-insert-item"></a>

다음 예제에서는 하위 레벨 모델을 사용하여 DynamoDB의 테이블에 두 개의 항목을 삽입합니다. 각 항목은 `PutItem` 클래스의 인스턴스를 사용해 `AmazonDynamoDBClient` 클래스의 `PutItemRequest` 메서드를 통해 삽입됩니다. `PutItemRequest` 클래스의 두 인스턴스 각각은 일련의 항목 속성 값과 함께 항목이 삽입될 테이블의 이름을 사용합니다.

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();

var request1 = new PutItemRequest
{
  TableName = "AnimalsInventory",
  Item = new Dictionary<string, AttributeValue>
  {
    { "Id", new AttributeValue { N = "1" }},
    { "Type", new AttributeValue { S = "Dog" }},
    { "Name", new AttributeValue { S = "Fido" }}
  }
};

var request2 = new PutItemRequest
{
  TableName = "AnimalsInventory",
  Item = new Dictionary<string, AttributeValue>
  {
    { "Id", new AttributeValue { N = "2" }},
    { "Type", new AttributeValue { S = "Cat" }},
    { "Name", new AttributeValue { S = "Patches" }}
  }
};
        
client.PutItem(request1);
client.PutItem(request2);
```

## 문서 모델
<a name="dynamodb-intro-apis-document"></a>

문서 프로그래밍 모델을 사용하면 DynamoDB에서 데이터 작업을 더 쉽게 수행할 수 있습니다. 이 모델은 테이블과 테이블 내 항목에 접근하기 위한 목적으로 고안되었습니다. [Amazon.DynamoDBv2.DocumentModel](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/DynamoDBv2/NDynamoDBv2DocumentModel.html) 네임스페이스를 통해 이 모델에 액세스합니다.

하위 레벨 프로그래밍 모델에 비해 문서 모델은 DynamoDB 데이터에 대해 더 쉽게 코딩할 수 있습니다. 예를 들어, 여러 가지 .NET 데이터 형식을 동등한 DynamoDB 데이터 형식으로 전환할 필요가 없습니다. 그러나 이 모델에서는 하위 수준 프로그래밍 모델과 같은 개수의 기능에 액세스하지는 못합니다. 예를 들면 이 모델을 사용하여 테이블의 항목을 생성, 검색, 업데이트 및 삭제할 수 있습니다. 그러나 테이블을 생성하려면 하위 수준 모델을 사용해야 합니다. 객체 지속성 모델에 비해 이 모델은 .NET 객체를 저장, 로드 및 쿼리할 코드를 더 많이 작성해야 합니다.

DynamoDB 문서 프로그래밍 모델에 대한 자세한 내용은 [Amazon DynamoDB 개발자 안내서](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/)의 [.NET: 문서 모델](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DotNetSDKMidLevel.html)을 참조하세요.

다음 섹션에서는 원하는 DynamoDB 테이블의 표현을 생성하는 방법에 대한 정보와 문서 모델을 사용하여 테이블에 항목을 삽입하고 테이블에서 항목을 가져오는 방법에 대한 예를 제공합니다.

### 테이블 표현 생성
<a name="dynamodb-intro-apis-document-table"></a>

이 문서 모델을 사용하여 데이터 작업을 수행하려면 먼저 특정 테이블을 나타내는 `Table` 클래스의 인스턴스를 만들어야 합니다. 이렇게 하는 두 가지 기본 방법이 있습니다.

**LoadTable 방법**

첫 번째 메커니즘은 다음 예제와 마찬가지로 [https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/DynamoDBv2/TTable.html](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/DynamoDBv2/TTable.html) 클래스의 정적 `LoadTable` 메서드 중 하나를 사용하는 것입니다.

```
var client = new AmazonDynamoDBClient();
Table table = Table.LoadTable(client, "Reply");
```

**참고**  
이 메커니즘은 작동하지만 특정 조건에서는 콜드 스타트 및 스레드 풀 동작으로 인해 추가 지연 시간이나 교착 상태가 발생할 수 있습니다. 이러한 동작에 대한 자세한 내용은 [AWS SDK for .NET에 대한 향상된 DynamoDB 초기화 패턴](https://aws.amazon.com/blogs/developer/improved-dynamodb-initialization-patterns-for-the-aws-sdk-for-net/) 블로그 게시물을 참조하세요.

**TableBuilder**

대체 메커니즘인 [https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/DynamoDBv2/TTableBuilder.html](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/DynamoDBv2/TTableBuilder.html) 클래스는 [ AWSSDK.DynamoDBv2 NuGet 패키지의 버전 3.7.203](https://www.nuget.org/packages/AWSSDK.DynamoDBv2/3.7.203)에 도입되었습니다. 이 메커니즘은 특정 암시적 메서드 호출, 특히 `DescribeTable` 메서드를 제거하여 위에서 언급한 동작을 해결할 수 있습니다. 이 메커니즘은 다음 예제와 비슷한 방식으로 사용됩니다.

```
var client = new AmazonDynamoDBClient();
var table = new TableBuilder(client, "Reply")
    .AddHashKey("Id", DynamoDBEntryType.String)
    .AddRangeKey("ReplyDateTime", DynamoDBEntryType.String)
    .AddGlobalSecondaryIndex("PostedBy-Message-index", "Author", DynamoDBEntryType.String, "Message", DynamoDBEntryType.String)
    .Build();
```

이 대체 메커니즘에 대한 자세한 내용은 [AWS SDK for .NET에 대한 향상된 DynamoDB 초기화 패턴](https://aws.amazon.com/blogs/developer/improved-dynamodb-initialization-patterns-for-the-aws-sdk-for-net/) 블로그 게시물을 참조하세요.

### 테이블에 항목 삽입
<a name="dynamodb-intro-apis-document-insert-item"></a>

다음 예제에서는 `PutItemAsync` 클래스의 `Table` 메서드를 통해 응답이 Reply 테이블에 삽입됩니다. `PutItemAsync` 메서드는 `Document` 클래스의 인스턴스를 취하고, `Document` 클래스는 단지 초기화된 속성을 모아놓은 것입니다.

```
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DocumentModel;

// Create a representation of the "Reply" table
//  by using one of the mechanisms described previously.

// Then, add a reply to the table.
var newReply = new Document();
newReply["Id"] = Guid.NewGuid().ToString();
newReply["ReplyDateTime"] = DateTime.UtcNow;
newReply["PostedBy"] = "Author1";
newReply["Message"] = "Thank you!";

await table.PutItemAsync(newReply);
```

### 테이블에서 항목 가져오기
<a name="dynamodb-intro-apis-document-get-item"></a>

다음 예제에서는 `GetItemAsync` 클래스의 `Table` 메서드를 통해 응답을 가져옵니다. 가져올 응답을 결정하기 위해 `GetItemAsync` 메서드는 대상 응답의 해시 및 범위 프라이머리 키를 사용합니다.

```
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DocumentModel;

// Create a representation of the "Reply" table
//  by using one of the mechanisms described previously.

// Then, get a reply from the table
//  where "guid" is the hash key and "datetime" is the range key.
var reply = await table.GetItemAsync(guid, datetime);
Console.WriteLine("Id = " + reply["Id"]);
Console.WriteLine("ReplyDateTime = " + reply["ReplyDateTime"]);
Console.WriteLine("PostedBy = " + reply["PostedBy"]);
Console.WriteLine("Message = " + reply["Message"]);
```

앞서 본 예제에서는 `WriteLine` 메서드를 위해 테이블 값을 문자열로 묵시적으로 변환합니다. `DynamoDBEntry` 클래스의 다양한 "As[type]" 메서드를 사용하여 명시적인 변환을 할 수 있습니다. 예를 들면 다음과 같이 `AsGuid()` 메서드를 통해 `Id`에 대한 값을 `Primitive` 데이터 형식에서 GUID로 묵시적으로 변환할 수 있습니다.

```
var guid = reply["Id"].AsGuid();
```

## 객체 지속성 모델
<a name="dynamodb-intro-apis-object-persistence"></a>

객체 지속성 프로그래밍 모델은 특히 DynamoDB에서 .NET 객체를 저장, 로드 및 쿼리하기 위해 설계되었습니다. [Amazon.DynamoDBv2.DataModel](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/DynamoDBv2/NDynamoDBv2DataModel.html) 네임스페이스를 통해 이 모델에 액세스합니다.

세 가지 모델 중에서 객체 지속성 모델은 DynamoDB 데이터를 저장, 로드 또는 쿼리할 때마다 가장 쉽게 코딩할 수 있습니다. 예를 들면 DynamoDB 데이터 형식을 직접 작업할 수 있습니다. 그러나 이 모델에서는 DynamoDB에서 .NET 객체를 저장, 로드 및 쿼리하는 작업에만 액세스할 수 있습니다. 예를 들면 이 모델을 사용하여 테이블의 항목을 생성, 검색, 업데이트 및 삭제할 수 있습니다. 그러나 먼저 하위 수준 모델을 사용하여 테이블을 생성한 후 이 모델을 사용하여 .NET 클래스를 테이블로 매핑해야 합니다.

DynamoDB 객체 지속성 프로그래밍 모델에 대한 자세한 내용은 [Amazon DynamoDB 개발자 안내서](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/)의 [.NET: 객체 지속성 모델](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DotNetSDKHighLevel.html)을 참조하세요.

다음 예제에서는 DynamoDB 항목을 나타내는 .NET 클래스를 정의하고, 항목을 DynamoDB 테이블에 삽입할 .NET 클래스의 인스턴스를 사용하며, .NET 클래스의 인스턴스를 사용하여 테이블에서 항목을 가져오는 방법을 보여줍니다.

### 테이블의 항목을 나타내는 .NET 클래스 정의
<a name="dynamodb-intro-apis-object-persistence-net-class-item"></a>

다음 클래스 정의의 예제에서 `DynamoDBTable` 속성은 테이블 이름을 지정하고, `DynamoDBHashKey` 및 `DynamoDBRangeKey` 속성은 테이블의 해시 및 범위 프라이머리 키를 만듭니다. `DynamoDBGlobalSecondaryIndexHashKey` 속성은 특정 작성자의 답변에 대한 쿼리를 구성할 수 있도록 정의됩니다.

```
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;

[DynamoDBTable("Reply")]
public class Reply
{
    [DynamoDBHashKey]
    public string Id { get; set; }

    [DynamoDBRangeKey(StoreAsEpoch = false)]
    public DateTime ReplyDateTime { get; set; }

    [DynamoDBGlobalSecondaryIndexHashKey("PostedBy-Message-Index",
        AttributeName ="PostedBy")]
    public string Author { get; set; }

    [DynamoDBGlobalSecondaryIndexRangeKey("PostedBy-Message-Index")]
    public string Message { get; set; }
}
```

### 객체 지속성 모델을 위한 컨텍스트 생성
<a name="dynamodb-intro-apis-object-persistence-context"></a>

DynamoDB에 대한 객체 지속성 프로그래밍 모델을 사용하려면 컨텍스트를 생성해야 합니다. 이 컨텍스트는 DynamoDB에 대한 연결을 제공하고, 테이블 액세스, 다양한 작업 수행, 쿼리 실행을 가능하게 합니다.

**기본 컨텍스트**

다음 예제에서는 가장 기본적인 컨텍스트를 만드는 방법을 보여줍니다.

```
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;

var client = new AmazonDynamoDBClient();
var context = new DynamoDBContext(client);
```

**DisableFetchingTableMetadata 속성이 포함된 컨텍스트**

다음 예제는 `DescribeTable` 메서드에 대한 암시적 호출을 방지하기 위해 `DynamoDBContextConfig` 클래스의 `DisableFetchingTableMetadata` 속성을 추가로 설정하는 방법을 보여줍니다.

```
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;

var client = new AmazonDynamoDBClient();
var context = new DynamoDBContext(client, new DynamoDBContextConfig
{
    DisableFetchingTableMetadata = true
});
```

첫 번째 예제와 같이 `DisableFetchingTableMetadata` 속성을 `false`(기본값)로 설정하면 `Reply` 클래스에서 테이블 항목의 키 및 인덱스 구조를 설명하는 속성을 생략할 수 있습니다. 대신 `DescribeTable` 메서드에 대한 암시적 호출을 통해 이러한 속성을 유추합니다. `DisableFetchingTableMetadata`를 `true`로 설정하면 두 번째 예제에서 볼 수 있듯이, `SaveAsync` 및 `QueryAsync`와 같은 객체 지속성 모델의 메서드는 `Reply` 클래스에 정의된 속성에 전적으로 의존합니다. 이 경우 `DescribeTable` 메서드에 대한 호출은 발생하지 않습니다.

**참고**  
특정 조건에서 `DescribeTable` 메서드에 대한 호출은 콜드 스타트 및 스레드 풀 동작으로 인해 추가 지연 시간이나 교착 상태가 발생할 수 있습니다. 이러한 이유로 해당 메서드에 대한 호출을 피하는 것이 유리한 경우도 있습니다.  
이러한 동작에 대한 자세한 내용은 [AWS SDK for .NET에 대한 향상된 DynamoDB 초기화 패턴](https://aws.amazon.com/blogs/developer/improved-dynamodb-initialization-patterns-for-the-aws-sdk-for-net/) 블로그 게시물을 참조하세요.

### .NET 클래스의 인스턴스를 사용하여 테이블에 항목을 삽입
<a name="dynamodb-intro-apis-object-persistence-net-insert-item"></a>

이 예제에서는 해당 항목을 나타내는 .NET 클래스의 초기화 인스턴스를 취하는 `SaveAsync` 클래스의 `DynamoDBContext` 메서드를 통해 항목이 삽입됩니다.

```
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;

// Create an appropriate context for the object persistence programming model,
//  examples of which have been described earlier.

// Create an object that represents the new item.
var reply = new Reply()
{
    Id = Guid.NewGuid().ToString(),
    ReplyDateTime = DateTime.UtcNow,
    Author = "Author1",
    Message = "Thank you!"
};

// Insert the item into the table.
await context.SaveAsync<Reply>(reply, new DynamoDBOperationConfig
{
    IndexName = "PostedBy-Message-index"
});
```

### .NET 클래스의 인스턴스를 사용하여 테이블에서 항목을 가져오기
<a name="dynamodb-intro-apis-object-persistence-net-get-item"></a>

이 예제에서는 `DynamoDBContext` 클래스의 `QueryAsync` 메서드를 사용하여 "Author1"의 모든 레코드를 찾는 쿼리를 만듭니다. 그런 다음 쿼리의 `GetNextSetAsync` 메서드를 통해 항목을 검색합니다.

```
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;

// Create an appropriate context for the object persistence programming model,
//  examples of which have been described earlier.

// Construct a query that finds all replies by a specific author.
var query = context.QueryAsync<Reply>("Author1", new DynamoDBOperationConfig
{
    IndexName = "PostedBy-Message-index"
});

// Display the result.
var set = await query.GetNextSetAsync();
foreach (var item in set)
{
    Console.WriteLine("Id = " + item.Id);
    Console.WriteLine("ReplyDateTime = " + item.ReplyDateTime);
    Console.WriteLine("PostedBy = " + item.Author);
    Console.WriteLine("Message = " + item.Message);
}
```

### 객체 지속성 모델에 대한 추가 정보
<a name="dynamodb-intro-apis-object-persistence-more-into"></a>

위에 표시된 예제와 설명에는 `DisableFetchingTableMetadata`라는 `DynamoDBContext` 클래스의 속성이 포함되는 경우가 있습니다. [AWSSDK.DynamoDBv2 NuGet 패키지 버전 3.7.203](https://www.nuget.org/packages/AWSSDK.DynamoDBv2/3.7.203)에 도입된 이 속성을 사용하면 콜드 스타트 및 스레드 풀 동작으로 인해 추가 지연 시간이나 교착 상태가 발생할 수 있는 특정 조건을 피할 수 있습니다. 자세한 내용은 [AWS SDK for .NET에 대한 향상된 DynamoDB 초기화 패턴](https://aws.amazon.com/blogs/developer/improved-dynamodb-initialization-patterns-for-the-aws-sdk-for-net/) 블로그 게시물을 참조하세요.

다음은 이 속성에 대한 몇 가지 추가 정보입니다.
+ .NET Framework를 사용하는 경우 `app.config` 또는 `web.config` 파일에서 이 속성을 전역적으로 설정할 수 있습니다.
+ 다음 예제와 같이 [https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/Amazon/TAWSConfigsDynamoDB.html](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/Amazon/TAWSConfigsDynamoDB.html) 클래스를 사용하여 이 속성을 전역적으로 설정할 수 있습니다.

  ```
  // Set the DisableFetchingTableMetadata property globally
  // before constructing any context objects.
  AWSConfigsDynamoDB.Context.DisableFetchingTableMetadata = true;
  
  var client = new AmazonDynamoDBClient();
  var context = new DynamoDBContext(client);
  ```
+ 경우에 따라 DynamoDB 속성을 .NET 클래스에 추가할 수 없습니다(예: 클래스가 종속성으로 정의된 경우). 이러한 경우에도 `DisableFetchingTableMetadata` 속성을 계속 활용할 수 있습니다. 이렇게 하려면 `DisableFetchingTableMetadata` 속성과 함께 [https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/DynamoDBv2/TTableBuilder.html](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/DynamoDBv2/TTableBuilder.html) 클래스를 사용합니다. 또한 `TableBuilder` 클래스는 [ AWSSDK.DynamoDBv2 NuGet 패키지의 버전 3.7.203](https://www.nuget.org/packages/AWSSDK.DynamoDBv2/3.7.203)에 도입되었습니다.

  ```
  // Set the DisableFetchingTableMetadata property globally
  // before constructing any context objects.
  AWSConfigsDynamoDB.Context.DisableFetchingTableMetadata = true;
  
  var client = new AmazonDynamoDBClient();
  var context = new DynamoDBContext(client);
  
  var table = new TableBuilder(client, "Reply")
      .AddHashKey("Id", DynamoDBEntryType.String)
      .AddRangeKey("ReplyDateTime", DynamoDBEntryType.String)
      .AddGlobalSecondaryIndex("PostedBy-Message-index", "Author", DynamoDBEntryType.String,
          "Message", DynamoDBEntryType.String)
      .Build();
  
  // This registers the "Reply" table we constructed via the builder.
  context.RegisterTableDefinition(table);
  
  // Now operations like this will work,
  // even if the Reply class was not annotated with this index.
  var query = context.QueryAsync<Reply>("Author1", new DynamoDBOperationConfig()
  {
      IndexName = "PostedBy-Message-index"
  });
  ```

## 추가 정보
<a name="dynamodb-intro-more-info"></a>

 ** AWS SDK for .NET 를 사용하여 DynamoDB 프로그래밍, 정보 및 예제**
+  [DynamoDB API](http://blogs.aws.amazon.com/net/post/Tx17SQHVEMW8MXC/DynamoDB-APIs) 
+  [DynamoDB 시리즈 시작](http://blogs.aws.amazon.com/net/post/Tx2XQOCY08QMTKO/DynamoDB-Series-Kickoff) 
+  [DynamoDB 시리즈 - 문서 모델](http://blogs.aws.amazon.com/net/post/Tx2R0WG46GQI1JI/DynamoDB-Series-Document-Model) 
+  [DynamoDB 시리즈 - 변환 스키마](http://blogs.aws.amazon.com/net/post/Tx2TCOGWG7ARUH5/DynamoDB-Series-Conversion-Schemas) 
+  [DynamoDB 시리즈 - 객체 지속성 모델](http://blogs.aws.amazon.com/net/post/Tx20L86FLMBW51P/DynamoDB-Series-Object-Persistence-Model) 
+  [DynamoDB 시리즈 - 표현식](http://blogs.aws.amazon.com/net/post/TxZQM7VA9AUZ9L/DynamoDB-Series-Expressions) 
+  [Amazon DynamoDB 및에서 표현식 사용 AWS SDK for .NET](dynamodb-expressions.md) 
+  [Amazon DynamoDB의 JSON 지원](dynamodb-json.md) 

 **하위 레벨 모델 정보 및 예제** 
+  [AWS SDK for .NET 하위 수준 API를 사용하여 테이블 작업](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetWorkingWithTables.html) 
+  [AWS SDK for .NET 하위 수준 API를 사용하여 항목 작업](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html) 
+  [AWS SDK for .NET 하위 수준 API를 사용하여 테이블 쿼리](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetQuerying.html) 
+  [AWS SDK for .NET 하위 수준 API를 사용하여 테이블 스캔](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetScanning.html) 
+  [AWS SDK for .NET 하위 수준 API를 사용하여 로컬 보조 인덱스 작업](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSILowLevelDotNet.html) 
+  [AWS SDK for .NET 하위 수준 API를 사용하여 글로벌 보조 인덱스 작업](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSILowLevelDotNet.html) 

 **문서 모델, 정보 및 예제** 
+  [DynamoDB 데이터 형식](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DataModel.html#DataModel.DataTypes) 
+  [DynamoDBEntry](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TDynamoDBv2DocumentModelDynamoDBEntry.html) 
+  [.NET: 문서 모델](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DotNetSDKMidLevel.html) 

 **객체 지속성 모델 정보 및 예제** 
+  [.NET: 객체 지속성 모델](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DotNetSDKHighLevel.html) 

 **기타 유용한 정보** 
+ .NET Aspire를 통한 Amazon DynamoDB 로컬 개발에 대한 자세한 내용은 [.NET Aspire AWS 와 통합](aspire-integrations.md) 섹션을 참조하세요.

**Topics**
+ [

## 하위 수준 모델
](#dynamodb-intro-apis-low-level)
+ [

## 문서 모델
](#dynamodb-intro-apis-document)
+ [

## 객체 지속성 모델
](#dynamodb-intro-apis-object-persistence)
+ [

## 추가 정보
](#dynamodb-intro-more-info)
+ [표현식 사용](dynamodb-expressions.md)
+ [JSON 지원](dynamodb-json.md)

# Amazon DynamoDB 및에서 표현식 사용 AWS SDK for .NET
<a name="dynamodb-expressions"></a>

**참고**  
이 주제의 정보는 .NET Framework 및 AWS SDK for .NET 버전 3.3 이하를 기반으로 하는 프로젝트에만 해당됩니다.

다음 코드 예제에서는를 사용하여 표현식으로 DynamoDB AWS SDK for .NET 를 프로그래밍하는 방법을 보여줍니다. *표현식*은 DynamoDB 테이블의 항목에서 읽을 속성을 나타냅니다. 또한 항목을 쓸 때도 표현식을 사용하여 충족해야 할 조건(*조건부 업데이트*라고도 함)을 나타내고 속성을 업데이트하는 방식을 나타낼 수 있습니다. 일부 업데이트 예에서는 속성을 새 값으로 바꾸거나 새 데이터를 목록이나 맵에 추가합니다. 자세한 내용은 [표현식을 사용하여 항목 읽기 및 쓰기](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.html)를 참조하십시오.

**Topics**
+ [

## 샘플 데이터
](#dynamodb-expressions-sample-data)
+ [

## 표현식 및 항목의 기본 키를 사용한 단일 항목 조회
](#dynamodb-expressions-get-item)
+ [

## 표현식 및 테이블의 기본 키를 사용한 다수 항목 조회
](#dynamodb-expressions-query)
+ [

## 표현식 및 기타 항목 속성을 사용한 다수 항목 조회
](#dynamodb-expressions-scan)
+ [

## 항목 출력
](#dynamodb-expressions-print-item)
+ [

## 표현식을 사용한 항목 생성 및 교체
](#dynamodb-expressions-put-item)
+ [

## 표현식을 사용한 항목 업데이트
](#dynamodb-expressions-update-item)
+ [

## 표현식을 사용한 항목 삭제
](#dynamodb-expressions-delete-item)
+ [

## 추가 정보
](#dynamodb-expressions-resources)

## 샘플 데이터
<a name="dynamodb-expressions-sample-data"></a>

이 주제의 코드 예제는 `ProductCatalog`라는 DynamoDB 테이블에 있는 다음 두 가지 예제 항목에 의존합니다. 이 항목들은 가상의 자전거 점포 카탈로그에 있는 제품 항목에 대한 정보를 알려줍니다. 이러한 항목은 [사례 연구: ProductCatalog 항목](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.CaseStudy.html)에서 제공하는 예제를 기반으로 합니다. `BOOL`, `L`, `M`, `N`, `NS`, `S`, `SS`과 같은 데이터 형식 서술자는 [JSON 데이터 형식](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DataFormat.html)의 서술자와 일치합니다.

```
{
  "Id": {
    "N": "205"
  },
  "Title": {
    "S": "20-Bicycle 205"
  },
  "Description": {
    "S": "205 description"
  },
  "BicycleType": {
    "S": "Hybrid"
  },
  "Brand": {
    "S": "Brand-Company C"
  },
  "Price": {
    "N": "500"
  },
  "Gender": {
    "S": "B"
  },
  "Color": {
    "SS": [
      "Red",
      "Black"
    ]
  },
  "ProductCategory": {
    "S": "Bike"
  },
  "InStock": {
    "BOOL": true
  },
  "QuantityOnHand": {
    "N": "1"
  },
  "RelatedItems": {
    "NS": [
      "341",
      "472",
      "649"
    ]
  },
  "Pictures": {
    "L": [
      {
        "M": {
          "FrontView": {
            "S": "http://example/products/205_front.jpg"
          }
        }
      },
      {
        "M": {
          "RearView": {
            "S": "http://example/products/205_rear.jpg"
          }
        }
      },
      {
        "M": {
          "SideView": {
            "S": "http://example/products/205_left_side.jpg"
          }
        }
      }
    ]
  },
  "ProductReviews": {
    "M": {
      "FiveStar": {
        "SS": [
          "Excellent! Can't recommend it highly enough! Buy it!",
          "Do yourself a favor and buy this."
        ]
      },
      "OneStar": {
        "SS": [
          "Terrible product! Do not buy this."
        ]
      }
    }
  }
},
{
  "Id": {
    "N": "301"
  },
  "Title": {
    "S": "18-Bicycle 301"
  },
  "Description": {
    "S": "301 description"
  },
  "BicycleType": {
    "S": "Road"
  },
  "Brand": {
    "S": "Brand-Company C"
  },
  "Price": {
    "N": "185"
  },
  "Gender": {
    "S": "F"
  },
  "Color": {
    "SS": [
      "Blue",
      "Silver"
    ]
  },
  "ProductCategory": {
    "S": "Bike"
  },
  "InStock": {
    "BOOL": true
  },
  "QuantityOnHand": {
    "N": "3"
  },
  "RelatedItems": {
    "NS": [
      "801",
      "822",
      "979"
    ]
  },
  "Pictures": {
    "L": [
      {
        "M": {
          "FrontView": {
            "S": "http://example/products/301_front.jpg"
          }
        }
      },
      {
        "M": {
          "RearView": {
            "S": "http://example/products/301_rear.jpg"
          }
        }
      },
      {
        "M": {
          "SideView": {
            "S": "http://example/products/301_left_side.jpg"
          }
        }
      }
    ]
  },
  "ProductReviews": {
    "M": {
      "FiveStar": {
        "SS": [
          "My daughter really enjoyed this bike!"
        ]
      },
      "ThreeStar": {
        "SS": [
          "This bike was okay, but I would have preferred it in my color.",
	      "Fun to ride."
        ]
      }
    }
  }
}
```

## 표현식 및 항목의 기본 키를 사용한 단일 항목 조회
<a name="dynamodb-expressions-get-item"></a>

다음 예제에는 `Amazon.DynamoDBv2.AmazonDynamoDBClient.GetItem`의 `Id`가 있는 항목을 조회하여 출력하는 데 사용할 `205` 메서드와 일련의 표현식이 포함되어 있습니다. 항목 속성 중에서 `Id`, `Title`, `Description`, `Color`, `RelatedItems`, `Pictures`, `ProductReviews` 속성만 반환됩니다.

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();
var request = new GetItemRequest
{
  TableName = "ProductCatalog",
  ProjectionExpression = "Id, Title, Description, Color, #ri, Pictures, #pr",
  ExpressionAttributeNames = new Dictionary<string, string>
  {
    { "#pr", "ProductReviews" },
    { "#ri", "RelatedItems" }
  },
  Key = new Dictionary<string, AttributeValue>
  {
    { "Id", new AttributeValue { N = "205" } }
  },
};
var response = client.GetItem(request);

// PrintItem() is a custom function.
PrintItem(response.Item);
```

앞서 다룬 예제에서 `ProjectionExpression` 속성은 반환될 속성을 지정합니다. `ExpressionAttributeNames` 속성에서는 `#pr` 속성을 나타낼 자리표시자 `ProductReviews`과 `#ri` 속성을 나타낼 자리표시자 `RelatedItems`를 지정합니다. `PrintItem`에 대한 호출은 [항목 인쇄](#dynamodb-expressions-print-item)의 설명과 같이 사용자 지정 함수를 참조합니다.

## 표현식 및 테이블의 기본 키를 사용한 다수 항목 조회
<a name="dynamodb-expressions-query"></a>

다음 예제에는 `Amazon.DynamoDBv2.AmazonDynamoDBClient.Query`의 값이 `Id`보다 큰 경우에 한해 `301`의 `Price`가 있는 항목을 조회하여 출력하는 데 사용할 `150` 메서드와 일련의 표현식이 포함되어 있습니다. 항목의 속성 중에서 반환되는 속성은 `Id`와 `Title`, 그리고 `ThreeStar`의 모든 `ProductReviews` 속성뿐입니다.

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();
var request = new QueryRequest
{
  TableName = "ProductCatalog",
  KeyConditions = new Dictionary<string,Condition>
  {
    { "Id", new Condition()
      {
        ComparisonOperator = ComparisonOperator.EQ,
        AttributeValueList = new List<AttributeValue>
        {
          new AttributeValue { N = "301" }
        }
      }
    }
  },
  ProjectionExpression = "Id, Title, #pr.ThreeStar",
  ExpressionAttributeNames = new Dictionary<string, string>
  {
    { "#pr", "ProductReviews" },
    { "#p", "Price" }
  },
  ExpressionAttributeValues = new Dictionary<string,AttributeValue>
  {
    { ":val", new AttributeValue { N = "150" } }
  },
  FilterExpression = "#p > :val"
};
var response = client.Query(request);

foreach (var item in response.Items)
{
  // Write out the first page of an item's attribute keys and values.
  // PrintItem() is a custom function.
  PrintItem(item);
  Console.WriteLine("=====");
}
```

앞서 다룬 예제에서 `ProjectionExpression` 속성은 반환될 속성을 지정합니다. `ExpressionAttributeNames` 속성은 `ProductReviews` 속성을 나타내는 자리 표시자 `#pr` 및 `Price` 속성을 나타내는 자리 표시자 `#p`를 지정합니다. `#pr.ThreeStar`는 `ThreeStar` 속성만 반환하도록 지정합니다. `ExpressionAttributeValues` 속성에서는 `:val`이라는 값을 나타낼 자리표시자 `150`을 지정합니다. `FilterExpression` 속성은 `#p`(`Price`)가 `:val`(`150`)보다 커야 한다는 조건을 지정합니다. `PrintItem`에 대한 호출은 [항목 인쇄](#dynamodb-expressions-print-item)의 설명과 같이 사용자 지정 함수를 참조합니다.

## 표현식 및 기타 항목 속성을 사용한 다수 항목 조회
<a name="dynamodb-expressions-scan"></a>

다음 예제에는 `Amazon.DynamoDBv2.AmazonDynamoDBClient.Scan`의 `ProductCategory`가 있는 항목을 모두 조회하여 출력하는 데 사용할 `Bike` 메서드와 일련의 표현식이 포함되어 있습니다. 항목의 속성 중에서 반환되는 속성은 `Id`와 `Title`, 그리고 `ProductReviews`의 모든 속성뿐입니다.

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();
var request = new ScanRequest
{
  TableName = "ProductCatalog",
  ProjectionExpression = "Id, Title, #pr",
  ExpressionAttributeValues = new Dictionary<string,AttributeValue>
  {
    { ":catg", new AttributeValue { S = "Bike" } }
  },
  ExpressionAttributeNames = new Dictionary<string, string>
  {
    { "#pr", "ProductReviews" },
    { "#pc", "ProductCategory" }
  },
  FilterExpression = "#pc = :catg",  
};
var response = client.Scan(request);

foreach (var item in response.Items)
{
  // Write out the first page/scan of an item's attribute keys and values.
  // PrintItem() is a custom function.
  PrintItem(item);
  Console.WriteLine("=====");
}
```

앞서 다룬 예제에서 `ProjectionExpression` 속성은 반환될 속성을 지정합니다. `ExpressionAttributeNames` 속성에서는 `#pr` 속성을 나타낼 자리표시자 `ProductReviews`과 `#pc` 속성을 나타낼 자리표시자 `ProductCategory`를 지정합니다. `ExpressionAttributeValues` 속성에서는 `:catg`이라는 값을 나타낼 자리표시자 `Bike`을 지정합니다. `FilterExpression` 속성에서는 `#pc`(`ProductCategory`)가 `:catg`(`Bike`)와 같아야 한다는 조건을 지정합니다. `PrintItem`에 대한 호출은 [항목 인쇄](#dynamodb-expressions-print-item)의 설명과 같이 사용자 지정 함수를 참조합니다.

## 항목 출력
<a name="dynamodb-expressions-print-item"></a>

다음 예제에서는 항목의 속성과 값을 출력하는 방법을 보여줍니다. 이 예제는 [표현식 및 항목의 기본 키를 사용하여 단일 항목 가져오기](#dynamodb-expressions-get-item), [표현식 및 테이블의 기본 키를 사용하여 다중 항목 가져오기](#dynamodb-expressions-query), [표현식 및 기타 항목 속성을 사용하여 다중 항목 가져오기](#dynamodb-expressions-scan) 방법을 보여주는 앞의 예제에서 사용됩니다.

```
// using Amazon.DynamoDBv2.Model;

// Writes out an item's attribute keys and values.
public static void PrintItem(Dictionary<string, AttributeValue> attrs)
{
  foreach (KeyValuePair<string, AttributeValue> kvp in attrs)
  {
    Console.Write(kvp.Key + " = ");
    PrintValue(kvp.Value);
  }
}

// Writes out just an attribute's value.
public static void PrintValue(AttributeValue value)
{
  // Binary attribute value.
  if (value.B != null)
  {
    Console.Write("Binary data");
  }
  // Binary set attribute value.
  else if (value.BS.Count > 0)
  {
    foreach (var bValue in value.BS)
    {
      Console.Write("\n  Binary data");
    }
  }
  // List attribute value.
  else if (value.L.Count > 0)
  {
    foreach (AttributeValue attr in value.L)
    {
      PrintValue(attr);
    }
  }
  // Map attribute value.
  else if (value.M.Count > 0)
  {
    Console.Write("\n");
    PrintItem(value.M);
  }
  // Number attribute value.
  else if (value.N != null)
  {
    Console.Write(value.N);
  }
  // Number set attribute value.
  else if (value.NS.Count > 0)
  {
    Console.Write("{0}", string.Join("\n", value.NS.ToArray()));
  }
  // Null attribute value.
  else if (value.NULL)
  {
    Console.Write("Null");
  }
  // String attribute value.
  else if (value.S != null)
  {
    Console.Write(value.S);
  }
  // String set attribute value.
  else if (value.SS.Count > 0)
  {
    Console.Write("{0}", string.Join("\n", value.SS.ToArray()));
  }
  // Otherwise, boolean value.
  else
  {
    Console.Write(value.BOOL);
  }
 
  Console.Write("\n");
}
```

앞서 다룬 예제에서는 속성을 출력할 정확한 형식을 결정하기 위해 평가할 수 있는 몇 가지 데이터 형식별 속성이 각 속성 값에 있습니다. 이러한 속성에는 [JSON 데이터 형식](DataFormat.html)에 해당하는 `B`, `BOOL`, `BS`, `L`, `M`, `N`, `NS`, `NULL`, `S` 및 `SS`가 포함됩니다. `B`, `N`, `NULL`, `S`와 같은 속성의 경우에는 상응하는 속성이 `null`이 아니라면 그 속성은 상응하는 `null` 아닌 데이터 형식에 대한 것입니다. `BS`, `L`, `M`, `NS`, `SS`와 같은 속성의 경우에는 `Count` 값이 0보다 크다면 그 속성은 상응하는 0 아닌 값 데이터 형식에 대한 것입니다. 그 속성의 데이터 형식별 속성 전체가 `null`이거나 `Count` 값이 0인 경우에 그 속성은 `BOOL` 데이터 형식에 상응하는 것입니다.

## 표현식을 사용한 항목 생성 및 교체
<a name="dynamodb-expressions-put-item"></a>

다음 예제에는 `Amazon.DynamoDBv2.AmazonDynamoDBClient.PutItem`의 `Title`이 있는 항목을 업데이트하는 데 사용할 `18-Bicycle 301` 메서드와 일련의 표현식이 포함되어 있습니다. 항목이 아직 없으면 새 항목이 추가됩니다.

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();
var request = new PutItemRequest
{
  TableName = "ProductCatalog",
  ExpressionAttributeNames = new Dictionary<string, string>
  {
    { "#title", "Title" }
  },
  ExpressionAttributeValues = new Dictionary<string, AttributeValue>
  {
    { ":product", new AttributeValue { S = "18-Bicycle 301" } }
  },
  ConditionExpression = "#title = :product", 
  // CreateItemData() is a custom function.
  Item = CreateItemData()
};
client.PutItem(request);
```

앞서 다룬 예제에서 `ExpressionAttributeNames` 속성은 `#title` 속성을 나타낼 자리표시자 `Title`을 지정합니다. `ExpressionAttributeValues` 속성에서는 `:product`이라는 값을 나타낼 자리표시자 `18-Bicycle 301`을 지정합니다. `ConditionExpression` 속성에서는 `#title`(`Title`)가 `:product`(`18-Bicycle 301`)와 같아야 한다는 조건을 지정합니다. `CreateItemData`에 대한 호출은 다음과 같은 사용자 지정 함수를 참조합니다.

```
// using Amazon.DynamoDBv2.Model;

// Provides a sample item that can be added to a table.
public static Dictionary<string, AttributeValue> CreateItemData()
{
  var itemData = new Dictionary<string, AttributeValue>
  {
    { "Id", new AttributeValue { N = "301" } },
    { "Title", new AttributeValue { S = "18\" Girl's Bike" } },
    { "BicycleType", new AttributeValue { S = "Road" } },
    { "Brand" , new AttributeValue { S = "Brand-Company C" } },
    { "Color", new AttributeValue { SS = new List<string>{ "Blue", "Silver" } } },
    { "Description", new AttributeValue { S = "301 description" } },
    { "Gender", new AttributeValue { S = "F" } },
    { "InStock", new AttributeValue { BOOL = true } },
    { "Pictures", new AttributeValue { L = new List<AttributeValue>{ 
      { new AttributeValue { M = new Dictionary<string,AttributeValue>{
        { "FrontView", new AttributeValue { S = "http://example/products/301_front.jpg" } } } } },
      { new AttributeValue { M = new Dictionary<string,AttributeValue>{
        { "RearView", new AttributeValue {S = "http://example/products/301_rear.jpg" } } } } },
      { new AttributeValue { M = new Dictionary<string,AttributeValue>{
        { "SideView", new AttributeValue { S = "http://example/products/301_left_side.jpg" } } } } }
    } } },
    { "Price", new AttributeValue { N = "185" } },
    { "ProductCategory", new AttributeValue { S = "Bike" } },
    { "ProductReviews", new AttributeValue { M = new Dictionary<string,AttributeValue>{
      { "FiveStar", new AttributeValue { SS = new List<string>{
        "My daughter really enjoyed this bike!" } } },
      { "OneStar", new AttributeValue { SS = new List<string>{
        "Fun to ride.",
        "This bike was okay, but I would have preferred it in my color." } } }
    } } },
    { "QuantityOnHand", new AttributeValue { N = "3" } },
    { "RelatedItems", new AttributeValue { NS = new List<string>{ "979", "822", "801" } } }
  };

  return itemData;
}
```

앞서 다룬 예제에서 샘플 데이터가 있는 예제 항목은 호출자에게 반환됩니다. 일련의 속성과 해당 값은 [JSON 데이터 형식](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DataFormat.html)에 해당하는 `BOOL`, `L`, `M`, `N`, `NS`, `S` 및 `SS` 등의 데이터 유형을 사용하여 구성됩니다.

## 표현식을 사용한 항목 업데이트
<a name="dynamodb-expressions-update-item"></a>

다음 예제에는 `Amazon.DynamoDBv2.AmazonDynamoDBClient.UpdateItem`의 `Title`가 있는 항목에 대해 `18" Girl's Bike`을 `Id`로 변경하는 데 사용할 `301` 메서드와 일련의 표현식이 포함되어 있습니다.

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();
var request = new UpdateItemRequest
{
  TableName = "ProductCatalog",
  Key = new Dictionary<string,AttributeValue>
  {
     { "Id", new AttributeValue { N = "301" } }
  },
  ExpressionAttributeNames = new Dictionary<string, string>
  {
    { "#title", "Title" }
  },
  ExpressionAttributeValues = new Dictionary<string, AttributeValue>
  {
    { ":newproduct", new AttributeValue { S = "18\" Girl's Bike" } }
  },
  UpdateExpression = "SET #title = :newproduct"
};
client.UpdateItem(request);
```

앞서 다룬 예제에서 `ExpressionAttributeNames` 속성은 `#title` 속성을 나타낼 자리표시자 `Title`을 지정합니다. `ExpressionAttributeValues` 속성에서는 `:newproduct`이라는 값을 나타낼 자리표시자 `18" Girl's Bike`을 지정합니다. `UpdateExpression` 속성에서는 `#title`(`Title`)을 `:newproduct`(`18" Girl's Bike`)로 변경하도록 지정합니다.

## 표현식을 사용한 항목 삭제
<a name="dynamodb-expressions-delete-item"></a>

다음 예제에는 항목의 `Title`이 `18-Bicycle 301`인 경우에 한해 `Id`가 `301`인 항목을 삭제하는 데 사용할 `Amazon.DynamoDBv2.AmazonDynamoDBClient.DeleteItem` 메서드와 일련의 표현식이 포함되어 있습니다.

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.Model;

var client = new AmazonDynamoDBClient();
var request = new DeleteItemRequest
{
  TableName = "ProductCatalog",
  Key = new Dictionary<string,AttributeValue>
  {
     { "Id", new AttributeValue { N = "301" } }
  },
  ExpressionAttributeNames = new Dictionary<string, string>
  {
    { "#title", "Title" }
  },
  ExpressionAttributeValues = new Dictionary<string, AttributeValue>
  {
    { ":product", new AttributeValue { S = "18-Bicycle 301" } }
  },
  ConditionExpression = "#title = :product"
};
client.DeleteItem(request);
```

앞서 다룬 예제에서 `ExpressionAttributeNames` 속성은 `#title` 속성을 나타낼 자리표시자 `Title`을 지정합니다. `ExpressionAttributeValues` 속성에서는 `:product`이라는 값을 나타낼 자리표시자 `18-Bicycle 301`을 지정합니다. `ConditionExpression` 속성에서는 `#title`(`Title`)이 `:product`(`18-Bicycle 301`)와 같아야 한다는 조건을 지정합니다.

## 추가 정보
<a name="dynamodb-expressions-resources"></a>

자세한 내용 및 코드 예제는 다음을 참조하십시오.
+  [DynamoDB 시리즈 - 표현식](http://blogs.aws.amazon.com/net/post/TxZQM7VA9AUZ9L/DynamoDB-Series-Expressions) 
+  [프로젝션 식을 사용하여 항목 속성 액세스](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.AccessingItemAttributes.html) 
+  [속성 이름 및 값에 자리 표시자 사용](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ExpressionPlaceholders.html) 
+  [조건식을 사용하여한 조건 지정](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.SpecifyingConditions.html) 
+  [업데이트 식을 사용하여 항목 및 속성 수정](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.Modifying.html) 
+  [AWS SDK for .NET 하위 수준 API를 사용하여 항목 작업](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html) 
+  [AWS SDK for .NET 하위 수준 API를 사용하여 테이블 쿼리](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetQuerying.html) 
+  [AWS SDK for .NET 하위 수준 API를 사용하여 테이블 스캔](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetScanning.html) 
+  [AWS SDK for .NET 하위 수준 API를 사용하여 로컬 보조 인덱스 작업](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSILowLevelDotNet.html) 
+  [AWS SDK for .NET 하위 수준 API를 사용하여 글로벌 보조 인덱스 작업](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSILowLevelDotNet.html) 

# Amazon DynamoDB의 JSON 지원
<a name="dynamodb-json"></a>

**참고**  
이 주제의 정보는 .NET Framework 및 AWS SDK for .NET 버전 3.3 이하를 기반으로 하는 프로젝트에만 해당됩니다.

는 Amazon DynamoDB로 작업할 때 JSON 데이터를 AWS SDK for .NET 지원합니다. 따라서 DynamoDB 테이블에서 JSON 형식 데이터를 더 쉽게 가져올 수 있으며 JSON 문서를 테이블에 더 쉽게 삽입할 수 있습니다.

**Topics**
+ [

## DynamoDB 테이블에서 JSON 형식으로 데이터 가져오기
](#dynamodb-json-get-table-data)
+ [

## DynamoDB 테이블에 JSON 형식 데이터 삽입
](#dynamodb-json-insert-table-data)
+ [

## JSON으로 DynamoDB 데이터 형식 변환
](#dynamodb-json-datatypes)
+ [

## 추가 정보
](#dynamodb-json-more-info)

## DynamoDB 테이블에서 JSON 형식으로 데이터 가져오기
<a name="dynamodb-json-get-table-data"></a>

다음 예제에서는 DynamoDB 테이블에서 JSON 형식으로 데이터를 가져오는 방법을 보여줍니다.

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.DocumentModel;

var client = new AmazonDynamoDBClient();
var table = Table.LoadTable(client, "AnimalsInventory");
var item = table.GetItem(3, "Horse");

var jsonText = item.ToJson();
Console.Write(jsonText);
      
// Output:
//   {"Name":"Shadow","Type":"Horse","Id":3}

var jsonPrettyText = item.ToJsonPretty();
Console.WriteLine(jsonPrettyText);
      
// Output:
//   {
//     "Name" : "Shadow",
//     "Type" : "Horse",
//     "Id"   : 3
//   }
```

앞의 예제에서는 `Document` 클래스의 `ToJson` 메서드가 테이블의 항목을 JSON 형식 문자열로 변환합니다. 해당 항목은 `Table` 클래스의 `GetItem` 메서드를 통해 가져옵니다. 가져올 항목을 결정하기 위해 이 예제에서는 `GetItem` 메서드가 대상 항목의 해시 및 범위 기본 키를 사용합니다. 항목을 가져올 테이블을 결정하기 위해 `Table` 클래스의 `LoadTable` 메서드는 DynamoDB에서 `AmazonDynamoDBClient` 클래스의 인스턴스와 대상 테이블의 이름을 사용합니다.

## DynamoDB 테이블에 JSON 형식 데이터 삽입
<a name="dynamodb-json-insert-table-data"></a>

다음 예제에서는 JSON 형식을 사용하여 DynamoDB 테이블에 항목을 삽입하는 방법을 보여줍니다.

```
// using Amazon.DynamoDBv2;
// using Amazon.DynamoDBv2.DocumentModel;

var client = new AmazonDynamoDBClient();
var table = Table.LoadTable(client, "AnimalsInventory");
var jsonText = "{\"Id\":6,\"Type\":\"Bird\",\"Name\":\"Tweety\"}";
var item = Document.FromJson(jsonText);

table.PutItem(item);
```

앞의 예제에서는 `FromJson` 클래스의 `Document` 메서드가 JSON 형식 문자열을 항목으로 변환합니다. 해당 항목은 이 항목을 포함하는 `PutItem` 클래스의 인스턴스를 사용하는 `Table` 클래스의 `Document` 메서드를 통해 테이블에 삽입됩니다. 항목을 삽입할 테이블을 결정하기 위해 `Table` 클래스의 `LoadTable` 메서드가 호출되어 DynamoDB에서 `AmazonDynamoDBClient` 클래스의 인스턴스와 대상 테이블의 이름을 지정합니다.

## JSON으로 DynamoDB 데이터 형식 변환
<a name="dynamodb-json-datatypes"></a>

`Document` 클래스의 `ToJson` 메서드를 호출한 다음 그 결과로 얻은 JSON 데이터에서 `FromJson` 메서드를 호출하여 JSON 데이터를 `Document` 클래스의 인스턴스로 다시 변환할 때마다 일부 DynamoDB 데이터 형식은 예상대로 변환되지 않습니다. 구체적으로 설명하면 다음과 같습니다.
+ DynamoDB 세트(`SS`, `NS` 및 `BS` 유형)는 JSON 어레이로 변환됩니다.
+ DynamoDB 이진수 스칼라 및 세트(`B` 및 `BS` 형식)는 base64로 인코딩된 JSON 문자열 또는 문자열 목록으로 변환됩니다.

  이 시나리오에서는 `DecodeBase64Attributes` 클래스의 `Document` 메서드를 호출하여 base64로 인코딩된 JSON 데이터를 정확한 이진수 표시로 바꿔야 합니다. 다음 예제에서는 `Document`라는 `Picture` 클래스의 인스턴스에 있는 base64로 인코딩된 이진수 스칼라 항목 속성을 정확한 이진수 표시로 바꿉니다. 또한 이 예제에서는 다음과 같이 `Document`라는 `RelatedPictures` 클래스의 동일한 인스턴스에 있는 base64로 인코딩된 이진수 세트 항목 속성에 대해 동일한 작업을 합니다.

  ```
  item.DecodeBase64Attributes("Picture", "RelatedPictures");
  ```

## 추가 정보
<a name="dynamodb-json-more-info"></a>

를 사용하여 DynamoDB로 JSON을 프로그래밍하는 방법에 대한 자세한 내용과 예제는 다음을 AWS SDK for .NET참조하세요.
+  [DynamoDB JSON 지원](https://aws.amazon.com/blogs/developer/dynamodb-json-support/) 
+  [Amazon DynamoDB 업데이트 - JSON, 확장 프리 티어, 유연한 조정, 대규모 항목](https://aws.amazon.com/blogs/aws/dynamodb-update-json-and-more/) 

# Amazon EC2 작업
<a name="ec2-apis-intro"></a>

는 크기 조정 가능한 컴퓨팅 용량을 제공하는 웹 서비스인 [Amazon EC2](https://docs.aws.amazon.com/ec2/)를 AWS SDK for .NET 지원합니다. 이 컴퓨팅 용량을 사용하여 소프트웨어 시스템을 구축하고 호스팅할 수 있습니다.

## API
<a name="w2aac19c15c21b5"></a>

는 Amazon EC2 클라이언트용 APIs AWS SDK for .NET 제공합니다. API를 사용하면 보안 그룹 및 키 페어와 같은 EC2 기능을 사용할 수 있습니다. 또한 API를 사용하여 Amazon EC2 인스턴스를 제어할 수 있습니다. 이 섹션에는 이러한 API로 작업할 때 따를 수 있는 패턴을 보여주는 몇 가지 예제가 포함되어 있습니다. 전체 API 세트를 보려면 [AWS SDK for .NET API 참조](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/)를 참조하세요(그리고 "Amazon.EC2"으로 스크롤하세요).

Amazon EC2 API는 [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2) NuGet 패키지를 통해 제공됩니다.

## 사전 조건
<a name="w2aac19c15c21b7"></a>

시작하기 전에 [환경을 설정하고](net-dg-config.md) [프로젝트를 구성](configuring-the-sdk.md)했는지 확인합니다. 또한 [SDK 사용](net-dg-sdk-features.md)의 정보를 검토합니다.

## 예시 관련 정보
<a name="ec2-apis-intro-about"></a>

이 섹션의 예시에서는 Amazon EC2 클라이언트 작업 방법과 Amazon EC2 인스턴스를 관리하는 방법을 보여줍니다.

[EC2 스팟 인스턴스 자습서](how-to-spot-instances.md)에서는 Amazon EC2 스팟 인스턴스를 요청하는 방법을 보여줍니다. 스팟 인스턴스를 사용하면 온디맨드 가격보다 저렴한 비용으로 미사용 EC2 용량에 액세스할 수 있습니다.

**Topics**
+ [

## API
](#w2aac19c15c21b5)
+ [

## 사전 조건
](#w2aac19c15c21b7)
+ [

## 예시 관련 정보
](#ec2-apis-intro-about)
+ [보안 그룹](security-groups.md)
+ [키 페어](key-pairs.md)
+ [리전 및 가용 영역](using-regions-and-availability-zones.md)
+ [EC2 인스턴스](how-to-ec2.md)
+ [스팟 인스턴스 자습서](how-to-spot-instances.md)

# Amazon EC2 에서 보안 그룹 작업
<a name="security-groups"></a>

Amazon EC2에서 *보안 그룹*은 하나 이상의 EC2 인스턴스에 대한 네트워크 트래픽을 제어하는 가상 방화벽 역할을 합니다. 기본적으로 EC2는 인바운드 트래픽을 허용하지 않는 보안 그룹과 인스턴스를 연결합니다. EC2 인스턴스가 특정 트래픽을 받아들이도록 허용하는 보안 그룹을 생성할 수 있습니다. 예를 들어 EC2 Windows 인스턴스에 연결해야 하는 경우 보안 그룹이 RDP 트래픽을 허용하도록 구성해야 합니다.

보안 그룹에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Amazon EC2 보안 그룹](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html)을 참조하세요.

**주의**  
EC2-Classic은 2022년 8월 15일에 사용 중지되었습니다. EC2-Classic에서 VPC로 마이그레이션하는 것이 좋습니다. 자세한 내용은 블로그 게시물 [EC2-Classic 네트워킹은 사용 중지 중입니다 - 준비 방법은 다음과 같습니다](https://aws.amazon.com/blogs/aws/ec2-classic-is-retiring-heres-how-to-prepare/)를 참조하세요.

API 및 사전 조건에 대한 자세한 내용은 상위 섹션([Amazon EC2 작업](ec2-apis-intro.md))을 참조하세요.

**Topics**
+ [

# 보안 그룹 열거
](enumerate-security-groups.md)
+ [

# 보안 그룹 생성
](creating-security-group.md)
+ [

# 보안 그룹 업데이트
](authorize-ingress.md)

# 보안 그룹 열거
<a name="enumerate-security-groups"></a>

이 예제에서는를 사용하여 보안 그룹을 열거 AWS SDK for .NET 하는 방법을 보여줍니다. [Amazon Virtual Private Cloud](https://docs.aws.amazon.com/vpc/latest/userguide/) ID를 제공하는 경우 애플리케이션은 해당 VPC의 보안 그룹을 열거합니다. 그렇지 않으면 애플리케이션은 사용 가능한 모든 보안 그룹 목록만 표시합니다.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#enum-sec-groups-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [

## 보안 그룹 열거
](#enum-sec-groups-enum)
+ [

## 전체 코드
](#enum-sec-groups-complete-code)
+ [

## 추가 고려 사항
](#enum-sec-groups-additional)

## 보안 그룹 열거
<a name="enum-sec-groups-enum"></a>

다음 코드 조각은 보안 그룹을 열거합니다. 모든 그룹을 열거하거나 특정 VPC의 그룹이 있는 경우 해당 그룹을 열거합니다.

[이 주제의 끝 부분에 있는](#enum-sec-groups-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to enumerate the security groups
    private static async Task EnumerateGroups(IAmazonEC2 ec2Client, string vpcID)
    {
      // A request object, in case we need it.
      var request = new DescribeSecurityGroupsRequest();

      // Put together the properties, if needed
      if(!string.IsNullOrEmpty(vpcID))
      {
        // We have a VPC ID. Find the security groups for just that VPC.
        Console.WriteLine($"\nGetting security groups for VPC {vpcID}...\n");
        request.Filters.Add(new Filter
        {
          Name = "vpc-id",
          Values = new List<string>() { vpcID }
        });
      }

      // Get the list of security groups
      DescribeSecurityGroupsResponse response =
        await ec2Client.DescribeSecurityGroupsAsync(request);

      // Display the list of security groups.
      foreach (SecurityGroup item in response.SecurityGroups)
      {
        Console.WriteLine("Security group: " + item.GroupId);
        Console.WriteLine("\tGroupId: " + item.GroupId);
        Console.WriteLine("\tGroupName: " + item.GroupName);
        Console.WriteLine("\tVpcId: " + item.VpcId);
        Console.WriteLine();
      }
    }
```

## 전체 코드
<a name="enum-sec-groups-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c21c13c13c15b5b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TEC2Client.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2Model.html)

  클래스 [DescribeSecurityGroupsRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TDescribeSecurityGroupsRequest.html)

  클래스 [DescribeSecurityGroupsResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TDescribeSecurityGroupsResponse.html)

  클래스 [Filter](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TFilter.html)

  클래스 [SecurityGroup](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TSecurityGroup.html)

### 코드
<a name="w2aac19c15c21c13c13c15b7b1"></a>

```
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2EnumerateSecGroups
{
  class Program
  {
    static async Task Main(string[] args)
    {
      // Parse the command line
       string vpcID = string.Empty;
      if(args.Length == 0)
      {
        Console.WriteLine("\nEC2EnumerateSecGroups [vpc_id]");
        Console.WriteLine("  vpc_id - The ID of the VPC for which you want to see security groups.");
        Console.WriteLine("\nSince you specified no arguments, showing all available security groups.");
      }
      else
      {
        vpcID = args[0];
      }

      if(vpcID.StartsWith("vpc-") || string.IsNullOrEmpty(vpcID))
      {
        // Create an EC2 client object
        var ec2Client = new AmazonEC2Client();

        // Enumerate the security groups
        await EnumerateGroups(ec2Client, vpcID);
      }
      else
      {
        Console.WriteLine("Could not find a valid VPC ID in the command-line arguments:");
        Console.WriteLine($"{args[0]}");
      }
    }


    //
    // Method to enumerate the security groups
    private static async Task EnumerateGroups(IAmazonEC2 ec2Client, string vpcID)
    {
      // A request object, in case we need it.
      var request = new DescribeSecurityGroupsRequest();

      // Put together the properties, if needed
      if(!string.IsNullOrEmpty(vpcID))
      {
        // We have a VPC ID. Find the security groups for just that VPC.
        Console.WriteLine($"\nGetting security groups for VPC {vpcID}...\n");
        request.Filters.Add(new Filter
        {
          Name = "vpc-id",
          Values = new List<string>() { vpcID }
        });
      }

      // Get the list of security groups
      DescribeSecurityGroupsResponse response =
        await ec2Client.DescribeSecurityGroupsAsync(request);

      // Display the list of security groups.
      foreach (SecurityGroup item in response.SecurityGroups)
      {
        Console.WriteLine("Security group: " + item.GroupId);
        Console.WriteLine("\tGroupId: " + item.GroupId);
        Console.WriteLine("\tGroupName: " + item.GroupName);
        Console.WriteLine("\tVpcId: " + item.VpcId);
        Console.WriteLine();
      }
    }
  }
}
```

## 추가 고려 사항
<a name="enum-sec-groups-additional"></a>
+ VPC의 경우 이름-값 쌍의 `Name` 부분이 "vpc-id"로 설정된 상태로 필터가 구성된다는 점에 유의하세요. 이 이름은 [DescribeSecurityGroupsRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TDescribeSecurityGroupsRequest.html) 클래스의 `Filters` 속성 설명에서 가져온 것입니다.
+ 전체 보안 그룹 목록을 가져오려면 [파라미터 없이 DescribeSecurityGroupsAsync](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/MEC2DescribeSecurityGroupsAsyncCancellationToken.html)를 사용할 수도 있습니다.
+ [Amazon EC2 콘솔](https://console.aws.amazon.com/ec2/v2/home#SecurityGroups)에서 보안 그룹 목록을 확인하여 결과를 확인할 수 있습니다.

# 보안 그룹 생성
<a name="creating-security-group"></a>

이 예제에서는를 사용하여 보안 그룹을 AWS SDK for .NET 생성하는 방법을 보여줍니다. 기존 VPC의 ID를 제공하여 VPC의 EC2에 대한 보안 그룹을 생성할 수 있습니다. 이러한 ID를 제공하지 않으면 AWS 계정이 이를 지원하는 경우 새 보안 그룹은 EC2-Classic용가 됩니다.

VPC ID를 제공하지 않고 AWS 계정이 EC2-Classic을 지원하지 않는 경우 새 보안 그룹은 계정의 기본 VPC에 속합니다.

**주의**  
EC2-Classic은 2022년 8월 15일에 사용 중지되었습니다. EC2-Classic에서 VPC로 마이그레이션하는 것이 좋습니다. 자세한 내용은 블로그 게시물 [EC2-Classic 네트워킹은 사용 중지 중입니다 - 준비 방법은 다음과 같습니다](https://aws.amazon.com/blogs/aws/ec2-classic-is-retiring-heres-how-to-prepare/)를 참조하세요.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#create-sec-groups-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [

## 기존 보안 그룹 찾기
](#create-sec-groups-find)
+ [

## 보안 그룹 생성
](#create-sec-groups-enum)
+ [

## 전체 코드
](#create-sec-groups-complete-code)

## 기존 보안 그룹 찾기
<a name="create-sec-groups-find"></a>

다음 코드 조각은 지정된 VPC에서 지정된 이름을 가진 기존 보안 그룹을 검색합니다.

[이 주제의 끝 부분에 있는](#create-sec-groups-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to determine if a security group with the specified name
    // already exists in the VPC
    private static async Task<List<SecurityGroup>> FindSecurityGroups(
      IAmazonEC2 ec2Client, string groupName, string vpcID)
    {
      var request = new DescribeSecurityGroupsRequest();
      request.Filters.Add(new Filter{
        Name = "group-name",
        Values = new List<string>() { groupName }
      });
      if(!string.IsNullOrEmpty(vpcID))
        request.Filters.Add(new Filter{
          Name = "vpc-id",
          Values = new List<string>() { vpcID }
        });

      var response = await ec2Client.DescribeSecurityGroupsAsync(request);
      return response.SecurityGroups;
    }
```

## 보안 그룹 생성
<a name="create-sec-groups-enum"></a>

다음 코드 조각은 해당 이름을 가진 그룹이 지정된 VPC에 없는 경우 새 보안 그룹을 생성합니다. VPC가 제공되지 않고 같은 이름을 가진 그룹이 하나 이상 존재할 경우 코드 조각은 단순히 그룹 목록을 반환합니다.

[이 주제의 끝 부분에 있는](#create-sec-groups-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to create a new security group (either EC2-Classic or EC2-VPC)
    // If vpcID is empty, the security group will be for EC2-Classic
    private static async Task<List<SecurityGroup>> CreateSecurityGroup(
      IAmazonEC2 ec2Client, string groupName, string vpcID)
    {
      // See if one or more security groups with that name
      // already exist in the given VPC. If so, return the list of them.
      var securityGroups = await FindSecurityGroups(ec2Client, groupName, vpcID);
      if (securityGroups.Count > 0)
      {
        Console.WriteLine(
          $"\nOne or more security groups with name {groupName} already exist.\n");
        return securityGroups;
      }

      // If the security group doesn't already exists, create it.
      var createRequest = new CreateSecurityGroupRequest{
        GroupName = groupName
      };
      if(string.IsNullOrEmpty(vpcID))
      {
        createRequest.Description = "My .NET example security group for EC2-Classic";
      }
      else
      {
        createRequest.VpcId = vpcID;
        createRequest.Description = "My .NET example security group for EC2-VPC";
      }
      CreateSecurityGroupResponse createResponse =
        await ec2Client.CreateSecurityGroupAsync(createRequest);

      // Return the new security group
      DescribeSecurityGroupsResponse describeResponse =
        await ec2Client.DescribeSecurityGroupsAsync(new DescribeSecurityGroupsRequest{
          GroupIds = new List<string>() { createResponse.GroupId }
        });
      return describeResponse.SecurityGroups;
    }
```

## 전체 코드
<a name="create-sec-groups-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c21c13c15c23b5b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TEC2Client.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2Model.html)

  클래스 [CreateSecurityGroupRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TCreateSecurityGroupRequest.html)

  클래스 [CreateSecurityGroupResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TCreateSecurityGroupResponse.html)

  클래스 [DescribeSecurityGroupsRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TDescribeSecurityGroupsRequest.html)

  클래스 [DescribeSecurityGroupsResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TDescribeSecurityGroupsResponse.html)

  필터 [Filter ](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TFilter.html)

  클래스 [SecurityGroup](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TSecurityGroup.html)

### 코드
<a name="w2aac19c15c21c13c15c23b7b1"></a>

```
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2CreateSecGroup
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to create a security group
  class Program
  {
    private const int MaxArgs = 2;

    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        return;
      }
      if(parsedArgs.Count > MaxArgs)
        CommandLine.ErrorExit("\nThe number of command-line arguments is incorrect." +
          "\nRun the command with no arguments to see help.");

      // Get the application arguments from the parsed list
      var groupName = CommandLine.GetArgument(parsedArgs, null, "-g", "--group-name");
      var vpcID = CommandLine.GetArgument(parsedArgs, null, "-v", "--vpc-id");
      if(string.IsNullOrEmpty(groupName))
        CommandLine.ErrorExit("\nYou must supply a name for the new group." +
          "\nRun the command with no arguments to see help.");
      if(!string.IsNullOrEmpty(vpcID) && !vpcID.StartsWith("vpc-"))
        CommandLine.ErrorExit($"\nNot a valid VPC ID: {vpcID}");

      // groupName has a value and vpcID either has a value or is null (which is fine)
      // Create the new security group and display information about it
      var securityGroups =
        await CreateSecurityGroup(new AmazonEC2Client(), groupName, vpcID);
      Console.WriteLine("Information about the security group(s):");
      foreach(var group in securityGroups)
      {
        Console.WriteLine($"\nGroupName: {group.GroupName}");
        Console.WriteLine($"GroupId: {group.GroupId}");
        Console.WriteLine($"Description: {group.Description}");
        Console.WriteLine($"VpcId (if any): {group.VpcId}");
      }
    }


    //
    // Method to create a new security group (either EC2-Classic or EC2-VPC)
    // If vpcID is empty, the security group will be for EC2-Classic
    private static async Task<List<SecurityGroup>> CreateSecurityGroup(
      IAmazonEC2 ec2Client, string groupName, string vpcID)
    {
      // See if one or more security groups with that name
      // already exist in the given VPC. If so, return the list of them.
      var securityGroups = await FindSecurityGroups(ec2Client, groupName, vpcID);
      if (securityGroups.Count > 0)
      {
        Console.WriteLine(
          $"\nOne or more security groups with name {groupName} already exist.\n");
        return securityGroups;
      }

      // If the security group doesn't already exists, create it.
      var createRequest = new CreateSecurityGroupRequest{
        GroupName = groupName
      };
      if(string.IsNullOrEmpty(vpcID))
      {
        createRequest.Description = "Security group for .NET code example (no VPC specified)";
      }
      else
      {
        createRequest.VpcId = vpcID;
        createRequest.Description = "Security group for .NET code example (VPC: " + vpcID + ")";
      }
      CreateSecurityGroupResponse createResponse =
        await ec2Client.CreateSecurityGroupAsync(createRequest);

      // Return the new security group
      DescribeSecurityGroupsResponse describeResponse =
        await ec2Client.DescribeSecurityGroupsAsync(new DescribeSecurityGroupsRequest{
          GroupIds = new List<string>() { createResponse.GroupId }
        });
      return describeResponse.SecurityGroups;
    }


    //
    // Method to determine if a security group with the specified name
    // already exists in the VPC
    private static async Task<List<SecurityGroup>> FindSecurityGroups(
      IAmazonEC2 ec2Client, string groupName, string vpcID)
    {
      var request = new DescribeSecurityGroupsRequest();
      request.Filters.Add(new Filter{
        Name = "group-name",
        Values = new List<string>() { groupName }
      });
      if(!string.IsNullOrEmpty(vpcID))
        request.Filters.Add(new Filter{
          Name = "vpc-id",
          Values = new List<string>() { vpcID }
        });

      var response = await ec2Client.DescribeSecurityGroupsAsync(request);
      return response.SecurityGroups;
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: EC2CreateSecGroup -g <group-name> [-v <vpc-id>]" +
        "\n  -g, --group-name: The name you would like the new security group to have." +
        "\n  -v, --vpc-id: The ID of a VPC to which the new security group will belong." +
        "\n     If vpc-id isn't present, the security group will be" +
        "\n     for EC2-Classic (if your AWS account supports this)" +
        "\n     or will use the default VCP for EC2-VPC.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

# 보안 그룹 업데이트
<a name="authorize-ingress"></a>

이 예제에서는 AWS SDK for .NET 를 사용하여 보안 그룹에 규칙을 추가하는 방법을 보여줍니다. 특히 이 예제에서는 지정된 TCP 포트에서 인바운드 트래픽을 허용하는 규칙을 추가합니다. 이 규칙은 예를 들어 EC2 인스턴스에 대한 원격 연결에 사용할 수 있습니다. 애플리케이션은 기존 보안 그룹의 ID, CIDR 형식의 IP 주소(또는 주소 범위), 그리고 선택적으로 TCP 포트 번호를 사용합니다. 그런 다음 지정된 보안 그룹에 인바운드 규칙을 추가합니다.

**참고**  
이 예제를 사용하려면 CIDR 형식의 IP 주소(또는 주소 범위)가 필요합니다. 로컬 컴퓨터의 IP 주소를 얻는 방법은 이 주제의 끝 부분에 있는 **추가 고려 사항**을 참조하세요.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#authorize-ingress-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [

## 인바운드 규칙 추가
](#authorize-ingress-add-rule)
+ [

## 전체 코드
](#authorize-ingress-complete-code)
+ [

## 추가 고려 사항
](#authorize-ingress-additional)

## 인바운드 규칙 추가
<a name="authorize-ingress-add-rule"></a>

다음 코드 조각은 특정 IP 주소(또는 범위) 및 TCP 포트에 대한 보안 그룹에 인바운드 규칙을 추가합니다.

[이 주제의 끝 부분에 있는](#authorize-ingress-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method that adds a TCP ingress rule to a security group
    private static async Task AddIngressRule(
      IAmazonEC2 eC2Client, string groupID, string ipAddress, int port)
    {
      // Create an object to hold the request information for the rule.
      // It uses an IpPermission object to hold the IP information for the rule.
      var ingressRequest = new AuthorizeSecurityGroupIngressRequest{
        GroupId = groupID};
      ingressRequest.IpPermissions.Add(new IpPermission{
        IpProtocol = "tcp",
        FromPort = port,
        ToPort = port,
        Ipv4Ranges = new List<IpRange>() { new IpRange { CidrIp = ipAddress } }
      });

      // Create the inbound rule for the security group
      AuthorizeSecurityGroupIngressResponse responseIngress =
        await eC2Client.AuthorizeSecurityGroupIngressAsync(ingressRequest);
      Console.WriteLine($"\nNew RDP rule was written in {groupID} for {ipAddress}.");
      Console.WriteLine($"Result: {responseIngress.HttpStatusCode}");
    }
```

## 전체 코드
<a name="authorize-ingress-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c21c13c17c17b5b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TEC2Client.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2Model.html)

  클래스 [AuthorizeSecurityGroupIngressRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TAuthorizeSecurityGroupIngressRequest.html)

  클래스 [AuthorizeSecurityGroupIngressResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TAuthorizeSecurityGroupIngressResponse.html)

  클래스 [IpPermission](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TIpPermission.html)

  클래스 [IpRange](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TIpRange.html)

### 코드
<a name="w2aac19c15c21c13c17c17b7b1"></a>

```
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2AddRuleForRDP
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to add a rule that allows inbound traffic on TCP a port
  class Program
  {
    private const int DefaultPort = 3389;

    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        return;
      }

      // Get the application arguments from the parsed list
      var groupID = CommandLine.GetArgument(parsedArgs, null, "-g", "--group-id");
      var ipAddress = CommandLine.GetArgument(parsedArgs, null, "-i", "--ip-address");
      var portStr = CommandLine.GetArgument(parsedArgs, DefaultPort.ToString(), "-p", "--port");
      if(string.IsNullOrEmpty(ipAddress))
        CommandLine.ErrorExit("\nYou must supply an IP address in CIDR format.");
      if(string.IsNullOrEmpty(groupID) || !groupID.StartsWith("sg-"))
        CommandLine.ErrorExit("\nThe ID for a security group is missing or incorrect.");
      if(int.Parse(portStr) == 0)
        CommandLine.ErrorExit($"\nThe given TCP port number, {portStr}, isn't allowed.");

      // Add a rule to the given security group that allows
      // inbound traffic on a TCP port
      await AddIngressRule(
        new AmazonEC2Client(), groupID, ipAddress, int.Parse(portStr));
    }


    //
    // Method that adds a TCP ingress rule to a security group
    private static async Task AddIngressRule(
      IAmazonEC2 eC2Client, string groupID, string ipAddress, int port)
    {
      // Create an object to hold the request information for the rule.
      // It uses an IpPermission object to hold the IP information for the rule.
      var ingressRequest = new AuthorizeSecurityGroupIngressRequest{
        GroupId = groupID};
      ingressRequest.IpPermissions.Add(new IpPermission{
        IpProtocol = "tcp",
        FromPort = port,
        ToPort = port,
        Ipv4Ranges = new List<IpRange>() { new IpRange { CidrIp = ipAddress } }
      });

      // Create the inbound rule for the security group
      AuthorizeSecurityGroupIngressResponse responseIngress =
        await eC2Client.AuthorizeSecurityGroupIngressAsync(ingressRequest);
      Console.WriteLine($"\nNew RDP rule was written in {groupID} for {ipAddress}.");
      Console.WriteLine($"Result: {responseIngress.HttpStatusCode}");
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: EC2AddRuleForRDP -g <group-id> -i <ip-address> [-p <port>]" +
        "\n  -g, --group-id: The ID of the security group to which you want to add the inbound rule." +
        "\n  -i, --ip-address: An IP address or address range in CIDR format." +
        "\n  -p, --port: The TCP port number. Defaults to 3389.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 추가 고려 사항
<a name="authorize-ingress-additional"></a>
+ 포트 번호를 제공하지 않는 경우 애플리케이션은 기본적으로 포트 3389로 설정됩니다. 이 포트는 Windows RDP용 포트로, Windows를 실행하는 EC2 인스턴스에 연결할 수 있습니다. Linux를 실행하는 EC2 인스턴스를 시작하는 경우에는 그 대신에 TCP 포트 22(SSH)를 사용하세요.
+ 참고로 이 예제에서는 `IpProtocol`이 “tcp”로 설정되어 있습니다. `IpProtocol`의 값은 [IpPermission](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TIpPermission.html) 클래스의 `IpProtocol` 속성에 대한 설명에서 찾을 수 있습니다.
+ 이 예를 사용할 때 로컬 컴퓨터의 IP 주소가 필요할 수 있습니다. 주소를 확인할 수 있는 몇 가지 방법은 다음과 같습니다.
  + EC2 인스턴스에 연결할 로컬 컴퓨터에 정적 퍼블릭 IP 주소가 있는 경우 서비스를 사용하여 해당 주소를 가져올 수 있습니다. 이러한 서비스 중 하나가 [http://checkip.amazonaws.com/](http://checkip.amazonaws.com/)입니다. 인바운드 트래픽 권한 부여에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [보안 그룹에 규칙 추가](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/working-with-security-groups.html#adding-security-group-rule) 및 [다양한 사용 사례에 대한 보안 그룹 규칙](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-rules-reference.html)을 참조하세요.
  + 로컬 컴퓨터의 IP 주소를 얻는 또 다른 방법은 [Amazon EC2 콘솔](https://console.aws.amazon.com/ec2/v2/home#SecurityGroups)을 사용하는 것입니다.

    보안 그룹 중 하나를 선택하고 **인바운드 규칙** 탭을 선택한 다음 **인바운드 규칙 편집**을 선택합니다. 인바운드 규칙에서 **소스** 열의 드롭다운 메뉴를 열고 **내 IP**를 선택하면 로컬 컴퓨터의 IP 주소가 CIDR 형식으로 표시됩니다. 작업을 **취소**해야 합니다.
+ [Amazon EC2 콘솔](https://console.aws.amazon.com/ec2/v2/home#SecurityGroups)에서 보안 그룹 목록을 검사하여 이 예제의 결과를 확인할 수 있습니다.

# Amazon EC2 키 페어로 작업
<a name="key-pairs"></a>

Amazon EC2는 퍼블릭 키 암호화 기법을 사용하여 로그인 정보를 암호화 및 해독합니다. 퍼블릭 키 암호화 기법에서는 퍼블릭 키를 사용하여 데이터를 암호화한 후 수신자가 개인 키를 사용하여 해당 데이터를 해독합니다. 퍼블릭 키와 프라이빗 키를 키 페어라고 합니다. EC2 인스턴스에 로그인하려면 시작 시 키 페어를 지정한 다음 연결할 때 키 페어의 프라이빗 키를 제공해야 합니다.

EC2 인스턴스를 시작할 때 해당 인스턴스에 대해 키 페어를 생성하거나 다른 인스턴스를 시작할 때 이미 사용한 키 페어를 사용할 수 있습니다. Amazon EC2 키 페어에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Amazon EC2 키 페어 작업](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html)을 참조하세요.

API 및 사전 조건에 대한 자세한 내용은 상위 섹션([Amazon EC2 작업](ec2-apis-intro.md))을 참조하세요.

**Topics**
+ [

# 키 페어 생성 및 표시
](create-save-key-pair.md)
+ [

# 키 페어 삭제
](delete-key-pairs.md)

# 키 페어 생성 및 표시
<a name="create-save-key-pair"></a>

이 예제에서는를 사용하여 키 페어를 AWS SDK for .NET 생성하는 방법을 보여줍니다. 애플리케이션은 새 키 페어의 이름과 PEM 파일(“.pem” 확장명)의 이름을 사용합니다. 키 페어를 생성하고 PEM 파일에 프라이빗 키를 쓴 다음 사용 가능한 모든 키 페어를 표시합니다. 명령줄 인수를 제공하지 않으면 애플리케이션은 사용 가능한 모든 키 페어만 표시합니다.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#create-save-key-pair-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [

## 키 페어 생성
](#create-save-key-pair-create)
+ [

## 사용 가능한 키 페어 표시
](#create-save-key-pair-display)
+ [

## 전체 코드
](#create-save-key-pair-complete-code)
+ [

## 추가 고려 사항
](#create-save-key-pair-additional)

## 키 페어 생성
<a name="create-save-key-pair-create"></a>

다음 코드 조각은 키 페어를 생성한 다음 지정된 PEM 파일에 프라이빗 키를 저장합니다.

[이 주제의 끝 부분에 있는](#create-save-key-pair-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to create a key pair and save the key material in a PEM file
    private static async Task CreateKeyPair(
      IAmazonEC2 ec2Client, string keyPairName, string pemFileName)
    {
      // Create the key pair
      CreateKeyPairResponse response =
        await ec2Client.CreateKeyPairAsync(new CreateKeyPairRequest{
          KeyName = keyPairName
        });
      Console.WriteLine($"\nCreated new key pair: {response.KeyPair.KeyName}");

      // Save the private key in a PEM file
      using (var s = new FileStream(pemFileName, FileMode.Create))
      using (var writer = new StreamWriter(s))
      {
        writer.WriteLine(response.KeyPair.KeyMaterial);
      }
    }
```

## 사용 가능한 키 페어 표시
<a name="create-save-key-pair-display"></a>

다음 코드 조각은 사용 가능한 키 페어의 목록을 표시합니다.

[이 주제의 끝 부분에 있는](#create-save-key-pair-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to show the key pairs that are available
    private static async Task EnumerateKeyPairs(IAmazonEC2 ec2Client)
    {
      DescribeKeyPairsResponse response = await ec2Client.DescribeKeyPairsAsync();
      Console.WriteLine("Available key pairs:");
      foreach (KeyPairInfo item in response.KeyPairs)
        Console.WriteLine($"  {item.KeyName}");
    }
```

## 전체 코드
<a name="create-save-key-pair-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c21c15c11c19b5b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TEC2Client.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2Model.html)

  클래스 [CreateKeyPairRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TCreateKeyPairRequest.html)

  클래스 [CreateKeyPairResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TCreateKeyPairResponse.html)

  클래스 [DescribeKeyPairsResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TDescribeKeyPairsResponse.html)

  클래스 [KeyPairInfo](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TKeyPairInfo.html)

### 코드
<a name="w2aac19c15c21c15c11c19b7b1"></a>

```
using System;
using System.Threading.Tasks;
using System.IO;
using Amazon.EC2;
using Amazon.EC2.Model;
using System.Collections.Generic;

namespace EC2CreateKeyPair
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to create and store a key pair
  class Program
  {
    static async Task Main(string[] args)
    {
      // Create the EC2 client
      var ec2Client = new AmazonEC2Client();

      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        // In the case of no command-line arguments,
        // just show help and the existing key pairs
        PrintHelp();
        Console.WriteLine("\nNo arguments specified.");
        Console.Write(
          "Do you want to see a list of the existing key pairs? ((y) or n): ");
        string response = Console.ReadLine();
        if((string.IsNullOrEmpty(response)) || (response.ToLower() == "y"))
          await EnumerateKeyPairs(ec2Client);
        return;
      }

      // Get the application arguments from the parsed list
      string keyPairName =
        CommandLine.GetArgument(parsedArgs, null, "-k", "--keypair-name");
      string pemFileName =
        CommandLine.GetArgument(parsedArgs, null, "-p", "--pem-filename");
      if(string.IsNullOrEmpty(keyPairName))
        CommandLine.ErrorExit("\nNo key pair name specified." +
          "\nRun the command with no arguments to see help.");
      if(string.IsNullOrEmpty(pemFileName) || !pemFileName.EndsWith(".pem"))
        CommandLine.ErrorExit("\nThe PEM filename is missing or incorrect." +
          "\nRun the command with no arguments to see help.");

      // Create the key pair
      await CreateKeyPair(ec2Client, keyPairName, pemFileName);
      await EnumerateKeyPairs(ec2Client);
    }


    //
    // Method to create a key pair and save the key material in a PEM file
    private static async Task CreateKeyPair(
      IAmazonEC2 ec2Client, string keyPairName, string pemFileName)
    {
      // Create the key pair
      CreateKeyPairResponse response =
        await ec2Client.CreateKeyPairAsync(new CreateKeyPairRequest{
          KeyName = keyPairName
        });
      Console.WriteLine($"\nCreated new key pair: {response.KeyPair.KeyName}");

      // Save the private key in a PEM file
      using (var s = new FileStream(pemFileName, FileMode.Create))
      using (var writer = new StreamWriter(s))
      {
        writer.WriteLine(response.KeyPair.KeyMaterial);
      }
    }


    //
    // Method to show the key pairs that are available
    private static async Task EnumerateKeyPairs(IAmazonEC2 ec2Client)
    {
      DescribeKeyPairsResponse response = await ec2Client.DescribeKeyPairsAsync();
      Console.WriteLine("Available key pairs:");
      foreach (KeyPairInfo item in response.KeyPairs)
        Console.WriteLine($"  {item.KeyName}");
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: EC2CreateKeyPair -k <keypair-name> -p <pem-filename>" +
        "\n  -k, --keypair-name: The name you want to assign to the key pair." +
        "\n  -p, --pem-filename: The name of the PEM file to create, with a \".pem\" extension.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 추가 고려 사항
<a name="create-save-key-pair-additional"></a>
+ 예제를 실행한 후 [Amazon EC2 콘솔](https://console.aws.amazon.com/ec2/#KeyPairs)에서 새 키 페어를 확인할 수 있습니다.
+ 나중에 프라이빗 키를 검색할 수 없으므로 키 페어를 생성할 때는 반환되는 프라이빗 키를 저장해야 합니다.

# 키 페어 삭제
<a name="delete-key-pairs"></a>

이 예제에서는를 사용하여 키 페어 AWS SDK for .NET 를 삭제하는 방법을 보여줍니다. 이 애플리케이션은 키 페어 이름을 사용합니다. 키 페어를 삭제한 다음 사용 가능한 모든 키 페어를 표시합니다. 명령줄 인수를 제공하지 않으면 애플리케이션은 사용 가능한 모든 키 페어만 표시합니다.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#delete-key-pairs-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [

## 키 페어 삭제
](#delete-key-pairs-create)
+ [

## 사용 가능한 키 페어 표시
](#delete-key-pairs-display)
+ [

## 전체 코드
](#delete-key-pairs-complete-code)

## 키 페어 삭제
<a name="delete-key-pairs-create"></a>

다음 코드 조각은 키 페어를 삭제합니다.

[이 주제의 끝 부분에 있는](#delete-key-pairs-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to delete a key pair
    private static async Task DeleteKeyPair(IAmazonEC2 ec2Client, string keyName)
    {
      await ec2Client.DeleteKeyPairAsync(new DeleteKeyPairRequest{
        KeyName = keyName});
      Console.WriteLine($"\nKey pair {keyName} has been deleted (if it existed).");
    }
```

## 사용 가능한 키 페어 표시
<a name="delete-key-pairs-display"></a>

다음 코드 조각은 사용 가능한 키 페어의 목록을 표시합니다.

[이 주제의 끝 부분에 있는](#delete-key-pairs-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to show the key pairs that are available
    private static async Task EnumerateKeyPairs(IAmazonEC2 ec2Client)
    {
      DescribeKeyPairsResponse response = await ec2Client.DescribeKeyPairsAsync();
      Console.WriteLine("Available key pairs:");
      foreach (KeyPairInfo item in response.KeyPairs)
        Console.WriteLine($"  {item.KeyName}");
    }
```

## 전체 코드
<a name="delete-key-pairs-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c21c15c13c19b5b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TEC2Client.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2Model.html)

  클래스 [https://docs.aws.amazon.com/sdkfornet/v4/apidocs/](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/)

  클래스 [DescribeKeyPairsResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TDescribeKeyPairsResponse.html)

  클래스 [KeyPairInfo](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TKeyPairInfo.html)

### 코드
<a name="w2aac19c15c21c15c13c19b7b1"></a>

```
using System;
using System.Threading.Tasks;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2DeleteKeyPair
{
  class Program
  {
    static async Task Main(string[] args)
    {
      // Create the EC2 client
      var ec2Client = new AmazonEC2Client();

      if(args.Length == 1)
      {
        // Delete a key pair (if it exists)
        await DeleteKeyPair(ec2Client, args[0]);

        // Display the key pairs that are left
        await EnumerateKeyPairs(ec2Client);
      }
      else
      {
        Console.WriteLine("\nUsage: EC2DeleteKeyPair keypair-name");
        Console.WriteLine("  keypair-name - The name of the key pair you want to delete.");
        Console.WriteLine("\nNo arguments specified.");
        Console.Write(
          "Do you want to see a list of the existing key pairs? ((y) or n): ");
        string response = Console.ReadLine();
        if((string.IsNullOrEmpty(response)) || (response.ToLower() == "y"))
          await EnumerateKeyPairs(ec2Client);
      }
    }


    //
    // Method to delete a key pair
    private static async Task DeleteKeyPair(IAmazonEC2 ec2Client, string keyName)
    {
      await ec2Client.DeleteKeyPairAsync(new DeleteKeyPairRequest{
        KeyName = keyName});
      Console.WriteLine($"\nKey pair {keyName} has been deleted (if it existed).");
    }


    //
    // Method to show the key pairs that are available
    private static async Task EnumerateKeyPairs(IAmazonEC2 ec2Client)
    {
      DescribeKeyPairsResponse response = await ec2Client.DescribeKeyPairsAsync();
      Console.WriteLine("Available key pairs:");
      foreach (KeyPairInfo item in response.KeyPairs)
        Console.WriteLine($"  {item.KeyName}");
    }
  }
}
```

# Amazon EC2 리전 및 가용 영역 표시
<a name="using-regions-and-availability-zones"></a>

Amazon EC2는 전 세계의 여러 곳에서 호스팅되고 있습니다. 해당 위치는 리전 및 가용 영역으로 구성됩니다. 각 리전은 지리적 개별 영역이며, 가용 영역이라고 알려진 여러 개의 격리된 위치가 있습니다.

리전 및 가용 영역에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [리전 및 가용 영역](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html)을 참조하세요.

이 예제에서는 AWS SDK for .NET 를 사용하여 EC2 클라이언트와 관련된 리전 및 가용 영역에 대한 세부 정보를 가져오는 방법을 보여줍니다. 애플리케이션은 EC2 클라이언트가 사용할 수 있는 리전 및 가용 영역 목록을 표시합니다.

## SDK 레퍼런스
<a name="w2aac19c15c21c17b9b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TEC2Client.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2Model.html)

  클래스 [DescribeAvailabilityZonesResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TDescribeAvailabilityZonesResponse.html)

  클래스 [DescribeRegionsResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TDescribeRegionsResponse.html)

  클래스 [AvailabilityZone](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TAvailabilityZone.html)

  클래스 [Region](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TRegion.html)

```
using System;
using System.Threading.Tasks;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2RegionsAndZones
{
  class Program
  {
    static async Task Main(string[] args)
    {
      Console.WriteLine(
        "Finding the Regions and Availability Zones available to an EC2 client...");

      // Create the EC2 client
      var ec2Client = new AmazonEC2Client();

      // Display the Regions and Availability Zones
      await DescribeRegions(ec2Client);
      await DescribeAvailabilityZones(ec2Client);
    }


    //
    // Method to display Regions
    private static async Task DescribeRegions(IAmazonEC2 ec2Client)
    {
      Console.WriteLine("\nRegions that are enabled for the EC2 client:");
      DescribeRegionsResponse response = await ec2Client.DescribeRegionsAsync();
      foreach (Region region in response.Regions)
        Console.WriteLine(region.RegionName);
    }


    //
    // Method to display Availability Zones
    private static async Task DescribeAvailabilityZones(IAmazonEC2 ec2Client)
    {
      Console.WriteLine("\nAvailability Zones for the EC2 client's region:");
      DescribeAvailabilityZonesResponse response =
        await ec2Client.DescribeAvailabilityZonesAsync();
      foreach (AvailabilityZone az in response.AvailabilityZones)
        Console.WriteLine(az.ZoneName);
    }
  }
}
```

# Amazon EC2 인스턴스 작업
<a name="how-to-ec2"></a>

 AWS SDK for .NET 를 사용하여 생성, 시작 및 종료와 같은 작업을 통해 Amazon EC2 인스턴스를 제어할 수 있습니다. 이 섹션의 주제에서는 이를 수행하는 방법의 몇 가지 예제를 제공합니다. EC2 인스턴스에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Amazon EC2 인스턴스](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Instances.html)를 참조하세요.

API 및 사전 조건에 대한 자세한 내용은 상위 섹션([Amazon EC2 작업](ec2-apis-intro.md))을 참조하세요.

**Topics**
+ [EC2 인스턴스 시작](run-instance.md)
+ [EC2 인스턴스 종료](terminate-instance.md)

# Amazon EC2 인스턴스 시작
<a name="run-instance"></a>

이 예제에서는 AWS SDK for .NET 를 사용하여 동일한 Amazon Machine Image(AMI)에서 하나 이상의 동일하게 구성된 Amazon EC2 인스턴스를 시작하는 방법을 보여줍니다. 애플리케이션은 사용자가 제공한 [여러 입력](#run-instance-gather)을 사용하여 EC2 인스턴스를 시작한 다음 “Pending” 상태가 아닐 때까지 인스턴스를 모니터링합니다.

[(선택 사항) 인스턴스에 연결](#connect-to-instance)에 설명된 대로 EC2 인스턴스가 실행될 때 EC2 인스턴스에 원격으로 연결할 수 있습니다.

**주의**  
EC2-Classic은 2022년 8월 15일에 사용 중지되었습니다. EC2-Classic에서 VPC로 마이그레이션하는 것이 좋습니다. 자세한 내용은 블로그 게시물 [EC2-Classic 네트워킹은 사용 중지 중입니다 - 준비 방법은 다음과 같습니다](https://aws.amazon.com/blogs/aws/ec2-classic-is-retiring-heres-how-to-prepare/)를 참조하세요.

다음 섹션에서는 이 예제의 코드 조각과 기타 정보를 제공합니다. [예제의 전체 코드](#run-instance-complete-code)는 코드 조각 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [

## 필요한 내용 수집
](#run-instance-gather)
+ [

## 인스턴스 시작
](#run-instance-launch)
+ [

## 인스턴스 모니터링
](#run-instance-monitor)
+ [

## 전체 코드
](#run-instance-complete-code)
+ [

## 추가 고려 사항
](#run-instance-additional)
+ [

## (선택 사항) 인스턴스에 연결
](#connect-to-instance)
+ [

## 정리
](#run-instance-cleanup)

## 필요한 내용 수집
<a name="run-instance-gather"></a>

EC2 인스턴스를 시작하려면 몇 가지 사항이 필요합니다.
+ 인스턴스가 시작될 [VPC](https://docs.aws.amazon.com/vpc/latest/userguide/)입니다. Windows 인스턴스이고 RDP를 통해 연결하려는 경우 VPC에 인터넷 게이트웨이가 연결되어 있어야 할 가능성이 높으며 라우팅 테이블의 인터넷 게이트웨이에 대한 항목도 있어야 합니다. 자세한 내용은 *Amazon VPC 사용 설명서*의 [인터넷 게이트웨이](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html)를 참조하세요.
+ 인스턴스가 시작될 VPC의 기존 서브넷 ID입니다. [Amazon VPC 콘솔](https://console.aws.amazon.com/vpc/home#subnets)에 로그인하는 것도 이를 찾거나 생성하는 쉬운 방법이지만, [CreateSubnetAsync](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/MEC2CreateSubnetAsyncCreateSubnetRequestCancellationToken.html) 및 [DescribeSubnetsAsync](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/MEC2DescribeSubnetsAsyncDescribeSubnetsRequestCancellationToken.html) 메서드를 사용하여 프로그래밍 방식으로 가져올 수도 있습니다.
**참고**  
이 파라미터를 제공하지 않으면 계정의 기본 VPC에서 새 인스턴스가 시작됩니다.
+ 인스턴스가 시작될 VPC에 속하는 기존 보안 그룹의 ID입니다. 자세한 내용은 [Amazon EC2 에서 보안 그룹 작업](security-groups.md) 단원을 참조하십시오.
+ 새 인스턴스에 연결하려면 앞서 언급한 보안 그룹에 포트 22에서의 SSH 트래픽(Linux 인스턴스) 또는 포트 3389에서의 RDP 트래픽(Windows 인스턴스)을 허용하는 적절한 인바운드 규칙이 있어야 합니다. 이를 수행하는 방법은 해당 주제의 끝 부분에 있는 [추가 고려 사항](authorize-ingress.md#authorize-ingress-additional)을 포함하여 [보안 그룹 업데이트](authorize-ingress.md)을 참조하세요.
+ 인스턴스를 생성하는 데 사용되는 Amazon Machine Image(AMI)입니다. AMI에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Amazon Machine Image(AMI)](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html)를 참조하세요. 특히 [AMI 찾기](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html) 및 [공유 AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/sharing-amis.html)를 참조하세요.
+ 새 인스턴스에 연결하는 데 사용되는 기존 EC2 키 페어의 이름입니다. 자세한 내용은 [Amazon EC2 키 페어로 작업](key-pairs.md) 단원을 참조하십시오.
+ 앞서 언급한 EC2 키 페어의 프라이빗 키가 포함된 PEM 파일의 이름입니다. PEM 파일은 인스턴스에 [원격으로 연결](#connect-to-instance)할 때 사용됩니다.

## 인스턴스 시작
<a name="run-instance-launch"></a>

다음 코드 조각은 EC2 인스턴스를 시작합니다.

[이 주제의 끝 부분에 있는](#run-instance-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to launch the instances
    // Returns a list with the launched instance IDs
    private static async Task<List<string>> LaunchInstances(
      IAmazonEC2 ec2Client, RunInstancesRequest requestLaunch)
    {
      var instanceIds = new List<string>();
      RunInstancesResponse responseLaunch =
        await ec2Client.RunInstancesAsync(requestLaunch);

      Console.WriteLine("\nNew instances have been created.");
      foreach (Instance item in responseLaunch.Reservation.Instances)
      {
        instanceIds.Add(item.InstanceId);
        Console.WriteLine($"  New instance: {item.InstanceId}");
      }

      return instanceIds;
    }
```

## 인스턴스 모니터링
<a name="run-instance-monitor"></a>

다음 코드 조각은 “Pending” 상태가 아닐 때까지 인스턴스를 모니터링합니다.

[이 주제의 끝 부분에 있는](#run-instance-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

`Instance.State.Code` 속성의 유효한 값은 [InstanceState](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TInstanceState.html) 클래스를 참조하세요.

```
    //
    // Method to wait until the instances are running (or at least not pending)
    private static async Task CheckState(IAmazonEC2 ec2Client, List<string> instanceIds)
    {
      Console.WriteLine(
        "\nWaiting for the instances to start." +
        "\nPress any key to stop waiting. (Response might be slightly delayed.)");

      int numberRunning;
      DescribeInstancesResponse responseDescribe;
      var requestDescribe = new DescribeInstancesRequest{
        InstanceIds = instanceIds};

      // Check every couple of seconds
      int wait = 2000;
      while(true)
      {
        // Get and check the status for each of the instances to see if it's past pending.
        // Once all instances are past pending, break out.
        // (For this example, we are assuming that there is only one reservation.)
        Console.Write(".");
        numberRunning = 0;
        responseDescribe = await ec2Client.DescribeInstancesAsync(requestDescribe);
        foreach(Instance i in responseDescribe.Reservations[0].Instances)
        {
          // Check the lower byte of State.Code property
          // Code == 0 is the pending state
          if((i.State.Code & 255) > 0) numberRunning++;
        }
        if(numberRunning == responseDescribe.Reservations[0].Instances.Count)
          break;

        // Wait a bit and try again (unless the user wants to stop waiting)
        Thread.Sleep(wait);
        if(Console.KeyAvailable)
          break;
      }

      Console.WriteLine("\nNo more instances are pending.");
      foreach(Instance i in responseDescribe.Reservations[0].Instances)
      {
        Console.WriteLine($"For {i.InstanceId}:");
        Console.WriteLine($"  VPC ID: {i.VpcId}");
        Console.WriteLine($"  Instance state: {i.State.Name}");
        Console.WriteLine($"  Public IP address: {i.PublicIpAddress}");
        Console.WriteLine($"  Public DNS name: {i.PublicDnsName}");
        Console.WriteLine($"  Key pair name: {i.KeyName}");
      }
    }
```

## 전체 코드
<a name="run-instance-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c21c19b9c27b5b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TEC2Client.html)

  클래스 [InstanceType](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TInstanceType.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2Model.html)

  클래스 [DescribeInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TDescribeInstancesRequest.html)

  클래스 [DescribeInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TDescribeInstancesResponse.html)

  클래스 [Instance](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TInstance.html)

  클래스 [InstanceNetworkInterfaceSpecification](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TInstanceNetworkInterfaceSpecification.html)

  클래스 [RunInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TRunInstancesRequest.html)

  클래스 [RunInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TRunInstancesResponse.html)

### 코드
<a name="w2aac19c15c21c19b9c27b7b1"></a>

```
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2LaunchInstance
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to launch an EC2 instance
  class Program
  {
    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        return;
      }

      // Get the application arguments from the parsed list
      string groupID =
        CommandLine.GetArgument(parsedArgs, null, "-g", "--group-id");
      string ami =
        CommandLine.GetArgument(parsedArgs, null, "-a", "--ami-id");
      string keyPairName =
        CommandLine.GetArgument(parsedArgs, null, "-k", "--keypair-name");
      string subnetID =
        CommandLine.GetArgument(parsedArgs, null, "-s", "--subnet-id");
      if(   (string.IsNullOrEmpty(groupID) || !groupID.StartsWith("sg-"))
         || (string.IsNullOrEmpty(ami) || !ami.StartsWith("ami-"))
         || (string.IsNullOrEmpty(keyPairName))
         || (!string.IsNullOrEmpty(subnetID) && !subnetID.StartsWith("subnet-")))
        CommandLine.ErrorExit(
          "\nOne or more of the required arguments is missing or incorrect." +
          "\nRun the command with no arguments to see help.");

      // Create an EC2 client
      var ec2Client = new AmazonEC2Client();

      // Create an object with the necessary properties
      RunInstancesRequest request = GetRequestData(groupID, ami, keyPairName, subnetID);

      // Launch the instances and wait for them to start running
      var instanceIds = await LaunchInstances(ec2Client, request);
      await CheckState(ec2Client, instanceIds);
    }


    //
    // Method to put together the properties needed to launch the instance.
    private static RunInstancesRequest GetRequestData(
      string groupID, string ami, string keyPairName, string subnetID)
    {
      // Common properties
      var groupIDs = new List<string>() { groupID };
      var request = new RunInstancesRequest()
      {
        // The first three of these would be additional command-line arguments or similar.
        InstanceType = InstanceType.T1Micro,
        MinCount = 1,
        MaxCount = 1,
        ImageId = ami,
        KeyName = keyPairName
      };

      // Properties specifically for EC2 in a VPC.
      if(!string.IsNullOrEmpty(subnetID))
      {
        request.NetworkInterfaces =
          new List<InstanceNetworkInterfaceSpecification>() {
            new InstanceNetworkInterfaceSpecification() {
              DeviceIndex = 0,
              SubnetId = subnetID,
              Groups = groupIDs,
              AssociatePublicIpAddress = true
            }
          };
      }

      // Properties specifically for EC2-Classic
      else
      {
        request.SecurityGroupIds = groupIDs;
      }
      return request;
    }


    //
    // Method to launch the instances
    // Returns a list with the launched instance IDs
    private static async Task<List<string>> LaunchInstances(
      IAmazonEC2 ec2Client, RunInstancesRequest requestLaunch)
    {
      var instanceIds = new List<string>();
      RunInstancesResponse responseLaunch =
        await ec2Client.RunInstancesAsync(requestLaunch);

      Console.WriteLine("\nNew instances have been created.");
      foreach (Instance item in responseLaunch.Reservation.Instances)
      {
        instanceIds.Add(item.InstanceId);
        Console.WriteLine($"  New instance: {item.InstanceId}");
      }

      return instanceIds;
    }


    //
    // Method to wait until the instances are running (or at least not pending)
    private static async Task CheckState(IAmazonEC2 ec2Client, List<string> instanceIds)
    {
      Console.WriteLine(
        "\nWaiting for the instances to start." +
        "\nPress any key to stop waiting. (Response might be slightly delayed.)");

      int numberRunning;
      DescribeInstancesResponse responseDescribe;
      var requestDescribe = new DescribeInstancesRequest{
        InstanceIds = instanceIds};

      // Check every couple of seconds
      int wait = 2000;
      while(true)
      {
        // Get and check the status for each of the instances to see if it's past pending.
        // Once all instances are past pending, break out.
        // (For this example, we are assuming that there is only one reservation.)
        Console.Write(".");
        numberRunning = 0;
        responseDescribe = await ec2Client.DescribeInstancesAsync(requestDescribe);
        foreach(Instance i in responseDescribe.Reservations[0].Instances)
        {
          // Check the lower byte of State.Code property
          // Code == 0 is the pending state
          if((i.State.Code & 255) > 0) numberRunning++;
        }
        if(numberRunning == responseDescribe.Reservations[0].Instances.Count)
          break;

        // Wait a bit and try again (unless the user wants to stop waiting)
        Thread.Sleep(wait);
        if(Console.KeyAvailable)
          break;
      }

      Console.WriteLine("\nNo more instances are pending.");
      foreach(Instance i in responseDescribe.Reservations[0].Instances)
      {
        Console.WriteLine($"For {i.InstanceId}:");
        Console.WriteLine($"  VPC ID: {i.VpcId}");
        Console.WriteLine($"  Instance state: {i.State.Name}");
        Console.WriteLine($"  Public IP address: {i.PublicIpAddress}");
        Console.WriteLine($"  Public DNS name: {i.PublicDnsName}");
        Console.WriteLine($"  Key pair name: {i.KeyName}");
      }
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: EC2LaunchInstance -g <group-id> -a <ami-id> -k <keypair-name> [-s <subnet-id>]" +
        "\n  -g, --group-id: The ID of the security group." +
        "\n  -a, --ami-id: The ID of an Amazon Machine Image." +
        "\n  -k, --keypair-name - The name of a key pair." +
        "\n  -s, --subnet-id: The ID of a subnet. Required only for EC2 in a VPC.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 추가 고려 사항
<a name="run-instance-additional"></a>
+ EC2 인스턴스의 상태를 확인할 때 [DescribeInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TDescribeInstancesRequest.html) 객체의 `Filter` 속성에 필터를 추가할 수 있습니다. 이 기술을 사용하면 요청을 특정 인스턴스(예: 특정 사용자 지정 태그가 있는 인스턴스)로 제한할 수 있습니다.
+ 간략하게 나타내기 위해 일부 속성에는 일반적인 값이 지정되었습니다. 대신 프로그래밍 방식으로 또는 사용자 입력을 통해 이러한 속성 중 일부 또는 전체를 결정할 수 있습니다.
+ [RunInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TRunInstancesRequest.html) 객체의 `MinCount` 및 `MaxCount` 속성에 사용할 수 있는 값은 대상 가용 영역과 해당 인스턴스 유형에 허용되는 최대 인스턴스 수에 따라 결정됩니다. 자세한 내용은 Amazon EC2 일반 FAQ의 [Amazon EC2에서 실행할 수 있는 인스턴스 수는 몇 개입니까](https://aws.amazon.com/ec2/faqs/#How_many_instances_can_I_run_in_Amazon_EC2)를 참조하세요.
+ 이 예제와 다른 인스턴스 유형을 사용하려는 경우 선택할 수 있는 여러 인스턴스 유형이 있습니다. 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Amazon EC2 인스턴스 유형](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html)을 참조하세요. [인스턴스 유형 세부 정보](https://aws.amazon.com/ec2/instance-types/) 및 [인스턴스 유형 탐색기](https://aws.amazon.com/ec2/instance-explorer/)도 참조하세요.
+ 인스턴스를 시작할 때 [IAM 역할](net-dg-hosm.md)을 인스턴스에 연결할 수도 있습니다. 이렇게 하려면 `Name` 속성이 IAM 역할의 이름으로 설정된 [IamInstanceProfileSpecification](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TIamInstanceProfileSpecification.html) 객체를 생성해야 합니다. 그런 다음 해당 객체를 [RunInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TRunInstancesRequest.html) 객체의 `IamInstanceProfile` 속성에 추가합니다.
**참고**  
IAM 역할이 연결된 EC2 인스턴스를 시작하려면 IAM 사용자의 구성에 특정 권한이 포함되어야 합니다. 필요한 권한에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [IAM 역할을 인스턴스에 전달할 수 있는 사용자 권한 부여](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#permission-to-pass-iam-roles)를 참조하세요.

## (선택 사항) 인스턴스에 연결
<a name="connect-to-instance"></a>

인스턴스가 실행된 후에는 적절한 원격 클라이언트를 사용하여 인스턴스에 원격으로 연결할 수 있습니다. Linux 및 Windows 인스턴스 모두에 대해 인스턴스의 퍼블릭 IP 주소 또는 퍼블릭 DNS 이름이 필요합니다. 또한 다음 항목이 필요합니다.

**Linux 인스턴스의 경우**

SSH 클라이언트를 사용하여 Linux 인스턴스에 연결할 수 있습니다. [보안 그룹 업데이트](authorize-ingress.md)에 설명된 대로 인스턴스를 시작할 때 사용한 보안 그룹이 포트 22에서의 SSH 트래픽을 허용하는지 확인합니다.

또한 인스턴스를 시작하는 데 사용한 키 페어의 비공개 부분, 즉 PEM 파일도 필요합니다.

자세한 내용은 [Amazon EC2 사용 설명서의 Linux 인스턴스에 연결](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-to-linux-instance.html)을 참조하세요.

**Windows 인스턴스의 경우**

RDP 클라이언트를 사용하여 인스턴스에 연결할 수 있습니다. [보안 그룹 업데이트](authorize-ingress.md)에 설명된 대로 인스턴스를 시작할 때 사용한 보안 그룹이 포트 3389에서의 RDP 트래픽을 허용하는지 확인합니다.

또한 관리자 암호가 필요합니다. 다음 예제 코드를 사용하여 이를 얻을 수 있습니다. 이 코드에는 인스턴스 ID와 인스턴스 시작에 사용된 키 페어의 비공개 부분, 즉 PEM 파일이 필요합니다.

자세한 내용은 Amazon EC2 사용 설명서의 [Windows 인스턴스에 연결](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connecting_to_windows_instance.html)을 참조하세요.

**주의**  
이 예제 코드는 인스턴스의 일반 텍스트 관리자 암호를 반환합니다.

### SDK 레퍼런스
<a name="w2aac19c15c21c19b9c35c23b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TEC2Client.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2Model.html)

  클래스 [GetPasswordDataRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TGetPasswordDataRequest.html)

  클래스 [GetPasswordDataResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TGetPasswordDataResponse.html)

### 코드
<a name="w2aac19c15c21c19b9c35c25b1"></a>

```
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2GetWindowsPassword
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to get the Administrator password of a Windows EC2 instance
  class Program
  {
    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        return;
      }

      // Get the application arguments from the parsed list
      string instanceID =
        CommandLine.GetArgument(parsedArgs, null, "-i", "--instance-id");
      string pemFileName =
        CommandLine.GetArgument(parsedArgs, null, "-p", "--pem-filename");
      if(   (string.IsNullOrEmpty(instanceID) || !instanceID.StartsWith("i-"))
         || (string.IsNullOrEmpty(pemFileName) || !pemFileName.EndsWith(".pem")))
        CommandLine.ErrorExit(
          "\nOne or more of the required arguments is missing or incorrect." +
          "\nRun the command with no arguments to see help.");

      // Create the EC2 client
      var ec2Client = new AmazonEC2Client();

      // Get and display the password
      string password = await GetPassword(ec2Client, instanceID, pemFileName);
      Console.WriteLine($"\nPassword: {password}");
    }


    //
    // Method to get the administrator password of a Windows EC2 instance
    private static async Task<string> GetPassword(
      IAmazonEC2 ec2Client, string instanceID, string pemFilename)
    {
      string password = string.Empty;
      GetPasswordDataResponse response =
        await ec2Client.GetPasswordDataAsync(new GetPasswordDataRequest{
          InstanceId = instanceID});
      if(response.PasswordData != null)
      {
        password = response.GetDecryptedPassword(File.ReadAllText(pemFilename));
      }
      else
      {
        Console.WriteLine($"\nThe password is not available for instance {instanceID}.");
        Console.WriteLine($"If this is a Windows instance, the password might not be ready.");
      }
      return password;
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: EC2GetWindowsPassword -i <instance-id> -p pem-filename" +
        "\n  -i, --instance-id: The name of the EC2 instance." +
        "\n  -p, --pem-filename: The name of the PEM file with the private key.");
    }
  }

  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 정리
<a name="run-instance-cleanup"></a>

EC2 인스턴스가 더 이상 필요하지 않은 경우 [Amazon EC2 인스턴스 종료](terminate-instance.md)의 설명에 따라 인스턴스를 삭제하십시오.

# Amazon EC2 인스턴스 종료
<a name="terminate-instance"></a>

하나 이상의 Amazon EC2 인스턴스가 더 이상 필요하지 않으면 인스턴스를 종료할 수 있습니다.

이 예제에서는를 사용하여 EC2 인스턴스 AWS SDK for .NET 를 종료하는 방법을 보여줍니다. 인스턴스 ID를 입력으로 사용합니다.

## SDK 레퍼런스
<a name="w2aac19c15c21c19c11b7b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TEC2Client.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2Model.html)

  클래스 [TerminateInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TTerminateInstancesRequest.html)

  클래스 [TerminateInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TTerminateInstancesResponse.html)

```
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2TerminateInstance
{
  class Program
  {
    static async Task Main(string[] args)
    {
      if((args.Length == 1) && (args[0].StartsWith("i-")))
      {
        // Terminate the instance
        var ec2Client = new AmazonEC2Client();
        await TerminateInstance(ec2Client, args[0]);
      }
      else
      {
        Console.WriteLine("\nCommand-line argument missing or incorrect.");
        Console.WriteLine("\nUsage: EC2TerminateInstance instance-ID");
        Console.WriteLine("  instance-ID - The EC2 instance you want to terminate.");
        return;
      }
    }

    //
    // Method to terminate an EC2 instance
    private static async Task TerminateInstance(IAmazonEC2 ec2Client, string instanceID)
    {
      var request = new TerminateInstancesRequest{
        InstanceIds = new List<string>() { instanceID }};
      TerminateInstancesResponse response =
        await ec2Client.TerminateInstancesAsync(new TerminateInstancesRequest{
          InstanceIds = new List<string>() { instanceID }
        });
      foreach (InstanceStateChange item in response.TerminatingInstances)
      {
        Console.WriteLine("Terminated instance: " + item.InstanceId);
        Console.WriteLine("Instance state: " + item.CurrentState.Name);
      }
    }
  }
}
```

예제를 실행한 후에는 [Amazon EC2 콘솔](https://console.aws.amazon.com/ec2/)에 로그인하여 [EC2 인스턴스](https://console.aws.amazon.com/ec2/v2/home#Instances)가 종료되었는지 확인하는 것이 좋습니다.

# Amazon EC2 스팟 인스턴스 자습서
<a name="how-to-spot-instances"></a>

이 자습서에서는를 사용하여 Amazon EC2 스팟 인스턴스 AWS SDK for .NET 를 관리하는 방법을 보여줍니다.

## 개요
<a name="tutor-spot-net-overview"></a>

스팟 인스턴스를 사용하면 온디맨드 가격보다 저렴한 비용으로 미사용 Amazon EC2 용량을 요청할 수 있습니다. 이렇게 하면 중단될 수 있는 애플리케이션에 대한 EC2 비용을 크게 낮출 수 있습니다.

다음은 스팟 인스턴스 요청 및 사용 방법의 전반적인 요약입니다.

1. 지불하고자 하는 최고 가격을 지정하는 스팟 인스턴스 요청을 생성합니다.

1. 요청이 처리되면 다른 Amazon EC2 인스턴스와 마찬가지로 인스턴스를 실행합니다.

1. 원하는 기간만큼 인스턴스를 실행한 다음 종료합니다. 단, *스팟 가격*이 변경되어 인스턴스가 자동으로 종료되는 경우는 예외입니다.

1. 더 이상 필요하지 않은 스팟 인스턴스 요청을 정리하여 스팟 인스턴스가 더 이상 생성되지 않도록 합니다.

여기까지 스팟 인스턴스의 대략적 개요였습니다. 스팟 인스턴스를 더 잘 이해하려면 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [스팟 인스턴스](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances.html)를 참조하세요.

## 이 자습서 소개
<a name="about-spot-instances-tutorial"></a>

이 자습서를 따르면 AWS SDK for .NET 를 사용하여 다음을 수행할 수 있습니다.
+ 스팟 인스턴스 요청 생성
+ 스팟 인스턴스 요청이 이행된 시점 확인
+ 스팟 인스턴스 요청 취소
+ 연결된 인스턴스 종료

다음 섹션에서는 이 예제의 코드 조각과 기타 정보를 제공합니다. [예제의 전체 코드](#tutor-spot-net-main)는 코드 조각 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [

## 개요
](#tutor-spot-net-overview)
+ [

## 이 자습서 소개
](#about-spot-instances-tutorial)
+ [

## 사전 조건
](#tutor-spot-net-prereq)
+ [

## 필요한 내용 수집
](#tutor-spot-net-gather)
+ [

## 스팟 인스턴스 요청 생성
](#tutor-spot-net-submit)
+ [

## 스팟 인스턴스 요청 상태 확인
](#tutor-spot-net-request-state)
+ [

## 스팟 인스턴스 요청 정리
](#tutor-spot-net-clean-up-request)
+ [

## 스팟 인스턴스 정리
](#tutor-spot-net-clean-up-instance)
+ [

## 전체 코드
](#tutor-spot-net-main)
+ [

## 추가 고려 사항
](#tutor-spot-net-additional)

## 사전 조건
<a name="tutor-spot-net-prereq"></a>

API 및 사전 조건에 대한 자세한 내용은 상위 섹션([Amazon EC2 작업](ec2-apis-intro.md))을 참조하세요.

## 필요한 내용 수집
<a name="tutor-spot-net-gather"></a>

스팟 인스턴스 요청을 생성하려면 몇 가지 사항이 필요합니다.
+ 인스턴스 수와 인스턴스 유형입니다. 선택할 수 있는 여러 인스턴스 유형이 있습니다. 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Amazon EC2 인스턴스 유형](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html)을 참조하세요. [인스턴스 유형 세부 정보](https://aws.amazon.com/ec2/instance-types/) 및 [인스턴스 유형 탐색기](https://aws.amazon.com/ec2/instance-explorer/)도 참조하세요.

  이 자습서에서 기본 수는 1입니다.
+ 인스턴스를 생성하는 데 사용되는 Amazon Machine Image(AMI)입니다. AMI에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Amazon Machine Image(AMI)](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html)를 참조하세요. 특히 [AMI 찾기](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html) 및 [공유 AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/sharing-amis.html)를 참조하세요.
+ 인스턴스 시간당 지불하려는 최고 가격입니다. [Amazon EC2 요금 페이지](https://aws.amazon.com/ec2/pricing/)에서 모든 인스턴스 유형(온디맨드 인스턴스 및 스팟 인스턴스 모두)의 가격을 확인할 수 있습니다. 이 자습서의 기본 가격은 나중에 설명합니다.
+ 인스턴스에 원격으로 연결하려는 경우 적절한 구성 및 리소스를 갖춘 보안 그룹을 사용합니다. 이에 대한 설명은 [Amazon EC2 에서 보안 그룹 작업](security-groups.md)에 나와 있으며 [필요한 정보를 수집](run-instance.md#run-instance-gather)하고 [인스턴스에 연결](run-instance.md#connect-to-instance)하는 방법에 대한 자세한 내용은 [Amazon EC2 인스턴스 시작](run-instance.md)에 나와 있습니다. 쉽게 설명하기 위해 이 자습서에서는 모든 새 AWS 계정에 있는 **default**라는 보안 그룹을 사용합니다.

스팟 인스턴스를 요청하기 위한 접근 방식에는 여러 가지가 있습니다. 다음은 일반적인 전략입니다.
+ 온디맨드 요금보다 저렴한 요금으로 요청하세요.
+ 결과 계산 값을 기준으로 요청하세요.
+ 가능한 한 빨리 컴퓨팅 용량을 획득하도록 요청하세요.

다음 설명은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [스팟 인스턴스 요금 기록](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances-history.html)을 참조합니다.

### 온디맨드 이하로 비용 줄이기
<a name="reduce-cost"></a>

실행하는 데 몇 시간 또는 며칠이 걸리는 일괄 처리 작업이 있습니다. 그러나 시작 및 종료 시점은 유연하게 선택할 수 있습니다. 그 작업을 온디맨드 인스턴스보다 적은 비용으로 완료할 수 있는지 확인하고 싶다면

Amazon EC2 콘솔 또는 Amazon EC2 API를 사용하여 인스턴스 유형에 대한 스팟 가격 기록을 살펴보십시오. 특정 가용 영역에서 원하는 인스턴스 유형의 가격 내역을 분석하였다면 요청에 사용할 수 있는 다른 방법이 두 가지 있습니다.
+ 일회적인 스팟 인스턴스 요청이 이행되고 해당 작업을 완료하기에 충분한 연속적 컴퓨팅 시간 동안 실행될 가능성이 높을 것이라는 기대로 스팟 가격 범위의 상단(여전히 온디맨드 가격보다는 낮음)에서 요청을 지정합니다.
+ 가격 범위의 하단에서 요청을 지정하고 영구 요청을 통해 시간이 지남에 따라 시작되는 여러 인스턴스를 결합하는 계획을 세울 수도 있습니다. 인스턴스는 합계 시간 동안 충분히 실행되어 훨씬 더 낮은 총 비용으로도 작업을 완료합니다.

### 결과의 가치에 대한 요금만 지불
<a name="value-of-result"></a>

실행할 데이터 처리 작업이 있는 경우 작업 당사자는 그 작업의 결과가 지니는 가치를 충분히 이해하여 컴퓨팅 비용 측면에서 그 결과의 가치가 얼마나 되는지 알 수 있습니다.

해당 인스턴스 유형의 스팟 가격 내역을 분석한 후 컴퓨팅 시간의 비용이 해당 작업의 결과가 지니는 가치보다 더 많지 않은 가격을 선택합니다. 영구 요청을 생성하여 스팟 가격이 요청 가격까지 또는 그 이하로 변동하는 과정에서 간헐적으로 실행되도록 합니다.

### 신속하게 컴퓨팅 용량 획득
<a name="acquire-quickly"></a>

온디맨드 인스턴스를 통해서는 사용할 수 없는 추가 용량이 예기치 않게 단기간 동안 필요한 경우가 있습니다. 이때는 해당 인스턴스 유형의 스팟 가격 내역을 분석한 후 과거의 최고 가격보다 높은 가격을 선택함으로써 요청이 신속하게 이행되고 완료될 때까지 컴퓨팅이 지속될 수 있는 가능성을 크게 높입니다.

필요한 사항을 수집하고 전략을 선택했으면 스팟 인스턴스를 요청할 준비가 된 것입니다. 이 자습서에서는 기본 스팟 인스턴스 최고 가격이 온디맨드 가격(이 자습서의 경우 0.003 USD)과 동일하게 설정됩니다. 이 방법으로 가격을 설정하면 요청이 이행될 가능성이 극대화됩니다.

## 스팟 인스턴스 요청 생성
<a name="tutor-spot-net-submit"></a>

다음 코드 조각은 이전에 수집한 요소를 사용하여 스팟 인스턴스 요청을 생성하는 방법을 보여줍니다.

[이 주제의 끝 부분에 있는](#tutor-spot-net-main) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to create a Spot Instance request
    private static async Task<SpotInstanceRequest> CreateSpotInstanceRequest(
      IAmazonEC2 ec2Client, string amiId, string securityGroupName,
      InstanceType instanceType, string spotPrice, int instanceCount)
    {
      var launchSpecification = new LaunchSpecification{
        ImageId = amiId,
        InstanceType = instanceType
      };
      launchSpecification.SecurityGroups.Add(securityGroupName);
      var request = new RequestSpotInstancesRequest{
        SpotPrice = spotPrice,
        InstanceCount = instanceCount,
        LaunchSpecification = launchSpecification
      };

      RequestSpotInstancesResponse result =
        await ec2Client.RequestSpotInstancesAsync(request);
      return result.SpotInstanceRequests[0];
    }
```

이 메서드에서 반환되는 중요한 값은 반환된 [SpotInstanceRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TSpotInstanceRequest.html) 객체의 `SpotInstanceRequestId` 멤버에 포함된 스팟 인스턴스 요청 ID입니다.

**참고**  
시작된 모든 스팟 인스턴스에 대해 요금이 부과됩니다. 불필요한 비용을 피하려면 [모든 요청을 취소](#tutor-spot-net-clean-up-request)하고 [인스턴스를 종료](#tutor-spot-net-clean-up-instance)해야 합니다.

## 스팟 인스턴스 요청 상태 확인
<a name="tutor-spot-net-request-state"></a>

다음 코드 조각은 스팟 인스턴스 요청에 대한 정보를 가져오는 방법을 보여줍니다. 이 정보를 사용하여 스팟 인스턴스 요청이 이행될 때까지 계속 기다릴지 여부와 같은 코드의 특정 결정을 내릴 수 있습니다.

[이 주제의 끝 부분에 있는](#tutor-spot-net-main) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to get information about a Spot Instance request, including the status,
    // instance ID, etc.
    // It gets the information for a specific request (as opposed to all requests).
    private static async Task<SpotInstanceRequest> GetSpotInstanceRequestInfo(
      IAmazonEC2 ec2Client, string requestId)
    {
      var describeRequest = new DescribeSpotInstanceRequestsRequest();
      describeRequest.SpotInstanceRequestIds.Add(requestId);

      DescribeSpotInstanceRequestsResponse describeResponse =
        await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);
      return describeResponse.SpotInstanceRequests[0];
    }
```

이 메서드는 인스턴스 ID, 상태 및 상태 코드와 같은 스팟 인스턴스 요청에 대한 정보를 반환합니다. 스팟 인스턴스 요청의 상태 코드에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [스팟 요청 상태](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-bid-status.html#spot-instance-bid-status-understand)를 참조하세요.

## 스팟 인스턴스 요청 정리
<a name="tutor-spot-net-clean-up-request"></a>

스팟 인스턴스를 더 이상 요청할 필요가 없는 경우, 미해결 요청을 모두 취소하여 해당 요청이 다시 처리되지 않도록 하는 것이 중요합니다. 다음 코드 조각은 스팟 인스턴스 요청을 취소하는 방법을 보여줍니다.

[이 주제의 끝 부분에 있는](#tutor-spot-net-main) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to cancel a Spot Instance request
    private static async Task CancelSpotInstanceRequest(
      IAmazonEC2 ec2Client, string requestId)
    {
      var cancelRequest = new CancelSpotInstanceRequestsRequest();
      cancelRequest.SpotInstanceRequestIds.Add(requestId);

      await ec2Client.CancelSpotInstanceRequestsAsync(cancelRequest);
    }
```

## 스팟 인스턴스 정리
<a name="tutor-spot-net-clean-up-instance"></a>

불필요한 비용을 피하려면 스팟 인스턴스 요청으로 시작된 인스턴스를 모두 종료하는 것이 중요합니다. 스팟 인스턴스를 취소하는 것만으로는 인스턴스가 종료되지 않는데, 이는 인스턴스에 대해 계속해서 비용이 부과됨을 뜻합니다. 다음 코드 조각은 활성 스팟 인스턴스의 인스턴스 식별자를 획득한 후 인스턴스를 종료하는 방법을 보여줍니다.

[이 주제의 끝 부분에 있는](#tutor-spot-net-main) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to terminate a Spot Instance
    private static async Task TerminateSpotInstance(
      IAmazonEC2 ec2Client, string requestId)
    {
      var describeRequest = new DescribeSpotInstanceRequestsRequest();
      describeRequest.SpotInstanceRequestIds.Add(requestId);

      // Retrieve the Spot Instance request to check for running instances.
      DescribeSpotInstanceRequestsResponse describeResponse =
        await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);

      // If there are any running instances, terminate them
      if(   (describeResponse.SpotInstanceRequests[0].Status.Code
              == "request-canceled-and-instance-running")
         || (describeResponse.SpotInstanceRequests[0].State == SpotInstanceState.Active))
      {
        TerminateInstancesResponse response =
          await ec2Client.TerminateInstancesAsync(new TerminateInstancesRequest{
            InstanceIds = new List<string>(){
              describeResponse.SpotInstanceRequests[0].InstanceId } });
        foreach (InstanceStateChange item in response.TerminatingInstances)
        {
          Console.WriteLine($"\n  Terminated instance: {item.InstanceId}");
          Console.WriteLine($"  Instance state: {item.CurrentState.Name}\n");
        }
      }
    }
```

## 전체 코드
<a name="tutor-spot-net-main"></a>

다음 코드 예제는 앞에서 설명한 메서드를 호출하여 스팟 인스턴스 요청을 생성 및 취소하고 스팟 인스턴스를 종료합니다.

### SDK 레퍼런스
<a name="w2aac19c15c21c21c43b5b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TEC2Client.html)

  클래스 [InstanceType](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TInstanceType.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/NEC2Model.html)

  클래스 [CancelSpotInstanceRequestsRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TCancelSpotInstanceRequestsRequest.html)

  클래스 [DescribeSpotInstanceRequestsRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TDescribeSpotInstanceRequestsRequest.html)

  클래스 [DescribeSpotInstanceRequestsResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TDescribeSpotInstanceRequestsResponse.html)

  클래스 [InstanceStateChange](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TInstanceStateChange.html)

  클래스 [LaunchSpecification](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TLaunchSpecification.html)

  클래스 [RequestSpotInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TRequestSpotInstancesRequest.html)

  클래스 [RequestSpotInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TRequestSpotInstancesResponse.html)

  클래스 [SpotInstanceRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TSpotInstanceRequest.html)

  클래스 [TerminateInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TTerminateInstancesRequest.html)

  클래스 [TerminateInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/EC2/TTerminateInstancesResponse.html)

### 코드
<a name="w2aac19c15c21c21c43b7b1"></a>

```
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2SpotInstanceRequests
{
  class Program
  {
    static async Task Main(string[] args)
    {
      // Some default values.
      // These could be made into command-line arguments instead.
      var instanceType = InstanceType.T1Micro;
      string securityGroupName = "default";
      string spotPrice = "0.003";
      int instanceCount = 1;

      // Parse the command line arguments
      if((args.Length != 1) || (!args[0].StartsWith("ami-")))
      {
        Console.WriteLine("\nUsage: EC2SpotInstanceRequests ami");
        Console.WriteLine("  ami: the Amazon Machine Image to use for the Spot Instances.");
        return;
      }

      // Create the Amazon EC2 client.
      var ec2Client = new AmazonEC2Client();

      // Create the Spot Instance request and record its ID
      Console.WriteLine("\nCreating spot instance request...");
      var req = await CreateSpotInstanceRequest(
        ec2Client, args[0], securityGroupName, instanceType, spotPrice, instanceCount);
      string requestId = req.SpotInstanceRequestId;

      // Wait for an EC2 Spot Instance to become active
      Console.WriteLine(
        $"Waiting for Spot Instance request with ID {requestId} to become active...");
      int wait = 1;
      var start = DateTime.Now;
      while(true)
      {
        Console.Write(".");

        // Get and check the status to see if the request has been fulfilled.
        var requestInfo = await GetSpotInstanceRequestInfo(ec2Client, requestId);
        if(requestInfo.Status.Code == "fulfilled")
        {
          Console.WriteLine($"\nSpot Instance request {requestId} " +
            $"has been fulfilled by instance {requestInfo.InstanceId}.\n");
          break;
        }

        // Wait a bit and try again, longer each time (1, 2, 4, ...)
        Thread.Sleep(wait);
        wait = wait * 2;
      }

      // Show the user how long it took to fulfill the Spot Instance request.
      TimeSpan span = DateTime.Now.Subtract(start);
      Console.WriteLine($"That took {span.TotalMilliseconds} milliseconds");

      // Perform actions here as needed.
      // For this example, simply wait for the user to hit a key.
      // That gives them a chance to look at the EC2 console to see
      // the running instance if they want to.
      Console.WriteLine("Press any key to start the cleanup...");
      Console.ReadKey(true);

      // Cancel the request.
      // Do this first to make sure that the request can't be re-fulfilled
      // once the Spot Instance has been terminated.
      Console.WriteLine("Canceling Spot Instance request...");
      await CancelSpotInstanceRequest(ec2Client, requestId);

      // Terminate the Spot Instance that's running.
      Console.WriteLine("Terminating the running Spot Instance...");
      await TerminateSpotInstance(ec2Client, requestId);

      Console.WriteLine("Done. Press any key to exit...");
      Console.ReadKey(true);
    }


    //
    // Method to create a Spot Instance request
    private static async Task<SpotInstanceRequest> CreateSpotInstanceRequest(
      IAmazonEC2 ec2Client, string amiId, string securityGroupName,
      InstanceType instanceType, string spotPrice, int instanceCount)
    {
      var launchSpecification = new LaunchSpecification{
        ImageId = amiId,
        InstanceType = instanceType
      };
      launchSpecification.SecurityGroups.Add(securityGroupName);
      var request = new RequestSpotInstancesRequest{
        SpotPrice = spotPrice,
        InstanceCount = instanceCount,
        LaunchSpecification = launchSpecification
      };

      RequestSpotInstancesResponse result =
        await ec2Client.RequestSpotInstancesAsync(request);
      return result.SpotInstanceRequests[0];
    }


    //
    // Method to get information about a Spot Instance request, including the status,
    // instance ID, etc.
    // It gets the information for a specific request (as opposed to all requests).
    private static async Task<SpotInstanceRequest> GetSpotInstanceRequestInfo(
      IAmazonEC2 ec2Client, string requestId)
    {
      var describeRequest = new DescribeSpotInstanceRequestsRequest();
      describeRequest.SpotInstanceRequestIds.Add(requestId);

      DescribeSpotInstanceRequestsResponse describeResponse =
        await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);
      return describeResponse.SpotInstanceRequests[0];
    }


    //
    // Method to cancel a Spot Instance request
    private static async Task CancelSpotInstanceRequest(
      IAmazonEC2 ec2Client, string requestId)
    {
      var cancelRequest = new CancelSpotInstanceRequestsRequest();
      cancelRequest.SpotInstanceRequestIds.Add(requestId);

      await ec2Client.CancelSpotInstanceRequestsAsync(cancelRequest);
    }


    //
    // Method to terminate a Spot Instance
    private static async Task TerminateSpotInstance(
      IAmazonEC2 ec2Client, string requestId)
    {
      var describeRequest = new DescribeSpotInstanceRequestsRequest();
      describeRequest.SpotInstanceRequestIds.Add(requestId);

      // Retrieve the Spot Instance request to check for running instances.
      DescribeSpotInstanceRequestsResponse describeResponse =
        await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);

      // If there are any running instances, terminate them
      if(   (describeResponse.SpotInstanceRequests[0].Status.Code
              == "request-canceled-and-instance-running")
         || (describeResponse.SpotInstanceRequests[0].State == SpotInstanceState.Active))
      {
        TerminateInstancesResponse response =
          await ec2Client.TerminateInstancesAsync(new TerminateInstancesRequest{
            InstanceIds = new List<string>(){
              describeResponse.SpotInstanceRequests[0].InstanceId } });
        foreach (InstanceStateChange item in response.TerminatingInstances)
        {
          Console.WriteLine($"\n  Terminated instance: {item.InstanceId}");
          Console.WriteLine($"  Instance state: {item.CurrentState.Name}\n");
        }
      }
    }
  }
}
```

## 추가 고려 사항
<a name="tutor-spot-net-additional"></a>
+ 자습서를 실행한 후에는 [Amazon EC2 콘솔](https://console.aws.amazon.com/ec2/)에 로그인하여 [스팟 인스턴스 요청](https://console.aws.amazon.com/ec2/home#SpotInstances:)이 취소되고 [스팟 인스턴스](https://console.aws.amazon.com/ec2/v2/home#Instances)가 종료되었는지 확인하는 것이 좋습니다.

# 를 사용하여 AWS Identity and Access Management (IAM)에 액세스 AWS SDK for .NET
<a name="iam-apis-intro"></a>

는 AWS 고객이에서 사용자 및 사용자 권한을 관리할 수 있는 웹 서비스[AWS Identity and Access Management](https://docs.aws.amazon.com/IAM/latest/UserGuide/)인를 AWS SDK for .NET 지원합니다 AWS.

 AWS Identity and Access Management (IAM) *사용자는* 사용자가 생성하는 엔터티입니다 AWS. 개체는와 상호 작용하는 사람 또는 애플리케이션을 나타냅니다 AWS. IAM 사용자에 대한 자세한 내용은 IAM 사용 설명서의 [IAM 사용자](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html) 및 [IAM 및 STS 제한](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-limits.html)을 참조하세요.

IAM *정책*을 생성하여 사용자에게 권한을 부여합니다. 정책에는 사용자가 수행할 수 있는 작업과 작업이 적용되는 리소스를 나열한 *정책 문서*가 포함됩니다. IAM 정책에 대한 자세한 내용은 *IAM 사용 설명서*에서 [정책 및 권한](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html)을 참조하세요.

**주의**  
보안 위험을 방지하려면 목적별 소프트웨어를 개발하거나 실제 데이터로 작업할 때 IAM 사용자를 인증에 사용하지 마세요. 대신 [AWS IAM Identity Center](https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.html)과 같은 보안 인증 공급자를 통한 페더레이션을 사용하십시오.

## API
<a name="w2aac19c15c23c13"></a>

는 IAM 클라이언트용 APIs AWS SDK for .NET 제공합니다. API를 사용하면 사용자, 역할, 액세스 키와 같은 IAM 기능을 사용할 수 있습니다.

이 섹션에는 이러한 API로 작업할 때 따를 수 있는 패턴을 보여주는 몇 가지 예제가 포함되어 있습니다. 전체 API 세트를 보려면 [AWS SDK for .NET API 참조](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/)를 참조하세요(그리고 "Amazon.IdentityManagement"로 스크롤하세요).

이 섹션에는 보안 인증을 보다 쉽게 관리할 수 있도록 Amazon EC2 인스턴스에 IAM 역할을 연결하는 방법을 보여주는 [예제](net-dg-hosm.md)도 포함되어 있습니다.

IAM API는 [AWSSDK.IdentityManagement](https://www.nuget.org/packages/AWSSDK.IdentityManagement) NuGet 패키지에서 제공됩니다.

## 사전 조건
<a name="w2aac19c15c23c15"></a>

시작하기 전에 [환경을 설정하고](net-dg-config.md) [프로젝트를 구성](configuring-the-sdk.md)했는지 확인합니다. 또한 [SDK 사용](net-dg-sdk-features.md)의 정보를 검토합니다.

## 주제
<a name="w2aac19c15c23c17"></a>

**Topics**
+ [

## API
](#w2aac19c15c23c13)
+ [

## 사전 조건
](#w2aac19c15c23c15)
+ [

## 주제
](#w2aac19c15c23c17)
+ [JSON에서 관리형 정책 생성](iam-policies-create-json.md)
+ [정책 문서 표시](iam-policies-display.md)
+ [역할을 사용하여 액세스 권한 부여](net-dg-hosm.md)

# JSON에서 IAM 관리형 정책 생성
<a name="iam-policies-create-json"></a>

이 예제에서는 AWS SDK for .NET 를 사용하여 JSON으로 지정된 [정책 문서에서 IAM 관리](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html#aws-managed-policies)형 정책을 생성하는 방법을 보여줍니다. 애플리케이션은 IAM 클라이언트 객체를 생성하고 파일에서 정책 문서를 읽은 다음 정책을 생성합니다.

**참고**  
JSON으로 된 예제 정책 문서는 이 주제 끝에 있는 [추가 고려 사항](#iam-policies-create-json-additional)을 참조하세요.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#iam-policies-create-json-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [

## 정책을 생성합니다.
](#iam-policies-create-json-create)
+ [

## 전체 코드
](#iam-policies-create-json-complete-code)
+ [

## 추가 고려 사항
](#iam-policies-create-json-additional)

## 정책을 생성합니다.
<a name="iam-policies-create-json-create"></a>

다음 코드 조각은 지정된 이름과 정책 문서로 IAM 관리형 정책을 생성합니다.

[이 주제의 끝 부분에 있는](#iam-policies-create-json-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to create an IAM policy from a JSON file
    private static async Task<CreatePolicyResponse> CreateManagedPolicy(
      IAmazonIdentityManagementService iamClient, string policyName, string jsonFilename)
    {
      return await iamClient.CreatePolicyAsync(new CreatePolicyRequest{
        PolicyName = policyName,
        PolicyDocument = File.ReadAllText(jsonFilename)});
    }
```

## 전체 코드
<a name="iam-policies-create-json-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c23c21c17b5b1"></a>

NuGet 패키지:
+ [AWSSDK.IdentityManagement](https://www.nuget.org/packages/AWSSDK.IdentityManagement)

프로그래밍 요소:
+ 네임스페이스 [Amazon.IdentityManagement](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/IAM/NIAM.html)

  클래스 [AmazonIdentityManagementServiceClient](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/IAM/TIAMServiceClient.html)
+ 네임스페이스 [Amazon.IdentityManagement.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/IAM/NIAMModel.html)

  클래스 [CreatePolicyRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/IAM/TCreatePolicyRequest.html)

  클래스 [CreatePolicyResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/IAM/TCreatePolicyResponse.html)

### 코드
<a name="w2aac19c15c23c21c17b7b1"></a>

```
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Amazon.IdentityManagement;
using Amazon.IdentityManagement.Model;

namespace IamCreatePolicyFromJson
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to create an IAM policy with a given policy document
  class Program
  {
    private const int MaxArgs = 2;

    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if((parsedArgs.Count == 0) || (parsedArgs.Count > MaxArgs))
      {
        PrintHelp();
        return;
      }

      // Get the application arguments from the parsed list
      string policyName =
        CommandLine.GetArgument(parsedArgs, null, "-p", "--policy-name");
      string policyFilename =
        CommandLine.GetArgument(parsedArgs, null, "-j", "--json-filename");
      if(   string.IsNullOrEmpty(policyName)
         || (string.IsNullOrEmpty(policyFilename) || !policyFilename.EndsWith(".json")))
        CommandLine.ErrorExit(
          "\nOne or more of the required arguments is missing or incorrect." +
          "\nRun the command with no arguments to see help.");

      // Create an IAM service client
      var iamClient = new AmazonIdentityManagementServiceClient();

      // Create the new policy
      var response = await CreateManagedPolicy(iamClient, policyName, policyFilename);
      Console.WriteLine($"\nPolicy {response.Policy.PolicyName} has been created.");
      Console.WriteLine($"  Arn: {response.Policy.Arn}");
    }


    //
    // Method to create an IAM policy from a JSON file
    private static async Task<CreatePolicyResponse> CreateManagedPolicy(
      IAmazonIdentityManagementService iamClient, string policyName, string jsonFilename)
    {
      return await iamClient.CreatePolicyAsync(new CreatePolicyRequest{
        PolicyName = policyName,
        PolicyDocument = File.ReadAllText(jsonFilename)});
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: IamCreatePolicyFromJson -p <policy-name> -j <json-filename>" +
        "\n  -p, --policy-name: The name you want the new policy to have." +
        "\n  -j, --json-filename: The name of the JSON file with the policy document.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 추가 고려 사항
<a name="iam-policies-create-json-additional"></a>
+ 다음은 JSON 파일에 복사하여 이 애플리케이션의 입력으로 사용할 수 있는 예제 정책 문서입니다.

------
#### [ JSON ]

****  

  ```
  {
    "Version":"2012-10-17",		 	 	 
    "Id"  : "DotnetTutorialPolicy",
    "Statement" : [
      {
        "Sid" : "DotnetTutorialPolicyS3",
        "Effect" : "Allow",
        "Action" : [
          "s3:Get*",
          "s3:List*"
        ],
        "Resource" : "*"
      },
      {
        "Sid" : "DotnetTutorialPolicyPolly",
        "Effect": "Allow",
        "Action": [
          "polly:DescribeVoices",
          "polly:SynthesizeSpeech"
        ],
        "Resource": "*"
      }
    ]
  }
  ```

------
+ [IAM 콘솔](https://console.aws.amazon.com/iam/home#/policies)을 살펴보면 정책이 생성되었는지 확인할 수 있습니다. **정책 필터** 드롭다운 목록에서 **고객 관리형**을 선택합니다. 필요 없는 정책을 삭제합니다.
+  정책 생성에 대한 자세한 내용은 [IAM 사용 설명서](https://docs.aws.amazon.com/IAM/latest/UserGuide/)의 [IAM 정책 생성](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create.html) 및 [IAM JSON 정책 참조](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies.html)를 참조하세요.

# IAM 관리형 정책의 정책 문서 표시
<a name="iam-policies-display"></a>

이 예제에서는를 사용하여 정책 문서를 AWS SDK for .NET 표시하는 방법을 보여줍니다. 애플리케이션은 IAM 클라이언트 객체를 생성하고, 지정된 IAM 관리형 정책의 기본 버전을 찾은 다음, 정책 문서를 JSON으로 표시합니다.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#iam-policies-display-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [

## 기본 버전 찾기
](#iam-policies-display-version)
+ [

## 정책 문서 표시
](#iam-policies-display-doc)
+ [

## 전체 코드
](#iam-policies-display-complete-code)

## 기본 버전 찾기
<a name="iam-policies-display-version"></a>

다음 코드 조각은 해당 IAM 정책의 기본 버전을 찾습니다.

[이 주제의 끝 부분에 있는](#iam-policies-display-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to determine the default version of an IAM policy
    // Returns a string with the version
    private static async Task<string> GetDefaultVersion(
      IAmazonIdentityManagementService iamClient, string policyArn)
    {
      // Retrieve all the versions of this policy
      string defaultVersion = string.Empty;
      ListPolicyVersionsResponse reponseVersions =
        await iamClient.ListPolicyVersionsAsync(new ListPolicyVersionsRequest{
          PolicyArn = policyArn});

      // Find the default version
      foreach(PolicyVersion version in reponseVersions.Versions)
      {
        if(version.IsDefaultVersion)
        {
          defaultVersion = version.VersionId;
          break;
        }
      }

      return defaultVersion;
    }
```

## 정책 문서 표시
<a name="iam-policies-display-doc"></a>

다음 코드 조각은 해당 IAM 정책의 정책 문서를 JSON으로 표시합니다.

[이 주제의 끝 부분에 있는](#iam-policies-display-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to retrieve and display the policy document of an IAM policy
    private static async Task ShowPolicyDocument(
      IAmazonIdentityManagementService iamClient, string policyArn, string defaultVersion)
    {
      // Retrieve the policy document of the default version
      GetPolicyVersionResponse responsePolicy =
        await iamClient.GetPolicyVersionAsync(new GetPolicyVersionRequest{
          PolicyArn = policyArn,
          VersionId = defaultVersion});

      // Display the policy document (in JSON)
      Console.WriteLine($"Version {defaultVersion} of the policy (in JSON format):");
      Console.WriteLine(
        $"{HttpUtility.UrlDecode(responsePolicy.PolicyVersion.Document)}");
    }
```

## 전체 코드
<a name="iam-policies-display-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c23c23c19b5b1"></a>

NuGet 패키지:
+ [AWSSDK.IdentityManagement](https://www.nuget.org/packages/AWSSDK.IdentityManagement)

프로그래밍 요소:
+ 네임스페이스 [Amazon.IdentityManagement](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/IAM/NIAM.html)

  클래스 [AmazonIdentityManagementServiceClient](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/IAM/TIAMServiceClient.html)
+ 네임스페이스 [Amazon.IdentityManagement.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/IAM/NIAMModel.html)

  클래스 [GetPolicyVersionRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/IAM/TGetPolicyVersionRequest.html)

  클래스 [GetPolicyVersionResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/IAM/TGetPolicyVersionResponse.html)

  클래스 [ListPolicyVersionsRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/IAM/TListPolicyVersionsRequest.html)

  클래스 [ListPolicyVersionsResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/IAM/TListPolicyVersionsResponse.html)

  클래스 [PolicyVersion](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/IAM/TPolicyVersion.html)

### 코드
<a name="w2aac19c15c23c23c19b7b1"></a>

```
using System;
using System.Web;
using System.Threading.Tasks;
using Amazon.IdentityManagement;
using Amazon.IdentityManagement.Model;

namespace IamDisplayPolicyJson
{
  class Program
  {
    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      if(args.Length != 1)
      {
        Console.WriteLine("\nUsage: IamDisplayPolicyJson policy-arn");
        Console.WriteLine("   policy-arn: The ARN of the policy to retrieve.");
        return;
      }
      if(!args[0].StartsWith("arn:"))
      {
        Console.WriteLine("\nCould not find policy ARN in the command-line arguments:");
        Console.WriteLine($"{args[0]}");
        return;
      }

      // Create an IAM service client
      var iamClient = new AmazonIdentityManagementServiceClient();

      // Retrieve and display the policy document of the given policy
      string defaultVersion = await GetDefaultVersion(iamClient, args[0]);
      if(string.IsNullOrEmpty(defaultVersion))
        Console.WriteLine($"Could not find the default version for policy {args[0]}.");
      else
        await ShowPolicyDocument(iamClient, args[0], defaultVersion);
    }


    //
    // Method to determine the default version of an IAM policy
    // Returns a string with the version
    private static async Task<string> GetDefaultVersion(
      IAmazonIdentityManagementService iamClient, string policyArn)
    {
      // Retrieve all the versions of this policy
      string defaultVersion = string.Empty;
      ListPolicyVersionsResponse reponseVersions =
        await iamClient.ListPolicyVersionsAsync(new ListPolicyVersionsRequest{
          PolicyArn = policyArn});

      // Find the default version
      foreach(PolicyVersion version in reponseVersions.Versions)
      {
        if(version.IsDefaultVersion)
        {
          defaultVersion = version.VersionId;
          break;
        }
      }

      return defaultVersion;
    }


    //
    // Method to retrieve and display the policy document of an IAM policy
    private static async Task ShowPolicyDocument(
      IAmazonIdentityManagementService iamClient, string policyArn, string defaultVersion)
    {
      // Retrieve the policy document of the default version
      GetPolicyVersionResponse responsePolicy =
        await iamClient.GetPolicyVersionAsync(new GetPolicyVersionRequest{
          PolicyArn = policyArn,
          VersionId = defaultVersion});

      // Display the policy document (in JSON)
      Console.WriteLine($"Version {defaultVersion} of the policy (in JSON format):");
      Console.WriteLine(
        $"{HttpUtility.UrlDecode(responsePolicy.PolicyVersion.Document)}");
    }
  }
}
```

# IAM 역할을 사용하여 액세스 권한 부여
<a name="net-dg-hosm"></a>

이 자습서에서는 AWS SDK for .NET 를 사용하여 Amazon EC2 인스턴스에서 IAM 역할을 활성화하는 방법을 보여줍니다.

## 개요
<a name="hosm-overview"></a>

에 대한 모든 요청은에서 발급한 자격 증명을 사용하여 암호화 방식으로 서명해야 AWS 합니다 AWS. 따라서 Amazon EC2 인스턴스에서 실행되는 애플리케이션에 대한 자격 증명을 관리할 전략이 필요합니다. 이 자격 증명을 안전하게 배포, 저장, 교체해야 할 뿐 아니라 애플리케이션에 접근할 수 있는 상태로 유지해야 합니다.

IAM 역할을 사용하면 이러한 자격 증명을 효과적으로 관리할 수 있습니다. IAM 역할을 생성하고 애플리케이션에 필요한 권한으로 구성한 다음 해당 역할을 EC2 인스턴스에 연결합니다. IAM 역할 사용의 이점에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Amazon EC2에 대한 IAM 역할](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html)을 참조하세요. 또한 IAM 사용 설명서의 [IAM 역할](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html)에 대한 정보도 참조하세요.

를 사용하여 빌드된 애플리케이션의 경우 애플리케이션이 AWS 서비스에 대한 클라이언트 객체를 구성할 AWS SDK for .NET때 객체는 여러 잠재적 소스에서 자격 증명을 검색합니다. 검색 순서는 [보안 인증 정보 및 프로파일 확인](creds-assign.md)에 나와 있습니다.

클라이언트 객체가 다른 소스에서 보안 인증을 찾을 수 없는 경우, IAM 역할에 구성된 것과 동일한 권한이 있고 EC2 인스턴스의 메타데이터에 있는 임시 보안 인증을 검색합니다. 이러한 자격 증명은 클라이언트 객체 AWS 에서를 호출하는 데 사용됩니다.

## 이 자습서 소개
<a name="about-hosm-tutorial"></a>

이 자습서를 따르면 AWS SDK for .NET (및 기타 도구)를 사용하여 IAM 역할이 연결된 Amazon EC2 인스턴스를 시작한 다음 IAM 역할의 권한을 사용하여 인스턴스에서 애플리케이션을 볼 수 있습니다.

**Topics**
+ [

## 개요
](#hosm-overview)
+ [

## 이 자습서 소개
](#about-hosm-tutorial)
+ [

## 샘플 Amazon S3 애플리케이션 생성
](#net-dg-hosm-sample-s3-app)
+ [

## IAM 역할 생성
](#net-dg-hosm-create-the-role)
+ [

## EC2 인스턴스 시작 및 IAM 역할 연결
](#net-dg-hosm-launch-ec2-instance)
+ [

## EC2 인스턴스에 연결
](#net-dg-hosm-connect)
+ [

## EC2 인스턴스에서 샘플 애플리케이션 실행
](#net-dg-hosm-run-the-app)
+ [

## 정리
](#net-dg-hosm-cleanup)

## 샘플 Amazon S3 애플리케이션 생성
<a name="net-dg-hosm-sample-s3-app"></a>

이 샘플 애플리케이션은 Amazon S3에서 객체를 검색합니다. 애플리케이션을 실행하려면 다음이 필요합니다.
+ 텍스트 파일이 포함된 Amazon S3 버킷입니다.
+ AWS 버킷에 액세스할 수 있는 개발 시스템의 자격 증명입니다.

Amazon S3 버킷 생성 및 객체 업로드 방법에 대한 자세한 내용은 [Amazon Simple Storage Service 사용 설명서](https://docs.aws.amazon.com/AmazonS3/latest/userguide/)를 참조하세요. 자격 AWS 증명에 대한 자세한 내용은 섹션을 참조하세요[를 AWS SDK for .NET 사용하여 인증 AWS](creds-idc.md).

다음 코드를 사용하여 .NET Core 프로젝트를 생성합니다. 그런 다음 개발 머신에서 애플리케이션을 테스트합니다.

**참고**  
개발 머신에 .NET Core 런타임이 설치되어 있으므로 애플리케이션을 게시하지 않고도 애플리케이션을 실행할 수 있습니다. 이 자습서의 후반부에서 EC2 인스턴스를 생성할 때 인스턴스에 .NET Core 런타임을 설치하도록 선택할 수 있습니다. 이렇게 하면 비슷한 환경에서 더 작은 파일을 전송할 수 있습니다.  
 하지만 인스턴스에 .NET Core 런타임을 설치하지 않도록 선택할 수도 있습니다. 이 작업 과정을 선택하는 경우 애플리케이션을 인스턴스로 전송할 때 모든 종속성이 포함되도록 애플리케이션을 게시해야 합니다.

### SDK 레퍼런스
<a name="w2aac19c15c23c25c17c13b1"></a>

NuGet 패키지:
+ [AWSSDK.S3](https://www.nuget.org/packages/AWSSDK.S3)

프로그래밍 요소:
+ 네임스페이스 [Amazon.S3](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/S3/NS3.html)

  클래스 [AmazonS3Client](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/S3/TS3Client.html)
+ 네임스페이스 [Amazon.S3.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/S3/NS3Model.html)

  클래스 [GetObjectResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/S3/TGetObjectResponse.html)

### 코드
<a name="w2aac19c15c23c25c17c15b1"></a>

```
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Amazon.S3;
using Amazon.S3.Model;

namespace S3GetTextItem
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to retrieve a text file from an S3 bucket and write it to a local file
  class Program
  {
    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        return;
      }

      // Get the application arguments from the parsed list
      string bucket =
        CommandLine.GetArgument(parsedArgs, null, "-b", "--bucket-name");
      string item =
        CommandLine.GetArgument(parsedArgs, null, "-t", "--text-object");
      string outFile =
        CommandLine.GetArgument(parsedArgs, null, "-o", "--output-filename");
      if(   string.IsNullOrEmpty(bucket)
         || string.IsNullOrEmpty(item)
         || string.IsNullOrEmpty(outFile))
        CommandLine.ErrorExit(
          "\nOne or more of the required arguments is missing or incorrect." +
          "\nRun the command with no arguments to see help.");

      // Create the S3 client object and get the file object from the bucket.
      var response = await GetObject(new AmazonS3Client(), bucket, item);

      // Write the contents of the file object to the given output file.
      var reader = new StreamReader(response.ResponseStream);
      string contents = reader.ReadToEnd();
      using (var s = new FileStream(outFile, FileMode.Create))
      using (var writer = new StreamWriter(s))
        writer.WriteLine(contents);
    }


    //
    // Method to get an object from an S3 bucket.
    private static async Task<GetObjectResponse> GetObject(
      IAmazonS3 s3Client, string bucket, string item)
    {
        Console.WriteLine($"Retrieving {item} from bucket {bucket}.");
        return await s3Client.GetObjectAsync(bucket, item);
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: S3GetTextItem -b <bucket-name> -t <text-object> -o <output-filename>" +
        "\n  -b, --bucket-name: The name of the S3 bucket." +
        "\n  -t, --text-object: The name of the text object in the bucket." +
        "\n  -o, --output-filename: The name of the file to write the text to.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

원하는 경우 개발 머신에서 사용하는 보안 인증을 일시적으로 제거하여 애플리케이션이 어떻게 반응하는지 확인할 수 있습니다. (하지만 작업을 마치면 보안 인증을 복원해야 합니다.)

## IAM 역할 생성
<a name="net-dg-hosm-create-the-role"></a>

Amazon S3에 액세스할 수 있는 적절한 권한이 있는 IAM 역할을 생성합니다.

1. [IAM 콘솔](https://console.aws.amazon.com/iam/)을 엽니다.

1. 탐색 창에서 **역할**을 선택한 후 **역할 생성**을 선택합니다.

1. **AWS 서비스**를 선택하고, **EC2**를 찾아 선택하고, **다음: 권한**을 선택합니다.

1. **권한 정책 연결**에서 **AmazonS3ReadOnlyAccess**를 찾아 선택합니다. 원하는 경우 정책을 검토한 후 **다음: 태그**를 선택합니다.

1. 원하는 경우 태그를 추가한 후 **다음: 검토**를 선택합니다.

1. 역할 이름 및 설명을 입력한 후 **역할 생성**을 선택합니다. EC2 인스턴스를 시작할 때 필요하므로 이 이름을 기억해 두십시오.

## EC2 인스턴스 시작 및 IAM 역할 연결
<a name="net-dg-hosm-launch-ec2-instance"></a>

앞에서 생성한 IAM 역할로 EC2 인스턴스를 시작합니다. 다음과 같은 방법으로 수행할 수 있습니다.
+ **EC2 콘솔 사용**

  EC2 콘솔을 사용하여 인스턴스를 실행하려면 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [새로운 인스턴스 시작 마법사를 사용하여 인스턴스 시작](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-launch-instance-wizard.html)을 참조하세요.

  시작 페이지를 살펴볼 때 **IAM 인스턴스 프로파일**에서 이전에 생성한 IAM 역할을 지정할 수 있도록 최소한 **고급 세부 정보** 창을 확장해야 합니다.
+ **사용 AWS SDK for .NET**

  이에 대한 자세한 내용은 해당 주제의 끝 부분에 있는 [추가 고려 사항](run-instance.md#run-instance-additional)을 포함하여 [Amazon EC2 인스턴스 시작](run-instance.md)을 참조하세요.

IAM 역할이 연결된 EC2 인스턴스를 시작하려면 IAM 사용자의 구성에 특정 권한이 포함되어야 합니다. 필요한 권한에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [IAM 역할을 인스턴스에 전달할 수 있는 사용자 권한 부여](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#permission-to-pass-iam-roles)를 참조하세요.

## EC2 인스턴스에 연결
<a name="net-dg-hosm-connect"></a>

EC2 인스턴스에 연결하면 샘플 애플리케이션을 전송한 다음 애플리케이션을 실행할 수 있습니다. 인스턴스를 시작하는 데 사용한 키 페어의 비공개 부분이 포함된 파일, 즉 PEM 파일이 필요합니다.

인스턴스에 연결하는 방법에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Linux 인스턴스에 연결](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-to-linux-instance.html) 또는 [Windows 인스턴스에 연결](https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/connecting_to_windows_instance.html)을 참조하세요. 연결할 때는 개발 머신에서 인스턴스로 파일을 전송할 수 있는 방식으로 연결합니다.

Windows에서 Visual Studio를 사용하는 경우, Visual Studio용 도구 키트를 사용하여 인스턴스에 연결할 수도 있습니다. 자세한 내용은 AWS Toolkit for Visual Studio 사용 설명서의 [ Amazon EC2 인스턴스에 연결을](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/tkv-ec2-ami.html#connect-ec2) 참조하세요.

## EC2 인스턴스에서 샘플 애플리케이션 실행
<a name="net-dg-hosm-run-the-app"></a>

1. 애플리케이션 파일을 로컬 드라이브에서 인스턴스로 복사합니다.

   전송하는 파일은 애플리케이션을 빌드한 방법과 인스턴스에 .NET Core 런타임이 설치되어 있는지 여부에 따라 달라집니다. 인스턴스로 파일을 전송하는 방법에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Linux 인스턴스에 연결](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-to-linux-instance.html)(해당 하위 섹션 참조) 또는 [Windows 인스턴스로 파일 전송](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-to-linux-instanceWindowsFileTransfer.html)을 참조하세요.

1. 애플리케이션을 시작하고 개발 머신에서와 동일한 결과로 실행되는지 확인합니다.

1. 애플리케이션이 IAM 역할에서 제공하는 보안 인증을 사용하는지 확인합니다.

   1. [Amazon EC2 콘솔](https://console.aws.amazon.com/ec2/)을 엽니다.

   1. 인스턴스를 선택하고 **작업**, **인스턴스 설정**, **IAM 역할 연결/바꾸기**를 통해 IAM 역할을 분리합니다.

   1. 애플리케이션을 다시 실행하여 권한 부여 오류가 반환되는지 확인합니다.

## 정리
<a name="net-dg-hosm-cleanup"></a>

이 자습서를 마치고 생성한 EC2 인스턴스를 더 이상 사용하지 않으려면 인스턴스를 종료하여 원치 않는 비용이 발생하지 않도록 하십시오. [Amazon EC2 콘솔](https://console.aws.amazon.com/ec2/)에서 또는 [Amazon EC2 인스턴스 종료](terminate-instance.md)에 설명된 대로 프로그래밍 방식으로 이 작업을 수행할 수 있습니다. 필요에 따라 이 자습서를 위해 생성한 다른 리소스도 삭제할 수 있습니다. 여기에는 IAM 역할, EC2 키 페어 및 PEM 파일, 보안 그룹 등이 포함될 수 있습니다.

# Amazon Simple Storage Service 인터넷 스토리지 사용
<a name="s3-apis-intro"></a>

는 인터넷용 스토리지인 [Amazon S3](https://aws.amazon.com/s3/)를 AWS SDK for .NET 지원합니다. 이 서비스는 개발자가 더 쉽게 웹 규모 컴퓨팅 작업을 수행할 수 있도록 설계되었습니다.

## API
<a name="w2aac19c15c25b5"></a>

는 Amazon S3 클라이언트에 대한 APIs AWS SDK for .NET 제공합니다. API를 사용하면 버킷 및 항목과 같은 Amazon S3 리소스를 사용할 수 있습니다. Amazon S3용 전체 API 세트를 보려면 다음을 참조하세요.
+ [AWS SDK for .NET API 참조](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/)(및 "Amazon.S3"로 스크롤).
+ [Amazon.Extensions.S3.Encryption](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.html) 문서

Amazon S3 API는 다음과 같은 NuGet 패키지에서 제공됩니다.
+ [AWSSDK.S3](https://www.nuget.org/packages/AWSSDK.S3)
+ [Amazon.Extensions.S3.Encryption](https://www.nuget.org/packages/Amazon.Extensions.S3.Encryption)

## 사전 조건
<a name="w2aac19c15c25b7"></a>

시작하기 전에 [환경을 설정하고](net-dg-config.md) [프로젝트를 구성](configuring-the-sdk.md)했는지 확인합니다. 또한 [SDK 사용](net-dg-sdk-features.md)의 정보를 검토합니다.

## 이 문서의 예제
<a name="s3-apis-examples"></a>

이 문서의 다음 주제에서는를 사용하여 Amazon S3로 작업 AWS SDK for .NET 하는 방법을 보여줍니다.
+ [S3 암호화에 KMS 키 사용](kms-keys-s3-encryption.md)

## 다른 문서에 있는 예제
<a name="s3-apis-examples-other"></a>

[Amazon S3 개발자 안내서](https://docs.aws.amazon.com/AmazonS3/latest/userguide/)에 대한 다음 링크는를 사용하여 Amazon S3로 작업하는 방법에 대한 추가 예제 AWS SDK for .NET 를 제공합니다.

**참고**  
이러한 예제와 추가 프로그래밍 고려 사항은 .NET Framework를 AWS SDK for .NET 사용하는 버전 3에 대해 생성되었지만 .NET Core를 AWS SDK for .NET 사용하는의 이후 버전에서도 실행 가능합니다. 코드를 약간 조정해야 하는 경우가 있습니다.

**Amazon S3 프로그래밍 예시**
+  [ACL 관리](https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-using-dot-net-sdk.html) 
+  [버킷 생성](https://docs.aws.amazon.com/AmazonS3/latest/dev/create-bucket-get-location-example.html#create-bucket-get-location-dotnet) 
+  [객체 업로드](https://docs.aws.amazon.com/AmazonS3/latest/dev/UploadObjSingleOpNET.html) 
+  [상위 수준 API([Amazon.S3.Transfer.TransferUtility](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/S3/TTransferUtility.html))를 사용한 멀티파트 업로드](https://docs.aws.amazon.com/AmazonS3/latest/dev/usingHLmpuDotNet.html) 
+  [하위 레벨 API를 통한 멀티파트 업로드](https://docs.aws.amazon.com/AmazonS3/latest/dev/usingLLmpuDotNet.html) 
+  [객체 나열](https://docs.aws.amazon.com/AmazonS3/latest/dev/list-obj-version-enabled-bucket.html#list-obj-version-enabled-bucket-sdk-examples) 
+  [키 나열](https://docs.aws.amazon.com/AmazonS3/latest/dev/ListingObjectKeysUsingNetSDK.html) 
+  [객체 가져오기](https://docs.aws.amazon.com/AmazonS3/latest/dev/RetrievingObjectUsingNetSDK.html) 
+  [객체 복사](https://docs.aws.amazon.com/AmazonS3/latest/dev/CopyingObjectUsingNetSDK.html) 
+  [멀티파트 업로드 API를 사용한 객체 복사](https://docs.aws.amazon.com/AmazonS3/latest/dev/CopyingObjctsUsingLLNetMPUapi.html) 
+  [객체 삭제](https://docs.aws.amazon.com/AmazonS3/latest/dev/DeletingOneObjectUsingNetSDK.html) 
+  [여러 객체 삭제](https://docs.aws.amazon.com/AmazonS3/latest/dev/DeletingMultipleObjectsUsingNetSDK.html) 
+  [객체 복원](https://docs.aws.amazon.com/AmazonS3/latest/dev/restore-object-dotnet.html) 
+  [알림용 버킷 구성](https://docs.aws.amazon.com/AmazonS3/latest/dev/ways-to-add-notification-config-to-bucket.html) 
+  [객체 수명 주기 관리](https://docs.aws.amazon.com/AmazonS3/latest/dev/manage-lifecycle-using-dot-net.html) 
+  [미리 서명된 객체 URL 생성](https://docs.aws.amazon.com/AmazonS3/latest/dev/ShareObjectPreSignedURLDotNetSDK.html) 
+  [웹 사이트 관리](https://docs.aws.amazon.com/AmazonS3/latest/dev/ConfigWebSiteDotNet.html) 
+  [Cross-Origin 리소스 공유(CORS) 활성화](https://docs.aws.amazon.com/AmazonS3/latest/dev/ManageCorsUsingDotNet.html) 

**추가 프로그래밍 고려 사항**
+  [Amazon S3 프로그래밍에 AWS SDK for .NET 사용](https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingTheMPDotNetAPI.html) 
+  [IAM 사용자 임시 자격 증명을 사용하여 요청하기](https://docs.aws.amazon.com/AmazonS3/latest/dev/AuthUsingTempSessionTokenDotNet.html) 
+  [연합된 사용자의 임시 자격 증명을 사용하여 요청하기](https://docs.aws.amazon.com/AmazonS3/latest/dev/AuthUsingTempFederationTokenDotNet.html) 
+  [서버 측 암호화 지정](https://docs.aws.amazon.com/AmazonS3/latest/dev/SSEUsingDotNetSDK.html) 
+  [고객 제공 암호화 키를 사용한 서버 측 암호화 지정](https://docs.aws.amazon.com/AmazonS3/latest/dev/sse-c-using-dot-net-sdk.html) 

# 에서 Amazon S3 암호화에 AWS KMS 키 사용 AWS SDK for .NET
<a name="kms-keys-s3-encryption"></a>

이 예제에서는 AWS Key Management Service 키를 사용하여 Amazon S3 객체를 암호화하는 방법을 보여줍니다. 애플리케이션은 고객 마스터 키(CMK)를 생성하고 이를 사용하여 클라이언트측 암호화를 위한 [AmazonS3EncryptionClientV2](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.AmazonS3EncryptionClientV2.html) 객체를 생성합니다. 애플리케이션은 해당 클라이언트를 사용하여 기존 Amazon S3 버킷의 지정된 텍스트 파일로부터 암호화된 객체를 생성합니다. 그런 다음 객체를 복호화하고 콘텐츠를 표시합니다.

**주의**  
`AmazonS3EncryptionClient`라는 유사한 클래스는 더 이상 사용되지 않으며 `AmazonS3EncryptionClientV2` 클래스보다 보안성이 떨어집니다. [S3 암호화 클라이언트 마이그레이션(V1에서 V2로)](s3-encryption-migration-v1-v2.md)를 사용하는 기존 코드를 마이그레이션하려면 `AmazonS3EncryptionClient`을 참조하세요.

**Topics**
+ [

## 암호화 자료 생성
](#kms-s3-enc-mat)
+ [

## Amazon S3 객체 생성 및 암호화
](#kms-s3-create-ojbect)
+ [

## 전체 코드
](#kms-s3-complete-code)
+ [

## 추가 고려 사항
](#kms-s3-additional)

## 암호화 자료 생성
<a name="kms-s3-enc-mat"></a>

다음 코드 조각은 KMS 키 ID가 포함된 `EncryptionMaterials` 객체를 생성합니다.

[이 주제의 끝 부분에 있는](#kms-s3-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
      // Create a customer master key (CMK) and store the result
      CreateKeyResponse createKeyResponse =
        await new AmazonKeyManagementServiceClient().CreateKeyAsync(new CreateKeyRequest());
      var kmsEncryptionContext = new Dictionary<string, string>();
      var kmsEncryptionMaterials = new EncryptionMaterialsV2(
        createKeyResponse.KeyMetadata.KeyId, KmsType.KmsContext, kmsEncryptionContext);
```

## Amazon S3 객체 생성 및 암호화
<a name="kms-s3-create-ojbect"></a>

다음 코드 조각은 앞서 생성한 암호화 자료를 사용하여 `AmazonS3EncryptionClientV2` 객체를 생성합니다. 그런 다음 클라이언트를 사용하여 새 Amazon S3 객체를 생성하고 암호화합니다.

[이 주제의 끝 부분에 있는](#kms-s3-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to create and encrypt an object in an S3 bucket
    static async Task<GetObjectResponse> CreateAndRetrieveObjectAsync(
      EncryptionMaterialsV2 materials, string bucketName,
      string fileName, string itemName)
    {
      // CryptoStorageMode.ObjectMetadata is required for KMS EncryptionMaterials
      var config = new AmazonS3CryptoConfigurationV2(SecurityProfile.V2AndLegacy)
      {
        StorageMode = CryptoStorageMode.ObjectMetadata
      };
      var s3EncClient = new AmazonS3EncryptionClientV2(config, materials);

      // Create, encrypt, and put the object
      await s3EncClient.PutObjectAsync(new PutObjectRequest
      {
        BucketName = bucketName,
        Key = itemName,
        ContentBody = File.ReadAllText(fileName)
      });

      // Get, decrypt, and return the object
      return await s3EncClient.GetObjectAsync(new GetObjectRequest
      {
        BucketName = bucketName,
        Key = itemName
      });
    }
```

## 전체 코드
<a name="kms-s3-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c25c13c15b5b1"></a>

NuGet 패키지:
+ [Amazon.Extensions.S3.Encryption](https://www.nuget.org/packages/Amazon.Extensions.S3.Encryption)

프로그래밍 요소:
+ 네임스페이스 [Amazon.Extensions.S3.Encryption](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.html)

  클래스 [AmazonS3EncryptionClientV2](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.AmazonS3EncryptionClientV2.html)

  클래스 [AmazonS3CryptoConfigurationV2](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.AmazonS3CryptoConfigurationV2.html)

  클래스 [CryptoStorageMode](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.CryptoStorageMode.html)

  클래스 [EncryptionMaterialsV2](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.EncryptionMaterialsV2.html)
+ 네임스페이스 [Amazon.Extensions.S3.Encryption.Primitives](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.Primitives.html)

  클래스 [KmsType](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.Primitives.KmsType.html)
+ 네임스페이스 [Amazon.S3.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/S3/NS3Model.html)

  클래스 [GetObjectRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/S3/TGetObjectRequest.html)

  클래스 [GetObjectResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/S3/TGetObjectResponse.html)

  클래스 [PutObjectRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/S3/TPutObjectRequest.html)
+ 네임스페이스 [Amazon.KeyManagementService](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/KeyManagementService/NKeyManagementService.html)

  클래스 [AmazonKeyManagementServiceClient](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/KeyManagementService/TKeyManagementServiceClient.html)
+ 네임스페이스 [Amazon.KeyManagementService.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/KeyManagementService/NKeyManagementServiceModel.html)

  클래스 [CreateKeyRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/KeyManagementService/TCreateKeyRequest.html)

  클래스 [CreateKeyResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/KeyManagementService/TCreateKeyResponse.html)

### 코드
<a name="w2aac19c15c25c13c15b7b1"></a>

```
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Amazon.Extensions.S3.Encryption;
using Amazon.Extensions.S3.Encryption.Primitives;
using Amazon.S3.Model;
using Amazon.KeyManagementService;
using Amazon.KeyManagementService.Model;

namespace KmsS3Encryption
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to store text in an encrypted S3 object.
  class Program
  {
    private const int MaxArgs = 3;

    public static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if((parsedArgs.Count == 0) || (parsedArgs.Count > MaxArgs))
      {
        PrintHelp();
        return;
      }

      // Get the application arguments from the parsed list
      string bucketName =
        CommandLine.GetArgument(parsedArgs, null, "-b", "--bucket-name");
      string fileName =
        CommandLine.GetArgument(parsedArgs, null, "-f", "--file-name");
      string itemName =
        CommandLine.GetArgument(parsedArgs, null, "-i", "--item-name");
      if(string.IsNullOrEmpty(bucketName) || (string.IsNullOrEmpty(fileName)))
        CommandLine.ErrorExit(
          "\nOne or more of the required arguments is missing or incorrect." +
          "\nRun the command with no arguments to see help.");
      if(!File.Exists(fileName))
        CommandLine.ErrorExit($"\nThe given file {fileName} doesn't exist.");
      if(string.IsNullOrEmpty(itemName))
        itemName = Path.GetFileName(fileName);

      // Create a customer master key (CMK) and store the result
      CreateKeyResponse createKeyResponse =
        await new AmazonKeyManagementServiceClient().CreateKeyAsync(new CreateKeyRequest());
      var kmsEncryptionContext = new Dictionary<string, string>();
      var kmsEncryptionMaterials = new EncryptionMaterialsV2(
        createKeyResponse.KeyMetadata.KeyId, KmsType.KmsContext, kmsEncryptionContext);

      // Create the object in the bucket, then display the content of the object
      var putObjectResponse =
        await CreateAndRetrieveObjectAsync(kmsEncryptionMaterials, bucketName, fileName, itemName);
      Stream stream = putObjectResponse.ResponseStream;
      StreamReader reader = new StreamReader(stream);
      Console.WriteLine(reader.ReadToEnd());
    }


    //
    // Method to create and encrypt an object in an S3 bucket
    static async Task<GetObjectResponse> CreateAndRetrieveObjectAsync(
      EncryptionMaterialsV2 materials, string bucketName,
      string fileName, string itemName)
    {
      // CryptoStorageMode.ObjectMetadata is required for KMS EncryptionMaterials
      var config = new AmazonS3CryptoConfigurationV2(SecurityProfile.V2AndLegacy)
      {
        StorageMode = CryptoStorageMode.ObjectMetadata
      };
      var s3EncClient = new AmazonS3EncryptionClientV2(config, materials);

      // Create, encrypt, and put the object
      await s3EncClient.PutObjectAsync(new PutObjectRequest
      {
        BucketName = bucketName,
        Key = itemName,
        ContentBody = File.ReadAllText(fileName)
      });

      // Get, decrypt, and return the object
      return await s3EncClient.GetObjectAsync(new GetObjectRequest
      {
        BucketName = bucketName,
        Key = itemName
      });
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: KmsS3Encryption -b <bucket-name> -f <file-name> [-i <item-name>]" +
        "\n  -b, --bucket-name: The name of an existing S3 bucket." +
        "\n  -f, --file-name: The name of a text file with content to encrypt and store in S3." +
        "\n  -i, --item-name: The name you want to use for the item." +
        "\n      If item-name isn't given, file-name will be used.");
    }

  }

  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 추가 고려 사항
<a name="kms-s3-additional"></a>
+ 이 예제의 결과를 확인할 수 있습니다. 이렇게 하려면 [Amazon S3 콘솔](https://console.aws.amazon.com/s3)로 이동하여 애플리케이션에 제공한 버킷을 엽니다. 그런 다음 새 객체를 찾아 다운로드한 다음 텍스트 편집기에서 엽니다.
+ [AmazonS3EncryptionClientV2](https://aws.github.io/amazon-s3-encryption-client-dotnet/api/Amazon.Extensions.S3.Encryption.AmazonS3EncryptionClientV2.html) 클래스는 표준 `AmazonS3Client` 클래스와 동일한 인터페이스를 구현합니다. 이렇게 하면 코드를 `AmazonS3EncryptionClientV2` 클래스로 쉽게 포팅하여 클라이언트에서 암호화와 복호화가 자동으로 투명하게 수행되도록 할 수 있습니다.
+ 키를 마스터 AWS KMS 키로 사용할 때의 한 가지 장점은 자체 마스터 키를 저장하고 관리할 필요가 없다는 것입니다.이 작업은에서 수행합니다 AWS. 두 번째 장점은의 `AmazonS3EncryptionClientV2` 클래스가의 `AmazonS3EncryptionClientV2` 클래스와 상호 운용 가능하다 AWS SDK for .NET 는 것입니다 AWS SDK for Java. 즉,를 사용하여 암호화 AWS SDK for Java 하고를 사용하여 복호화할 수 있으며 AWS SDK for .NET그 반대의 경우도 마찬가지입니다.
**참고**  
의 `AmazonS3EncryptionClientV2` 클래스는 메타데이터 모드에서 실행되는 경우에만 KMS 마스터 키를 AWS SDK for .NET 지원합니다. 의 `AmazonS3EncryptionClientV2` 클래스에 대한 명령 파일 모드 AWS SDK for .NET 는의 `AmazonS3EncryptionClientV2` 클래스와 호환되지 않습니다 AWS SDK for Java.
+ `AmazonS3EncryptionClientV2` 클래스를 사용한 클라이언트 측 암호화 및 봉투 암호화 작동 방식에 대한 자세한 내용은 [AWS SDK for .NET 및 Amazon S3를 사용한 클라이언트 측 데이터 암호화를](https://aws.amazon.com/blogs/developer/client-side-data-encryption-with-aws-sdk-for-net-and-amazon-s3/) 참조하세요.

# Amazon Simple Notification Service를 사용하여 클라우드에서 알림 전송
<a name="sns-apis-intro"></a>

**참고**  
이 주제의 정보는 .NET Framework 및 AWS SDK for .NET 버전 3.3 이하를 기반으로 하는 프로젝트에만 해당됩니다.

는 애플리케이션, 최종 사용자 및 디바이스가 클라우드에서 즉시 알림을 보낼 수 있는 웹 서비스인 Amazon Simple Notification Service(Amazon SNS)를 AWS SDK for .NET 지원합니다. 자세한 내용은 [Amazon SNS](https://aws.amazon.com/sns/)를 참조하세요.

## Amazon SNS 주제 나열
<a name="sns-list-example"></a>

다음 예제는 Amazon SNS 주제, 각 주제에 대한 구독, 각 주제에 대한 속성을 나열하는 방법을 보여줍니다. 이 예제에서는 기본 [AmazonSimpleNotificationServiceClient](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SNS/TSNSClient.html)를 사용합니다.

```
// using Amazon.SimpleNotificationService;
// using Amazon.SimpleNotificationService.Model;

var client = new AmazonSimpleNotificationServiceClient();
var request = new ListTopicsRequest();
var response = new ListTopicsResponse();

do
{
  response = client.ListTopics(request);

  foreach (var topic in response.Topics)
  {
    Console.WriteLine("Topic: {0}", topic.TopicArn);

    var subs = client.ListSubscriptionsByTopic(
      new ListSubscriptionsByTopicRequest
      {
        TopicArn = topic.TopicArn
      });

    var ss = subs.Subscriptions;

    if (ss.Any())
    {
      Console.WriteLine("  Subscriptions:");

      foreach (var sub in ss)
      {
        Console.WriteLine("    {0}", sub.SubscriptionArn);
      }
    }

    var attrs = client.GetTopicAttributes(
      new GetTopicAttributesRequest
      {
        TopicArn = topic.TopicArn
      }).Attributes;

    if (attrs.Any())
    {
      Console.WriteLine("  Attributes:");

      foreach (var attr in attrs)
      {
        Console.WriteLine("    {0} = {1}", attr.Key, attr.Value);
      }
    }

    Console.WriteLine();
  }

  request.NextToken = response.NextToken;

} while (!string.IsNullOrEmpty(response.NextToken));
```

## Amazon SNS 주제로 메시지 전송
<a name="sns-send-message-example"></a>

다음 예제는 메시지를 Amazon SNS 주제로 전송하는 방법을 보여줍니다. 이 예제에서는 하나의 인수(Amazon SNS 주제의 ARN)가 필요합니다.

```
using System;
using System.Linq;
using System.Threading.Tasks;

using Amazon;
using Amazon.SimpleNotificationService;
using Amazon.SimpleNotificationService.Model;

namespace SnsSendMessage
{
    class Program
    {
        static void Main(string[] args)
        {
            /* Topic ARNs must be in the correct format:
             *   arn:aws:sns:REGION:ACCOUNT_ID:NAME
             *
             *  where:
             *  REGION     is the region in which the topic is created, such as us-west-2
             *  ACCOUNT_ID is your (typically) 12-character account ID
             *  NAME       is the name of the topic
             */
            string topicArn = args[0];
            string message = "Hello at " + DateTime.Now.ToShortTimeString();

            var client = new AmazonSimpleNotificationServiceClient(region: Amazon.RegionEndpoint.USWest2);

            var request = new PublishRequest
            {
                Message = message,
                TopicArn = topicArn
            };

            try
            {
                var response = client.Publish(request);

                Console.WriteLine("Message sent to topic:");
                Console.WriteLine(message);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Caught exception publishing request:");
                Console.WriteLine(ex.Message);
            }
        }
    }
}
```

GitHub의 명령줄에서 예제를 빌드하고 실행하는 방법에 대한 정보를 비롯한 [전체 예제](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/.dotnet/example_code_legacy/SNS/SnsSendMessage.cs)를 참조하십시오.

## 전화 번호로 SMS 메시지 전송
<a name="sns-send-sms-example"></a>

다음 예제는 SMS 메시지를 전화 번호로 전송하는 방법을 보여줍니다. 이 예제에서는 하나의 인수(전화 번호)가 필요합니다. 전화 번호는 주석에 설명된 두 가지 형식 중 하나여야 합니다.

```
using System;
using System.Linq;
using System.Threading.Tasks;
using Amazon;
using Amazon.SimpleNotificationService;
using Amazon.SimpleNotificationService.Model;

namespace SnsPublish
{
    class Program
    {
        static void Main(string[] args)
        {
            // US phone numbers must be in the correct format:
            // +1 (nnn) nnn-nnnn OR +1nnnnnnnnnn
            string number = args[0];
            string message = "Hello at " + DateTime.Now.ToShortTimeString();

            var client = new AmazonSimpleNotificationServiceClient(region: Amazon.RegionEndpoint.USWest2);
            var request = new PublishRequest
            {
                Message = message,
                PhoneNumber = number
            };

            try
            {
                var response = client.Publish(request);

                Console.WriteLine("Message sent to " + number + ":");
                Console.WriteLine(message);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Caught exception publishing request:");
                Console.WriteLine(ex.Message);
            }
        }
    }
}
```

GitHub의 명령줄에서 예제를 빌드하고 실행하는 방법에 대한 정보를 비롯한 [전체 예제](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/.dotnet/example_code_legacy/SNS/SnsPublish.cs)를 참조하십시오.

# Amazon SQS를 사용한 메시징
<a name="sqs-apis-intro"></a>

는 시스템의 구성 요소 간에 메시지 또는 워크플로를 처리하는 메시지 대기열 서비스인 [Amazon Simple Queue Service(Amazon SQS)](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/)를 AWS SDK for .NET 지원합니다.

Amazon SQS 대기열은 마이크로서비스, 분산 시스템, 서버리스 애플리케이션과 같은 소프트웨어 구성 요소 간에 메시지를 전송, 저장 및 수신할 수 있게 해주는 메커니즘을 제공합니다. 이를 통해 이러한 구성 요소를 분리할 수 있으므로 고유의 메시징 시스템을 설계하고 운영할 필요가 없습니다. Amazon SQS에서 대기열 및 메시지가 작동하는 방식에 대한 자세한 내용은 [Amazon Simple Queue Service 개발자 안내서](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/)의 [Amazon SQS 자습서](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-other-tutorials.html) 및 [기본 Amazon SQS 아키텍처](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-basic-architecture.html)를 참조하세요.

**중요**  
Amazon SQS는 대기열의 분산 특성 때문에 메시지를 전송한 순서대로 수신하도록 보장할 수 없습니다. 메시지 순서를 유지해야 하는 경우 [Amazon SQS FIFO 대기열](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-fifo-queues.html)을 사용합니다.

## API
<a name="w2aac19c15c29b9"></a>

는 Amazon SQS 클라이언트에 대한 APIs AWS SDK for .NET 제공합니다. API를 사용하면 대기열 및 메시지와 같은 Amazon SQS 기능을 사용할 수 있습니다. 이 섹션에는 이러한 API로 작업할 때 따를 수 있는 패턴을 보여주는 몇 가지 예제가 포함되어 있습니다. 전체 API 세트를 보려면 [AWS SDK for .NET API 참조](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/)를 참조하세요(그리고 "Amazon.SQS"로 스크롤하세요).

Amazon SQS API는 [AWSSDK.SQS](https://www.nuget.org/packages/AWSSDK.SQS) NuGet 패키지를 통해 제공됩니다.

## 사전 조건
<a name="w2aac19c15c29c11"></a>

시작하기 전에 [환경을 설정하고](net-dg-config.md) [프로젝트를 구성](configuring-the-sdk.md)했는지 확인합니다. 또한 [SDK 사용](net-dg-sdk-features.md)의 정보를 검토합니다.

## 주제
<a name="w2aac19c15c29c13"></a>

**Topics**
+ [

## API
](#w2aac19c15c29b9)
+ [

## 사전 조건
](#w2aac19c15c29c11)
+ [

## 주제
](#w2aac19c15c29c13)
+ [대기열 생성](CreateQueue.md)
+ [대기열 업데이트](UpdateSqsQueue.md)
+ [대기열 삭제](DeleteSqsQueue.md)
+ [메시지 보내기](SendMessage.md)
+ [메시지 수신](ReceiveMessage.md)

# Amazon SQS 대기열 생성
<a name="CreateQueue"></a>

이 예제에서는를 사용하여 Amazon SQS 대기열을 AWS SDK for .NET 생성하는 방법을 보여줍니다. ARN을 제공하지 않으면 애플리케이션에서 [DLQ(Dead Letter Queue)](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html)를 생성합니다. 그런 다음 DLQ(Dead Letter Queue)(사용자가 제공한 대기열 또는 생성한 대기열)가 포함된 표준 메시지 대기열을 생성합니다.

명령줄 인수를 제공하지 않으면 애플리케이션은 모든 기존 대기열에 대한 정보만 표시합니다.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#CreateQueue-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [

## 기존 대기열 표시
](#CreateQueue-show-queues)
+ [

## 대기열 생성
](#CreateQueue-create-queue)
+ [

## 대기열의 ARN 가져오기
](#CreateQueue-get-arn)
+ [

## 전체 코드
](#CreateQueue-complete-code)
+ [

## 추가 고려 사항
](#CreateQueue-additional)

## 기존 대기열 표시
<a name="CreateQueue-show-queues"></a>

다음 코드 조각은 SQS 클라이언트 리전의 기존 대기열 목록과 각 대기열의 속성을 보여줍니다.

[이 주제의 끝 부분에 있는](#CreateQueue-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to show a list of the existing queues
    private static async Task ShowQueues(IAmazonSQS sqsClient)
    {
      ListQueuesResponse responseList = await sqsClient.ListQueuesAsync("");
      Console.WriteLine();
      foreach(string qUrl in responseList.QueueUrls)
      {
        // Get and show all attributes. Could also get a subset.
        await ShowAllAttributes(sqsClient, qUrl);
      }
    }

    //
    // Method to show all attributes of a queue
    private static async Task ShowAllAttributes(IAmazonSQS sqsClient, string qUrl)
    {
      var attributes = new List<string>{ QueueAttributeName.All };
      GetQueueAttributesResponse responseGetAtt =
        await sqsClient.GetQueueAttributesAsync(qUrl, attributes);
      Console.WriteLine($"Queue: {qUrl}");
      foreach(var att in responseGetAtt.Attributes)
        Console.WriteLine($"\t{att.Key}: {att.Value}");
    }
```

## 대기열 생성
<a name="CreateQueue-create-queue"></a>

다음 조각은 대기열을 생성합니다. 코드 조각에는 DLQ(Dead Letter Queue) 사용이 포함되지만 대기열에 반드시 DLQ(Dead Letter Queue)가 필요한 것은 아닙니다.

[이 주제의 끝 부분에 있는](#CreateQueue-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to create a queue. Returns the queue URL.
    private static async Task<string> CreateQueue(
      IAmazonSQS sqsClient, string qName, string deadLetterQueueUrl=null,
      string maxReceiveCount=null, string receiveWaitTime=null)
    {
      var attrs = new Dictionary<string, string>();

      // If a dead-letter queue is given, create a message queue
      if(!string.IsNullOrEmpty(deadLetterQueueUrl))
      {
        attrs.Add(QueueAttributeName.ReceiveMessageWaitTimeSeconds, receiveWaitTime);
        attrs.Add(QueueAttributeName.RedrivePolicy,
          $"{{\"deadLetterTargetArn\":\"{await GetQueueArn(sqsClient, deadLetterQueueUrl)}\"," +
          $"\"maxReceiveCount\":\"{maxReceiveCount}\"}}");
        // Add other attributes for the message queue such as VisibilityTimeout
      }

      // If no dead-letter queue is given, create one of those instead
      //else
      //{
      //  // Add attributes for the dead-letter queue as needed
      //  attrs.Add();
      //}

      // Create the queue
      CreateQueueResponse responseCreate = await sqsClient.CreateQueueAsync(
          new CreateQueueRequest{QueueName = qName, Attributes = attrs});
      return responseCreate.QueueUrl;
    }
```

## 대기열의 ARN 가져오기
<a name="CreateQueue-get-arn"></a>

다음 코드 조각은 지정된 대기열 URL로 식별되는 대기열의 ARN을 가져옵니다.

[이 주제의 끝 부분에 있는](#CreateQueue-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to get the ARN of a queue
    private static async Task<string> GetQueueArn(IAmazonSQS sqsClient, string qUrl)
    {
      GetQueueAttributesResponse responseGetAtt = await sqsClient.GetQueueAttributesAsync(
        qUrl, new List<string>{QueueAttributeName.QueueArn});
      return responseGetAtt.QueueARN;
    }
```

## 전체 코드
<a name="CreateQueue-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c29c17c25b5b1"></a>

NuGet 패키지:
+ [AWSSDK.SQS](https://www.nuget.org/packages/AWSSDK.SQS)

프로그래밍 요소:
+ 네임스페이스 [Amazon.SQS](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/NSQS.html)

  클래스 [AmazonSQSClient](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TSQSClient.html)

  클래스 [QueueAttributeName](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TQueueAttributeName.html)
+ 네임스페이스 [Amazon.SQS.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/NSQSModel.html)

  클래스 [CreateQueueRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TCreateQueueRequest.html)

  클래스 [CreateQueueResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TCreateQueueResponse.html)

  클래스 [GetQueueAttributesResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TGetQueueAttributesResponse.html)

  클래스 [ListQueuesResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TListQueuesResponse.html)

### 코드
<a name="w2aac19c15c29c17c25b7b1"></a>

```
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.SQS;
using Amazon.SQS.Model;

namespace SQSCreateQueue
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to create a queue
  class Program
  {
    private const string MaxReceiveCount = "10";
    private const string ReceiveMessageWaitTime = "2";
    private const int MaxArgs = 3;

    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count > MaxArgs)
        CommandLine.ErrorExit(
          "\nToo many command-line arguments.\nRun the command with no arguments to see help.");

      // Create the Amazon SQS client
      var sqsClient = new AmazonSQSClient();

      // In the case of no command-line arguments, just show help and the existing queues
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        Console.WriteLine("\nNo arguments specified.");
        Console.Write("Do you want to see a list of the existing queues? ((y) or n): ");
        string response = Console.ReadLine();
        if((string.IsNullOrEmpty(response)) || (response.ToLower() == "y"))
          await ShowQueues(sqsClient);
        return;
      }

      // Get the application arguments from the parsed list
      string queueName =
        CommandLine.GetArgument(parsedArgs, null, "-q", "--queue-name");
      string deadLetterQueueUrl =
        CommandLine.GetArgument(parsedArgs, null, "-d", "--dead-letter-queue");
      string maxReceiveCount =
        CommandLine.GetArgument(parsedArgs, MaxReceiveCount, "-m", "--max-receive-count");
      string receiveWaitTime =
        CommandLine.GetArgument(parsedArgs, ReceiveMessageWaitTime, "-w", "--wait-time");

      if(string.IsNullOrEmpty(queueName))
        CommandLine.ErrorExit(
          "\nYou must supply a queue name.\nRun the command with no arguments to see help.");

      // If a dead-letter queue wasn't given, create one
      if(string.IsNullOrEmpty(deadLetterQueueUrl))
      {
        Console.WriteLine("\nNo dead-letter queue was specified. Creating one...");
        deadLetterQueueUrl = await CreateQueue(sqsClient, queueName + "__dlq");
        Console.WriteLine($"Your new dead-letter queue:");
        await ShowAllAttributes(sqsClient, deadLetterQueueUrl);
      }

      // Create the message queue
      string messageQueueUrl = await CreateQueue(
        sqsClient, queueName, deadLetterQueueUrl, maxReceiveCount, receiveWaitTime);
      Console.WriteLine($"Your new message queue:");
      await ShowAllAttributes(sqsClient, messageQueueUrl);
    }


    //
    // Method to show a list of the existing queues
    private static async Task ShowQueues(IAmazonSQS sqsClient)
    {
      ListQueuesResponse responseList = await sqsClient.ListQueuesAsync("");
      Console.WriteLine();
      foreach(string qUrl in responseList.QueueUrls)
      {
        // Get and show all attributes. Could also get a subset.
        await ShowAllAttributes(sqsClient, qUrl);
      }
    }


    //
    // Method to create a queue. Returns the queue URL.
    private static async Task<string> CreateQueue(
      IAmazonSQS sqsClient, string qName, string deadLetterQueueUrl=null,
      string maxReceiveCount=null, string receiveWaitTime=null)
    {
      var attrs = new Dictionary<string, string>();

      // If a dead-letter queue is given, create a message queue
      if(!string.IsNullOrEmpty(deadLetterQueueUrl))
      {
        attrs.Add(QueueAttributeName.ReceiveMessageWaitTimeSeconds, receiveWaitTime);
        attrs.Add(QueueAttributeName.RedrivePolicy,
          $"{{\"deadLetterTargetArn\":\"{await GetQueueArn(sqsClient, deadLetterQueueUrl)}\"," +
          $"\"maxReceiveCount\":\"{maxReceiveCount}\"}}");
        // Add other attributes for the message queue such as VisibilityTimeout
      }

      // If no dead-letter queue is given, create one of those instead
      //else
      //{
      //  // Add attributes for the dead-letter queue as needed
      //  attrs.Add();
      //}

      // Create the queue
      CreateQueueResponse responseCreate = await sqsClient.CreateQueueAsync(
          new CreateQueueRequest{QueueName = qName, Attributes = attrs});
      return responseCreate.QueueUrl;
    }


    //
    // Method to get the ARN of a queue
    private static async Task<string> GetQueueArn(IAmazonSQS sqsClient, string qUrl)
    {
      GetQueueAttributesResponse responseGetAtt = await sqsClient.GetQueueAttributesAsync(
        qUrl, new List<string>{QueueAttributeName.QueueArn});
      return responseGetAtt.QueueARN;
    }


    //
    // Method to show all attributes of a queue
    private static async Task ShowAllAttributes(IAmazonSQS sqsClient, string qUrl)
    {
      var attributes = new List<string>{ QueueAttributeName.All };
      GetQueueAttributesResponse responseGetAtt =
        await sqsClient.GetQueueAttributesAsync(qUrl, attributes);
      Console.WriteLine($"Queue: {qUrl}");
      foreach(var att in responseGetAtt.Attributes)
        Console.WriteLine($"\t{att.Key}: {att.Value}");
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
      "\nUsage: SQSCreateQueue -q <queue-name> [-d <dead-letter-queue>]" +
        " [-m <max-receive-count>] [-w <wait-time>]" +
      "\n  -q, --queue-name: The name of the queue you want to create." +
      "\n  -d, --dead-letter-queue: The URL of an existing queue to be used as the dead-letter queue."+
      "\n      If this argument isn't supplied, a new dead-letter queue will be created." +
      "\n  -m, --max-receive-count: The value for maxReceiveCount in the RedrivePolicy of the queue." +
      $"\n      Default is {MaxReceiveCount}." +
      "\n  -w, --wait-time: The value for ReceiveMessageWaitTimeSeconds of the queue for long polling." +
      $"\n      Default is {ReceiveMessageWaitTime}.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 추가 고려 사항
<a name="CreateQueue-additional"></a>
+ 대기열 이름은 영문자, 숫자, 하이픈, 밑줄로 구성되어야 합니다.
+ 대기열 이름과 대기열 URL은 대소문자를 구분합니다
+ 대기열 URL이 필요한데 대기열 이름만 있는 경우에는 `AmazonSQSClient.GetQueueUrlAsync` 방법 중 하나를 사용합니다.
+ 설정할 수 있는 다양한 대기열 속성에 대한 자세한 내용은 [AWS SDK for .NET API 참조](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/)의 [CreateQueueRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TCreateQueueRequest.html) 또는 [Amazon Simple Queue Service API 참조](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/)의 [SetQueueAttributes](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SetQueueAttributes.html)를 참조하세요.
+ 이 예제는 생성한 대기열에 있는 모든 메시지에 대해 긴 폴링을 지정합니다. `ReceiveMessageWaitTimeSeconds` 속성을 사용하여 이를 수행합니다.

  [AmazonSQSClient](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TSQSClient.html) 클래스의 `ReceiveMessageAsync` 메서드를 호출하는 동안 긴 폴링을 지정할 수도 있습니다. 자세한 내용은 [Amazon SQS 메시지 수신](ReceiveMessage.md) 단원을 참조하십시오.

  짧은 폴링과 긴 폴링에 대한 자세한 내용은 *Amazon Simple Queue Service 개발자 안내서*의 [짧은 폴링 및 긴 폴링](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-short-and-long-polling.html)을 참조하세요.
+ 배달 못한 편지 대기열은 성공적으로 처리되지 않은 메시지를 다른 (소스) 대기열에서 보낼 수 있는 대기열입니다. 자세한 정보는 Amazon Simple Queue Service 개발자 안내서의 [Amazon SQS DLQ(Dead Letter Queue)](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html)를 참조하세요.
+ [Amazon SQS 콘솔](https://console.aws.amazon.com/sqs)에서도 대기열 목록과 이 예제의 결과를 확인할 수 있습니다.

# Amazon SQS 대기열 업데이트
<a name="UpdateSqsQueue"></a>

이 예제에서는를 사용하여 Amazon SQS 대기열 AWS SDK for .NET 을 업데이트하는 방법을 보여줍니다. 일부 점검 후 애플리케이션은 지정된 속성을 지정된 값으로 업데이트한 다음 대기열의 모든 속성을 표시합니다.

명령줄 인수에 대기열 URL만 포함된 경우 애플리케이션에서는 단순히 대기열의 모든 속성을 표시합니다.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#UpdateSqsQueue-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [

## 대기열 속성 표시
](#UpdateSqsQueue-show-attributes)
+ [

## 속성 이름 검증
](#UpdateSqsQueue-validate-attribute)
+ [

## 대기열 속성 업데이트
](#UpdateSqsQueue-update-attribute)
+ [

## 전체 코드
](#UpdateSqsQueue-complete-code)
+ [

## 추가 고려 사항
](#UpdateSqsQueue-additional)

## 대기열 속성 표시
<a name="UpdateSqsQueue-show-attributes"></a>

다음 코드 조각은 지정된 대기열 URL로 식별되는 대기열의 속성을 보여줍니다.

[이 주제의 끝 부분에 있는](#UpdateSqsQueue-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to show all attributes of a queue
    private static async Task ShowAllAttributes(IAmazonSQS sqsClient, string qUrl)
    {
      GetQueueAttributesResponse responseGetAtt =
        await sqsClient.GetQueueAttributesAsync(qUrl,
          new List<string>{ QueueAttributeName.All });
      Console.WriteLine($"Queue: {qUrl}");
      foreach(var att in responseGetAtt.Attributes)
        Console.WriteLine($"\t{att.Key}: {att.Value}");
    }
```

## 속성 이름 검증
<a name="UpdateSqsQueue-validate-attribute"></a>

다음 코드 조각은 업데이트 중인 속성의 이름을 검증합니다.

[이 주제의 끝 부분에 있는](#UpdateSqsQueue-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to check the name of the attribute
    private static bool ValidAttribute(string attribute)
    {
      var attOk = false;
      var qAttNameType = typeof(QueueAttributeName);
      List<string> qAttNamefields = new List<string>();
      foreach(var field in qAttNameType.GetFields())
       qAttNamefields.Add(field.Name);
      foreach(var name in qAttNamefields)
        if(attribute == name) { attOk = true; break; }
      return attOk;
    }
```

## 대기열 속성 업데이트
<a name="UpdateSqsQueue-update-attribute"></a>

다음 코드 조각은 지정된 대기열 URL로 식별되는 대기열의 속성을 업데이트합니다.

[이 주제의 끝 부분에 있는](#UpdateSqsQueue-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to update a queue attribute
    private static async Task UpdateAttribute(
      IAmazonSQS sqsClient, string qUrl, string attribute, string value)
    {
      await sqsClient.SetQueueAttributesAsync(qUrl,
        new Dictionary<string, string>{{attribute, value}});
    }
```

## 전체 코드
<a name="UpdateSqsQueue-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c29c19c25b5b1"></a>

NuGet 패키지:
+ [AWSSDK.SQS](https://www.nuget.org/packages/AWSSDK.SQS)

프로그래밍 요소:
+ 네임스페이스 [Amazon.SQS](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/NSQS.html)

  클래스 [AmazonSQSClient](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TSQSClient.html)

  클래스 [QueueAttributeName](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TQueueAttributeName.html)
+ 네임스페이스 [Amazon.SQS.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/NSQSModel.html)

  클래스 [GetQueueAttributesResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TGetQueueAttributesResponse.html)

### 코드
<a name="w2aac19c15c29c19c25b7b1"></a>

```
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Amazon.SQS;
using Amazon.SQS.Model;

namespace SQSUpdateQueue
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to update a queue
  class Program
  {
    private const int MaxArgs = 3;
    private const int InvalidArgCount = 2;

    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        return;
      }
      if((parsedArgs.Count > MaxArgs) || (parsedArgs.Count == InvalidArgCount))
        CommandLine.ErrorExit("\nThe number of command-line arguments is incorrect." +
          "\nRun the command with no arguments to see help.");

      // Get the application arguments from the parsed list
      var qUrl = CommandLine.GetArgument(parsedArgs, null, "-q");
      var attribute = CommandLine.GetArgument(parsedArgs, null, "-a");
      var value = CommandLine.GetArgument(parsedArgs, null, "-v", "--value");

      if(string.IsNullOrEmpty(qUrl))
        CommandLine.ErrorExit("\nYou must supply at least a queue URL." +
          "\nRun the command with no arguments to see help.");

      // Create the Amazon SQS client
      var sqsClient = new AmazonSQSClient();

      // In the case of one command-line argument, just show the attributes for the queue
      if(parsedArgs.Count == 1)
        await ShowAllAttributes(sqsClient, qUrl);

      // Otherwise, attempt to update the given queue attribute with the given value
      else
      {
        // Check to see if the attribute is valid
        if(ValidAttribute(attribute))
        {
          // Perform the update and then show all the attributes of the queue
          await UpdateAttribute(sqsClient, qUrl, attribute, value);
          await ShowAllAttributes(sqsClient, qUrl);
        }
        else
        {
          Console.WriteLine($"\nThe given attribute name, {attribute}, isn't valid.");
        }
      }
    }


    //
    // Method to show all attributes of a queue
    private static async Task ShowAllAttributes(IAmazonSQS sqsClient, string qUrl)
    {
      GetQueueAttributesResponse responseGetAtt =
        await sqsClient.GetQueueAttributesAsync(qUrl,
          new List<string>{ QueueAttributeName.All });
      Console.WriteLine($"Queue: {qUrl}");
      foreach(var att in responseGetAtt.Attributes)
        Console.WriteLine($"\t{att.Key}: {att.Value}");
    }


    //
    // Method to check the name of the attribute
    private static bool ValidAttribute(string attribute)
    {
      var attOk = false;
      var qAttNameType = typeof(QueueAttributeName);
      List<string> qAttNamefields = new List<string>();
      foreach(var field in qAttNameType.GetFields())
       qAttNamefields.Add(field.Name);
      foreach(var name in qAttNamefields)
        if(attribute == name) { attOk = true; break; }
      return attOk;
    }


    //
    // Method to update a queue attribute
    private static async Task UpdateAttribute(
      IAmazonSQS sqsClient, string qUrl, string attribute, string value)
    {
      await sqsClient.SetQueueAttributesAsync(qUrl,
        new Dictionary<string, string>{{attribute, value}});
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine("\nUsage: SQSUpdateQueue -q queue_url [-a attribute -v value]");
      Console.WriteLine("  -q: The URL of the queue you want to update.");
      Console.WriteLine("  -a: The name of the attribute to update.");
      Console.WriteLine("  -v, --value: The value to assign to the attribute.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 추가 고려 사항
<a name="UpdateSqsQueue-additional"></a>
+ `RedrivePolicy` 속성을 업데이트하려면 운영 체제에 맞게 전체 값을 인용하고 키/값 페어의 따옴표는 이스케이프 처리해야 합니다.

  예를 들어 Windows에서는 값이 다음과 비슷한 방식으로 구성됩니다.

  ```
  "{\"deadLetterTargetArn\":\"DEAD_LETTER-QUEUE-ARN\",\"maxReceiveCount\":\"10\"}"
  ```

# Amazon SQS 대기열 삭제
<a name="DeleteSqsQueue"></a>

이 예제에서는를 사용하여 Amazon SQS 대기열 AWS SDK for .NET 을 삭제하는 방법을 보여줍니다. 애플리케이션은 대기열을 삭제하고 대기열이 없어질 때까지 지정된 시간까지 기다린 다음 나머지 대기열의 목록을 표시합니다.

명령줄 인수를 제공하지 않으면 애플리케이션은 단순히 기존 대기열의 목록을 표시합니다.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#DeleteSqsQueue-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [

## 대기열 삭제
](#DeleteSqsQueue-delete-queue)
+ [

## 대기열이 없어질 때까지 대기
](#DeleteSqsQueue-wait)
+ [

## 기존 대기열 목록 표시
](#DeleteSqsQueue-list-queues)
+ [

## 전체 코드
](#DeleteSqsQueue-complete-code)
+ [

## 추가 고려 사항
](#DeleteSqsQueue-additional)

## 대기열 삭제
<a name="DeleteSqsQueue-delete-queue"></a>

다음 코드 조각은 지정된 대기열 URL로 식별되는 대기열을 삭제합니다.

[이 주제의 끝 부분에 있는](#DeleteSqsQueue-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to delete an SQS queue
    private static async Task DeleteQueue(IAmazonSQS sqsClient, string qUrl)
    {
      Console.WriteLine($"Deleting queue {qUrl}...");
      await sqsClient.DeleteQueueAsync(qUrl);
      Console.WriteLine($"Queue {qUrl} has been deleted.");
    }
```

## 대기열이 없어질 때까지 대기
<a name="DeleteSqsQueue-wait"></a>

다음 코드 조각은 삭제 프로세스가 완료될 때까지 기다립니다. 이 과정은 60초 정도 걸릴 수 있습니다.

[이 주제의 끝 부분에 있는](#DeleteSqsQueue-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to wait up to a given number of seconds
    private static async Task Wait(
      IAmazonSQS sqsClient, int numSeconds, string qUrl)
    {
      Console.WriteLine($"Waiting for up to {numSeconds} seconds.");
      Console.WriteLine("Press any key to stop waiting. (Response might be slightly delayed.)");
      for(int i=0; i<numSeconds; i++)
      {
        Console.Write(".");
        Thread.Sleep(1000);
        if(Console.KeyAvailable) break;

        // Check to see if the queue is gone yet
        var found = false;
        ListQueuesResponse responseList = await sqsClient.ListQueuesAsync("");
        foreach(var url in responseList.QueueUrls)
        {
          if(url == qUrl)
          {
            found = true;
            break;
          }
        }
        if(!found) break;
      }
    }
```

## 기존 대기열 목록 표시
<a name="DeleteSqsQueue-list-queues"></a>

다음 코드 조각은 SQS 클라이언트 리전의 기존 대기열 목록을 보여줍니다.

[이 주제의 끝 부분에 있는](#DeleteSqsQueue-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to show a list of the existing queues
    private static async Task ListQueues(IAmazonSQS sqsClient)
    {
      ListQueuesResponse responseList = await sqsClient.ListQueuesAsync("");
      Console.WriteLine("\nList of queues:");
      foreach(var qUrl in responseList.QueueUrls)
        Console.WriteLine($"- {qUrl}");
    }
```

## 전체 코드
<a name="DeleteSqsQueue-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c29c21c25b5b1"></a>

NuGet 패키지:
+ [AWSSDK.SQS](https://www.nuget.org/packages/AWSSDK.SQS)

프로그래밍 요소:
+ 네임스페이스 [Amazon.SQS](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/NSQS.html)

  클래스 [AmazonSQSClient](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TSQSClient.html)
+ 네임스페이스 [Amazon.SQS.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/NSQSModel.html)

  클래스 [ListQueuesResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TListQueuesResponse.html)

### 코드
<a name="w2aac19c15c29c21c25b7b1"></a>

```
using System;
using System.Threading;
using System.Threading.Tasks;
using Amazon.SQS;
using Amazon.SQS.Model;

namespace SQSDeleteQueue
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to update a queue
  class Program
  {
    private const int TimeToWait = 60;

    static async Task Main(string[] args)
    {
      // Create the Amazon SQS client
      var sqsClient = new AmazonSQSClient();

      // If no command-line arguments, just show a list of the queues
      if(args.Length == 0)
      {
        Console.WriteLine("\nUsage: SQSCreateQueue queue_url");
        Console.WriteLine("   queue_url - The URL of the queue you want to delete.");
        Console.WriteLine("\nNo arguments specified.");
        Console.Write("Do you want to see a list of the existing queues? ((y) or n): ");
        var response = Console.ReadLine();
        if((string.IsNullOrEmpty(response)) || (response.ToLower() == "y"))
          await ListQueues(sqsClient);
        return;
      }

      // If given a queue URL, delete that queue
      if(args[0].StartsWith("https://sqs."))
      {
        // Delete the queue
        await DeleteQueue(sqsClient, args[0]);
        // Wait for a little while because it takes a while for the queue to disappear
        await Wait(sqsClient, TimeToWait, args[0]);
        // Show a list of the remaining queues
        await ListQueues(sqsClient);
      }
      else
      {
        Console.WriteLine("The command-line argument isn't a queue URL:");
        Console.WriteLine($"{args[0]}");
      }
    }


    //
    // Method to delete an SQS queue
    private static async Task DeleteQueue(IAmazonSQS sqsClient, string qUrl)
    {
      Console.WriteLine($"Deleting queue {qUrl}...");
      await sqsClient.DeleteQueueAsync(qUrl);
      Console.WriteLine($"Queue {qUrl} has been deleted.");
    }


    //
    // Method to wait up to a given number of seconds
    private static async Task Wait(
      IAmazonSQS sqsClient, int numSeconds, string qUrl)
    {
      Console.WriteLine($"Waiting for up to {numSeconds} seconds.");
      Console.WriteLine("Press any key to stop waiting. (Response might be slightly delayed.)");
      for(int i=0; i<numSeconds; i++)
      {
        Console.Write(".");
        Thread.Sleep(1000);
        if(Console.KeyAvailable) break;

        // Check to see if the queue is gone yet
        var found = false;
        ListQueuesResponse responseList = await sqsClient.ListQueuesAsync("");
        foreach(var url in responseList.QueueUrls)
        {
          if(url == qUrl)
          {
            found = true;
            break;
          }
        }
        if(!found) break;
      }
    }


    //
    // Method to show a list of the existing queues
    private static async Task ListQueues(IAmazonSQS sqsClient)
    {
      ListQueuesResponse responseList = await sqsClient.ListQueuesAsync("");
      Console.WriteLine("\nList of queues:");
      foreach(var qUrl in responseList.QueueUrls)
        Console.WriteLine($"- {qUrl}");
    }
  }
}
```

## 추가 고려 사항
<a name="DeleteSqsQueue-additional"></a>
+ `DeleteQueueAsync` API 직접 호출은 삭제하려는 대기열이 DLQ(Dead Letter Queue)로 사용되고 있는지 확인하지 않습니다. 좀 더 정교한 절차를 통해 이를 확인할 수 있습니다.
+ [Amazon SQS 콘솔](https://console.aws.amazon.com/sqs)에서도 대기열 목록과 이 예제의 결과를 확인할 수 있습니다.

# Amazon SQS 메시지 전송
<a name="SendMessage"></a>

이 예제에서는 AWS SDK for .NET 를 사용하여 [프로그래밍 방식으로](CreateQueue.md) 또는 Amazon SQS [콘솔을 사용하여 생성할 수 있는 Amazon SQS ](https://console.aws.amazon.com/sqs)대기열로 메시지를 전송하는 방법을 보여줍니다. 애플리케이션은 대기열에 메시지 하나를 전송한 다음 메시지 배치를 전송합니다. 그러면 애플리케이션은 사용자 입력을 기다립니다. 사용자 입력은 대기열에 보낼 추가 메시지나 애플리케이션 종료 요청일 수 있습니다.

이 예제와 [메시지 수신에 대한 다음 예제](ReceiveMessage.md)를 함께 사용하여 Amazon SQS의 메시지 흐름을 확인할 수 있습니다.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#SendMessage-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [

## 메시지 전송
](#SendMessage-send-message)
+ [

## 메시지 배치 전송
](#SendMessage-send-batch)
+ [

## 대기열에서 모든 메시지 삭제
](#SendMessage-purge-messages)
+ [

## 전체 코드
](#SendMessage-complete-code)
+ [

## 추가 고려 사항
](#SendMessage-additional)

## 메시지 전송
<a name="SendMessage-send-message"></a>

다음 코드 조각은 지정된 대기열 URL로 식별되는 대기열에 메시지를 전송합니다.

[이 주제의 끝 부분에 있는](#SendMessage-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to put a message on a queue
    // Could be expanded to include message attributes, etc., in a SendMessageRequest
    private static async Task SendMessage(
      IAmazonSQS sqsClient, string qUrl, string messageBody)
    {
      SendMessageResponse responseSendMsg =
        await sqsClient.SendMessageAsync(qUrl, messageBody);
      Console.WriteLine($"Message added to queue\n  {qUrl}");
      Console.WriteLine($"HttpStatusCode: {responseSendMsg.HttpStatusCode}");
    }
```

## 메시지 배치 전송
<a name="SendMessage-send-batch"></a>

다음 코드 조각은 지정된 대기열 URL로 식별되는 대기열에 메시지 배치를 전송합니다.

[이 주제의 끝 부분에 있는](#SendMessage-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to put a batch of messages on a queue
    // Could be expanded to include message attributes, etc.,
    // in the SendMessageBatchRequestEntry objects
    private static async Task SendMessageBatch(
      IAmazonSQS sqsClient, string qUrl, List<SendMessageBatchRequestEntry> messages)
    {
      Console.WriteLine($"\nSending a batch of messages to queue\n  {qUrl}");
      SendMessageBatchResponse responseSendBatch =
        await sqsClient.SendMessageBatchAsync(qUrl, messages);
      // Could test responseSendBatch.Failed here
      foreach(SendMessageBatchResultEntry entry in responseSendBatch.Successful)
        Console.WriteLine($"Message {entry.Id} successfully queued.");
    }
```

## 대기열에서 모든 메시지 삭제
<a name="SendMessage-purge-messages"></a>

다음 코드 조각은 지정된 대기열 URL로 식별되는 대기열에서 모든 메시지를 삭제합니다. 이를 *대기열 비우기*라고도 합니다.

[이 주제의 끝 부분에 있는](#SendMessage-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to delete all messages from the queue
    private static async Task DeleteAllMessages(IAmazonSQS sqsClient, string qUrl)
    {
      Console.WriteLine($"\nPurging messages from queue\n  {qUrl}...");
      PurgeQueueResponse responsePurge = await sqsClient.PurgeQueueAsync(qUrl);
      Console.WriteLine($"HttpStatusCode: {responsePurge.HttpStatusCode}");
    }
```

## 전체 코드
<a name="SendMessage-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c29c23c25b5b1"></a>

NuGet 패키지:
+ [AWSSDK.SQS](https://www.nuget.org/packages/AWSSDK.SQS)

프로그래밍 요소:
+ 네임스페이스 [Amazon.SQS](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/NSQS.html)

  클래스 [AmazonSQSClient](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TSQSClient.html)
+ 네임스페이스 [Amazon.SQS.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/NSQSModel.html)

  클래스 [PurgeQueueResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TPurgeQueueResponse.html)

  클래스 [SendMessageBatchResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TSendMessageBatchResponse.html)

  클래스 [SendMessageResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TSendMessageResponse.html)

  클래스 [SendMessageBatchRequestEntry](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TSendMessageBatchRequestEntry.html)

  클래스 [SendMessageBatchResultEntry](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TSendMessageBatchResultEntry.html)

### 코드
<a name="w2aac19c15c29c23c25b7b1"></a>

```
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Amazon.SQS;
using Amazon.SQS.Model;

namespace SQSSendMessages
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to send messages to a queue
  class Program
  {
    // Some example messages to send to the queue
    private const string JsonMessage = "{\"product\":[{\"name\":\"Product A\",\"price\": \"32\"},{\"name\": \"Product B\",\"price\": \"27\"}]}";
    private const string XmlMessage = "<products><product name=\"Product A\" price=\"32\" /><product name=\"Product B\" price=\"27\" /></products>";
    private const string CustomMessage = "||product|Product A|32||product|Product B|27||";
    private const string TextMessage = "Just a plain text message.";

    static async Task Main(string[] args)
    {
      // Do some checks on the command-line
      if(args.Length == 0)
      {
        Console.WriteLine("\nUsage: SQSSendMessages queue_url");
        Console.WriteLine("   queue_url - The URL of an existing SQS queue.");
        return;
      }
      if(!args[0].StartsWith("https://sqs."))
      {
        Console.WriteLine("\nThe command-line argument isn't a queue URL:");
        Console.WriteLine($"{args[0]}");
        return;
      }

      // Create the Amazon SQS client
      var sqsClient = new AmazonSQSClient();

      // (could verify that the queue exists)
      // Send some example messages to the given queue
      // A single message
      await SendMessage(sqsClient, args[0], JsonMessage);

      // A batch of messages
      var batchMessages = new List<SendMessageBatchRequestEntry>{
        new SendMessageBatchRequestEntry("xmlMsg", XmlMessage),
        new SendMessageBatchRequestEntry("customeMsg", CustomMessage),
        new SendMessageBatchRequestEntry("textMsg", TextMessage)};
      await SendMessageBatch(sqsClient, args[0], batchMessages);

      // Let the user send their own messages or quit
      await InteractWithUser(sqsClient, args[0]);

      // Delete all messages that are still in the queue
      await DeleteAllMessages(sqsClient, args[0]);
    }


    //
    // Method to put a message on a queue
    // Could be expanded to include message attributes, etc., in a SendMessageRequest
    private static async Task SendMessage(
      IAmazonSQS sqsClient, string qUrl, string messageBody)
    {
      SendMessageResponse responseSendMsg =
        await sqsClient.SendMessageAsync(qUrl, messageBody);
      Console.WriteLine($"Message added to queue\n  {qUrl}");
      Console.WriteLine($"HttpStatusCode: {responseSendMsg.HttpStatusCode}");
    }


    //
    // Method to put a batch of messages on a queue
    // Could be expanded to include message attributes, etc.,
    // in the SendMessageBatchRequestEntry objects
    private static async Task SendMessageBatch(
      IAmazonSQS sqsClient, string qUrl, List<SendMessageBatchRequestEntry> messages)
    {
      Console.WriteLine($"\nSending a batch of messages to queue\n  {qUrl}");
      SendMessageBatchResponse responseSendBatch =
        await sqsClient.SendMessageBatchAsync(qUrl, messages);
      // Could test responseSendBatch.Failed here
      foreach(SendMessageBatchResultEntry entry in responseSendBatch.Successful)
        Console.WriteLine($"Message {entry.Id} successfully queued.");
    }


    //
    // Method to get input from the user
    // They can provide messages to put in the queue or exit the application
    private static async Task InteractWithUser(IAmazonSQS sqsClient, string qUrl)
    {
      string response;
      while (true)
      {
        // Get the user's input
        Console.WriteLine("\nType a message for the queue or \"exit\" to quit:");
        response = Console.ReadLine();
        if(response.ToLower() == "exit") break;

        // Put the user's message in the queue
        await SendMessage(sqsClient, qUrl, response);
      }
    }


    //
    // Method to delete all messages from the queue
    private static async Task DeleteAllMessages(IAmazonSQS sqsClient, string qUrl)
    {
      Console.WriteLine($"\nPurging messages from queue\n  {qUrl}...");
      PurgeQueueResponse responsePurge = await sqsClient.PurgeQueueAsync(qUrl);
      Console.WriteLine($"HttpStatusCode: {responsePurge.HttpStatusCode}");
    }
  }
}
```

## 추가 고려 사항
<a name="SendMessage-additional"></a>
+ 허용되는 문자를 비롯한 다양한 메시지 제한 사항에 대한 자세한 내용은 [Amazon Simple Queue Service 개발자 안내서](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/)의 [메시지 관련 할당량](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-quotas.html#quotas-messages)을 참조하세요.
+ 메시지는 삭제되거나 대기열이 제거될 때까지 대기열에 남아 있습니다. 애플리케이션이 메시지를 수신한 경우 해당 메시지가 대기열에 남아 있더라도 대기열에 표시되지 않습니다. 제한 시간 초과에 대한 자세한 내용은 [Amazon SQS 제한 시간 초과](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AboutVT.html)를 참조하세요.
+ 메시지 본문 외에도 메시지에 속성을 추가할 수 있습니다. 자세한 내용은 [메시지 메타데이터](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-message-metadata.html)를 참조하세요.

# Amazon SQS 메시지 수신
<a name="ReceiveMessage"></a>

이 예제에서는를 사용하여 [프로그래밍 방식으로](CreateQueue.md) 또는 Amazon SQS [콘솔을 사용하여 생성할 수 있는 Amazon SQS ](https://console.aws.amazon.com/sqs)대기열에서 메시지를 AWS SDK for .NET 수신하는 방법을 보여줍니다. 애플리케이션은 대기열에서 단일 메시지를 읽고 메시지를 처리한 다음(이 경우 콘솔에 메시지 본문을 표시) 대기열에서 메시지를 삭제합니다. 애플리케이션은 사용자가 키보드로 키를 입력할 때까지 이 단계를 반복합니다.

이 예제와 [메시지 전송에 대한 이전 예제](SendMessage.md)를 함께 사용하여 Amazon SQS의 메시지 흐름을 확인할 수 있습니다.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#ReceiveMessage-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [

## 메시지 수신
](#ReceiveMessage-receive)
+ [

## 메시지 삭제
](#ReceiveMessage-delete)
+ [

## 전체 코드
](#ReceiveMessage-complete-code)
+ [

## 추가 고려 사항
](#ReceiveMessage-additional)

## 메시지 수신
<a name="ReceiveMessage-receive"></a>

다음 코드 조각은 지정된 대기열 URL로 식별되는 대기열에서 메시지를 수신합니다.

[이 주제의 끝 부분에 있는](#ReceiveMessage-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to read a message from the given queue
    // In this example, it gets one message at a time
    private static async Task<ReceiveMessageResponse> GetMessage(
      IAmazonSQS sqsClient, string qUrl, int waitTime=0)
    {
      return await sqsClient.ReceiveMessageAsync(new ReceiveMessageRequest{
        QueueUrl=qUrl,
        MaxNumberOfMessages=MaxMessages,
        WaitTimeSeconds=waitTime
        // (Could also request attributes, set visibility timeout, etc.)
      });
    }
```

## 메시지 삭제
<a name="ReceiveMessage-delete"></a>

다음 코드 조각은 지정된 대기열 URL로 식별되는 대기열에서 메시지를 삭제합니다.

[이 주제의 끝 부분에 있는](#ReceiveMessage-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to delete a message from a queue
    private static async Task DeleteMessage(
      IAmazonSQS sqsClient, Message message, string qUrl)
    {
      Console.WriteLine($"\nDeleting message {message.MessageId} from queue...");
      await sqsClient.DeleteMessageAsync(qUrl, message.ReceiptHandle);
    }
```

## 전체 코드
<a name="ReceiveMessage-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c29c25c21b5b1"></a>

NuGet 패키지:
+ [AWSSDK.SQS](https://www.nuget.org/packages/AWSSDK.SQS)

프로그래밍 요소:
+ 네임스페이스 [Amazon.SQS](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/NSQS.html)

  클래스 [AmazonSQSClient](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TSQSClient.html)
+ 네임스페이스 [Amazon.SQS.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/NSQSModel.html)

  클래스 [ReceiveMessageRequest](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TReceiveMessageRequest.html)

  클래스 [ReceiveMessageResponse](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TReceiveMessageResponse.html)

### 코드
<a name="w2aac19c15c29c25c21b7b1"></a>

```
using System;
using System.Threading.Tasks;
using Amazon.SQS;
using Amazon.SQS.Model;

namespace SQSReceiveMessages
{
  class Program
  {
    private const int MaxMessages = 1;
    private const int WaitTime = 2;
    static async Task Main(string[] args)
    {
      // Do some checks on the command-line
      if(args.Length == 0)
      {
        Console.WriteLine("\nUsage: SQSReceiveMessages queue_url");
        Console.WriteLine("   queue_url - The URL of an existing SQS queue.");
        return;
      }
      if(!args[0].StartsWith("https://sqs."))
      {
        Console.WriteLine("\nThe command-line argument isn't a queue URL:");
        Console.WriteLine($"{args[0]}");
        return;
      }

      // Create the Amazon SQS client
      var sqsClient = new AmazonSQSClient();

      // (could verify that the queue exists)
      // Read messages from the queue and perform appropriate actions
      Console.WriteLine($"Reading messages from queue\n  {args[0]}");
      Console.WriteLine("Press any key to stop. (Response might be slightly delayed.)");
      do
      {
        var msg = await GetMessage(sqsClient, args[0], WaitTime);
        if(msg.Messages.Count != 0)
        {
          if(ProcessMessage(msg.Messages[0]))
            await DeleteMessage(sqsClient, msg.Messages[0], args[0]);
        }
      } while(!Console.KeyAvailable);
    }


    //
    // Method to read a message from the given queue
    // In this example, it gets one message at a time
    private static async Task<ReceiveMessageResponse> GetMessage(
      IAmazonSQS sqsClient, string qUrl, int waitTime=0)
    {
      return await sqsClient.ReceiveMessageAsync(new ReceiveMessageRequest{
        QueueUrl=qUrl,
        MaxNumberOfMessages=MaxMessages,
        WaitTimeSeconds=waitTime
        // (Could also request attributes, set visibility timeout, etc.)
      });
    }


    //
    // Method to process a message
    // In this example, it simply prints the message
    private static bool ProcessMessage(Message message)
    {
      Console.WriteLine($"\nMessage body of {message.MessageId}:");
      Console.WriteLine($"{message.Body}");
      return true;
    }


    //
    // Method to delete a message from a queue
    private static async Task DeleteMessage(
      IAmazonSQS sqsClient, Message message, string qUrl)
    {
      Console.WriteLine($"\nDeleting message {message.MessageId} from queue...");
      await sqsClient.DeleteMessageAsync(qUrl, message.ReceiptHandle);
    }
  }
}
```

## 추가 고려 사항
<a name="ReceiveMessage-additional"></a>
+ 긴 폴링을 지정하기 위해 이 예제에서는 `ReceiveMessageAsync` 메서드를 호출할 때마다 `WaitTimeSeconds` 속성을 사용합니다.

  대기열을 [생성](CreateQueue.md)하거나 [업데이트](UpdateSqsQueue.md)할 때 `ReceiveMessageWaitTimeSeconds` 속성을 사용하여 대기열의 모든 메시지에 대해 긴 폴링을 지정할 수도 있습니다.

  짧은 폴링과 긴 폴링에 대한 자세한 내용은 *Amazon Simple Queue Service 개발자 안내서*의 [짧은 폴링 및 긴 폴링](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-short-and-long-polling.html)을 참조하세요.
+ 메시지 처리 중에 수신 핸들을 사용하여 메시지 제한 시간 초과를 변경할 수 있습니다. 이를 수행하는 방법에 대한 자세한 내용은 [AmazonSQSClient](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/SQS/TSQSClient.html) 클래스의 `ChangeMessageVisibilityAsync` 메서드를 참조하세요.
+ 조건 없이 `DeleteMessageAsync` 메서드를 호출하면 제한 시간 설정에 상관없이 대기열에서 메시지가 제거됩니다.

# AWS Lambda 컴퓨팅 서비스에 사용
<a name="aws-lambda"></a>

는 서버를 프로비저닝하거나 관리하지 않고도 코드를 실행할 수 AWS Lambda있도록 AWS SDK for .NET 지원합니다. 자세한 내용은 [AWS Lambda 제품 페이지](https://aws.amazon.com/lambda/) 및 [AWS Lambda 개발자 안내서](https://docs.aws.amazon.com/lambda/latest/dg/), 특히 [C\$1을 사용한 작업](https://docs.aws.amazon.com/lambda/latest/dg/lambda-csharp.html) 섹션을 참조하세요.

## API
<a name="w2aac19c17b5"></a>

는에 대한 APIs AWS SDK for .NET 제공합니다 AWS Lambda. API를 사용하면 [함수](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-concepts.html#gettingstarted-concepts-function), [트리거](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-concepts.html#gettingstarted-concepts-trigger), [이벤트](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-concepts.html#gettingstarted-concepts-event)와 같은 Lambda 기능을 사용할 수 있습니다. 전체 API 세트를 보려면 [AWS SDK for .NET API 참조](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/)의 [Lambda](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/Lambda/NLambda.html)를 참조하세요.

Lambda API는 [NuGet 패키지](https://www.nuget.org/packages?page=2&q=aws%20lambda&sortBy=relevance)를 통해 제공됩니다.

## 사전 조건
<a name="w2aac19c17b7"></a>

시작하기 전에 [환경을 설정하고](net-dg-config.md) [프로젝트를 구성](configuring-the-sdk.md)했는지 확인합니다. 또한 [SDK 사용](net-dg-sdk-features.md)의 정보를 검토합니다.

## 추가 정보
<a name="w2aac19c17b9"></a>

.NET Aspire를 AWS Lambda 통해를 사용하여 개발하는 방법에 대한 [.NET Aspire AWS 와 통합](aspire-integrations.md) 자세한 내용은 섹션을 참조하세요.

## 주제
<a name="w2aac19c17c11"></a>

**Topics**
+ [

## API
](#w2aac19c17b5)
+ [

## 사전 조건
](#w2aac19c17b7)
+ [

## 추가 정보
](#w2aac19c17b9)
+ [

## 주제
](#w2aac19c17c11)
+ [Lambda 주석](aws-lambda-annotations.md)

# 주석을 사용하여 AWS Lambda 함수 작성
<a name="aws-lambda-annotations"></a>

Lambda 함수를 작성할 때 다른 작업 중에서도 대량의 핸들러 코드를 작성하고 AWS CloudFormation 템플릿을 업데이트해야 하는 경우가 있습니다. Lambda 주석은 .NET 6 Lambda 함수에 대한 이러한 부담을 덜어주는 프레임워크로, C \$1에서 Lambda 작성 경험이 더욱 자연스럽게 느껴지도록 합니다.

Lambda 주석 프레임워크를 사용할 때의 이점을 예로 들어 두 개의 숫자를 추가하는 다음 코드 조각을 고려해 보세요.

**Lambda 주석 없음**

```
public class Functions
{
    public APIGatewayProxyResponse LambdaMathPlus(APIGatewayProxyRequest request, ILambdaContext context)
    {
        if (!request.PathParameters.TryGetValue("x", out var xs))
        {
            return new APIGatewayProxyResponse
            {
                StatusCode = (int)HttpStatusCode.BadRequest
            };
        }
        if (!request.PathParameters.TryGetValue("y", out var ys))
        {
            return new APIGatewayProxyResponse
            {
                StatusCode = (int)HttpStatusCode.BadRequest
            };
        }

        var x = int.Parse(xs);
        var y = int.Parse(ys);

        return new APIGatewayProxyResponse
        {
            StatusCode = (int)HttpStatusCode.OK,
            Body = (x + y).ToString(),
            Headers = new Dictionary<string, string> { { "Content-Type", "text/plain" } }
        };
    } 
}
```

**Lambda 주석 포함**

```
public class Functions
{
    [LambdaFunction]
    [RestApi("/plus/{x}/{y}")]
    public int Plus(int x, int y)
    {
        return x + y;
    }
}
```

예제에서 볼 수 있듯이 Lambda 주석을 사용하면 특정 보일러 플레이트 코드가 필요하지 않습니다.

프레임워크 사용 방법 및 추가 정보에 대한 자세한 내용은 다음 리소스를 참조하세요.
+ Lambda 주석의 API 및 속성에 대한 설명서는 [GitHub README](https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.Annotations/README.md)를 참조하세요.
+ Lambda 주석에 대한 [블로그 게시물](https://aws.amazon.com/blogs/developer/net-lambda-annotations-framework/).
+ [https://www.nuget.org/packages/Amazon.Lambda.Annotations](https://www.nuget.org/packages/Amazon.Lambda.Annotations) NuGet 패키지.
+ GitHub의 [사진 자산 관리 프로젝트](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3/cross-service/PhotoAssetManager) 구체적으로는 프로젝트 [README](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/dotnetv3/cross-service/PhotoAssetManager/README.md)의 [PamApiAnnotations](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3/cross-service/PhotoAssetManager/PamApiAnnotations) 폴더와 Lambda 주석에 대한 참조를 참조하세요.
**참고**  
앞의 예제는의 V3에만 해당됩니다 AWS SDK for .NET. SDK의 V4(최신 버전)에서 예제를 사용하는 경우의 정보에 따라 조정해야 할 수 있습니다[버전 4로 마이그레이션](net-dg-v4.md).

# 에 대한 상위 수준 라이브러리 및 프레임워크 AWS SDK for .NET
<a name="high-level-libraries"></a>

다음 섹션에는 핵심 SDK 기능에 포함되지 않은 개요 수준 라이브러리 및 프레임워크에 대한 정보가 포함되어 있습니다. 이러한 라이브러리와 프레임워크는 핵심 SDK 기능을 사용하여 특정 작업을 용이하게 하는 기능을 만듭니다.

를 처음 사용하는 경우 먼저 [간단한 애플리케이션 생성](quick-start.md) 주제를 확인할 AWS SDK for .NET수 있습니다. SDK에 대한 소개를 제공합니다.

시작하기 전에 [환경을 설정하고](net-dg-config.md) [프로젝트를 구성](configuring-the-sdk.md)했는지 확인합니다. 또한 [SDK 사용](net-dg-sdk-features.md)의 정보를 검토합니다.

**Topics**
+ [메시지 처리 프레임워크](msg-proc-fw.md)
+ [.NET Aspire AWS 와 통합](aspire-integrations.md)

# AWS .NET용 메시지 처리 프레임워크
<a name="msg-proc-fw"></a>

.NET용 AWS 메시지 처리 프레임워크는 Amazon Simple Queue Service(SQS), Amazon Simple Notification Service(SNS) 및 Amazon EventBridge와 같은 AWS 서비스를 사용하는 .NET 메시지 처리 애플리케이션의 개발을 간소화하는 AWS네이티브 프레임워크입니다. 이 프레임워크는 개발자가 작성해야 하는 상용구 코드의 양을 줄여주므로 메시지를 게시하고 소비할 때 비즈니스 로직에 집중할 수 있습니다. 프레임워크가 개발을 간소화하는 방법에 대한 자세한 내용은 블로그 게시물 [.NET용 AWS 메시지 처리 프레임워크 소개(미리 보기)를](https://aws.amazon.com/blogs/developer/introducing-the-aws-message-processing-framework-for-net-preview/) 참조하세요. 특히 첫 번째 부분에서는 하위 수준 API 호출을 사용하는 것과 프레임워크를 사용하는 것의 차이점을 보여주는 데모를 제공합니다.

메시지 처리 프레임워크는 다음과 같은 활동 및 기능을 지원합니다.
+ SQS에 메시지를 보내고 SNS 및 EventBridge에 이벤트를 게시합니다.
+ 일반적으로 백그라운드 서비스에서 사용되는 장기 실행 폴러를 사용하여 SQS에서 메시지를 수신하고 처리합니다. 여기에는 메시지를 처리하는 동안 다른 클라이언트가 메시지를 처리하지 못하도록 제한 시간 초과를 관리하는 것이 포함됩니다.
+  AWS Lambda 함수의 메시지 처리.
+ FIFO(first-in-first-out) SQS 대기열 및 SNS 주제.
+ 로깅을 위한 OpenTelemetry.

이러한 활동 및 기능에 대한 자세한 내용은 [블로그 게시물](https://aws.amazon.com/blogs/developer/introducing-the-aws-message-processing-framework-for-net-preview/)의 **기능** 섹션과 아래에 나열된 주제를 참조하세요.

시작하기 전에 [환경을 설정하고](net-dg-config.md) [프로젝트를 구성](configuring-the-sdk.md)했는지 확인합니다. 또한 [SDK 사용](net-dg-sdk-features.md)의 정보를 검토합니다.

**추가 리소스**
+ [NuGet.org](https://www.nuget.org/)의 [https://www.nuget.org/packages/AWS.Messaging/](https://www.nuget.org/packages/AWS.Messaging/) 패키지입니다.
+ [API 참조](https://aws.github.io/aws-dotnet-messaging/)입니다.
+ 의 GitHub 리포지토리에 있는 `README` 파일 [https://github.com/aws/aws-dotnet-messaging/](https://github.com/aws/aws-dotnet-messaging/)
+ Microsoft의 [.NET 종속성 주입](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection).
+ Microsoft의 [.NET 일반 호스트](https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host).

**Topics**
+ [시작](msg-proc-fw-get-started.md)
+ [메시지 게시](msg-proc-fw-publish.md)
+ [메시지 사용](msg-proc-fw-consume.md)
+ [FIFO](msg-proc-fw-fifo.md)
+ [로깅 및 텔레메트리 열기](msg-proc-fw-telemetry.md)
+ [사용자 지정](msg-proc-fw-customize.md)
+ [보안](msg-proc-fw-security.md)

# .NET용 AWS 메시지 처리 프레임워크 시작하기
<a name="msg-proc-fw-get-started"></a>

시작하기 전에 [환경을 설정하고](net-dg-config.md) [프로젝트를 구성](configuring-the-sdk.md)했는지 확인합니다. 또한 [SDK 사용](net-dg-sdk-features.md)의 정보를 검토합니다.

이 주제에서는 메시지 처리 프레임워크 사용을 시작하는 데 도움이 되는 정보를 제공합니다. 사전 조건 및 구성 정보 외에도 일반적인 시나리오를 구현하는 방법을 보여주는 자습서가 제공됩니다.

## 사전 조건 및 구성
<a name="mpf-get-started-prereq"></a>
+ 애플리케이션에 제공하는 자격 증명에는 사용하는 메시징 서비스 및 작업에 대한 적절한 권한이 있어야 합니다. 자세한 내용은 해당 개발자 안내서의 [SQS](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-authentication-and-access-control.html), [SNS](https://docs.aws.amazon.com/sns/latest/dg/security-iam.html) 및 [EventBridge](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-iam.html)에 대한 보안 주제를 참조하세요. 또한 특정 [권한을](https://github.com/aws/aws-dotnet-messaging/blob/main/README.md#permissions) 설명하는 GitHub의 [README](https://github.com/aws/aws-dotnet-messaging/) 파일 부분을 참조하세요.
+ .NET용 AWS 메시지 처리 프레임워크를 사용하려면 프로젝트에 [https://www.nuget.org/packages/AWS.Messaging](https://www.nuget.org/packages/AWS.Messaging) NuGet 패키지를 추가해야 합니다. 예시:

  ```
  dotnet add package AWS.Messaging
  ```
+ 프레임워크는 .NET의 [종속성 주입(DI) 서비스 컨테이너](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection)와 통합됩니다. 애플리케이션을 시작하는 동안를 호출`AddAWSMessageBus`하여 프레임워크를 구성하여 DI 컨테이너에 추가할 수 있습니다.

  ```
  var builder = WebApplication.CreateBuilder(args);
  
  // Register the AWS Message Processing Framework for .NET
  builder.Services.AddAWSMessageBus(builder =>
  {
      // Register that you'll publish messages of type ChatMessage to an existing queue
      builder.AddSQSPublisher<ChatMessage>("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd");
  });
  ```

## 자습서
<a name="mpf-get-started-tutorial"></a>

이 자습서에서는 .NET용 AWS 메시지 처리 프레임워크를 사용하는 방법을 보여줍니다. API 엔드포인트에서 요청을 수신할 때 Amazon SQS 대기열로 메시지를 보내는 ASP.NET Core Minimal API와 이러한 메시지를 폴링하고 처리하는 장기 실행 콘솔 애플리케이션이라는 두 가지 애플리케이션을 생성합니다.
+ 이 자습서의 지침은 .NET CLI를 선호하지만 .NET CLI 또는 Microsoft Visual Studio와 같은 교차 플랫폼 도구를 사용하여이 자습서를 수행할 수 있습니다. 도구에 대한 자세한 내용은 단원을 참조하십시오[용 도구 체인 설치 및 구성 AWS SDK for .NET](net-dg-dev-env.md).
+ 이 자습서에서는 자격 증명에 `[default]` 프로필을 사용하고 있다고 가정합니다. 또한 Amazon SQS 메시지를 보내고 받기 위한 적절한 권한과 함께 단기 자격 증명을 사용할 수 있다고 가정합니다. 자세한 내용은 [를 AWS SDK for .NET 사용하여 인증 AWS](creds-idc.md) 및 [SQS](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-authentication-and-access-control.html) 보안 주제를 참조하세요.

**참고**  
이 자습서를 실행하면 SQS 메시징 비용이 발생할 수 있습니다.

### 단계
<a name="mpf-tutorial-steps"></a>
+ [SQS 대기열 생성](#mpf-tutorial-queue)
+ [게시 애플리케이션 생성 및 실행](#mpf-tutorial-publish)
+ [처리 애플리케이션 생성 및 실행](#mpf-tutorial-handle)
+ [정리](#mpf-tutorial-cleanup)

### SQS 대기열 생성
<a name="mpf-tutorial-queue"></a>

이 자습서에서는 메시지를 보내고 받으려면 SQS 대기열이 필요합니다. AWS CLI 또는에 대해 다음 명령 중 하나를 사용하여 대기열을 생성할 수 있습니다 AWS Tools for PowerShell. 다음 프레임워크 구성에서 지정할 수 있도록 반환되는 대기열 URL을 기록해 둡니다.

------
#### [ AWS CLI ]

```
aws sqs create-queue --queue-name DemoQueue
```

------
#### [ AWS Tools for PowerShell ]

```
New-SQSQueue -QueueName DemoQueue
```

------

### 게시 애플리케이션 생성 및 실행
<a name="mpf-tutorial-publish"></a>

다음 절차에 따라 게시 애플리케이션을 생성하고 실행합니다.

1. 명령 프롬프트 또는 터미널을 엽니다. .NET 프로젝트를 만들 수 있는 운영 체제 폴더를 찾거나 생성합니다.

1. 해당 폴더에서 다음 명령을 실행하여 .NET 프로젝트를 만듭니다.

   ```
   dotnet new webapi --name Publisher
   ```

1. 새 프로젝트의 폴더로 이동합니다. .NET용 AWS 메시지 처리 프레임워크에 대한 종속성을 추가합니다.

   ```
   cd Publisher
   dotnet add package AWS.Messaging
   ```
**참고**  
인증 AWS IAM Identity Center 에를 사용하는 경우 `AWSSDK.SSO` 및 도 추가해야 합니다`AWSSDK.SSOOIDC`.

1. 의 코드를 다음 코드`Program.cs`로 바꿉니다.

   ```
   using AWS.Messaging;
   using Microsoft.AspNetCore.Mvc;
   using Publisher;
   
   var builder = WebApplication.CreateBuilder(args);
   
   // Add services to the container.
   // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle.
   builder.Services.AddEndpointsApiExplorer();
   builder.Services.AddSwaggerGen();
   
   
   // Configure the AWS Message Processing Framework for .NET.
   builder.Services.AddAWSMessageBus(builder =>
   {
       // Check for input SQS URL.
       // The SQS URL should be passed as a command line argument or set in the Debug launch profile.
       if ((args.Length == 1) && (args[0].Contains("https://sqs.")))
       {
           // Register that you'll publish messages of type GreetingMessage:
           // 1. To a specified queue.
           // 2. Using the message identifier "greetingMessage", which will be used
           //    by handlers to route the message to the appropriate handler.
           builder.AddSQSPublisher<GreetingMessage>(args[0], "greetingMessage");
       }
       // You can map additional message types to queues or topics here as well.
   });
   var app = builder.Build();
   
   
   // Configure the HTTP request pipeline.
   if (app.Environment.IsDevelopment())
   {
       app.UseSwagger();
       app.UseSwaggerUI();
   }
   
   app.UseHttpsRedirection();
   
   // Create an API Endpoint that receives GreetingMessage objects
   // from the caller and then sends them as an SQS message.
   app.MapPost("/greeting", async ([FromServices] IMessagePublisher publisher, Publisher.GreetingMessage message) =>
       {
           return await PostGreeting(message, publisher);
       })
   .WithName("SendGreeting")
   .WithOpenApi();
   
   app.Run();
   
   public partial class Program
   {
       /// <summary>
       /// Endpoint for posting a greeting message.
       /// </summary>
       /// <param name="greetingMessage">The greeting message.</param>
       /// <param name="messagePublisher">The message publisher.</param>
       /// <returns>Async task result.</returns>
       public static async Task<IResult> PostGreeting(GreetingMessage greetingMessage,
           IMessagePublisher messagePublisher)
       {
           if (greetingMessage.SenderName == null || greetingMessage.Greeting == null)
           {
               return Results.BadRequest();
           }
   
           // Publish the message to the queue configured above.
           await messagePublisher.PublishAsync(greetingMessage);
   
           return Results.Ok();
       }
   }
   
   namespace Publisher
   {
       /// <summary>
       /// This class represents the message contents.
       /// </summary>
       public class GreetingMessage
       {
           public string? SenderName { get; set; }
           public string? Greeting { get; set; }
       }
   }
   ```

1. 다음 명령을 실행합니다. 그러면 API를 탐색하고 테스트할 수 있는 Swagger UI가 있는 브라우저 창이 열립니다.

   ```
   dotnet watch run <queue URL created earlier>
   ```

1. `/greeting` 엔드포인트를 열고 **시도를** 선택합니다.

1. 메시지의 `senderName` 및 `greeting` 값을 지정하고 **실행**을 선택합니다. 그러면 API가 호출되어 SQS 메시지가 전송됩니다.

### 처리 애플리케이션 생성 및 실행
<a name="mpf-tutorial-handle"></a>

다음 절차에 따라 처리 애플리케이션을 생성하고 실행합니다.

1. 명령 프롬프트 또는 터미널을 엽니다. .NET 프로젝트를 만들 수 있는 운영 체제 폴더를 찾거나 생성합니다.

1. 해당 폴더에서 다음 명령을 실행하여 .NET 프로젝트를 만듭니다.

   ```
   dotnet new console --name Handler
   ```

1. 새 프로젝트의 폴더로 이동합니다. .NET용 AWS 메시지 처리 프레임워크에 대한 종속성을 추가합니다. 또한 `Microsoft.Extensions.Hosting` 패키지를 추가하여 [.NET 일반 호스트](https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host)를 통해 프레임워크를 구성할 수 있습니다.

   ```
   cd Handler
   dotnet add package AWS.Messaging
   dotnet add package Microsoft.Extensions.Hosting
   ```
**참고**  
인증 AWS IAM Identity Center 에를 사용하는 경우 `AWSSDK.SSO` 및 도 추가해야 합니다`AWSSDK.SSOOIDC`.

1. 의 코드를 다음 코드`Program.cs`로 바꿉니다.

   ```
   using AWS.Messaging;
   using Handler;
   using Microsoft.Extensions.DependencyInjection;
   using Microsoft.Extensions.Hosting;
   
   var builder = Host.CreateDefaultBuilder(args);
   
   builder.ConfigureServices(services =>
   {
       // Register the AWS Message Processing Framework for .NET.
       services.AddAWSMessageBus(builder =>
       {
           // Check for input SQS URL.
           // The SQS URL should be passed as a command line argument or set in the Debug launch profile.
           if ((args.Length == 1) && (args[0].Contains("https://sqs.")))
           {
               // Register you'll poll the following queue.
               builder.AddSQSPoller(args[0]);
   
               // And that messages of type "greetingMessage" should be:
               // 1. Deserialized as GreetingMessage objects.
               // 2. Which are then passed to GreetingMessageHandler.
               builder.AddMessageHandler<GreetingMessageHandler, GreetingMessage>("greetingMessage");
   
           }
           // You can add additional message handlers here, using different message types. 
       });
   });
   
   var host = builder.Build();
   await host.RunAsync();
   
   namespace Handler
   {
       /// <summary>
       /// This class represents the message contents.
       /// </summary>
       public class GreetingMessage
       {
           public string? SenderName { get; set; }
           public string? Greeting { get; set; }
       }
   
       /// <summary>
       /// This handler is invoked each time you receive the message.
       /// </summary>
       public class GreetingMessageHandler : IMessageHandler<GreetingMessage>
       {
           public Task<MessageProcessStatus> HandleAsync(
               MessageEnvelope<GreetingMessage> messageEnvelope,
               CancellationToken token = default)
           {
               Console.WriteLine(
                   $"Received message {messageEnvelope.Message.Greeting} from {messageEnvelope.Message.SenderName}");
               return Task.FromResult(MessageProcessStatus.Success());
           }
       }
   }
   ```

1. 다음 명령을 실행합니다. 이렇게 하면 장기 실행 폴러가 시작됩니다.

   ```
   dotnet run <queue URL created earlier>
   ```

   시작 직후 애플리케이션은이 자습서의 첫 부분에서 전송된 메시지를 수신하고 다음 메시지를 기록합니다.

   ```
   Received message {greeting} from {senderName}
   ```

1. 폴러를 중지`Ctrl+C`하려면를 누릅니다.

### 정리
<a name="mpf-tutorial-cleanup"></a>

 AWS CLI 또는에 대해 다음 명령 중 하나를 사용하여 대기열 AWS Tools for PowerShell 을 삭제합니다.

------
#### [ AWS CLI ]

```
aws sqs delete-queue --queue-url "<queue URL created earlier>"
```

------
#### [ AWS Tools for PowerShell ]

```
Remove-SQSQueue -QueueUrl "<queue URL created earlier>"
```

------

# .NET용 AWS 메시지 처리 프레임워크를 사용하여 메시지 게시
<a name="msg-proc-fw-publish"></a>

.NET용 AWS 메시지 처리 프레임워크는 하나 이상의 메시지 유형을 게시하거나, 하나 이상의 메시지 유형을 처리하거나, 동일한 애플리케이션에서 둘 다 수행할 수 있도록 지원합니다.

다음 코드는 다양한 메시지 유형을 다양한 AWS 서비스에 게시하는 애플리케이션의 구성을 보여줍니다.

```
var builder = WebApplication.CreateBuilder(args);

// Register the AWS Message Processing Framework for .NET
builder.Services.AddAWSMessageBus(builder =>
{
    // Register that you'll send messages of type ChatMessage to an existing queue
    builder.AddSQSPublisher<ChatMessage>("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd");

    // Register that you'll publish messages of type OrderInfo to an existing SNS topic
    builder.AddSNSPublisher<OrderInfo>("arn:aws:sns:us-west-2:012345678910:MyAppProd");

    // Register that you'll publish messages of type FoodItem to an existing EventBridge bus
    builder.AddEventBridgePublisher<FoodItem>("arn:aws:events:us-west-2:012345678910:event-bus/default");
});
```

시작 중에 프레임워크를 등록했으면 일반를 코드`IMessagePublisher`에 주입합니다. `PublishAsync` 메서드를 호출하여 위에 구성된 메시지 유형을 게시합니다. 일반 게시자는 유형에 따라 메시지를 라우팅할 대상을 결정합니다.

 다음 예제에서 ASP.NET MVC 컨트롤러는 사용자로부터 `ChatMessage` 메시지와 `OrderInfo` 이벤트를 모두 수신한 다음 각각 Amazon SQS 및 Amazon SNS에 게시합니다. 두 메시지 유형 모두 위에 구성된 일반 게시자를 사용하여 게시할 수 있습니다.

```
[ApiController]
[Route("[controller]")]
public class PublisherController : ControllerBase
{
    private readonly IMessagePublisher _messagePublisher;

    public PublisherController(IMessagePublisher messagePublisher)
    {
        _messagePublisher = messagePublisher;
    }

    [HttpPost("chatmessage", Name = "Chat Message")]
    public async Task<IActionResult> PublishChatMessage([FromBody] ChatMessage message)
    {
        // Perform business and validation logic on the ChatMessage here.
        if (message == null)
        {
            return BadRequest("A chat message was not submitted. Unable to forward to the message queue.");
        }
        if (string.IsNullOrEmpty(message.MessageDescription))
        {
            return BadRequest("The MessageDescription cannot be null or empty.");
        }

        // Send the ChatMessage to SQS, using the generic publisher.
        await _messagePublisher.PublishAsync(message);

        return Ok();
    }

    [HttpPost("order", Name = "Order")]
    public async Task<IActionResult> PublishOrder([FromBody] OrderInfo message)
    {
        if (message == null)
        {
            return BadRequest("An order was not submitted.");
        }

        // Publish the OrderInfo to SNS, using the generic publisher.
        await _messagePublisher.PublishAsync(message);

        return Ok();
    }
}
```

적절한 처리 로직으로 메시지를 라우팅하기 위해 프레임워크는 *메시지 유형 식별자*라는 메타데이터를 사용합니다. 기본적으로 이는 어셈블리 이름을 포함하여 메시지의 .NET 유형의 전체 이름입니다. 메시지를 보내고 처리하는 경우 프로젝트 간에 메시지 객체의 정의를 공유하는 경우이 메커니즘이 잘 작동합니다. 그러나 메시지가 다른 네임스페이스에서 재정의되거나 다른 프레임워크 또는 프로그래밍 언어와 메시지를 교환하는 경우 메시지 유형 식별자를 재정의해야 할 수 있습니다.

```
var builder = Host.CreateDefaultBuilder(args);

builder.ConfigureServices(services =>
{
    // Register the AWS Message Processing Framework for .NET
    services.AddAWSMessageBus(builder =>
    {
        // Register that you'll publish messages of type GreetingMessage to an existing queue
        builder.AddSQSPublisher<GreetingMessage>("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd", "greetingMessage");
    });
});
```

## 서비스별 게시자
<a name="service-specific-publishers"></a>

위에 표시된 예제에서는 구성된 메시지 유형에 따라 지원되는 모든 AWS 서비스에 게시할 수 `IMessagePublisher`있는 일반를 사용합니다. 또한 프레임워크는 Amazon SQS, Amazon SNS 및 Amazon EventBridge에 대한 서비스별 게시자를 제공합니다. 이러한 특정 게시자는 해당 서비스에만 적용되는 옵션을 노출하며, `ISQSPublisher`, `ISNSPublisher`및 유형을 사용하여 삽입할 수 있습니다`IEventBridgePublisher`.

예를 들어 SQS FIFO 대기열로 메시지를 전송할 때는 적절한 [메시지 그룹 ID](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/FIFO-key-terms.html)를 설정해야 합니다. 다음 코드는 `ChatMessage` 예제를 다시 보여 주지만 이제 `ISQSPublisher`를 사용하여 SQS별 옵션을 설정합니다.

```
public class PublisherController : ControllerBase
{
    private readonly ISQSPublisher _sqsPublisher;

    public PublisherController(ISQSPublisher sqsPublisher)
    {
        _sqsPublisher = sqsPublisher;
    }

    [HttpPost("chatmessage", Name = "Chat Message")]
    public async Task<IActionResult> PublishChatMessage([FromBody] ChatMessage message)
    {
        // Perform business and validation logic on the ChatMessage here
        if (message == null)
        {
            return BadRequest("A chat message was not submitted. Unable to forward to the message queue.");
        }
        if (string.IsNullOrEmpty(message.MessageDescription))
        {
            return BadRequest("The MessageDescription cannot be null or empty.");
        }

        // Send the ChatMessage to SQS using the injected ISQSPublisher, with SQS-specific options
        await _sqsPublisher.SendAsync(message, new SQSOptions
        {
            DelaySeconds = <delay-in-seconds>,
            MessageAttributes = <message-attributes>,
            MessageDeduplicationId = <message-deduplication-id>,
            MessageGroupId = <message-group-id>
        });

        return Ok();
    }
}
```

SNS 및 EventBridge에 대해 `IEventBridgePublisher` 각각 `ISNSPublisher` 및를 사용하여 동일한 작업을 수행할 수 있습니다.

```
await _snsPublisher.PublishAsync(message, new SNSOptions
{
    Subject = <subject>,
    MessageAttributes = <message-attributes>,
    MessageDeduplicationId = <message-deduplication-id>,
    MessageGroupId = <message-group-id>
});
```

```
await _eventBridgePublisher.PublishAsync(message, new EventBridgeOptions
{
    DetailType = <detail-type>,
    Resources = <resources>,
    Source = <source>,
    Time = <time>,
    TraceHeader = <trace-header>
});
```

기본적으로 지정된 유형의 메시지는 미리 구성된 대상으로 전송됩니다. 그러나 메시지별 게시자를 사용하여 단일 메시지의 대상을 재정의할 수 있습니다. 메시지를 게시하는 데 사용되는 기본 AWS SDK for .NET 클라이언트를 재정의할 수도 있습니다. 이는 대상에 따라 역할 또는 자격 증명을 변경해야 하는 다중 테넌트 애플리케이션에서 유용할 수 있습니다.

```
await _sqsPublisher.SendAsync(message, new SQSOptions
{
    OverrideClient = <override IAmazonSQS client>,
    QueueUrl = <override queue URL>
});
```

# .NET용 메시지 처리 프레임워크를 사용하여 AWS 메시지 사용
<a name="msg-proc-fw-consume"></a>

.NET용 AWS 메시지 처리 프레임워크를 사용하면 프레임워크 또는 메시징 서비스 중 하나를 사용하여 [게시](msg-proc-fw-publish.md)된 메시지를 사용할 수 있습니다. 메시지는 다양한 방식으로 사용할 수 있으며, 그 중 일부는 아래에 설명되어 있습니다.

## 메시지 핸들러
<a name="handle-a-message"></a>

메시지를 사용하려면 처리하려는 각 메시지 유형에 대한 `IMessageHandler` 인터페이스를 사용하여 메시지 핸들러를 구현합니다. 메시지 유형과 메시지 핸들러 간의 매핑은 프로젝트 시작 시 구성됩니다.

```
await Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        // Register the AWS Message Processing Framework for .NET
        services.AddAWSMessageBus(builder =>
        {
            // Register an SQS Queue that the framework will poll for messages.
            // NOTE: The URL given below is an example. Use the appropriate URL for your SQS Queue.
            builder.AddSQSPoller("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd");

            // Register all IMessageHandler implementations with the message type they should process. 
            // Here messages that match our ChatMessage .NET type will be handled by our ChatMessageHandler
            builder.AddMessageHandler<ChatMessageHandler, ChatMessage>();
        });
    })
    .Build()
    .RunAsync();
```

다음 코드는 메시지에 대한 샘플 `ChatMessage` 메시지 핸들러를 보여줍니다.

```
public class ChatMessageHandler : IMessageHandler<ChatMessage>
{
    public Task<MessageProcessStatus> HandleAsync(MessageEnvelope<ChatMessage> messageEnvelope, CancellationToken token = default)
    {
        // Add business and validation logic here.
        if (messageEnvelope == null)
        {
            return Task.FromResult(MessageProcessStatus.Failed());
        }

        if (messageEnvelope.Message == null)
        {
            return Task.FromResult(MessageProcessStatus.Failed());
        }

        ChatMessage message = messageEnvelope.Message;

        Console.WriteLine($"Message Description: {message.MessageDescription}");

        // Return success so the framework will delete the message from the queue.
        return Task.FromResult(MessageProcessStatus.Success());
    }
}
```

외부에는 프레임워크에서 사용하는 메타데이터가 `MessageEnvelope` 포함되어 있습니다. `message` 속성은 메시지 유형입니다(이 경우 `ChatMessage`).

`MessageProcessStatus.Success()` 로 돌아가 메시지가 성공적으로 처리되었으며 프레임워크가 Amazon SQS 대기열에서 메시지를 삭제함을 나타낼 수 있습니다. `MessageProcessStatus.Failed()`를 반환할 때 메시지는 다시 처리하거나 구성된 경우 배달 [못한 편지](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html) 대기열로 이동할 수 있는 대기열에 남아 있습니다.

## 장기 실행 프로세스에서 메시지 처리
<a name="long-running-process"></a>

SQS 대기열 URL`AddSQSPoller`로를 호출하여 대기열을 [https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.backgroundservice](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.backgroundservice) 지속적으로 폴링하고 메시지를 처리하는 장기 실행을 시작할 수 있습니다.

```
await Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        // Register the AWS Message Processing Framework for .NET
        services.AddAWSMessageBus(builder =>
        {
            // Register an SQS Queue that the framework will poll for messages.
            // NOTE: The URL given below is an example. Use the appropriate URL for your SQS Queue.
            builder.AddSQSPoller("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd", options => 
            {
                // The maximum number of messages from this queue that the framework will process concurrently on this client.
                options.MaxNumberOfConcurrentMessages = 10;

                // The duration each call to SQS will wait for new messages.
                options.WaitTimeSeconds = 20; 
            });

            // Register all IMessageHandler implementations with the message type they should process.
            builder.AddMessageHandler<ChatMessageHandler, ChatMessage>();
        });
    })
    .Build()
    .RunAsync();
```

### SQS 메시지 폴러 구성
<a name="config-msg-poller"></a>

를 호출할 `SQSMessagePollerOptions` 때에서 SQS 메시지 폴러를 구성할 수 있습니다`AddSQSPoller`.
+ `MaxNumberOfConcurrentMessages` - 동시에 처리할 대기열의 최대 메시지 수입니다. 기본값은 10입니다.
+ `WaitTimeSeconds` - `ReceiveMessage` SQS 호출이 반환되기 전에 대기열에 메시지가 도착할 때까지 기다리는 기간(초)입니다. 메시지를 사용할 수 있는 경우 호출은 보다 빨리 반환됩니다`WaitTimeSeconds`. 기본값은 20입니다.

**메시지 가시성 제한 시간 처리**

SQS 메시지에는 [표시 제한](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html) 시간이 있습니다. 한 소비자가 지정된 메시지를 처리하기 시작하면 대기열에 남아 있지만 두 번 이상 처리하지 않도록 다른 소비자에게 숨겨집니다. 메시지가 다시 표시되기 전에 처리 및 삭제되지 않으면 다른 소비자가 동일한 메시지를 처리하려고 할 수 있습니다.

프레임워크는 현재 처리 중인 메시지의 제한 시간 초과를 추적하고 연장하려고 시도합니다. 를 호출할 `SQSMessagePollerOptions` 때에서이 동작을 구성할 수 있습니다`AddSQSPoller`.
+ `VisibilityTimeout` - 수신된 메시지가 후속 검색 요청에서 숨겨지는 초 단위 기간입니다. 기본값은 30입니다.
+ `VisibilityTimeoutExtensionThreshold` - 메시지의 제한 시간 초과가 만료된 후이 몇 초 내에 있는 경우 프레임워크는 제한 시간 초과를 (`VisibilityTimeout`초 더) 연장합니다. 기본값은 5입니다.
+ `VisibilityTimeoutExtensionHeartbeatInterval` - 프레임워크가 만료 후 몇 초 이내에 메시지를 확인한 다음 표시 제한 시간을 연장하는 `VisibilityTimeoutExtensionThreshold` 초 단위의 빈도입니다. 기본값은 1입니다.

 다음 예제에서 프레임워크는 1초마다 처리 중인 메시지를 확인합니다. 이러한 메시지가 다시 표시된 후 5초 이내에 표시되는 경우 프레임워크는 각 메시지의 표시 제한 시간을 자동으로 30초 더 연장합니다.

```
// NOTE: The URL given below is an example. Use the appropriate URL for your SQS Queue.
builder.AddSQSPoller("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd", options => 
{
    options.VisibilityTimeout = 30;
    options.VisibilityTimeoutExtensionThreshold = 5;
    VisibilityTimeoutExtensionHeartbeatInterval = 1;
});
```

## AWS Lambda 함수의 메시지 처리
<a name="lambda-functions"></a>

SQS와 Lambda의 통합과 함께 .NET용 AWS 메시지 처리 프레임워크를 사용할 수 있습니다. [https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html) 이는 `AWS.Messaging.Lambda` 패키지에서 제공합니다. 시작하려면 [README](https://github.com/aws/aws-dotnet-messaging/blob/main/src/AWS.Messaging.Lambda/README.md)를 참조하세요.

# .NET용 AWS 메시지 처리 프레임워크에서 FIFO 사용
<a name="msg-proc-fw-fifo"></a>

메시지 순서 지정 및 메시지 중복 제거가 중요한 사용 사례의 경우 .NET용 AWS 메시지 처리 프레임워크는 first-in-first-out(FIFO) [Amazon SQS 대기열](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-fifo-queues.html) 및 [Amazon SNS 주제를](https://docs.aws.amazon.com/sns/latest/dg/sns-fifo-topics.html) 지원합니다.

## 게시
<a name="mpf-fifo-publish"></a>

메시지를 FIFO 대기열 또는 주제에 게시할 때는 메시지가 속한 그룹을 지정하는 메시지 그룹 ID를 설정해야 합니다. 그룹 내의 메시지는 순서대로 처리됩니다. SQS별 및 SNS별 메시지 게시자에서 이를 설정할 수 있습니다.

```
await _sqsPublisher.PublishAsync(message, new SQSOptions
{
    MessageDeduplicationId = <message-deduplication-id>,
    MessageGroupId = <message-group-id>
});
```

## 구독
<a name="mpf-fifo-subscribe"></a>

FIFO 대기열의 메시지를 처리할 때 프레임워크는 각 `ReceiveMessages` 호출에 대해 수신된 순서대로 지정된 메시지 그룹 내의 메시지를 처리합니다. 로 끝나는 대기열로 구성된 경우 프레임워크는이 작업 모드로 자동으로 전환됩니다`.fifo`.

```
await Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        // Register the AWS Message Processing Framework for .NET.
        services.AddAWSMessageBus(builder =>
        {
            // Because this is a FIFO queue, the framework automatically handles these messages in order.
            builder.AddSQSPoller("https://sqs.us-west-2.amazonaws.com/012345678910/MPF.fifo");
            builder.AddMessageHandler<OrderMessageHandler, OrderMessage>();
        });
    })
    .Build()
    .RunAsync();
```

# .NET용 AWS 메시지 처리 프레임워크에 대한 로깅 및 개방형 텔레메트리
<a name="msg-proc-fw-telemetry"></a>

.NET용 AWS 메시지 처리 프레임워크는 OpenTelemetry가 프레임워크에서 게시하거나 처리하는 각 메시지에 대한 [추적](https://opentelemetry.io/docs/concepts/signals/traces/)을 로깅하도록 계측됩니다. 이는 [https://www.nuget.org/packages/AWS.Messaging.Telemetry.OpenTelemetry](https://www.nuget.org/packages/AWS.Messaging.Telemetry.OpenTelemetry) 패키지에서 제공합니다. 시작하려면 [README](https://github.com/aws/aws-dotnet-messaging/blob/main/src/AWS.Messaging.Telemetry.OpenTelemetry/README.md)를 참조하세요.

**참고**  
로깅과 관련된 보안 정보는 섹션을 참조하세요[.NET용 AWS 메시지 처리 프레임워크의 보안](msg-proc-fw-security.md).

# .NET용 AWS 메시지 처리 프레임워크 사용자 지정
<a name="msg-proc-fw-customize"></a>

.NET용 AWS 메시지 처리 프레임워크는 세 가지 "계층"으로 메시지를 빌드, 전송 및 처리합니다.

1. 가장 바깥쪽 계층에서 프레임워크는 서비스와 관련된 AWS네이티브 요청 또는 응답을 빌드합니다. 예를 들어 Amazon SQS를 사용하면 [https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html) 요청을 빌드하고 서비스에서 정의한 [https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_Message.html](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_Message.html) 객체와 함께 작동합니다.

1. SQS 요청 및 응답 내에서 프레임워크는 `MessageBody` 요소(또는 Amazon SNS`Message`의 경우 또는 Amazon EventBridge의 `Detail` 경우)를 [JSON 형식의 CloudEvent](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/formats/json-format.md)로 설정합니다. 여기에는 메시지를 처리할 때 `MessageEnvelope` 객체에서 액세스할 수 있는 프레임워크에서 설정한 메타데이터가 포함됩니다.

1. 가장 안쪽 계층에서 CloudEvent JSON 객체 내부의 `data` 속성에는 메시지로 전송되거나 수신된 .NET 객체의 JSON 직렬화가 포함됩니다.

   ```
   {
       "id":"b02f156b-0f02-48cf-ae54-4fbbe05cffba",
       "source":"/aws/messaging",
       "specversion":"1.0",
       "type":"Publisher.Models.ChatMessage",
       "time":"2023-11-21T16:36:02.8957126+00:00",
       "data":"<the ChatMessage object serialized as JSON>"
   }
   ```

메시지 봉투를 구성하고 읽는 방법을 사용자 지정할 수 있습니다.
+ `"id"`는 메시지를 고유하게 식별합니다. 기본적으로 새 GUID로 설정되지만 자체 GUID를 구현`IMessageIdGenerator`하고 이를 DI 컨테이너에 주입하여 재정의할 수 있습니다.
+ `"type"`는 메시지가 핸들러로 라우팅되는 방식을 제어합니다. 기본적으로 메시지에 해당하는 .NET 유형의 전체 이름을 사용합니다. `AddSQSPublisher`, `AddSNSPublisher`또는를 통해 메시지 유형을 대상에 매핑할 때 `messageTypeIdentifier` 파라미터를 통해 이를 재정의할 수 있습니다`AddEventBridgePublisher`.
+ `"source"`는 메시지를 전송한 시스템 또는 서버를 나타냅니다.
  + 이는에서 게시하는 경우 함수 이름 AWS Lambda, Amazon ECS에서 게시하는 경우 클러스터 이름 및 작업 ARN, Amazon EC2에서 게시하는 경우 인스턴스 ID, 그렇지 않으면 폴백 값 입니다`/aws/messaging`.
  + 를 통해 `AddMessageSource` 또는 `AddMessageSourceSuffix`에서 이를 재정의할 수 있습니다`MessageBusBuilder`.
+ `"time"`를 UTC의 현재 DateTime으로 설정합니다. 이는 자체를 구현`IDateTimeHandler`하고 이를 DI 컨테이너에 주입하여 재정의할 수 있습니다.
+ `"data"` 에는 메시지로 전송되거나 수신된 .NET 객체의 JSON 표현이 포함되어 있습니다.
  + `ConfigureSerializationOptions`의를 `MessageBusBuilder` 사용하면 메시지를 직렬화 및 역직렬화할 때 사용할를 구성할 [https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions](https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions) 수 있습니다.
  + 프레임워크가 빌드되면 추가 속성을 주입하거나 메시지 봉투를 변환하려면 `AddSerializationCallback`의를 통해 해당 속성을 구현`ISerializationCallback`하고 등록할 수 있습니다`MessageBusBuilder`.

# .NET용 AWS 메시지 처리 프레임워크의 보안
<a name="msg-proc-fw-security"></a>

.NET용 AWS 메시지 처리 프레임워크는와 통신하기 AWS SDK for .NET 위해에 의존합니다 AWS. 의 보안에 대한 자세한 내용은 단원을 AWS SDK for .NET참조하십시오[이 AWS 제품 또는 서비스에 대한 보안](security.md).

보안을 위해 프레임워크는 사용자가 보낸 데이터 메시지를 로깅하지 않습니다. 디버깅을 위해이 기능을 활성화하려면 다음과 같이 메시지 버스`EnableDataMessageLogging()`에서를 호출해야 합니다.

```
builder.Services.AddAWSMessageBus(bus =>
{
    builder.EnableDataMessageLogging();
});
```

잠재적 보안 문제가 발견되면 [보안 정책](https://github.com/aws/aws-dotnet-messaging/security/policy)에서 보고 정보를 참조하세요.

# 에서 .NET Aspire AWS 와 통합 AWS SDK for .NET
<a name="aspire-integrations"></a>

.NET Aspire는 클라우드 지원 애플리케이션을 구축하는 새로운 방법입니다. 특히 분산 애플리케이션의 구성 요소를 실행, 연결 및 디버깅할 로컬 환경에 대한 오케스트레이션을 제공합니다. 클라우드 지원 애플리케이션의 내부 개발 루프를 개선하기 위해 .NET 애플리케이션을 AWS 리소스에 연결하기 위해 .NET Aspire와의 통합이 생성되었습니다. 이러한 통합은 [Aspire.Hosting.AWS](https://www.nuget.org/packages/Aspire.Hosting.AWS) NuGet 패키지를 통해 사용할 수 있습니다.

다음과 같은 .NET Aspire 통합을 사용할 수 있습니다.
+ 를 통해 AWS 리소스를 프로비저닝할 수 있는 기능입니다[CloudFormation](https://aws.amazon.com/cloudformation/). 이 통합은 .NET Aspire AppHost 프로젝트 내에서 활용됩니다.

  자세한 내용은 블로그 게시물 [Integrating AWS with .NET Aspire](https://aws.amazon.com/blogs/developer/integrating-aws-with-net-aspire/)를 참조하세요.
+ 를 설치, 구성 및 [Amazon DynamoDB 로컬](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocalHistory.html) AWS SDK for .NET 에 연결합니다. 이 통합은 .NET Aspire AppHost 프로젝트 내에서 활용됩니다.

  자세한 내용은 블로그 게시물 [Integrating AWS with .NET Aspire](https://aws.amazon.com/blogs/developer/integrating-aws-with-net-aspire/)를 참조하세요.
+ [AWS Lambda](https://aws.amazon.com/lambda/) 함수에 대한 로컬 개발 환경을 활성화합니다. 이 통합은 .NET Aspire AppHost 프로젝트 내에서 활용됩니다.

  자세한 내용은 블로그 게시물 [Building and Debugging .NET Lambda applications with .NET Aspire (Part 1)](https://aws.amazon.com/blogs/developer/building-lambda-with-aspire-part-1/) 및 [Building and Debugging .NET Lambda applications with .NET Aspire (Part 2)](https://aws.amazon.com/blogs/developer/building-lambda-with-aspire-part-2/)를 참조하세요.
**참고**  
이 시험판 설명서는 프리뷰 릴리즈의 기능에 관한 것입니다. 내용은 변경될 수 있습니다.

  이 기능은 미리 보기이므로 미리 보기 기능을 옵트인해야 합니다. 이 미리 보기 기능과 옵트인 방법에 대한 자세한 내용은 [ GitHub의 개발 트래커 문제를 참조하세요](https://github.com/aws/integrations-on-dotnet-aspire-for-aws/issues/17).

## 추가 정보
<a name="aspire-integrations-additional"></a>

[Aspire.Hosting.AWS](https://www.nuget.org/packages/Aspire.Hosting.AWS)에서 사용할 수 있는 통합을 사용하는 방법에 대한 추가 정보와 세부 정보는 다음 리소스를 참조하세요.
+ 블로그 게시물 [Integrating AWS with .NET Aspire](https://aws.amazon.com/blogs/developer/integrating-aws-with-net-aspire/).
+ 블로그 게시물 [Building and Debugging .NET Lambda applications with .NET Aspire(1부)](https://aws.amazon.com/blogs/developer/building-lambda-with-aspire-part-1/) 및 [Building and Debugging .NET Lambda applications with .NET Aspire(2부)](https://aws.amazon.com/blogs/developer/building-lambda-with-aspire-part-2/)
+ GitHub의 [integrations-on-dotnet-aspire-for-aws](https://github.com/aws/integrations-on-dotnet-aspire-for-aws) 리포지토리입니다.
+ [Aspire.Hosting.AWS](https://www.nuget.org/packages/Aspire.Hosting.AWS) NuGet 패키지에 대한 세부 [README](https://github.com/aws/integrations-on-dotnet-aspire-for-aws/blob/main/src/Aspire.Hosting.AWS/README.md)입니다.

# 기타 AWS 서비스 및 구성 지원
<a name="other-apis-intro"></a>

는 이전 단원에서 설명한 서비스 외에도 AWS 서비스를 AWS SDK for .NET 지원합니다. 지원되는 모든 서비스의 API에 대한 자세한 내용은 [AWS SDK for .NET API 참조](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/)를 참조하세요.

는 개별 AWS 서비스의 네임스페이스 외에도 다음 APIs AWS SDK for .NET 도 제공합니다.


****  

| 영역 | 설명 | 리소스 | 
| --- | --- | --- | 
|  AWS 지원  |   AWS 지원 사례 및 Trusted Advisor 기능에 대한 프로그래밍 방식 액세스.  |  [Amazon.AWSSupport](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/AWSSupport/NAWSSupport.html) 및 [Amazon.AWSSupport.Model](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/AWSSupport/NAWSSupportModel.html)을 참조하십시오.  | 
|  일반  |  헬퍼 클래스 및 열거입니다.  |  [Amazon](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/Amazon/N.html) 및 [Amazon.Util](https://docs.aws.amazon.com/sdkfornet/v4/apidocs/items/Util/NUtil.html)을 참조하십시오.  | 