

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

# 사용 AWS SDK for Java 2.x
<a name="using"></a>

이 장에서는를 AWS SDK for Java 2.x 효과적으로 사용하는 방법을 보여줍니다. 서비스 클라이언트를 만들고, 요청을 만들고, 응답을 처리하고, 오류를 관리하는 방법을 알아봅니다. 이 장에서는 동기식 및 비동기 프로그래밍, 페이지 매김 결과, 리소스 모니터링을 위한 웨이터 및 성능 최적화를 다룹니다.

클라이언트 재사용, 문제 해결 가이드, Lambda 시작 최적화, HTTP/2 지원 및 DNS 구성에 대한 모범 사례도 확인할 수 있습니다.

**Contents**
+ [를 사용하여 AWS 서비스 요청 AWS SDK for Java 2.x](work-witih-clients.md)
  + [서비스 클라이언트를 사용하여 요청 만들기](work-witih-clients.md#using-service-client)
    + [서비스 클라이언트 생성](work-witih-clients.md#work-with-clients-create)
    + [기본 클라이언트 구성](work-witih-clients.md#using-default-client)
    + [서비스 클라이언트 구성](work-witih-clients.md#using-configure-service-clients)
    + [서비스 클라이언트 닫기](work-witih-clients.md#using-closing-client)
  + [요청을 생성](work-witih-clients.md#using-making-requests)
    + [요청을 사용하여 클라이언트 구성 재정의](work-witih-clients.md#using-override-client-config-request)
  + [응답 처리](work-witih-clients.md#using-handling-responses)
+ [AWS SDK for Java 2.x를 사용한 비동기식 프로그래밍 작업](asynchronous.md)
  + [비동기식 클라이언트 API 사용](asynchronous.md#basics-async-non-streaming)
  + [비동기식 메서드에서 스트리밍 처리](asynchronous.md#basics-async-streaming)
  + [고급 비동기 옵션 구성](asynchronous.md#advanced-operations)
+ [사용 모범 사례 AWS SDK for Java 2.x](best-practices.md)
  + [API 제한 시간을 구성하여 중단 요청 방지](best-practices.md#bestpractice5)
  + [서비스 클라이언트를 재사용하여 성능 향상](best-practices.md#bestpractice1)
  + [미사용 서비스 클라이언트를 닫아 리소스 누수 방지](best-practices.md#bestpractice-close-client)
  + [입력 스트림을 닫아 연결 풀 소진 방지](best-practices.md#bestpractice2)
  + [애플리케이션 워크로드에 대한 HTTP 성능 최적화](best-practices.md#bestpractice3)
  + [비동기식 클라이언트용 OpenSSL로 SSL 성능 개선](best-practices.md#bestpractice4)
  + [SDK 지표를 사용하여 애플리케이션 성능 모니터링](best-practices.md#bestpractice6)
+ [의 오류 처리 AWS SDK for Java 2.x](handling-exceptions.md)
  + [확인되지 않은 예외가 발생하는 이유](handling-exceptions.md#why-unchecked-exceptions)
  + [AwsServiceException(및 하위 클래스)](handling-exceptions.md#sdkserviceexception-and-subclasses)
  + [SdkClientException](handling-exceptions.md#sdkclientexception)
  + [예외 및 재시도 동작](handling-exceptions.md#retried-exceptions)
+ [AWS SDK for Java 2.x에서 페이지가 매겨진 결과 사용](pagination.md)
  + [동기식 페이지 매김](pagination.md#synchronous-pagination)
    + [페이지 반복](pagination.md#iterate-pages)
    + [객체 반복](pagination.md#iterate-objects)
      + [스트림 사용](pagination.md#use-a-stream)
      + [for-each 루프를 사용](pagination.md#for-loop)
    + [수동 페이지 매김](pagination.md#manual-pagination)
  + [비동기 페이지 매김](pagination.md#asynchronous-pagination)
    + [테이블 이름 페이지 반복](pagination.md#iterate-pages-async)
      + [`Subscriber` 사용](pagination.md#use-a-subscriber)
      + [`Consumer` 사용](pagination.md#id1pagination)
    + [테이블 이름 반복](pagination.md#iterate-objects-async)
      + [`Subscriber` 사용](pagination.md#id2)
      + [`Consumer` 사용](pagination.md#for-loop-async)
    + [타사 라이브러리 사용](pagination.md#use-third-party-library)
+ [에서 웨이터 사용 AWS SDK for Java 2.x](waiters.md)
  + [사전 조건](waiters.md#prerequisiteswaiters)
  + [웨이터 사용](waiters.md#id1waiters)
    + [동기 프로그래밍](waiters.md#synchronous-programming)
    + [비동기 프로그래밍](waiters.md#asynchronous-programming)
  + [웨이터 설정](waiters.md#configuring-waiters)
    + [웨이터 구성](waiters.md#configure-a-waiter)
    + [특정 요청에 대한 구성 재정의](waiters.md#override-configuration-for-a-specific-request)
  + [코드 예제](waiters.md#code-examples)
+ [문제 해결 FAQ](troubleshooting.md)
  + [‘`java.net.SocketException`: 연결 재설정’ 또는 ‘서버가 응답을 완료하지 못함’ 오류 해결 방법](troubleshooting.md#faq-socketexception)
  + [‘연결 제한 시간’을 수정하는 방법](troubleshooting.md#faq-connection-timeout)
  + [‘`java.net.SocketTimeoutException`: 읽기 제한 시간’을 수정하는 방법](troubleshooting.md#faq-socket-timeout)
  + [‘HTTP 요청을 실행할 수 없음: 풀에서 연결을 기다리는 제한 시간’ 오류를 해결하는 방법](troubleshooting.md#faq-pool-timeout)
  + [`NoClassDefFoundError`, `NoSuchMethodError` 또는 `NoSuchFieldError`를 수정하는 방법](troubleshooting.md#faq-classpath-errors)
  + [‘`SignatureDoesNotMatch`’ 오류 또는 ‘계산한 요청 서명이 제공한 서명과 일치하지 않음’ 오류를 해결하는 방법](troubleshooting.md#faq-signature-does-not-match-error)
  + [‘`java.lang.IllegalStateException`: 연결 풀 종료’ 오류를 해결하는 방법](troubleshooting.md#faq-connection-pool-shutdown-exception)
  + [‘AwsCredentialsProviderChain 체인의 공급자로부터 자격 증명을 로드할 수 없음’ 오류를 수정하는 방법](troubleshooting.md#faq-credentials-provider-chain)
    + [일반적인 원인 및 솔루션](troubleshooting.md#faq-cred-provider-chain-common-causes-and-solutions)
      + [자격 증명 구성 검토](troubleshooting.md#faq-cred-provider-chain-check-config)
        + [Amazon EC2 인스턴스의 경우](troubleshooting.md#faq-cred-check-ec2)
        + [컨테이너 환경의 경우](troubleshooting.md#faq-cred-check-container-env)
        + [로컬 개발의 경우](troubleshooting.md#faq-cred-check-local-dev)
        + [웹 ID 페더레이션의 경우](troubleshooting.md#faq-cred-check-web-id-federation)
      + [네트워크 또는 프록시 연결 문제](troubleshooting.md#faq-credentials-provider-chain-network-issues)
+ [의 SDK 시작 시간 단축 AWS Lambda](lambda-optimize-starttime.md)
  + [AWS CRT 기반 HTTP 클라이언트 사용](lambda-optimize-starttime.md#lambda-quick-url)
  + [사용하지 않는 HTTP 클라이언트 종속성을 제거](lambda-optimize-starttime.md#lambda-quick-remove-deps)
  + [바로가기 검색이 가능하도록 서비스 클라이언트를 구성](lambda-optimize-starttime.md#lambda-quick-clients)
  + [Lambda 함수 핸들러 외부에서 SDK 클라이언트 초기화](lambda-optimize-starttime.md#lambda-quick-initialize)
  + [종속성 주입을 최소화](lambda-optimize-starttime.md#lambda-quick-di)
  + [Maven Archetype 타겟팅 사용 AWS Lambda](lambda-optimize-starttime.md#lambda-quick-maven)
  + [Java용 Lambda SnapStart를 고려](lambda-optimize-starttime.md#lambda-quick-snapstart)
  + [시작 시간에 영향을 미치는 버전 2.x 변경 사항](lambda-optimize-starttime.md#example-client-configuration)
  + [추가 리소스](lambda-optimize-starttime.md#lambda-quick-resources)
+ [`ContentStreamProvider`에서 구현 AWS SDK for Java 2.x](content-stream-provider.md)
  + [`mark()` 및 `reset()` 사용](content-stream-provider.md#csp-impl-mark-reset)
  + [`mark()` 및 `reset()`을 사용할 수 없는 경우 버퍼링 사용](content-stream-provider.md#csp-impl-unsupported-streams)
  + [새 스트림 만들기](content-stream-provider.md#csp-impl-new-stream)
+ [DNS 이름 조회를 위한 JVM TTL 설정](jvm-ttl-dns.md)
  + [JVM TTL을 설정하는 방법](jvm-ttl-dns.md#how-to-set-the-jvm-ttl)
    + [옵션 1: 애플리케이션에서 프로그래밍 방식으로 설정](jvm-ttl-dns.md#set-ttl-programmatically)
    + [옵션 2: java.security 파일에서 설정](jvm-ttl-dns.md#set-ttl-java-security-file)
    + [옵션 3: JDK 시스템 속성 대체 사용(명령줄)](jvm-ttl-dns.md#set-ttl-system-property)
+ [AWS SDK for Java에서 HTTP/2 작업](http2.md)

# 를 사용하여 AWS 서비스 요청 AWS SDK for Java 2.x
<a name="work-witih-clients"></a>

## 서비스 클라이언트를 사용하여 요청 만들기
<a name="using-service-client"></a>

[SDK 설정](setup.md)의 단계를 완료하고 [서비스 클라이언트를 구성하는](configuring-service-clients.md) 방법을 이해하면 Amazon S3, Amazon DynamoDB, , AWS Identity and Access Management Amazon EC2 등과 같은 AWS 서비스에 요청할 준비가 된 것입니다.

### 서비스 클라이언트 생성
<a name="work-with-clients-create"></a>

에 요청하려면 먼저 정적 팩토리 메서드를 사용하여 해당 서비스에 대한 서비스 클라이언트를 인스턴스화 AWS 서비스해야 합니다`builder()`. 이 `builder()` 메서드는 서비스 클라이언트를 사용자 지정할 수 있는 `builder` 객체를 반환합니다. 유용한 setter 메서드는 `builder` 객체를 반환해 읽기 쉽고 편리하도록 메서드 호출을 묶을 수 있게 도와줍니다. 원하는 속성을 구성한 후에는 `build()` 메서드를 호출하여 클라이언트를 생성할 수 있습니다.

예를 들어, 이 코드 조각은 `Ec2Client` 객체를 Amazon EC2용 서비스 클라이언트로 인스턴스화합니다.

```
Region region = Region.US_WEST_2;
Ec2Client ec2Client = Ec2Client.builder()
        .region(region)
        .build();
```

**참고**  
SDK의 서비스 클라이언트는 스레드 세이프입니다. 최상의 성능을 위해 수명이 긴 객체로 처리합니다. 각 클라이언트마다 클라이언트에서 가비지가 수집될 때 릴리스되는 고유한 연결 풀 리소스가 있습니다.  
서비스 클라이언트 객체는 변경할 수 없으므로 요청하는 각 서비스에 대해 또는 동일한 서비스에 대한 요청을 위해 다른 구성을 사용하려는 경우 새 클라이언트를 만들어야 합니다.  
서비스 클라이언트 빌더`Region`에서를 지정하는 것은 모든 AWS 서비스에 필수는 아니지만 애플리케이션에서 수행하는 API 호출에 대해 리전을 설정하는 것이 좋습니다. 자세한 내용은 [AWS 리전 선택을](region-selection.md) 참조하세요.

### 기본 클라이언트 구성
<a name="using-default-client"></a>

클라이언트 빌더에는 `create()`라는 또 다른 팩토리 메서드가 있습니다. 기본 구성을 사용해 서비스 클라이언트를 생성하는 메서드입니다. [기본 공급자 체인](credentials-chain.md)을 사용하여 자격 증명을 로드하고 [기본 AWS 리전 공급자 체인](region-selection.md#automatically-determine-the-aws-region-from-the-environment)을 로드합니다. 애플리케이션이 실행되는 환경에서 자격 증명이나 리전을 확인할 수 없는 경우 `create` 에 대한 호출이 실패합니다. SDK가 사용할 자격 증명 및 리전을 결정하는 방법에 대한 자세한 내용은 [자격 증명 사용](credentials.md) 및 [리전 선택](region-selection.md)을 참조하세요.

예를 들어, 이 코드 조각은 `DynamoDbClient` 객체를 Amazon DynamoDB용 서비스 클라이언트로 인스턴스화합니다.

```
DynamoDbClient dynamoDbClient = DynamoDbClient.create();
```

### 서비스 클라이언트 구성
<a name="using-configure-service-clients"></a>

서비스 클라이언트를 구성하는 방법에 대한 자세한 내용은 [외부에서 클라이언트 구성](configuring-service-clients-ext.md) 및 [코드의 클라이언트 구성](configuring-service-clients-code.md) 섹션을 참조하세요.

### 서비스 클라이언트 닫기
<a name="using-closing-client"></a>

가장 좋은 방법은 애플리케이션 수명 동안 여러 API 서비스 호출에 서비스 클라이언트를 사용하는 것입니다. 하지만 일회용으로 사용할 서비스 클라이언트가 필요하거나 더 이상 필요 없는 서비스 클라이언트는 닫으세요.

리소스를 확보하는 데 서비스 클라이언트가 더 이상 필요하지 않을 때 `close()` 메서드를 호출하세요.

```
ec2Client.close();
```

일회용으로 사용할 서비스 클라이언트가 필요한 경우 `try`-with-resources 문을 사용하여 서비스 클라이언트를 리소스로 인스턴스화할 수 있습니다. 서비스 클라이언트가 `[Autoclosable](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/AutoCloseable.html)` 인터페이스를 구현하므로 JDK는 명령문 끝에서 `close()` 메서드를 자동으로 호출합니다.

다음 예제에서는 서비스 클라이언트를 사용하여 일회성 호출을 실행하는 방법을 보여줍니다. 를 호출`StsClient`하는 AWS Security Token Service 는 계정 ID를 반환한 후 닫힙니다.

```
import software.amazon.awssdk.services.sts.StsClient;

String getAccountID() {
    try (StsClient stsClient = StsClient.create()) {
       return stsClient.getCallerIdentity().account();
    }
}
```

## 요청을 생성
<a name="using-making-requests"></a>

서비스 클라이언트를 사용하여 해당에 요청합니다 AWS 서비스.

예를 들어, 이 코드 조각은 `RunInstancesRequest` 객체를 생성하여 새 Amazon EC2 인스턴스를 생성하는 방법을 보여줍니다.

```
// Create the request by using the fluid setter methods of the request builder.
RunInstancesRequest runInstancesRequest = RunInstancesRequest.builder()
        .imageId(amiId)
        .instanceType(InstanceType.T1_MICRO)
        .maxCount(1)
        .minCount(1)
        .build();

// Use the configured request with the service client.
RunInstancesResponse response = ec2Client.runInstances(runInstancesRequest);
```

요청을 만들고 인스턴스를 전달하는 대신 SDK는 요청을 만드는 데 사용할 수 있는 유용한 API를 제공합니다. 유용한 API를 사용하면 Java lambda 표현식을 사용하여 요청을 '인라인'으로 만들 수 있습니다.

다음 예제에서는 [빌더를 사용](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#runInstances(java.util.function.Consumer))하여 요청을 생성하는 `runInstances` 메서드의 버전을 사용하여 이전 예제를 다시 작성합니다.

```
// Create the request by using a lambda expression.
RunInstancesResponse response = ec2.runInstances(r -> r
                .imageId(amiId)
                .instanceType(InstanceType.T1_MICRO)
                .maxCount(1)
                .minCount(1));
```

### 요청을 사용하여 클라이언트 구성 재정의
<a name="using-override-client-config-request"></a>

서비스 클라이언트는 변경할 수 없지만 요청 수준에서 많은 해당 설정을 재정의할 수 있습니다. 요청을 구축할 때 [AwsRequestOverrideConfiguration](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/AwsRequestOverrideConfiguration.html) 인스턴스를 제공하여 재정의된 설정을 제공할 수 있습니다. 클라이언트 설정을 재정의하는 데 사용할 수 있는 몇 가지 방법은 다음과 같습니다.
+ `apiCallAttemptTimeout`
+ `apiCallTimeout`
+ `credentialProvider`
+ `compressionConfiguration`
+ `putHeader`

클라이언트 설정을 요청으로 재정의하는 예제의 경우 기본 설정을 사용하는 다음 S3 클라이언트가 있다고 가정합니다.

```
S3Client s3Client = S3Client.create();
```

대용량 파일을 다운로드하고 다운로드가 완료되기 전에 요청의 제한 시간이 초과되지 않도록 하려고 합니다. 이렇게 하려면 다음 코드와 같이 단일 `GetObject` 요청의 제한 시간 값만 늘립니다.

------
#### [ Standard API ]

```
AwsRequestOverrideConfiguration overrideConfiguration = AwsRequestOverrideConfiguration.builder()
    .apiCallTimeout(Duration.ofSeconds(100L))
    .apiCallAttemptTimeout(Duration.ofSeconds(25L))
    .build();

GetObjectRequest request = GetObjectRequest.builder()
    .bucket("amzn-s3-demo-bucket")
    .key("demo-key")
    .overrideConfiguration(overrideConfiguration)
    .build();

s3Client.getObject(request, myPath);
```

------
#### [ Fluent API ]

```
s3Client.getObject(b -> b
        .bucket("amzn-s3-demo-bucket")
        .key("demo-key")
        .overrideConfiguration(c -> c
            .apiCallTimeout(Duration.ofSeconds(100L))
            .apiCallAttemptTimeout(Duration.ofSeconds(25L))),
    myPath);
```

------

## 응답 처리
<a name="using-handling-responses"></a>

SDK는 대부분의 서비스 작업에 대한 응답 객체를 반환합니다. 코드는 필요에 따라 응답 객체의 정보를 처리할 수 있습니다.

예를 들어 다음 코드 조각은 이전 요청에서 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/RunInstancesResponse.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/RunInstancesResponse.html) 객체와 함께 반환된 첫 번째 인스턴스 ID를 출력합니다.

```
RunInstancesResponse runInstancesResponse = ec2Client.runInstances(runInstancesRequest);
System.out.println(runInstancesResponse.instances().get(0).instanceId());
```

그러나 일부 작업은 서비스별 데이터가 포함된 응답 객체를 반환하지 않습니다. 이러한 상황에서는 HTTP 응답 상태를 쿼리하여 작업이 성공했는지 확인할 수 있습니다.

예를 들어 다음 코드 조각의 코드는 HTTP 응답을 확인하여 Amazon Simple Email Service의 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sesv2/SesV2Client.html#deleteContactList(software.amazon.awssdk.services.sesv2.model.DeleteContactListRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sesv2/SesV2Client.html#deleteContactList(software.amazon.awssdk.services.sesv2.model.DeleteContactListRequest)) 작업이 성공했는지 확인합니다.

```
SesV2Client sesv2Client = SesV2Client.create();

DeleteContactListRequest request = DeleteContactListRequest.builder()
    .contactListName("ExampleContactListName")
    .build();

DeleteContactListResponse response = sesv2Client.deleteContactList(request);
if (response.sdkHttpResponse().isSuccessful()) {
    System.out.println("Contact list deleted successfully");
} else {
    System.out.println("Failed to delete contact list. Status code: " + response.sdkHttpResponse().statusCode());
}
```

# AWS SDK for Java 2.x를 사용한 비동기식 프로그래밍 작업
<a name="asynchronous"></a>

AWS SDK for Java 2.x는 일부 스레드에 높은 동시성을 구현하는 비차단형 입출력 지원이 포함된 비동기식 클라이언트가 특징입니다. 그러나 전체 비차단 입출력은 보장되지 않습니다. 비동기식 클라이언트는 자격 증명 검색, [AWS Signature Version 4(SigV4)](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html)를 사용한 요청 서명 또는 엔드포인트 검색과 같은 경우에 차단 호출을 수행할 수 있습니다.

비동기 메서드는 클라이언트가 서비스로부터 응답을 받을 때까지 스레드의 실행을 차단합니다. 비동기 메서드는 (값을) 즉시 반환하며, 응답을 기다리지 않고 제어 권한을 호출 스레드에 넘겨줍니다.

비동기 메서드는 응답이 제공되기 전에 (값을) 반환하므로 준비되었을 때 응답을 가져올 방법이 필요합니다. AWS SDK for Java의 2.x에서 비동기식 클라이언트 메서드는 준비가 되었을 때 응답에 액세스하도록 허용하는 *CompletableFuture 객체*를 반환합니다.

## 비동기식 클라이언트 API 사용
<a name="basics-async-non-streaming"></a>

비동기식 클라이언트 메서드의 서명은 동기식 클라이언트 메서드와 동일하지만 비동기식 메서드는 *향후* 비동기식 작업의 결과가 포함된 [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/concurrent/CompletableFuture.html) 객체를 반환합니다. SDK의 비동기식 메서드가 실행되는 동안 오류가 발생하면 오류가 `CompletionException`으로 발생합니다.

결과를 얻는 데 사용할 수 있는 한 가지 접근 방식은 SDK 메서드 호출에서 반환된 `whenComplete()`에 `CompletableFuture` 메서드를 연결하는 것입니다. `whenComplete()` 메서드는 비동기식 호출이 완료된 방식에 따라 결과 또는 `CompletionException` 유형의 Throwable 객체를 수신합니다. 호출 코드로 반환되기 전에 결과를 처리하거나 확인하는 작업을 `whenComplete()`에 제공합니다.

SDK 메서드에서 반환한 객체 이외의 다른 것을 반환하려면 `handle()` 메서드를 대신 사용합니다. `handle()` 메서드는 `whenComplete()`와 동일한 파라미터를 사용하지만 결과를 처리하고 객체를 반환할 수 있습니다.

비동기식 체인이 완료되고 완료 결과를 검색할 때까지 기다리기 위해 `join()` 메서드를 호출할 수 있습니다. `Throwable` 객체가 체인에서 처리되지 않은 경우 `join()` 메서드는 원래 예외를 래핑하는 선택되지 않은`CompletionException`을 발생시킵니다. `CompletionException#getCause()`를 사용하여 원래 예외에 액세스합니다. 또한 `CompletableFuture#get()` 메서드를 호출하여 완료 결과를 가져올 수 있습니다. 그러나 `get()` 메서드는 확인된 예외를 발생시킬 수 있습니다.

다음 예제에서는 DynamoDB 비동기식 클라이언트의 `listTables()` 메서드를 사용하는 방법에 대한 2가지 변형을 보여줍니다. `whenComplete()`에 전달된 작업은 성공적인 응답을 로깅하는 반면, `handle()` 버전은 테이블 이름 목록을 추출하고 목록을 반환합니다. 두 경우 모두 비동기식 체인에서 오류가 생성되는 경우 클라이언트 코드가 오류를 처리할 수 있도록 오류를 다시 발생시킵니다.

 **가져옵니다**.

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.awssdk.services.dynamodb.model.ListTablesRequest;
import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse;

import java.util.List;
import java.util.concurrent.CompletableFuture;
```

 ** 코드** 

------
#### [ whenComplete() variation ]

```
public class DynamoDbAsyncListTables {

    public static void main(String[] args) {
        Region region = Region.US_EAST_1;
        DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbAsyncClient.builder().region(region).build();
        try {
            ListTablesResponse listTablesResponse = listTablesWhenComplete(dynamoDbAsyncClient).join();  // The join() method may throw a CompletionException.
            if (listTablesResponse.hasTableNames()){
                System.out.println("Table exist in this region: " + region.id());
            }
        } catch (RuntimeException e) {
            // Handle as needed. Here we simply print out the class names.
            System.out.println(e.getClass()); // Prints 'class java.util.concurrent.CompletionException'.
            System.out.println(e.getCause().getClass()); // Prints 'class software.amazon.awssdk.services.dynamodb.model.DynamoDbException'.
        }
    }

    public static CompletableFuture<ListTablesResponse> listTablesWhenComplete(DynamoDbAsyncClient client) {
        return client.listTables(ListTablesRequest.builder().build())
            .whenComplete((listTablesResponse, throwable) -> {
                if (listTablesResponse != null) {  // Consume the response.
                    System.out.println("The SDK's listTables method completed successfully.");
                } else {
                    RuntimeException cause = (RuntimeException) throwable.getCause(); // If an error was thrown during the SDK's listTables method it is wrapped in a CompletionException.
                                                                                      // The SDK throws only RuntimeExceptions, so this is a safe cast.
                    System.out.println(cause.getMessage());  // Log error here, but rethrow so the calling code can handle as needed.
                    throw cause;
                }
            });
    }
```

------
#### [ handle() variation ]

```
public class DynamoDbAsyncListTables {

    public static void main(String[] args) {
        Region region = Region.US_EAST_1;
        DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbAsyncClient.builder().region(region).build();
        try {
            List<String> tableNames = listTablesHandle(dynamoDbAsyncClient).join(); // The join() method may throw a CompletionException.
            tableNames.forEach(System.out::println);
        } catch (RuntimeException e) {
            // Handle as needed. Here we simply print out the class names.
            System.out.println(e.getClass()); // Prints 'class java.util.concurrent.CompletionException'.
            System.out.println(e.getCause().getClass()); // Prints 'class software.amazon.awssdk.services.dynamodb.model.DynamoDbException'.
        }
    }

    public static CompletableFuture<List<String>> listTablesHandle(DynamoDbAsyncClient client) {
        return client.listTables(ListTablesRequest.builder().build())
            .handle((listTablesResponse, throwable) -> {
                if (listTablesResponse != null) {
                    return listTablesResponse.tableNames(); // Return the list of table names.
                } else {
                    RuntimeException cause = (RuntimeException) throwable.getCause(); // If an error was thrown during the SDK's listTables method it is wrapped in a CompletionException.
                                                                                      // The SDK throws only RuntimeExceptions, so this is a safe cast.
                    System.out.println(cause.getMessage());  // Log error here, but rethrow so the calling code can handle as needed.
                    throw cause;
                }
            });
    }
}
```

------

## 비동기식 메서드에서 스트리밍 처리
<a name="basics-async-streaming"></a>

스트리밍 콘텐츠에서 비동기식 메서드를 사용할 경우 증분 방식으로 콘텐츠를 제공하기 위해 [AsyncRequestBody](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html)를 제공하거나, 응답을 수신해 처리할 수 있도록 [AsyncResponseTransformer](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncResponseTransformer.html)를 제공해야 합니다.

다음 예제는 `PutObject` 작업의 비동기식 양식을 사용하여 파일을 Amazon S3에 비동기식으로 업로드합니다.

 **가져옵니다**.

```
import software.amazon.awssdk.core.async.AsyncRequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
import java.nio.file.Paths;
import java.util.concurrent.CompletableFuture;
```

 ** 코드** 

```
/**
 * To run this AWS code example, ensure that you have setup your development environment, including your AWS credentials.
 *
 * For information, see this documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */

public class S3AsyncOps {

     public static void main(String[] args) {

         final String USAGE = "\n" +
                 "Usage:\n" +
                 "    S3AsyncOps <bucketName> <key> <path>\n\n" +
                 "Where:\n" +
                 "    bucketName - the name of the Amazon S3 bucket (for example, bucket1). \n\n" +
                 "    key - the name of the object (for example, book.pdf). \n" +
                 "    path - the local path to the file (for example, C:/AWS/book.pdf). \n" ;

        if (args.length != 3) {
            System.out.println(USAGE);
             System.exit(1);
        }

        String bucketName = args[0];
        String key = args[1];
        String path = args[2];

        Region region = Region.US_WEST_2;
        S3AsyncClient client = S3AsyncClient.builder()
                .region(region)
                .build();

        PutObjectRequest objectRequest = PutObjectRequest.builder()
                .bucket(bucketName)
                .key(key)
                .build();

        // Put the object into the bucket
        CompletableFuture<PutObjectResponse> future = client.putObject(objectRequest,
                AsyncRequestBody.fromFile(Paths.get(path))
        );
        future.whenComplete((resp, err) -> {
            try {
                if (resp != null) {
                    System.out.println("Object uploaded. Details: " + resp);
                } else {
                    // Handle error
                    err.printStackTrace();
                }
            } finally {
                // Only close the client when you are completely done with it
                client.close();
            }
        });

        future.join();
    }
}
```

다음 예제는 Amazon S3에서 `GetObject` 작업의 비동기식 양식을 사용해 파일을 가져옵니다.

 **가져옵니다**.

```
import software.amazon.awssdk.core.async.AsyncResponseTransformer;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import java.nio.file.Paths;
import java.util.concurrent.CompletableFuture;
```

 ** 코드** 

```
/**
 * To run this AWS code example, ensure that you have setup your development environment, including your AWS credentials.
 *
 * For information, see this documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */

public class S3AsyncStreamOps {

    public static void main(String[] args) {

        final String USAGE = "\n" +
                "Usage:\n" +
                "    S3AsyncStreamOps <bucketName> <objectKey> <path>\n\n" +
                "Where:\n" +
                "    bucketName - the name of the Amazon S3 bucket (for example, bucket1). \n\n" +
                "    objectKey - the name of the object (for example, book.pdf). \n" +
                "    path - the local path to the file (for example, C:/AWS/book.pdf). \n" ;

        if (args.length != 3) {
            System.out.println(USAGE);
            System.exit(1);
         }

        String bucketName = args[0];
        String objectKey = args[1];
        String path = args[2];

        Region region = Region.US_WEST_2;
        S3AsyncClient client = S3AsyncClient.builder()
                .region(region)
                .build();

        GetObjectRequest objectRequest = GetObjectRequest.builder()
                .bucket(bucketName)
                .key(objectKey)
                .build();

        CompletableFuture<GetObjectResponse> futureGet = client.getObject(objectRequest,
                AsyncResponseTransformer.toFile(Paths.get(path)));

        futureGet.whenComplete((resp, err) -> {
            try {
                if (resp != null) {
                    System.out.println("Object downloaded. Details: "+resp);
                } else {
                    err.printStackTrace();
                }
            } finally {
               // Only close the client when you are completely done with it
                client.close();
            }
        });
        futureGet.join();
    }
}
```

## 고급 비동기 옵션 구성
<a name="advanced-operations"></a>

AWS SDK for Java 2.x는 비동기식 이벤트 중심 네트워크 애플리케이션 프레임워크인 [Netty](https://netty.io)를 사용하여 I/O 스레드를 처리합니다. AWS SDK for Java 2.x는 Netty 뒤에 `ExecutorService`를 생성하여 HTTP 클라이언트 요청에서 Netty 클라이언트로 반환된 선물을 완료합니다. 이러한 추상화는 개발자가 스레드 중지 또는 대기를 선택하는 경우 애플리케이션이 비동기 프로세스를 차단하는 위험을 줄여줍니다. 기본적으로 각 비동기 클라이언트는 프로세서 수에 따라 스레드 풀을 만들고 `ExecutorService` 내에서 대기열의 작업을 관리합니다.

비동기식 클라이언트를 구축할 경우 `ExecutorService`의 특정 JDK 구현을 지정할 수 있습니다. 다음 코드 조각은 고정된 수의 스레드로 `ExecutorService`를 만듭니다.

 ** 코드** 

```
S3AsyncClient clientThread = S3AsyncClient.builder()
  .asyncConfiguration(
    b -> b.advancedOption(SdkAdvancedAsyncClientOption
      .FUTURE_COMPLETION_EXECUTOR,
      Executors.newFixedThreadPool(10)
    )
  )
  .build();
```

성능을 최적화하려면 자체 스레드 풀 실행 도구를 관리하고 클라이언트를 구성할 때 이를 포함시킬 수 있습니다.

```
ThreadPoolExecutor executor = new ThreadPoolExecutor(50, 50,
    10, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(<custom_value>),
    new ThreadFactoryBuilder()
      .threadNamePrefix("sdk-async-response").build());

// Allow idle core threads to time out
executor.allowCoreThreadTimeOut(true);

S3AsyncClient clientThread = S3AsyncClient.builder()
  .asyncConfiguration(
    b -> b.advancedOption(SdkAdvancedAsyncClientOption
      .FUTURE_COMPLETION_EXECUTOR,
      executor
    )
  )
  .build();
```

# 사용 모범 사례 AWS SDK for Java 2.x
<a name="best-practices"></a>

## API 제한 시간을 구성하여 중단 요청 방지
<a name="bestpractice5"></a>

SDK는 연결 제한 시간 및 소켓 제한 시간과 같은 일부 제한 시간 옵션에 대한 [기본값](https://github.com/aws/aws-sdk-java-v2/blob/a0c8a0af1fa572b16b5bd78f310594d642324156/http-client-spi/src/main/java/software/amazon/awssdk/http/SdkHttpConfigurationOption.java#L134)을 제공하지만 API 호출 제한 시간이나 개별 API 호출 시도 제한 시간에 대해서는 기본값을 제공하지 않습니다. 개별 시도와 전체 요청 모두에 대해 제한 시간을 설정하는 것이 좋습니다. 이렇게 하면 요청 시도를 완료하는 데 시간이 더 오래 걸리거나 치명적인 네트워크 문제를 일으킬 수 있는 일시적인 문제가 있는 경우 최적의 방식으로 애플리케이션이 빠르게 실패하도록 보장합니다.

`[ClientOverrideConfiguration\$1apiCallAttemptTimeout](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.html#apiCallAttemptTimeout())` 및 `[ClientOverrideConfiguration\$1apiCallTimeout](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.html#apiCallTimeout())`를 사용하여 서비스 클라이언트의 모든 요청에 대한 제한 시간을 구성할 수 있습니다.

다음 예제는 사용자 지정 시간 제한 값을 사용하는 Amazon S3 클라이언트의 구성을 보여줍니다.

```
S3Client.builder()
        .overrideConfiguration(
             b -> b.apiCallTimeout(Duration.ofSeconds(<custom value>))
                   .apiCallAttemptTimeout(Duration.ofMillis(<custom value>)))
        .build();
```

**`apiCallAttemptTimeout`**  
이 설정은 단일 HTTP 시도 시간을 설정합니다. 이 시간이 지나면 API 호출을 재시도할 수 있습니다.

**`apiCallTimeout`**  
이 속성의 값은 모든 재시도를 포함하여 전체 실행에 걸리는 시간을 구성합니다.

서비스 클라이언트에서 이러한 제한 시간 값을 설정하는 대신 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/RequestOverrideConfiguration.html#apiCallTimeout()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/RequestOverrideConfiguration.html#apiCallTimeout()) 및 `[RequestOverrideConfiguration\$1apiCallAttemptTimeout()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/RequestOverrideConfiguration.html#apiCallAttemptTimeout())`를 사용하여 단일 요청을 구성할 수 있습니다.

다음 예시에서는 사용자 지정 제한 시간 값을 사용하여 단일 `listBuckets` 요청을 구성합니다.

```
s3Client.listBuckets(lbr -> lbr.overrideConfiguration(
        b -> b.apiCallTimeout(Duration.ofSeconds(<custom value>))
               .apiCallAttemptTimeout(Duration.ofMillis(<custom value>))));
```

이러한 속성을 함께 사용하면 재시도 전체에 걸쳐 모든 시도에 소요되는 총 시간에 대한 엄격한 제한을 설정합니다. 또한 느린 요청에 대해 빠르게 실패하도록 개별 HTTP 요청을 설정합니다.

## 서비스 클라이언트를 재사용하여 성능 향상
<a name="bestpractice1"></a>

각 [서비스 클라이언트](work-witih-clients.md)는 자체 HTTP 연결 풀을 유지합니다. 풀에 이미 있는 연결을 새 요청에 재사용하여 새 연결을 설정하는 시간을 줄일 수 있습니다. 효과적으로 사용되지 않는 연결 풀이 너무 많아서 발생하는 오버헤드를 방지하려면 클라이언트의 단일 인스턴스를 공유하는 것이 좋습니다. 모든 서비스 클라이언트는 스레드로부터 안전합니다.

클라이언트 인스턴스를 공유하지 않으려면 클라이언트가 필요하지 않을 때 인스턴스를 `close()` 호출하여 리소스를 릴리스하세요.

## 미사용 서비스 클라이언트를 닫아 리소스 누수 방지
<a name="bestpractice-close-client"></a>

더 이상 필요하지 않은 경우 [서비스 클라이언트](work-witih-clients.md)를 종료해 스레드 등의 리소스를 릴리스합니다. 클라이언트 인스턴스를 공유하지 않으려면 클라이언트가 필요하지 않을 때 인스턴스를 `close()` 호출하여 리소스를 릴리스하세요.

## 입력 스트림을 닫아 연결 풀 소진 방지
<a name="bestpractice2"></a>

`[ResponseInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/ResponseInputStream.html)`를 사용하여 직접 작업하는 경우 `[S3Client\$1getObject](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#getObject(java.util.function.Consumer,java.nio.file.Path))`와 같은 스트리밍 작업의 경우 다음을 수행하는 것이 좋습니다.
+ 가능한 한 빨리 입력 스트림에서 모든 데이터를 읽습니다.
+ 가능한 한 빨리 입력 스트림을 닫습니다.

입력 스트림은 HTTP 연결의 데이터에 대한 직접적인 스트림이고 스트림의 모든 데이터를 읽고 스트림을 닫을 때까지 기본 HTTP 연결을 재사용할 수 없기 때문에 이러한 권장 사항을 제시합니다. 이러한 규칙을 따르지 않으면 클라이언트는 열려 있지만 사용되지 않는 HTTP 연결을 너무 많이 할당하여 리소스가 부족해질 수 있습니다.

## 애플리케이션 워크로드에 대한 HTTP 성능 최적화
<a name="bestpractice3"></a>

SDK는 일반 사용 사례에 적용되는 [기본 http 구성](https://github.com/aws/aws-sdk-java-v2/blob/master/http-client-spi/src/main/java/software/amazon/awssdk/http/SdkHttpConfigurationOption.java) 세트를 제공합니다. 고객은 사용 사례에 따라 애플리케이션에 대한 HTTP 구성을 조정하는 것이 좋습니다.

좋은 출발점으로 SDK는 [스마트 구성 기본값](http-configuration.md#http-config-smart-defaults) 기능을 제공합니다. 이 기능은 버전 2.17.102부터 사용할 수 있습니다. 사용 사례에 따라 적절한 구성 값을 제공하는 모드를 선택합니다.

## 비동기식 클라이언트용 OpenSSL로 SSL 성능 개선
<a name="bestpractice4"></a>

기본적으로 SDK의 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.html)는 JDK의 기본 SSL 구현을 `SslProvider`과 같이 사용합니다. 테스트 결과 OpenSSL이 JDK의 기본 구현보다 성능이 더 좋은 것으로 나타났습니다. Netty 커뮤니티에서도 [OpenSSL 사용을 권장합니다](https://netty.io/wiki/requirements-for-4.x.html#tls-with-openssl).

OpenSSL을 사용하려면 종속성에 `netty-tcnative`을 추가하세요. 구성 세부 정보는 [Netty 프로젝트 설명서](https://netty.io/wiki/forked-tomcat-native.html)를 참조하세요.

프로젝트에 맞게 `netty-tcnative`를 구성한 후 `NettyNioAsyncHttpClient` 인스턴스는 자동으로 OpenSSL을 선택합니다. 또는 다음 코드 조각과 같이 `NettyNioAsyncHttpClient` 빌더를 사용하여 `SslProvider`를 명시적으로 설정할 수 있습니다.

```
NettyNioAsyncHttpClient.builder()
                        .sslProvider(SslProvider.OPENSSL)
                        .build();
```

## SDK 지표를 사용하여 애플리케이션 성능 모니터링
<a name="bestpractice6"></a>

Java용 SDK는 애플리케이션의 서비스 클라이언트에 대한 [메트릭을 수집](metrics.md)할 수 있습니다. 이러한 지표를 사용하여 성능 문제를 식별하고, 전반적인 사용 추세를 검토하고, 반환된 서비스 클라이언트 예외를 검토하거나, 특정 문제를 이해하기 위해 자세히 알아볼 수 있습니다.

애플리케이션 성능을 더 깊이 이해하려면 지표를 수집한 다음 Amazon CloudWatch Logs를 분석하는 것이 좋습니다.

# 의 오류 처리 AWS SDK for Java 2.x
<a name="handling-exceptions"></a>

에서 예외가 AWS SDK for Java 2.x 발생하는 방법과 시기를 이해하는 것은 SDK를 사용하여 고품질 애플리케이션을 구축하는 데 중요합니다. 다음 단원에서는 SDK에서 발생하는 다양한 예외의 경우와 이러한 예외를 적절히 처리하는 방법에 대해 설명합니다.

## 확인되지 않은 예외가 발생하는 이유
<a name="why-unchecked-exceptions"></a>

는 다음과 같은 이유로 확인된 예외 대신 런타임(또는 확인되지 않은) 예외를 AWS SDK for Java 사용합니다.
+ 개발자가 중요하지 않은 예외 경우를 강제로 처리하지 않고 (또한 해당 코드를 상세 표시 모드로 설정하지 않고) 처리하고자 하는 오류에 대해서만 세부적으로 제어할 수 있도록 하기 위해
+ 대규모 애플리케이션에서 확인된 예외 고유의 확장성 문제를 방지하기 위해

일반적으로 확인된 예외는 소규모 애플리케이션에서 잘 작동하는 편이지만, 애플리케이션이 확장되고 복잡해짐에 따라 문제가 될 수도 있습니다.

## AwsServiceException(및 하위 클래스)
<a name="sdkserviceexception-and-subclasses"></a>

 [AwsServiceException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsServiceException.html)은 사용 시 발생할 수 있는 가장 일반적인 예외입니다 AWS SDK for Java.는 보다 일반적인 [SdkServiceException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html)의 하위 클래스`AwsServiceException`입니다. `AwsServiceException`s는의 오류 응답을 나타냅니다 AWS 서비스. 예를 들어 존재하지 않는 Amazon EC2 인스턴스를 종료하려고 하면 Amazon EC2 가 오류 응답을 반환하고 해당 오류 응답의 모든 세부 정보가 `AwsServiceException` 발생한에 포함됩니다.

가 발생하면 요청이 로 성공적으로 전송되었지만 성공적으로 처리할 AWS 서비스 수 없다는 것을 `AwsServiceException`알 수 있습니다. 이는 요청의 파라미터 오류 또는 서비스 측의 문제로 인해 발생할 수 있습니다.

 `AwsServiceException`은 다음과 같은 정보를 제공합니다.
+ 반환된 HTTP 상태 코드
+ 반환된 AWS 오류 코드
+ [AWSerrorDetails](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsErrorDetails.html) 클래스에 있는 서비스의 자세한 오류 메시지
+  AWS 실패한 요청에 대한 요청 ID

경우에 따라서는 개발자가 catch 블록을 통해 오류 경우 처리를 세부적으로 제어할 수 있도록 하기 위해 `AwsServiceException`의 하위 클래스가 발생하기도 합니다. [AwsServiceException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsServiceException.html)에 대한 Java SDK API 참조에는 많은 수의 `AwsServiceException` 서브클래스가 표시됩니다. 서브클래스 링크를 사용하여 서비스에서 발생하는 세분화된 예외를 자세히 확인할 수 있습니다.

예를 들어 SDK API 참조에 대한 다음 링크는 몇 가지 일반적인 AWS 서비스에 대한 예외 계층 구조를 보여줍니다. 각 페이지에 표시된 서브클래스 목록은 코드가 포착할 수 있는 특정 예외를 보여줍니다.
+ [Amazon S3](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model/S3Exception.html)
+ [DynamoDB](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html)
+ [ Amazon SQS](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/SqsException.html)

예외에 대해 자세히 알아보려면 [AwsErrorDetails](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsErrorDetails.html) 객체의 `errorCode`를 살펴보세요. 이 `errorCode` 값을 사용하여 서비스 가이드 API에서 정보를 조회할 수 있습니다. 예를 들어 `S3Exception`가 `InvalidRequest` 발견되었는데 `AwsErrorDetails#errorCode()` 값이 인 경우 Amazon S3 API [참조의 오류 코드 목록을](https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html#ErrorCodeList) 사용하여 자세한 내용을 확인하세요.

## SdkClientException
<a name="sdkclientexception"></a>

 [SdkClientException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkClientException.html)은 요청을에 보내려고 하거나 응답을 구문 분석하려고 할 AWS 때 Java 클라이언트 코드 내에서 문제가 발생했음을 나타냅니다 AWS. `SdkClientException`는 일반적으로 보다 심각`SdkServiceException`하며 클라이언트가 서비스를 호출하지 못하게 하는 주요 문제를 나타냅니다 AWS . 예를 들어 클라이언트 중 하나에서 작업을 호출하려고 할 때 네트워크 연결을 사용할 수 없는 `SdkClientException` 경우에서이 AWS SDK for Java 발생합니다.

## 예외 및 재시도 동작
<a name="retried-exceptions"></a>

SDK for Java는 여러 [클라이언트 측 예외](https://github.com/aws/aws-sdk-java-v2/blob/13985e0668a9a0b12ad331644e3c4fd1385c2cd7/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/retry/SdkDefaultRetrySetting.java#L79C41-L79C41) 및 AWS 서비스 응답에서 수신하는 [HTTP 상태 코드](https://github.com/aws/aws-sdk-java-v2/blob/13985e0668a9a0b12ad331644e3c4fd1385c2cd7/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/retry/SdkDefaultRetrySetting.java#L72C31-L72C31)에 대한 요청을 재시도합니다. 이러한 오류는 서비스 클라이언트가 기본적으로 `RetryMode` 사용하는 레거시의 일부로 처리됩니다. 의 Java API 참조에서는 모드를 구성할 수 있는 다양한 방법을 `[RetryMode](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/RetryMode.html)` 설명합니다.

자동 재시도를 트리거하는 예외 및 HTTP 상태 코드를 사용자 정의하려면 `[RetryOnExceptionsCondition](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryOnExceptionsCondition.html)` 및 `[RetryOnStatusCodeCondition](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/retry/conditions/RetryOnErrorCodeCondition.html)` 인스턴스를 추가하는 `[RetryPolicy](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/RetryPolicy.html)`와 함께 서비스 클라이언트를 구성하세요.

# AWS SDK for Java 2.x에서 페이지가 매겨진 결과 사용
<a name="pagination"></a>

응답 객체가 너무 커서 단일 응답으로 반환할 수 없는 경우 많은 AWS 작업이 페이지 매김된 결과를 반환합니다. AWS SDK for Java 1.0에서 응답에는 결과의 다음 페이지를 검색하는 데 사용하는 토큰이 포함되어 있습니다. 반면, AWS SDK for Java 2.x에는 자동으로 결과의 다음 페이지를 가져오기 위해 여러 서비스를 호출하는 자동 페이지 매김 메서드가 있습니다. 결과를 처리할 코드를 작성하기만 하면 됩니다. 자동 페이지 매김은 동기 클라이언트와 비동기 클라이언트 모두에서 사용할 수 있습니다.

**참고**  
이러한 코드 조각은 사용자가 [SDK 사용의 기본](using.md) 사항을 이해하고 [Single Sign-On 액세스](get-started-auth.md#setup-credentials)로 환경을 구성했다고 가정합니다.

## 동기식 페이지 매김
<a name="synchronous-pagination"></a>

다음 예제에서는 Amazon S3 버킷의 객체를 나열하는 동기식 페이지 매김 방법을 보여줍니다.

### 페이지 반복
<a name="iterate-pages"></a>

첫 번째 예제는 `listRes` 페이지네이터 객체인 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/paginators/ListObjectsV2Iterable.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/paginators/ListObjectsV2Iterable.html) 인스턴스를 사용하여 `stream` 메서드로 모든 응답 페이지를 반복하는 방법을 보여줍니다. 코드는 응답 페이지를 통해 스트리밍하고 응답 스트림을 `[S3Object](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model/S3Object.html)` 콘텐츠 스트림으로 변환한 다음 Amazon S3 객체의 콘텐츠를 처리합니다.

다음 가져오기는 이 동기 페이지 매김 단원의 모든 예제에 적용됩니다.

#### 가져오기
<a name="synchronous-pagination-ex-import"></a>

```
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Random;

import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.paginators.ListObjectsV2Iterable;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
import software.amazon.awssdk.services.s3.model.ListObjectsV2Response;
import software.amazon.awssdk.services.s3.model.S3Object;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.DeleteBucketRequest;
import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest;
import software.amazon.awssdk.services.s3.model.CreateMultipartUploadResponse;
import software.amazon.awssdk.services.s3.model.CompletedMultipartUpload;
import software.amazon.awssdk.services.s3.model.CreateBucketRequest;
import software.amazon.awssdk.services.s3.model.CompletedPart;
import software.amazon.awssdk.services.s3.model.CreateBucketConfiguration;
import software.amazon.awssdk.services.s3.model.UploadPartRequest;
import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest;
import software.amazon.awssdk.services.s3.waiters.S3Waiter;
import software.amazon.awssdk.services.s3.model.HeadBucketRequest;
import software.amazon.awssdk.services.s3.model.HeadBucketResponse;
```

```
        ListObjectsV2Request listReq = ListObjectsV2Request.builder()
            .bucket(bucketName)
            .maxKeys(1)
            .build();

        ListObjectsV2Iterable listRes = s3.listObjectsV2Paginator(listReq);
        // Process response pages
        listRes.stream()
            .flatMap(r -> r.contents().stream())
            .forEach(content -> System.out
                .println(" Key: " + content.key() + " size = " + content.size()));
```

GitHub의 [전체 예제](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/s3/src/main/java/com/example/s3/S3ObjectOperations.java#L112)를 참조하세요.

### 객체 반복
<a name="iterate-objects"></a>

다음은 응답 페이지 대신 응답에서 반환된 객체에 대해 반복하는 방법을 설명한 예입니다. `ListObjectsV2Iterable`클래스의 `contents` 메서드는 기본 콘텐츠 요소를 처리하기 위한 여러 메서드를 제공하는 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html)을 반환합니다.

#### 스트림 사용
<a name="use-a-stream"></a>

다음 코드 조각은 응답 내용에 대해 `stream` 메서드를 사용하여 페이지 매김 항목 모음에 대해 반복합니다.

```
        // Helper method to work with paginated collection of items directly.
        listRes.contents().stream()
            .forEach(content -> System.out
                .println(" Key: " + content.key() + " size = " + content.size()));
```

GitHub의 [전체 예제](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/s3/src/main/java/com/example/s3/S3ObjectOperations.java#L127)를 참조하세요.

#### for-each 루프를 사용
<a name="for-loop"></a>

`SdkIterable`는 `Iterable` 인터페이스를 확장하므로 내용을 `Iterable`과 같이 처리할 수 있습니다. 다음 코드 조각은 표준 `for-each` 루프를 사용하여 응답 내용을 반복합니다.

```
        for (S3Object content : listRes.contents()) {
            System.out.println(" Key: " + content.key() + " size = " + content.size());
        }
```

GitHub의 [전체 예제](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/s3/src/main/java/com/example/s3/S3ObjectOperations.java#L133)를 참조하세요.

### 수동 페이지 매김
<a name="manual-pagination"></a>

사용 사례에 따라 필요한 경우 수동 페이지 매김을 사용할 수도 있습니다. 후속 요청에 응답 객체의 다음 토큰을 사용합니다. 다음 예에는 `while` 루프가 사용됩니다.

```
        ListObjectsV2Request listObjectsReqManual = ListObjectsV2Request.builder()
            .bucket(bucketName)
            .maxKeys(1)
            .build();

        boolean done = false;
        while (!done) {
            ListObjectsV2Response listObjResponse = s3.listObjectsV2(listObjectsReqManual);
            for (S3Object content : listObjResponse.contents()) {
                System.out.println(content.key());
            }

            if (listObjResponse.nextContinuationToken() == null) {
                done = true;
            }

            listObjectsReqManual = listObjectsReqManual.toBuilder()
                .continuationToken(listObjResponse.nextContinuationToken())
                .build();
        }
```

GitHub의 [전체 예제](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/s3/src/main/java/com/example/s3/S3ObjectOperations.java#L90)를 참조하세요.

## 비동기 페이지 매김
<a name="asynchronous-pagination"></a>

다음 예제에서는 DynamoDB 테이블을 나열하는 비동기 페이지 매김 방법을 보여줍니다.

### 테이블 이름 페이지 반복
<a name="iterate-pages-async"></a>

다음 두 예제는 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/paginators/ListTablesPublisher.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/paginators/ListTablesPublisher.html)를 가져오기 위한 요청과 함께 `listTablesPaginator` 메서드를 직접적으로 호출하는 비동기식 DynamoDB 클라이언트를 사용합니다. `ListTablesPublisher`는 응답을 처리하기 위한 많은 옵션을 제공하는 2개의 인터페이스를 구현합니다. 각 인터페이스의 메서드를 살펴보겠습니다.

#### `Subscriber` 사용
<a name="use-a-subscriber"></a>

다음 코드 예제에서는 `ListTablesPublisher`에서 구현된 `org.reactivestreams.Publisher` 인터페이스를 사용하여 페이지가 매겨진 결과를 처리하는 방법을 보여줍니다. 반응적 스트림 모델에 대한 자세한 내용은 [반응적 스트림 GitHub 리포지토리](https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.2/README.md)를 참조하세요.

다음 가져오기는 이 비동기 페이지 매김 단원의 모든 예제에 적용됩니다.

##### 가져오기
<a name="use-a-subscriber-ex-imports"></a>

```
import io.reactivex.rxjava3.core.Flowable;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.publisher.Flux;
import software.amazon.awssdk.core.async.SdkPublisher;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.awssdk.services.dynamodb.model.ListTablesRequest;
import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse;
import software.amazon.awssdk.services.dynamodb.paginators.ListTablesPublisher;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
```

다음 코드는 `ListTablesPublisher` 인스턴스를 획득합니다.

```
        // Creates a default client with credentials and region loaded from the
        // environment.
        final DynamoDbAsyncClient asyncClient = DynamoDbAsyncClient.create();

        ListTablesRequest listTablesRequest = ListTablesRequest.builder().limit(3).build();
        ListTablesPublisher publisher = asyncClient.listTablesPaginator(listTablesRequest);
```

다음 코드는 `org.reactivestreams.Subscriber`의 익명 구현을 사용하여 각 페이지의 결과를 처리합니다.

`onSubscribe` 메서드가 `Subscription.request` 메서드를 호출해 게시자에게 데이터를 요청하기 시작합니다. 게시자에게 데이터를 가져오기 시작할 때 호출해야 하는 메서드입니다.

구독자의 `onNext` 메서드는 모든 테이블 이름에 액세스하고 각 이름을 인쇄하여 응답 페이지를 처리합니다. 페이지가 처리된 후 게시자에게 다른 페이지가 요청됩니다. 모든 페이지를 검색할 때까지 반복적으로 호출되는 메서드입니다.

데이터 검색 동안 오류가 발생할 경우 `onError` 메서드가 트리거됩니다. 마지막으로 모든 페이지를 다 요청했을 때 `onComplete` 메서드가 호출됩니다.

```
        // A Subscription represents a one-to-one life-cycle of a Subscriber subscribing
        // to a Publisher.
        publisher.subscribe(new Subscriber<ListTablesResponse>() {
            // Maintain a reference to the subscription object, which is required to request
            // data from the publisher.
            private Subscription subscription;

            @Override
            public void onSubscribe(Subscription s) {
                subscription = s;
                // Request method should be called to demand data. Here we request a single
                // page.
                subscription.request(1);
            }

            @Override
            public void onNext(ListTablesResponse response) {
                response.tableNames().forEach(System.out::println);
                // After you process the current page, call the request method to signal that
                // you are ready for next page.
                subscription.request(1);
            }

            @Override
            public void onError(Throwable t) {
                // Called when an error has occurred while processing the requests.
            }

            @Override
            public void onComplete() {
                // This indicates all the results are delivered and there are no more pages
                // left.
            }
        });
```

GitHub의 [전체 예제](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/dynamodbasync/src/main/java/com/example/dynamodbasync/AsyncPagination.java#L83)를 참조하세요.

#### `Consumer` 사용
<a name="id1pagination"></a>

`ListTablesPublisher`가 구현하는 `SdkPublisher` 인터페이스에는 `Consumer`를 받아서 `CompletableFuture<Void>`를 반환하는 `subscribe` 메서드가 있습니다.

이 인터페이스의 `subscribe` 메서드는 `org.reactivestreams.Subscriber`의 오버헤드가 너무 클 때 간단한 사용 사례에 사용할 수 있습니다. 아래 코드는 각 페이지를 사용하므로 각 페이지에서 `tableNames` 메서드를 호출합니다. `tableNames` 메서드는 `forEach` 메서드로 처리된 DynamoDB 테이블 이름 중 `java.util.List`를 반환합니다.

```
        // Use a Consumer for simple use cases.
        CompletableFuture<Void> future = publisher.subscribe(
                response -> response.tableNames()
                        .forEach(System.out::println));
```

GitHub의 [전체 예제](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/dynamodbasync/src/main/java/com/example/dynamodbasync/AsyncPagination.java#L96)를 참조하세요.

### 테이블 이름 반복
<a name="iterate-objects-async"></a>

다음은 응답 페이지 대신 응답에서 반환된 객체에 대해 반복하는 방법을 설명한 예입니다. 이전에 해당 `contents` 메서드와 함께 표시된 동기 Amazon S3 예제와 마찬가지로, DynamoDB 비동기 결과 클래스 `ListTablesPublisher`에는 기본 항목 컬렉션과 상호 작용할 수 있는 편리한 `tableNames` 메서드가 있습니다. `tableNames` 메서드의 반환 유형은 모든 페이지에서 항목을 요청하기 위해 사용할 수 있는 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/SdkPublisher.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/SdkPublisher.html)입니다.

#### `Subscriber` 사용
<a name="id2"></a>

다음 코드는 테이블 이름의 기본 컬렉션 중 `SdkPublisher`를 가져옵니다.

```
        // Create a default client with credentials and region loaded from the
        // environment.
        final DynamoDbAsyncClient asyncClient = DynamoDbAsyncClient.create();

        ListTablesRequest listTablesRequest = ListTablesRequest.builder().limit(3).build();
        ListTablesPublisher listTablesPublisher = asyncClient.listTablesPaginator(listTablesRequest);
        SdkPublisher<String> publisher = listTablesPublisher.tableNames();
```

다음 코드는 `org.reactivestreams.Subscriber`의 익명 구현을 사용하여 각 페이지의 결과를 처리합니다.

구독자의 `onNext` 메서드는 컬렉션의 개별 요소를 처리합니다. 이 경우에는 테이블 이름입니다. 테이블 이름이 처리된 후 게시자로부터 다른 테이블 이름을 요청합니다. 이 메서드는 모든 페이지를 검색할 때까지 호출을 반복합니다.

```
        // Use a Subscriber.
        publisher.subscribe(new Subscriber<String>() {
            private Subscription subscription;

            @Override
            public void onSubscribe(Subscription s) {
                subscription = s;
                subscription.request(1);
            }

            @Override
            public void onNext(String tableName) {
                System.out.println(tableName);
                subscription.request(1);
            }

            @Override
            public void onError(Throwable t) {
            }

            @Override
            public void onComplete() {
            }
        });
```

GitHub의 [전체 예제](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/dynamodbasync/src/main/java/com/example/dynamodbasync/AsyncPagination.java#L147)를 참조하세요.

#### `Consumer` 사용
<a name="for-loop-async"></a>

다음 예제는 `SdkPublisher`의 `subscribe` 메서드를 사용해 `Consumer`로 각 항목을 처리합니다.

```
        // Use a Consumer.
        CompletableFuture<Void> future = publisher.subscribe(System.out::println);
        future.get();
```

GitHub의 [전체 예제](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/dynamodbasync/src/main/java/com/example/dynamodbasync/AsyncPagination.java#L161)를 참조하세요.

### 타사 라이브러리 사용
<a name="use-third-party-library"></a>

사용자 지정 구독자를 구현하는 대신 타사 라이브러리를 사용할 수 있습니다. 이 예제는 RxJava의 사용을 보여 주지만 반응형 스트림 인터페이스를 구현하는 모든 라이브러리를 사용할 수 있습니다. 해당 라이브러리에 대한 자세한 내용은 [GitHub의 RxJava 위키 페이지](https://github.com/ReactiveX/RxJava/wiki)를 참조하세요.

라이브러리를 사용하려면 종속성으로 추가합니다. 이 예에서는 Maven을 사용하는 경우에 사용할 POM 조각을 알려줍니다.

 **POM 항목** 

```
<dependency>
      <groupId>io.reactivex.rxjava3</groupId>
      <artifactId>rxjava</artifactId>
      <version>3.1.6</version>
</dependency>
```

 **코드** 

```
        DynamoDbAsyncClient asyncClient = DynamoDbAsyncClient.create();
        ListTablesPublisher publisher = asyncClient.listTablesPaginator(ListTablesRequest.builder()
                .build());

        // The Flowable class has many helper methods that work with
        // an implementation of an org.reactivestreams.Publisher.
        List<String> tables = Flowable.fromPublisher(publisher)
                .flatMapIterable(ListTablesResponse::tableNames)
                .toList()
                .blockingGet();
        System.out.println(tables);
```

GitHub의 [전체 예제](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/dynamodbasync/src/main/java/com/example/dynamodbasync/AsyncPagination.java#L198)를 참조하세요.

# 에서 웨이터 사용 AWS SDK for Java 2.x
<a name="waiters"></a>

 AWS SDK for Java 2.x의 웨이터 유틸리티를 사용하면 해당 AWS 리소스에 대한 작업을 수행하기 전에 리소스가 지정된 상태에 있는지 확인할 수 있습니다.

*웨이터*는 원하는 상태에 도달할 때까지(또는 AWS 리소스가 원하는 상태에 도달하지 않을 것으로 결정될 때까지) DynamoDB 테이블 또는 Amazon S3 버킷과 같은 리소스를 폴링하는 데 사용되는 추상화입니다. 번거롭고 오류가 발생하기 쉬운 AWS 리소스를 지속적으로 폴링하는 로직을 작성하는 대신 웨이터를 사용하여 리소스를 폴링하고 리소스가 준비된 후 코드를 계속 실행하도록 할 수 있습니다.

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

프로젝트에서와 함께 웨이터를 사용하려면 먼저 [AWS SDK for Java 2.x 설정의](setup.md) 단계를 완료해야 AWS SDK for Java합니다.

또한 AWS SDK for Java버전 `2.15.0` 또는 그 이상의 버전을 사용하도록 프로젝트 종속성(예: `pom.xml` 또는 `build.gradle` 파일)을 구성해야 합니다.

예제:

```
<project>
  <dependencyManagement>
   <dependencies>
      <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>bom</artifactId>
        <version>2.27.21</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
   </dependencies>
  </dependencyManagement>
</project>
```

## 웨이터 사용
<a name="id1waiters"></a>

웨이터 객체를 인스턴스화하려면 먼저 서비스 클라이언트를 생성해야 합니다. 서비스 클라이언트의 `waiter()` 메서드를 웨이터 객체의 값으로 설정합니다. 웨이터 인스턴스가 존재하면 적절한 코드를 실행하도록 응답 옵션을 설정합니다.

### 동기 프로그래밍
<a name="synchronous-programming"></a>

다음 코드 조각은 DynamoDB 테이블이 존재하고 **ACTIVE** 상태가 될 때까지 기다리는 방법을 보여줍니다.

```
DynamoDbClient dynamo = DynamoDbClient.create();
DynamoDbWaiter waiter = dynamo.waiter();

WaiterResponse<DescribeTableResponse> waiterResponse =
  waiter.waitUntilTableExists(r -> r.tableName("myTable"));

// print out the matched response with a tableStatus of ACTIVE
waiterResponse.matched().response().ifPresent(System.out::println);
```

### 비동기 프로그래밍
<a name="asynchronous-programming"></a>

다음 코드 조각은 DynamoDB 테이블이 더 이상 존재하지 않을 때까지 기다리는 방법을 보여줍니다.

```
DynamoDbAsyncClient asyncDynamo = DynamoDbAsyncClient.create();
DynamoDbAsyncWaiter asyncWaiter = asyncDynamo.waiter();

CompletableFuture<WaiterResponse<DescribeTableResponse>> waiterResponse =
          asyncWaiter.waitUntilTableNotExists(r -> r.tableName("myTable"));

waiterResponse.whenComplete((r, t) -> {
  if (t == null) {
   // print out the matched ResourceNotFoundException
   r.matched().exception().ifPresent(System.out::println);
  }
}).join();
```

## 웨이터 설정
<a name="configuring-waiters"></a>

빌더에서 `overrideConfiguration()`를 사용하여 웨이터 구성을 사용자 지정할 수 있습니다. 일부 작업의 경우 요청 시 사용자 지정 구성을 적용할 수 있습니다.

### 웨이터 구성
<a name="configure-a-waiter"></a>

다음 코드 조각은 웨이터의 구성을 재정의하는 방법을 보여줍니다.

```
// sync
DynamoDbWaiter waiter =
   DynamoDbWaiter.builder()
          .overrideConfiguration(b -> b.maxAttempts(10))
          .client(dynamoDbClient)
          .build();
// async
DynamoDbAsyncWaiter asyncWaiter =
   DynamoDbAsyncWaiter.builder()
          .client(dynamoDbAsyncClient)
          .overrideConfiguration(o -> o.backoffStrategy(
               FixedDelayBackoffStrategy.create(Duration.ofSeconds(2))))
          .scheduledExecutorService(Executors.newScheduledThreadPool(3))
          .build();
```

### 특정 요청에 대한 구성 재정의
<a name="override-configuration-for-a-specific-request"></a>

다음 코드 조각은 요청별로 웨이터의 구성을 재정의하는 방법을 보여줍니다. 단, 일부 작업에만 사용자 지정 가능한 구성이 있습니다.

```
waiter.waitUntilTableNotExists(b -> b.tableName("myTable"),
               o -> o.maxAttempts(10));

asyncWaiter.waitUntilTableExists(b -> b.tableName("myTable"),
                 o -> o.waitTimeout(Duration.ofMinutes(1)));
```

## 코드 예제
<a name="code-examples"></a>

와 함께 웨이터를 사용하는 전체 예제는 AWS 코드 예제 리포지토리의 [CreateTable.java](https://github.com/awsdocs/aws-doc-sdk-examples/blob/869b7ddbc7c8f66c7c45acd5b813429aff37003e/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/CreateTable.java)를 DynamoDB참조하세요.

와 함께 웨이터를 사용하는 전체 예제는 AWS 코드 예제 리포지토리의 [S3BucketOps.java](https://github.com/awsdocs/aws-doc-sdk-examples/blob/869b7ddbc7c8f66c7c45acd5b813429aff37003e/javav2/example_code/s3/src/main/java/com/example/s3/S3BucketOps.java)를 Amazon S3참조하세요.

# 문제 해결 FAQ
<a name="troubleshooting"></a>

애플리케이션에서 AWS SDK for Java 2.x 를 사용할 때이 주제에 나열된 런타임 오류가 발생할 수 있습니다. 여기에서 제안 사항을 사용하면 근본 원인을 파악하고 오류를 해결하는 데 도움이 됩니다.

## ‘`java.net.SocketException`: 연결 재설정’ 또는 ‘서버가 응답을 완료하지 못함’ 오류 해결 방법
<a name="faq-socketexception"></a>

연결 재설정 오류는 호스트 AWS 서비스, 또는 중개자(예: NAT 게이트웨이, 프록시, 로드 밸런서)가 요청이 완료되기 전에 연결을 종료했음을 나타냅니다. 원인에는 여러 가지가 있으므로 솔루션을 찾으려면 연결이 종료되는 이유를 알아야 합니다. 다음 항목으로 인해 일반적으로 연결이 종료됩니다.
+ **연결이 비활성 상태입니다. ** 이는 스트리밍 작업에서 데이터가 일정 기간 동안 유선에 기록되지 않아 중개자가 연결이 멈춘 것으로 감지하여 종료되는 경우에 흔히 발생합니다. 이를 방지하려면 애플리케이션에서 데이터를 적극적으로 다운로드하거나 업로드해야 합니다.
+ **HTTP 또는 SDK 클라이언트를 종료했습니다.** 사용 중인 리소스는 종료하지 않아야 합니다.
+ **잘못 구성된 프록시입니다.** 문제가 해결되었는지 확인하기 위해 구성한 프록시를 우회해 봅니다. 이렇게 해서 문제가 해결되면 어떤 이유로든 프록시는 연결을 종료합니다. 특정 프록시를 조사하여 연결을 닫는 이유를 확인합니다.

문제를 파악할 수 없는 경우 네트워크의 클라이언트 엣지에서 영향을 받는 연결에 대해 TCP 덤프를 실행합니다(예: 제어하는 프록시 이후).

 AWS 엔드포인트가 `TCP RST`를 전송(재설정)하는 경우 [영향을 받는 서비스에 문의하여](https://aws.amazon.com/contact-us/) 재설정이 발생하는 이유를 확인할 수 있는지 확인합니다. 문제가 발생한 때의 요청 ID와 타임스탬프를 제공할 준비를 합니다. AWS 또한 지원 팀은 애플리케이션이 전송 및 수신하는 바이트와 시기를 정확하게 보여주는 [유선 로그](logging-slf4j.md#sdk-java-logging-verbose)의 이점을 누릴 수 있습니다.

## ‘연결 제한 시간’을 수정하는 방법
<a name="faq-connection-timeout"></a>

연결 제한 시간 오류는 호스트 AWS 서비스, 또는 중개자(예: NAT 게이트웨이, 프록시, 로드 밸런서)가 구성된 연결 제한 시간 내에 서버와 새 연결을 설정하지 못했음을 나타냅니다. 다음 항목에서는 이 문제의 일반적인 원인을 설명합니다.
+ **구성된 연결 제한 시간이 너무 짧습니다.** 기본적으로 연결 제한 시간은 AWS SDK for Java 2.x에서 2초입니다. 연결 제한 시간을 너무 작게 설정하면 이 오류가 발생할 수 있습니다. 권장 연결 제한 시간은 리전 내 직접 호출만 수행하는 경우 1초, 크로스 리전 요청을 수행하는 경우 3초입니다.
+ **잘못 구성된 프록시입니다.** 문제 해결 여부를 보려면 구성한 프록시를 우회해 봅니다. 이렇게 해서 문제가 해결되면 연결 제한 시간의 원인은 프록시입니다. 특정 프록시를 조사하여 이 문제가 발생하는 이유를 확인합니다.

문제를 파악할 수 없는 경우 네트워크의 클라이언트 엣지에서 영향을 받는 연결에 대해 TCP 덤프를 실행하여(예: 제어하는 프록시 이후) 네트워크 문제를 조사합니다.

## ‘`java.net.SocketTimeoutException`: 읽기 제한 시간’을 수정하는 방법
<a name="faq-socket-timeout"></a>

읽기 제한 시간 오류는 JVM이 기본 운영 체제에서 데이터를 읽으려고 시도했지만 SDK를 통해 구성된 시간 내에 데이터가 반환되지 않았음을 나타냅니다. 이 오류는 운영 체제 AWS 서비스, 또는 중개자(예: NAT 게이트웨이, 프록시, 로드 밸런서)가 JVM에서 예상한 시간 내에 데이터를 전송하지 못하는 경우에 발생할 수 있습니다. 원인에는 여러 가지가 있으므로 솔루션을 찾으려면 데이터가 반환되지 않는 이유를 알아야 합니다.

네트워크의 클라이언트 엣지에서 영향을 받는 연결에 대해 TCP 덤프를 실행해 봅니다(예: 제어하는 프록시 이후).

 AWS 엔드포인트가 `TCP RST` (재설정)를 보내는 것이 확인되면 [영향을 받는 서비스에 문의하십시오](https://aws.amazon.com/contact-us/). 문제가 발생한 때의 요청 ID와 타임스탬프를 제공할 준비를 합니다. AWS 또한 지원 팀은 애플리케이션이 전송 및 수신하는 바이트와 시기를 정확하게 보여주는 [유선 로그](logging-slf4j.md#sdk-java-logging-verbose)의 이점을 누릴 수 있습니다.

## ‘HTTP 요청을 실행할 수 없음: 풀에서 연결을 기다리는 제한 시간’ 오류를 해결하는 방법
<a name="faq-pool-timeout"></a>

이 오류는 요청이 지정된 최대 시간 내에 풀에서 연결을 가져올 수 없음을 나타냅니다. 문제를 해결하려면 [SDK 클라이언트 측 지표를 사용](metrics.md)하여 Amazon CloudWatch에 지표를 게시하는 것이 좋습니다. HTTP 지표는 근본 원인의 범위를 좁히는 데 도움이 될 수 있습니다. 다음 항목에서는 이 오류의 일반적인 원인을 설명합니다.
+ **연결 누수**입니다. `LeasedConcurrency` , `AvailableConcurrency` 및 `MaxConcurrency` 지표를 확인하여 이를 조사할 수 있습니다. `LeasedConcurrency`가 `MaxConcurrency`에 도달할 때까지 증가하지만 감소하지 않으면 연결 누수가 발생할 수 있습니다. 누수의 일반적인 원인은 S3 `getObject` 메서드 등의 스트리밍 작업이 종료되지 않기 때문입니다. 애플리케이션은 가능한 한 빨리 입력 스트림에서 모든 데이터를 읽고 [나중에 입력 스트림을 종료하는 것](best-practices.md#bestpractice2)이 좋습니다. 다음 차트는 연결 누수에 대한 SDK 지표를 보여줍니다.  
![\[연결 누수 가능성을 보여주는 CloudWatch 지표의 스크린샷입니다.\]](http://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/images/JavaDevGuide-connection-leak-metrics-chart.png)
+ **연결 풀 결핍**입니다. 이는 요청 속도가 너무 높고 구성된 연결 풀 크기가 요청 수요를 충족할 수 없는 경우에 발생할 수 있습니다. 기본 연결 풀 크기는 50이며 풀의 연결이 최대에 도달하면 HTTP 클라이언트는 연결을 사용할 수 있을 때까지 수신 요청을 대기열에 넣습니다. 다음 차트는 연결 풀 결핍에 대한 SDK 지표의 모습을 보여줍니다.  
![\[연결 풀 결핍의 모습을 보여주는 CloudWatch 지표의 스크린샷입니다.\]](http://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/images/JavaDevGuide-connection-pool-starvation-chart.png)

  이 문제를 완화하려면 다음 작업 중 하나를 수행하는 것이 좋습니다.
  + 연결 풀 크기를 늘립니다.
  + 획득 제한 시간을 늘립니다.
  + 요청 속도를 늦춥니다.

  최대 연결 수를 늘리면 클라이언트 처리량이 증가할 수 있습니다(네트워크 인터페이스가 이미 완전히 활용되지 않은 경우). 그러나 결국 프로세스에서 사용하는 파일 설명자 수에 대한 운영 체제 제한에 도달할 수 있습니다. 이미 네트워크 인터페이스를 완전히 사용하고 있거나 연결 수를 더 이상 늘릴 수 없는 경우 획득 제한 시간을 늘립니다. 값이 늘면 제한 시간을 초과하기 전에 요청을 통해 연결을 획득할 수 있는 추가 시간을 확보할 수 있습니다. 연결 시간이 확보되지 않으면 후속 요청 역시 제한 시간을 초과합니다.

  처음 두 메커니즘을 사용하여 문제를 해결할 수 없는 경우 다음 옵션을 시도하여 요청 속도를 늦춥니다.
  + 대규모 트래픽 버스트가 클라이언트에 과부하를 주지 않도록 요청을 완화합니다.
  + 에 대한 호출을 더 효율적으로 수행합니다 AWS 서비스.
  + 요청을 보내는 호스트 수를 늘립니다.
+ **I/O 스레드가 사용 중입니다.** 이는 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.html)에서 비동기식 SDK 클라이언트를 사용하는 경우에만 적용됩니다. 풀에서 연결을 사용할 수 있고 `AvailableConcurrency` 지표가 낮지 않지만 `ConcurrencyAcquireDuration`이 높으면 I/O 스레드가 요청을 처리할 수 없기 때문일 수 있습니다. 이는 I/O 스레드를 차단할 수 있으므로 `Runnable:run`을 [향후 완료 실행자](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/client/config/SdkAdvancedAsyncClientOption.html#FUTURE_COMPLETION_EXECUTOR)로 전달하고 향후 응답 완료 체인에서 시간이 많이 걸리는 작업을 수행하지 않아야 합니다. 그렇지 않은 경우 `[eventLoopGroupBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.Builder.html#eventLoopGroupBuilder(software.amazon.awssdk.http.nio.netty.SdkEventLoopGroup.Builder))` 메서드를 사용하여 I/O 스레드 수를 늘리는 것이 좋습니다. 참조용으로 `NettyNioAsyncHttpClient` 인스턴스의 기본 I/O 스레드 수는 호스트의 CPU 코어 수의 2배입니다.
+ **TLS 핸드셰이크 지연 시간이 높습니다.** `AvailableConcurrency` 지표가 거의 0이고 `LeasedConcurrency`는 `MaxConcurrency`보다 낮은 경우 TLS 핸드셰이크 지연 시간이 높기 때문일 수 있습니다. 다음 차트는 높은 TLS 핸드셰이크 지연 시간에 대한 SDK 지표의 모습을 보여줍니다.  
![\[높은 TLS 핸드셰이크 지연 시간을 나타낼 수 있는 CloudWatch 지표의 스크린샷입니다.\]](http://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/images/JavaDevGuide-high-tls-latency-chart.png)

  CRT를 기반으로 하지 않는 Java SDK에서 제공하는 HTTP 클라이언트의 경우 [TLS 로그](security-java-tls.md)를 사용하여 TLS 문제를 해결해 봅니다. AWS CRT 기반 HTTP 클라이언트의 경우 [AWS CRT 로그](logging-slf4j.md#sdk-java-logging-verbose)를 사용해 봅니다. AWS 엔드포인트가 TLS 핸드셰이크를 수행하는 데 시간이 오래 걸리는 것으로 보이는 경우 [영향을 받는 서비스에 문의](https://aws.amazon.com/contact-us/)해야 합니다.

## `NoClassDefFoundError`, `NoSuchMethodError` 또는 `NoSuchFieldError`를 수정하는 방법
<a name="faq-classpath-errors"></a>

`NoClassDefFoundError`는 런타임에 클래스를 로드할 수 없음을 나타냅니다. 이 오류의 가장 일반적인 2가지 원인은 다음과 같습니다.
+ JAR이 누락되었거나 잘못된 버전의 JAR이 클래스 경로에 있기 때문에 클래스가 클래스 경로에 존재하지 않습니다.
+ 정적 이니셜라이저가 예외를 발생시켜 클래스를 로드하지 못했습니다.

마찬가지로 `NoSuchMethodError` 및 `NoSuchFieldError`는 일반적으로 JAR 버전이 일치하지 않아 발생합니다. 다음 단계를 수행하는 것을 권장합니다.

1. **종속성을 확인**하여 *모든 SDK jar에서 동일한 버전*을 사용하고 있는지 확인합니다. 클래스, 메서드 또는 필드를 찾을 수 없는 가장 일반적인 이유는 새 클라이언트 버전으로 업그레이드하지만 이전 ‘공유’ SDK 종속성 버전을 계속 사용하는 경우입니다. 새 클라이언트 버전은 최신 ‘공유’ SDK 종속성에만 있는 클래스를 사용하려고 할 수 있습니다. `mvn dependency:tree` 또는 `gradle dependencies`(Gradle용)를 실행하여 SDK 라이브러리 버전이 모두 일치하는지 확인합니다. 향후 이 문제 발생을 완전히 방지하려면 [BOM(Bill of Materials)](setup-project-maven.md#sdk-as-dependency)을 사용하여 SDK 모듈 버전을 관리하는 것이 좋습니다.

   다음 예제에서는 혼합 SDK 버전의 예를 보여줍니다.

   ```
   [INFO] +- software.amazon.awssdk:dynamodb:jar:2.20.00:compile
   [INFO] |  +- software.amazon.awssdk:aws-core:jar:2.13.19:compile
   [INFO] +- software.amazon.awssdk:netty-nio-client:jar:2.20.00:compile
   ```

   `dynamodb`의 버전은 2.20.00이고 `aws-core`의 버전은 2.13.19입니다. `aws-core` 아티팩트 버전도 2.20.00이어야 합니다.

1. **로그 초기에 문을 확인**하여 정적 초기화 실패로 인해 클래스가 로드되지 않는지 확인합니다. 클래스가 처음 로드되지 않으면 클래스를 로드할 수 없는 *이유*를 지정하는 더 유용한 다른 예외가 발생할 수 있습니다. 이 잠재적으로 유용한 예외는 한 번만 발생하므로 이후 로그 문은 클래스를 찾을 수 없음을 보고합니다.

1. **배포 프로세스를 확인**하여 애플리케이션과 함께 필요한 JAR 파일을 실제로 배포하는지 확인합니다. 올바른 버전으로 구축하고 있지만 애플리케이션에 대한 클래스 경로를 만드는 프로세스가 필수 종속성을 제외하고 있는 경우일 수 있습니다.

## ‘`SignatureDoesNotMatch`’ 오류 또는 ‘계산한 요청 서명이 제공한 서명과 일치하지 않음’ 오류를 해결하는 방법
<a name="faq-signature-does-not-match-error"></a>

`SignatureDoesNotMatch` 오류는에서 생성된 서명 AWS SDK for Java 과에서 생성된 서명이 일치하지 AWS 서비스 않음을 나타냅니다. 다음 항목에서는 잠재적 원인을 설명합니다.
+ 프록시 또는 중개자가 요청을 수정합니다. 예를 들어 프록시 또는 로드 밸런서는 SDK에서 서명한 헤더, 경로 또는 쿼리 문자열을 수정할 수 있습니다.
+ 서비스 및 SDK는 각각에서 서명할 문자열을 생성할 때 요청을 인코딩하는 방식이 다릅니다.

이 문제를 디버깅하려면 SDK에 대한 [디버그 로깅을 사용](logging-slf4j.md#sdk-debug-level-logging)하는 것이 좋습니다. 오류를 재현하고 SDK가 생성한 정식 요청을 찾습니다. 로그에서 정식 요청에는 레이블이 `AWS4 String to sign: ...`으로 지정되고 서명할 문자열에는 `AWS4 Canonical Request: ...` 레이블이 지정됩니다.

예를 들어 프로덕션 환경에서만 재현할 수 있기 때문에 디버깅을 사용할 수 없는 경우 오류가 발생하면 요청에 대한 정보를 로깅하는 로직을 애플리케이션에 추가합니다. 그런 다음 해당 정보를 사용하여 디버그 로깅이 사용된 통합 테스트 시 프로덕션 외부에서 오류를 복제할 수 있습니다.

서명할 정식 요청과 문자열을 수집한 후 [AWS 서명 버전 4 사양](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-signing.html)과 비교하여 SDK가 서명할 문자열을 생성한 방식에 문제가 있는지 확인합니다. 문제가 있는 것 같으면 AWS SDK for Java에 대한 [GitHub 버그 보고서](https://github.com/aws/aws-sdk-java-v2/issues/new/choose)를 만들 수 있습니다.

아무것도 잘못 표시되지 않으면 서명할 SDK의 문자열을 문자열과 비교하여 일부가 실패 응답의 일부로 AWS 서비스 반환된다고 서명할 수 있습니다(예: Amazon S3). 사용할 수 없는 경우 [영향을 받는 서비스에 문의](https://aws.amazon.com/contact-us/)하여 비교를 위해 생성한 정식 요청과 문자열을 확인해야 합니다. 이러한 비교는 서비스와 클라이언트 간의 요청 또는 인코딩 차이를 수정했을 수 있는 중개자를 식별하는 데 도움이 될 수 있습니다.

요청 서명에 대한 자세한 내용은 AWS Identity and Access Management 사용 설명서의 [AWS API 요청 서명을](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-signing.html) 참조하세요.

**Example 표준 요청**  

```
PUT
/Example-Bucket/Example-Object
partNumber=19&uploadId=string
amz-sdk-invocation-id:f8c2799d-367c-f024-e8fa-6ad6d0a1afb9
amz-sdk-request:attempt=1; max=4
content-encoding:aws-chunked
content-length:51
content-type:application/octet-stream
host:xxxxx
x-amz-content-sha256:STREAMING-UNSIGNED-PAYLOAD-TRAILER
x-amz-date:20240308T034733Z
x-amz-decoded-content-length:10
x-amz-sdk-checksum-algorithm:CRC32
x-amz-trailer:x-amz-checksum-crc32
```

**Example 서명할 문자열**  

```
AWS4-HMAC-SHA256
20240308T034435Z
20240308/us-east-1/s3/aws4_request
5f20a7604b1ef65dd89c333fd66736fdef9578d11a4f5d22d289597c387dc713
```

## ‘`java.lang.IllegalStateException`: 연결 풀 종료’ 오류를 해결하는 방법
<a name="faq-connection-pool-shutdown-exception"></a>

이 오류는 기본 Apache HTTP 연결 풀이 종료되었음을 나타냅니다. 다음 항목에서는 잠재적 원인을 설명합니다.
+ **SDK 클라이언트가 조기에 종료되었습니다. ** SDK는 연결된 클라이언트가 종료될 때만 연결 풀을 종료합니다. 사용 중인 리소스는 종료하지 않아야 합니다.
+ **`java.lang.Error`가 발생했습니다.** `OutOfMemoryError` 등의 오류로 인해 Apache HTTP 연결 풀이 [종료됩니다](https://github.com/apache/httpcomponents-client/blob/6a741b4f8f23e6c5c7cc42c36c2acabfac19c3d6/httpclient/src/main/java/org/apache/http/impl/execchain/MainClientExec.java#L368). 로그에서 오류 스택 추적을 검사합니다. 또한 코드를 검토하여 `Throwable` 또는 `Error`를 포착하지만 표시에서 오류를 방지하는 출력을 포함하는 위치를 찾습니다. 코드가 오류를 보고하지 않는 경우 정보가 로그되도록 코드를 다시 작성합니다. 로그된 정보는 오류의 근본 원인을 확인하는 데 도움이 됩니다.
+ **종료된 후 `DefaultCredentialsProvider#create()`에서 반환된 자격 증명 공급자를 사용하려고 했습니다**. [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/DefaultCredentialsProvider.html#create()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/DefaultCredentialsProvider.html#create())는 싱글톤 인스턴스를 반환하므로 종료되어 있고 코드가 `resolveCredentials` 메서드를 직접적으로 호출하면 캐시된 자격 증명(또는 토큰)이 만료된 후 예외가 발생합니다.

  다음 예제와 같이 `DefaultCredentialsProvider`가 종료된 위치가 있는지 코드를 확인합니다.
  + 싱글톤 인스턴스는 `DefaultCredentialsProvider#close().`를 직접적으로 호출하여 종료됩니다.

    ```
    DefaultCredentialsProvider defaultCredentialsProvider = DefaultCredentialsProvider.create(); // Singleton instance returned.
    AwsCredentials credentials = defaultCredentialsProvider.resolveCredentials();
    
    // Make calls to AWS 서비스.
    
    defaultCredentialsProvider.close();  // Explicit close.
    
    // Make calls to AWS 서비스.
    
    // After the credentials expire, either of the following calls eventually results in a "Connection pool shut down" exception.
    credentials = defaultCredentialsProvider.resolveCredentials();
    // Or
    credentials = DefaultCredentialsProvider.create().resolveCredentials();
    ```
  + try-with-resources 블록에서 `DefaultCredentialsProvider#create()`를 간접적으로 호출합니다.

    ```
    try (DefaultCredentialsProvider defaultCredentialsProvider = DefaultCredentialsProvider.create()) {
        AwsCredentials credentials = defaultCredentialsProvider.resolveCredentials();
        
        // Make calls to AWS 서비스.
    
    } // After the try-with-resources block exits, the singleton DefaultCredentialsProvider is closed.
    
    // Make calls to AWS 서비스.
    
    DefaultCredentialsProvider defaultCredentialsProvider = DefaultCredentialsProvider.create(); // The closed singleton instance is returned.
    // If the credentials (or token) has expired, the following call results in the error.
    AwsCredentials credentials = defaultCredentialsProvider.resolveCredentials();
    ```

  코드가 싱글톤 인스턴스를 종료하고 `DefaultCredentialsProvider`를 사용하여 자격 증명을 해결해야 하는 경우 `DefaultCredentialsProvider.builder().build()`를 직접적으로 호출하여 새로운 비싱글톤 인스턴스를 만듭니다.

## ‘AwsCredentialsProviderChain 체인의 공급자로부터 자격 증명을 로드할 수 없음’ 오류를 수정하는 방법
<a name="faq-credentials-provider-chain"></a>

이 오류는가 기본 AWS 자격 증명 공급자 체인의 자격 증명 공급자를 통해 유효한 자격 증명을 찾을 AWS SDK for Java 2.x 수 없음을 나타냅니다. SDK는 특정 순서로 자격 증명을 자동으로 검색하며, 이 오류는 체인의 모든 공급자가 유효한 자격 증명을 제공하지 못할 때 발생합니다.

전체 오류 메시지는 일반적으로 다음과 같습니다(가독성을 높이기 위해 줄 끝 및 들여쓰기가 추가됨).

```
Unable to load credentials from any of the providers in the chain AwsCredentialsProviderChain(
    credentialsProviders=[
        SystemPropertyCredentialsProvider(),
        EnvironmentVariableCredentialsProvider(), 
        WebIdentityTokenCredentialsProvider(), 
        ProfileCredentialsProvider(profileName=default, profileFile=ProfileFile(sections=[])), 
        ContainerCredentialsProvider(), 
        InstanceProfileCredentialsProvider()
    ]) : [
        SystemPropertyCredentialsProvider(): Unable to load credentials from system settings.
        Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) 
        or system property (aws.accessKeyId)., 

        EnvironmentVariableCredentialsProvider(): Unable to load credentials from system settings. 
        Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) 
        or system property (aws.accessKeyId)., 

        WebIdentityTokenCredentialsProvider(): To use web identity tokens, the 'sts' service module 
        must be on the class path., 

        ProfileCredentialsProvider(profileName=default, profileFile=ProfileFile(sections=[])): 
        Profile file contained no credentials for profile 'default': ProfileFile(sections=[]), 

        ContainerCredentialsProvider(): Cannot fetch credentials from container - neither 
        AWS_CONTAINER_CREDENTIALS_FULL_URI or AWS_CONTAINER_CREDENTIALS_RELATIVE_URI environment 
        variables are set., 

        InstanceProfileCredentialsProvider(): Failed to load credentials from IMDS.]
```

### 일반적인 원인 및 솔루션
<a name="faq-cred-provider-chain-common-causes-and-solutions"></a>

#### 자격 증명 구성 검토
<a name="faq-cred-provider-chain-check-config"></a>

기본 자격 증명 공급자를 사용하는 경우(자격 증명을 명시적으로 구성하지 않고 `ServiceClient.create()` 직접 호출) SDK는 특정 순서로 자격 증명을 검색합니다. [기본 자격 증명 공급자 체인이 작동하는 방식](credentials-chain.md)을 검토하여 SDK가 확인하는 자격 증명과 순서를 파악합니다.

사용하려는 자격 증명 구성 메서드가 환경에 올바르게 설정되어 있는지 확인합니다.

##### Amazon EC2 인스턴스의 경우
<a name="faq-cred-check-ec2"></a>
+ **IAM 역할 확인:** IAM 역할이 인스턴스에 연결되어 있는지 확인합니다.
+ **간헐적 IMDS 장애:** 간헐적 장애(일반적으로 수백 밀리초 지속)가 발생하는 경우 이는 일반적으로 인스턴스 메타데이터 서비스(IMDS)에 도달하는 일시적인 네트워크 문제를 나타냅니다.

  솔루션
  + [디버그 로깅](logging-slf4j.md#sdk-debug-level-logging)을 사용하여 장애의 타이밍 및 빈도 분석
  + 자격 증명 관련 장애에 대해 애플리케이션에서 재시도 로직 구현 고려
  + 인스턴스와 IMDS 엔드포인트 간의 네트워크 연결 문제 확인

##### 컨테이너 환경의 경우
<a name="faq-cred-check-container-env"></a>

작업 역할(Amazon ECS) 또는 서비스 계정(Amazon EKS)이 구성되어 있고 필요한 환경 변수가 설정되어 있는지 확인합니다.

##### 로컬 개발의 경우
<a name="faq-cred-check-local-dev"></a>

환경 변수, 자격 증명 파일 또는 IAM Identity Center 구성이 있는지 확인합니다.

##### 웹 ID 페더레이션의 경우
<a name="faq-cred-check-web-id-federation"></a>
+ **구성 확인: **웹 자격 증명 토큰 파일이 존재하고 필요한 환경 변수가 구성되어 있는지 확인합니다.
+ **STS 모듈 종속성 누락:** 오류(`To use web identity tokens, the 'sts' service module must be on the class path`)가 표시되면 STS 모듈을 종속성으로 추가해야 합니다. 이는 Amazon EKS Pod Identity 또는 기타 웹 자격 증명 토큰 인증을 사용할 때 흔히 발생합니다.

  솔루션: 프로젝트 종속성에 STS 모듈을 추가합니다.
  + 

    ```
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>sts</artifactId>
    </dependency>
    ```

    일부 서비스의 경우 `aws-query-protocol` 종속성이 필요할 수 있습니다.

    ```
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>aws-query-protocol</artifactId>
    </dependency>
    ```

#### 네트워크 또는 프록시 연결 문제
<a name="faq-credentials-provider-chain-network-issues"></a>

자격 증명 공급자 체인에 `Connection refused` 오류가 표시되는 경우 이는 일반적으로 SDK가 AWS 엔드포인트에 도달하려고 할 때의 네트워크 연결 문제를 나타냅니다.

**솔루션:**
+ 프록시 서버를 사용하는 경우 프록시 구성 확인
+ 네트워크가 AWS 엔드포인트에 대한 아웃바운드 HTTPS 연결을 허용하는지 확인
+ [디버그 로깅](logging-slf4j.md#sdk-debug-level-logging)을 사용하여 자세한 연결 시도 확인
+ `curl` 등의 도구를 사용하여 AWS 엔드포인트에 대한 네트워크 액세스를 확인하는 연결 테스트

# 의 SDK 시작 시간 단축 AWS Lambda
<a name="lambda-optimize-starttime"></a>

의 목표 중 하나는 함수의 시작 지연 시간을 AWS Lambda 줄이는 AWS SDK for Java 2.x 것입니다. SDK에는 시작 시간을 줄이는 변경 사항이 포함되어 있으며, 이에 대해서는 이 항목의 마지막 부분에서 설명합니다.

먼저, 이 항목에서는 콜드 스타트 시간을 줄이기 위해 적용할 수 있는 변경 사항에 초점을 맞춥니다. 여기에는 코드 구조 및 서비스 클라이언트 구성 변경이 포함됩니다.

## AWS CRT 기반 HTTP 클라이언트 사용
<a name="lambda-quick-url"></a>

작업의 경우 동기식 시나리오[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/crt/AwsCrtHttpClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/crt/AwsCrtHttpClient.html)의 경우 AWS Lambda, 비동기식 시나리오의 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/crt/AwsCrtAsyncHttpClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/crt/AwsCrtAsyncHttpClient.html) 경우를 사용하는 것이 좋습니다.

이 가이드의 [AWS CRT 기반 HTTP 클라이언트 구성](http-configuration-crt.md) 주제에서는 HTTP 클라이언트 사용 시 이점, 종속성을 추가하는 방법, 서비스 클라이언트에서 이를 사용하도록 구성하는 방법을 설명합니다.

## 사용하지 않는 HTTP 클라이언트 종속성을 제거
<a name="lambda-quick-remove-deps"></a>

 AWS CRT 기반 클라이언트를 명시적으로 사용하는 것 외에도 SDK가 기본적으로 제공하는 다른 HTTP 클라이언트를 제거할 수 있습니다. 로드해야 하는 라이브러리 수가 적을수록 Lambda 시작 시간이 단축되므로 JVM이 로드해야 하는 사용되지 않은 아티팩트를 제거해야 합니다.

Maven `pom.xml` 파일의 다음 코드 조각은 Apache 기반 HTTP 클라이언트와 Netty 기반 HTTP 클라이언트의 제외를 보여줍니다. (이 클라이언트는 AWS CRT 기반 클라이언트를 사용할 때 필요하지 않습니다.) 이 예제에서는 S3 클라이언트 종속성에서 HTTP 클라이언트 아티팩트를 제외하고 AWS CRT 기반 HTTP 클라이언트에 대한 액세스를 허용하는 아`aws-crt-client`티팩트를 추가합니다.

```
<project>
    <properties>
        <aws.java.sdk.version>2.27.21</aws.java.sdk.version>
    <properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>software.amazon.awssdk</groupId>
                <artifactId>bom</artifactId>
                <version>${aws.java.sdk.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>aws-crt-client</artifactId>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>s3</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>software.amazon.awssdk</groupId>
                    <artifactId>netty-nio-client</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>software.amazon.awssdk</groupId>
                    <artifactId>apache-client</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
</project>
```

**참고**  
`pom.xml` 파일의 모든 서비스 클라이언트 종속성에 `<exclusions>` 요소를 추가합니다.

## 바로가기 검색이 가능하도록 서비스 클라이언트를 구성
<a name="lambda-quick-clients"></a>

**리전 지정**  
서비스 클라이언트를 생성할 때는 서비스 클라이언트 빌더에서 `region` 메서드를 호출하세요. 이렇게 하면 여러 위치에서 AWS 리전 정보를 확인하는 SDK의 기본 [리전 조회 프로세스가](region-selection.md#default-region-provider-chain) 단축됩니다.  
Lambda 코드를 리전과 독립적으로 유지하려면 `region` 메서드 내에서 다음 코드를 사용하세요. 이 코드는 Lambda 컨테이너에서 설정한 `AWS_REGION` 환경 변수에 액세스합니다.  

```
Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))
```

**`EnvironmentVariableCredentialProvider` 사용**  
리전 정보에 대한 기본 조회 동작과 마찬가지로 SDK는 여러 위치에서 자격 증명을 찾습니다. 서비스 클라이언트를 구축할 때 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/EnvironmentVariableCredentialsProvider.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/EnvironmentVariableCredentialsProvider.html)를 지정하면 자격 증명용 SDK의 조회 프로세스에서 시간을 절약할 수 있습니다.  
이 자격 증명 공급자를 사용하면 코드를 Lambda 함수에 사용할 수 있지만 Amazon EC2 또는 다른 시스템에서는 작동하지 않을 수 있습니다.  
어느 시점에서 [Lambda SnapStart for Java](#lambda-quick-snapstart)를 사용하려는 경우 기본 자격 증명 공급자 체인을 사용하여 자격 증명을 조회해야 합니다. `EnvironmentVariableCredentialsProvider`를 지정하면 초기 자격 증명 조회가 작동하지만 SnapStart가 활성화되면 [Java 런타임이 컨테이너 자격 증명 환경 변수를 설정](https://docs.aws.amazon.com/lambda/latest/dg/snapstart-activate.html#snapstart-credentials)합니다. 활성화 시 `EnvironmentVariableCredentialsProvider`(액세스 키 환경 변수)에서 사용하는 환경 변수는 Java SDK에서 사용할 수 없습니다.

다음 코드 조각은 Lambda 환경에서 사용하도록 적절하게 구성된 S3 서비스 클라이언트를 보여줍니다.

```
S3Client s3Client = S3Client.builder()
    .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable())))
    .credentialsProvider(EnvironmentVariableCredentialsProvider.create())
    .httpClient(AwsCrtHttpClient.builder().build())
    .build();
```

## Lambda 함수 핸들러 외부에서 SDK 클라이언트 초기화
<a name="lambda-quick-initialize"></a>

Lambda 핸들러 메서드 외부에서 SDK 클라이언트를 초기화하는 것이 좋습니다. 이렇게 하면 실행 컨텍스트를 재사용하는 경우 서비스 클라이언트의 초기화를 건너뛸 수 있습니다. 클라이언트 인스턴스와 해당 연결을 재사용하면 핸들러 메서드의 후속 호출이 더 빠르게 발생합니다.

다음 예제에서는 정적 팩토리 메서드를 사용하여 생성자에서 `S3Client` 인스턴스를 초기화합니다. Lambda 환경에서 관리하는 컨테이너를 재사용하는 경우 초기화된 `S3Client` 인스턴스가 재사용됩니다.

```
public class App implements RequestHandler<Object, Object> {
    private final S3Client s3Client;

    public App() {
        s3Client = DependencyFactory.s3Client();
    }

    @Override
    public Object handle Request(final Object input, final Context context) {
         ListBucketResponse response = s3Client.listBuckets();
         // Process the response.
    }
}
```

## 종속성 주입을 최소화
<a name="lambda-quick-di"></a>

종속성 주입(DI) 프레임워크는 설정 프로세스를 완료하는 데 시간이 더 걸릴 수 있습니다. 또한 추가 종속성이 필요할 수 있으며, 이 경우 로드하는 데 시간이 걸립니다.

DI 프레임워크가 필요한 경우 [Dagger](https://dagger.dev/dev-guide/)와 같은 가벼운 DI 프레임워크를 사용하는 것이 좋습니다.

## Maven Archetype 타겟팅 사용 AWS Lambda
<a name="lambda-quick-maven"></a>

 AWS Java SDK 팀은 시작 시간을 최소화하면서 Lambda 프로젝트를 부트스트랩하기 위해 [Maven Archetype](https://github.com/aws/aws-sdk-java-v2/tree/master/archetypes/archetype-lambda) 템플릿을 개발했습니다. 아키타입에서 Maven 프로젝트를 구축하고 종속성이 Lambda 환경에 적합하게 구성되어 있는지 알 수 있습니다.

아키타입에 대해 자세히 알아보고 예제 배포를 진행하려면 이 [블로그 게시물](https://aws.amazon.com/blogs/developer/bootstrapping-a-java-lambda-application-with-minimal-aws-java-sdk-startup-time-using-maven/)을 참조하세요.

## Java용 Lambda SnapStart를 고려
<a name="lambda-quick-snapstart"></a>

런타임 요구 사항이 호환되는 경우는 [Java용 Lambda SnapStart](https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html)를 AWS 제공합니다. Lambda SnapStart는 Java 기능의 시작 성능을 향상시키는 인프라 기반 솔루션입니다. 새 버전의 함수를 게시하면 Lambda SnapStart가 이를 초기화하고 메모리 및 디스크 상태에 대한 변경이 불가능하고 암호화된 스냅샷을 생성합니다. 그런 다음 SnapStart는 재사용을 위해 스냅샷을 캐싱합니다.

## 시작 시간에 영향을 미치는 버전 2.x 변경 사항
<a name="example-client-configuration"></a>

코드 변경 사항 외에도 SDK for Java 버전 2.x에는 시작 시간을 줄이는 세 가지 주요 변경 사항이 포함되어 있습니다.
+ 초기화 시간을 개선하는 직렬화 라이브러리인 [jackson-jr](https://github.com/FasterXML/jackson-jr)을 사용
+ JDK의 일부인 날짜 및 시간 객체에 대해 [java.time](https://docs.oracle.com/javase/8/docs/api/index.html?java/time.html) 라이브러리를 사용
+ 로깅 facade에 [Slf4j](https://www.slf4j.org/)를 사용

## 추가 리소스
<a name="lambda-quick-resources"></a>

 AWS Lambda 개발자 안내서에는 Java 전용이 아닌 Lambda 함수를 개발하기 위한 [모범 사례에 대한 섹션](https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html)이 포함되어 있습니다.

가 사용하는 Java에서 클라우드 네이티브 애플리케이션을 빌드하는 예제는이 [워크숍 콘텐츠를](https://github.com/aws-samples/aws-lambda-java-workshop) AWS Lambda참조하세요. 워크숍에서는 성능 최적화 및 기타 모범 사례에 대해 논의합니다.

시작 대기 시간을 줄이기 위해 미리 컴파일된 정적 이미지를 사용하는 것을 고려할 수 있습니다. 예를 들어 Java 2.x 및 Maven용 SDK를 사용하여 [GraalVM 네이티브 이미지를 빌드](setup-project-graalvm.md)할 수 있습니다.

# `ContentStreamProvider`에서 구현 AWS SDK for Java 2.x
<a name="content-stream-provider"></a>

`ContentStreamProvider`는에서 입력 데이터의 여러 읽기 AWS SDK for Java 2.x 를 허용하는 데 사용되는 추상화입니다. 이 주제에서는 애플리케이션에서 `ContentStreamProvider`를 올바르게 구현하는 방법을 설명합니다.

SDK for Java 2.x는 전체 스트림을 읽어야 할 때마다 `ContentStreamProvider#newStream()` 메서드를 사용합니다. 전체 스트림에서 작동하려면 반환된 스트림이 항상 콘텐츠의 시작 부분에 있어야 하며 동일한 데이터를 포함해야 합니다.

다음 섹션에서는 이 동작을 올바르게 구현하는 방법에 대한 3가지 접근 방식을 제공합니다.

## `mark()` 및 `reset()` 사용
<a name="csp-impl-mark-reset"></a>

아래 예제에서는 읽기를 시작하기 전에 생성자에서 `mark(int)`를 사용하여 스트림을 다시 시작으로 재설정할 수 있도록 합니다. `newStream()`을 간접 호출할 때마다 스트림이 재설정됩니다.

```
public class MyContentStreamProvider implements ContentStreamProvider {  
    private InputStream contentStream;  
  
    public MyContentStreamProvider(InputStream contentStream) {  
        this.contentStream = contentStream;  
        this.contentStream.mark(MAX_LEN);  
    }  
  
    @Override  
    public InputStream newStream() {  
        contentStream.reset();  
        return contentStream;  
    }  
}
```

## `mark()` 및 `reset()`을 사용할 수 없는 경우 버퍼링 사용
<a name="csp-impl-unsupported-streams"></a>

 스트림이 `mark()` 및 `reset()`을 직접 지원하지 않는 경우 먼저 스트림을 `BufferedInputStream`에 래핑하여 이전에 표시된 솔루션을 계속 사용할 수 있습니다.

```
public class MyContentStreamProvider implements ContentStreamProvider {  
    private BufferedReader contentStream;  
  
    public MyContentStreamProvider(InputStream contentStream) {  
        this.contentStream = new BufferedInputStream(contentStream);  
        this.contentStream.mark(MAX_LEN);
    }  
  
    @Override  
    public InputStream newStream() {  
        contentStream.reset();  
        return contentStream;  
    }  
}
```

## 새 스트림 만들기
<a name="csp-impl-new-stream"></a>

더 간단한 접근 방식은 각 간접 호출에서 데이터에 대한 새 스트림을 얻고 이전 스트림을 종료하는 것입니다.

```
public class MyContentStreamProvider implements ContentStreamProvider {  
    private InputStream contentStream;  
  
    @Override  
    public InputStream newStream() {  
        if (contentStream != null) {  
            contentStream.close();  
        }  
        contentStream = openStream();  
        return contentStream;  
    }  
}
```

# DNS 이름 조회를 위한 JVM TTL 설정
<a name="jvm-ttl-dns"></a>

Java 가상 머신(JVM)은 DNS 이름 조회를 캐시합니다. JVM은 호스트 이름을 IP 주소로 확인하는 경우 *Time-To-Live*(TTL)라고 하는 지정된 기간 동안 IP 주소를 캐시합니다.

 AWS 리소스는 가끔 변경되는 DNS 이름 항목을 사용하므로 TTL 값이 5초인 JVM을 구성하는 것이 좋습니다. 이렇게 하면 리소스의 IP 주소가 변경될 때 애플리케이션이 DNS를 다시 쿼리하여 리소스의 새 IP 주소를 수신하고 사용할 수 있습니다.

일부 Java 구성에서는 JVM이 다시 시작될 때까지 DNS 항목을 새로 고치지 *않도록* JVM 기본 TTL이 설정되기도 합니다. 따라서 애플리케이션이 실행되는 동안 AWS 리소스의 IP 주소가 변경되면 JVM을 *수동으로 다시* 시작하고 캐시된 IP 정보가 새로 고쳐질 때까지 해당 리소스를 사용할 수 없습니다. 이 경우 캐시된 IP 정보를 정기적으로 새로 고치도록 JVM의 TTL을 설정해야 합니다.

## JVM TTL을 설정하는 방법
<a name="how-to-set-the-jvm-ttl"></a>

JVM의 TTL을 수정하려면 [networkaddress.cache.ttl](https://docs.oracle.com/en/java/javase/17/core/java-networking.html#GUID-A680DADB-C4C1-40F1-B568-D9A97C917F5D) 보안 속성 값을 설정합니다. `networkaddress.cache.ttl`는 시스템 *속성이 아닌 보안* 속성입니다. 즉, `-D` 명령줄 플래그로 설정할 수 없습니다.

### 옵션 1: 애플리케이션에서 프로그래밍 방식으로 설정
<a name="set-ttl-programmatically"></a>

애플리케이션 시작 [https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/security/Security.html](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/security/Security.html) 초기, AWS SDK 클라이언트가 생성되기 전 및 네트워크 요청이 이루어지기 전에를 호출합니다.

```
import java.security.Security;

public class MyApplication {
    public static void main(String[] args) {
        Security.setProperty("networkaddress.cache.ttl", "5");

        // ... create SDK clients and run application
    }
}
```

### 옵션 2: java.security 파일에서 설정
<a name="set-ttl-java-security-file"></a>

Java 8용 `$JAVA_HOME/jre/lib/security/java.security` 파일 또는 Java 11 이상의 `$JAVA_HOME/conf/security/java.security` 파일에서 `networkaddress.cache.ttl` 속성을 설정합니다.

다음은 5초로 설정된 TTL 캐시를 보여주는 `java.security` 파일의 코드 조각입니다.

```
#
# The Java-level namelookup cache policy for successful lookups:
#
# any negative value: caching forever
# any positive value: the number of seconds to cache an address for
# zero: do not cache
#
...
networkaddress.cache.ttl=5
...
```

`$JAVA_HOME` 환경 변수로 표시되는 JVM에서 실행되는 모든 애플리케이션은이 설정을 사용합니다.

### 옵션 3: JDK 시스템 속성 대체 사용(명령줄)
<a name="set-ttl-system-property"></a>

보안 구성 또는 코드를 수정할 수 없는 경우 JDK 시스템 속성을 사용할 수 있습니다. 보안 속성이 정의되지 않은 경우 폴백 역할을 합니다.
+ `sun.net.inetaddr.ttl` - 성공적인 조회 제어(긍정 TTL)
+ `sun.net.inetaddr.negative.ttl` - 실패한 조회 제어(음의 TTL)

```
java -Dsun.net.inetaddr.ttl=5 -Dsun.net.inetaddr.negative.ttl=1 -jar myapp.jar
```

**참고**  
이는 "향후 릴리스에서 지원되지 않을 수 있음"이라는 프라이빗 속성으로 [Oracle Java 8 네트워킹 속성](https://docs.oracle.com/javase/8/docs/technotes/guides/net/properties.html) 참조에 문서화된 JDK 내부 속성입니다. 가능하면 옵션 1-2를 사용합니다.

# AWS SDK for Java에서 HTTP/2 작업
<a name="http2"></a>

HTTP/2는 HTTP 프로토콜의 주요 내용 개정입니다. 이 새 버전에는 성능 개선을 위한 여러 기능 향상이 있습니다.
+ 이진 데이터 인코딩으로 더욱 효율적인 데이터 전송을 제공합니다.
+ 헤더 압축으로 클라이언트가 다운로드하는 오버헤드 바이트를 줄여 클라이언트로 더욱 빠르게 콘텐츠를 가져오는 데 도움이 됩니다. 이는 특히 이미 대역폭 제약이 있는 모바일 클라이언트에 유용합니다.
+ 양방향 비동기 통신(멀티플렉싱)을 통해 클라이언트와 AWS 간에 여러 요청 및 응답 메시지가 여러 개의 연결 대신 단일 연결로 동시에 전송되어 성능이 개선됩니다.

최신 SDK로 업그레이드한 개발자는 사용하는 서비스에서 지원할 때 HTTP/2를 자동으로 사용합니다. 새로운 프로그래밍 인터페이스는 HTTP/2 기능을 원활하게 활용하게 애플리케이션을 빌드하는 새로운 방법을 제공합니다.

AWS SDK for Java 2.x에는 HTTP/2 프로토콜을 구현하는 이벤트 스트리밍에 대한 새 API가 있습니다. 이러한 새 API를 사용하는 방법에 대한 예는 [Kinesis로 작업](examples-kinesis.md)을 참조하세요.