

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

# Amazon S3와 작업
<a name="examples-s3"></a>

이 섹션에서는 AWS SDK for Java 2.x를 사용하여 Amazon S3로 작업하는 경우의 배경 정보를 제공합니다. 이 섹션에서는 이 가이드의 *코드 예제* 섹션에 제시된 [Amazon S3 Java v2 예제](java_s3_code_examples.md)를 보완합니다.

## 의 S3 클라이언트 AWS SDK for Java 2.x
<a name="s3-clients"></a>

는 다양한 유형의 S3 클라이언트를 AWS SDK for Java 2.x 제공합니다. 다음 표는 차이점을 보여주며 이를 통해 사용 사례에 가장 적합한 것을 결정할 수 있습니다.


**Amazon S3 클라이언트의 다양한 종류**  

| S3 클라이언트 | 간단한 설명 | 사용해야 하는 경우 | 제한 및 단점 | 
| --- | --- | --- | --- | 
|  **AWS CRT 기반 S3 클라이언트** 인터페이스: [S3AsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html) Builder: [S3CrtAsyncClientBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/examples-s3.html) [성능 S3 클라이언트: AWS CRT 기반 S3 클라이언트 사용](crt-based-s3-client.md)을(를) 참조하세요.  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/examples-s3.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/examples-s3.html)  | 
|  **멀티파트가 *사용 설정된* Java 기반 S3 비동기식 클라이언트** 인터페이스: [S3AsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html) Builder: [S3AsyncClientBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClientBuilder.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/examples-s3.html) [병렬 전송을 사용하도록 Java 기반 S3 비동기식 클라이언트 구성](s3-async-client-multipart.md)을(를) 참조하세요.  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/examples-s3.html)  |  AWS CRT 기반 S3 클라이언트보다 성능이 떨어집니다. | 
|  **멀티파트가 *사용 설정되지 않은* Java 기반 S3 비동기식 클라이언트** 인터페이스: [S3AsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html) Builder: [S3AsyncClientBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClientBuilder.html) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/examples-s3.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/examples-s3.html)  |  성능 최적화가 필요하지 않습니다.  | 
|  **Java 기반 S3 동기식 클라이언트** 인터페이스: [S3Client](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html) Builder: [S3ClientBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3ClientBuilder.html) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/examples-s3.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/examples-s3.html)  |  성능 최적화가 필요하지 않습니다.  | 

**참고**  
버전 2.18.x 이상에서는 엔드포인트 재정의를 포함할 때 [가상 호스팅 방식 주소 지정](https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html#virtual-hosted-style-access)을 AWS SDK for Java 2.x 사용합니다. 이는 버킷 이름이 유효한 DNS 레이블인 한 적용됩니다.  
`true`에서 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3BaseClientBuilder.html#forcePathStyle(java.lang.Boolean](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3BaseClientBuilder.html#forcePathStyle(java.lang.Boolean) 메서드를 호출하여 클라이언트가 버킷에 경로 스타일 주소 지정을 사용하도록 강제합니다.  
다음 예제는 엔드포인트 재정의 및 경로 스타일 주소 지정을 사용하여 구성된 서비스 클라이언트를 보여줍니다.  

```
S3Client client = S3Client.builder()
                          .region(Region.US_WEST_2)
                          .endpointOverride(URI.create("https://s3.us-west-2.amazonaws.com"))
                          .forcePathStyle(true)
                          .build();
```

**Topics**
+ [SDK의 S3 클라이언트](#s3-clients)
+ [S3에 스트림 업로드](best-practices-s3-uploads.md)
+ [미리 서명된 URL](examples-s3-presign.md)
+ [교차 리전 액세스](s3-cross-region.md)
+ [체크섬을 통한 데이터 무결성 보호](s3-checksums.md)
+ [고성능 S3 클라이언트 사용](crt-based-s3-client.md)
+ [병렬 전송 지원 구성](s3-async-client-multipart.md)
+ [파일 및 디렉터리 전송](transfer-manager.md)
+ [S3 이벤트 알림](examples-s3-event-notifications.md)

# 를 사용하여 Amazon S3에 스트림 업로드 AWS SDK for Java 2.x
<a name="best-practices-s3-uploads"></a>

스트림을 사용하여 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#putObject(software.amazon.awssdk.services.s3.model.PutObjectRequest,software.amazon.awssdk.core.sync.RequestBody)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#putObject(software.amazon.awssdk.services.s3.model.PutObjectRequest,software.amazon.awssdk.core.sync.RequestBody)) 또는 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#uploadPart(software.amazon.awssdk.services.s3.model.UploadPartRequest,software.amazon.awssdk.core.sync.RequestBody)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#uploadPart(software.amazon.awssdk.services.s3.model.UploadPartRequest,software.amazon.awssdk.core.sync.RequestBody))를 사용하여 S3에 콘텐츠를 업로드하는 경우 동기식 API의 `RequestBody` 팩토리 클래스를 사용하여 스트림을 제공합니다. 비동기식 API의 경우 `AsyncRequestBody`는 동등한 팩토리 클래스입니다.

## 스트림을 업로드하는 방법
<a name="s3-stream-upload-methods"></a>

동기식 API의 경우 다음과 같은 `RequestBody`의 팩토리 메서드를 사용하여 스트림을 제공할 수 있습니다.
+ `[fromInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/sync/RequestBody.html#fromInputStream(java.io.InputStream,long))(InputStream inputStream, long contentLength)`

  `[fromContentProvider](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/sync/RequestBody.html#fromContentProvider(software.amazon.awssdk.http.ContentStreamProvider,long,java.lang.String))(ContentStreamProvider provider, long contentLength, String mimeType)`
  + `ContentStreamProvider`에는 `fromInputStream(InputStream inputStream)` 팩토리 메서드가 있습니다.
+ `[fromContentProvider](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/sync/RequestBody.html#fromContentProvider(software.amazon.awssdk.http.ContentStreamProvider,java.lang.String))(ContentStreamProvider provider, String mimeType)`

비동기식 API의 경우 다음과 같은 `AsyncRequestBody`의 팩토리 메서드를 사용할 수 있습니다.
+ `[fromInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html#fromInputStream(java.io.InputStream,java.lang.Long,java.util.concurrent.ExecutorService))(InputStream inputStream, Long contentLength, ExecutorService executor)` 
+ `[fromInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html#fromInputStream(software.amazon.awssdk.core.async.AsyncRequestBodyFromInputStreamConfiguration))(AsyncRequestBodyFromInputStreamConfiguration configuration)`
  + AsyncRequestBodyFromInputStreamConfiguration.Builder를 사용하여 스트림을 제공합니다.
+ `[fromInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html#fromInputStream(java.util.function.Consumer))(Consumer<AsyncRequestBodyFromInputStreamConfiguration.Builder> configuration)`
+ `[forBlockingInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html#forBlockingInputStream(java.lang.Long))(Long contentLength)`
  + 결과 `[BlockingInputStreamAsyncRequestBody](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/BlockingInputStreamAsyncRequestBody.html)`에는 스트림을 제공하는 데 사용할 수 있는 `writeInputStream(InputStream inputStream)` 메서드가 포함되어 있습니다.

## 업로드 수행
<a name="s3-upload-stream-perform"></a>

### 스트림의 길이를 알고 있는 경우
<a name="s3-stream-upload-supply-content-length"></a>

이전에 표시된 메서드의 서명에서 볼 수 있듯이 대부분의 메서드는 콘텐츠 길이 파라미터를 허용합니다.

바이트 단위로 콘텐츠 길이를 알고 있는 경우 정확한 값을 입력합니다.

```
// Always provide the exact content length when it's available.
long contentLength = 1024; // Exact size in bytes.
s3Client.putObject(req -> req
    .bucket("amzn-s3-demo-bucket")
    .key("my-key"),
RequestBody.fromInputStream(inputStream, contentLength));
```

**주의**  
 입력 스트림에서 업로드할 때 지정된 콘텐츠 길이가 실제 바이트 수와 일치하지 않으면 다음과 같은 상황이 발생할 수 있습니다.  
잘린 객체 - 지정된 길이가 너무 작을 경우
업로드 실패 또는 연결 중단 - 지정된 길이가 너무 큰 경우

### 스트림 길이를 모르는 경우
<a name="s3-stream-upload-unknown-length"></a>

#### 동기식 API 사용
<a name="s3-upload-unknown-sync-client"></a>

`fromContentProvider(ContentStreamProvider provider, String mimeType)`를 사용합니다.

```
public PutObjectResponse syncClient_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3Client s3Client = S3Client.create();

    RequestBody body = RequestBody.fromContentProvider(ContentStreamProvider.fromInputStream(inputStream), "text/plain");
    PutObjectResponse putObjectResponse = s3Client.putObject(b -> b.bucket(BUCKET_NAME).key(KEY_NAME), body);
    return putObjectResponse;
}
```

SDK는 메모리의 전체 스트림을 버퍼링하여 콘텐츠 길이를 계산하므로 대용량 스트림에서 메모리 문제가 발생할 수 있습니다. 동기식 클라이언트로 대용량 스트림을 업로드해야 하는 경우 멀티파트 API를 사용하는 것이 좋습니다.

##### 동기식 클라이언트 API 및 멀티파트 API를 사용하여 스트림 업로드
<a name="sync-multipart-upload-stream"></a>

```
public static void uploadStreamToS3(String bucketName, String key, InputStream inputStream) {
    // Create S3 client
    S3Client s3Client = S3Client.create();
    try {
        // Step 1: Initiate the multipart upload
        CreateMultipartUploadRequest createMultipartUploadRequest = CreateMultipartUploadRequest.builder()
                .bucket(bucketName)
                .key(key)
                .build();

        CreateMultipartUploadResponse createResponse = s3Client.createMultipartUpload(createMultipartUploadRequest);
        String uploadId = createResponse.uploadId();
        System.out.println("Started multipart upload with ID: " + uploadId);

        // Step 2: Upload parts
        List<CompletedPart> completedParts = new ArrayList<>();
        int partNumber = 1;
        byte[] buffer = new byte[PART_SIZE];
        int bytesRead;

        try {
            while ((bytesRead = readFullyOrToEnd(inputStream, buffer)) > 0) {
                // Create request to upload a part
                UploadPartRequest uploadPartRequest = UploadPartRequest.builder()
                        .bucket(bucketName)
                        .key(key)
                        .uploadId(uploadId)
                        .partNumber(partNumber)
                        .build();

                // If we didn't read a full buffer, create a properly sized byte array
                RequestBody requestBody;
                if (bytesRead < PART_SIZE) {
                    byte[] lastPartBuffer = new byte[bytesRead];
                    System.arraycopy(buffer, 0, lastPartBuffer, 0, bytesRead);
                    requestBody = RequestBody.fromBytes(lastPartBuffer);
                } else {
                    requestBody = RequestBody.fromBytes(buffer);
                }

                // Upload the part and save the response's ETag
                UploadPartResponse uploadPartResponse = s3Client.uploadPart(uploadPartRequest, requestBody);
                CompletedPart part = CompletedPart.builder()
                        .partNumber(partNumber)
                        .eTag(uploadPartResponse.eTag())
                        .build();
                completedParts.add(part);

                System.out.println("Uploaded part " + partNumber + " with size " + bytesRead + " bytes");
                partNumber++;
            }

            // Step 3: Complete the multipart upload
            CompletedMultipartUpload completedMultipartUpload = CompletedMultipartUpload.builder()
                    .parts(completedParts)
                    .build();

            CompleteMultipartUploadRequest completeRequest = CompleteMultipartUploadRequest.builder()
                    .bucket(bucketName)
                    .key(key)
                    .uploadId(uploadId)
                    .multipartUpload(completedMultipartUpload)
                    .build();

            CompleteMultipartUploadResponse completeResponse = s3Client.completeMultipartUpload(completeRequest);
            System.out.println("Multipart upload completed. Object URL: " + completeResponse.location());

        } catch (Exception e) {
            // If an error occurs, abort the multipart upload
            System.err.println("Error during multipart upload: " + e.getMessage());
            AbortMultipartUploadRequest abortRequest = AbortMultipartUploadRequest.builder()
                    .bucket(bucketName)
                    .key(key)
                    .uploadId(uploadId)
                    .build();
            s3Client.abortMultipartUpload(abortRequest);
            System.err.println("Multipart upload aborted");
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                System.err.println("Error closing input stream: " + e.getMessage());
            }
        }
    } finally {
        s3Client.close();
    }
}

/**
 * Reads from the input stream into the buffer, attempting to fill the buffer completely
 * or until the end of the stream is reached.
 *
 * @param inputStream the input stream to read from
 * @param buffer      the buffer to fill
 * @return the number of bytes read, or -1 if the end of the stream is reached before any bytes are read
 * @throws IOException if an I/O error occurs
 */
private static int readFullyOrToEnd(InputStream inputStream, byte[] buffer) throws IOException {
    int totalBytesRead = 0;
    int bytesRead;

    while (totalBytesRead < buffer.length) {
        bytesRead = inputStream.read(buffer, totalBytesRead, buffer.length - totalBytesRead);
        if (bytesRead == -1) {
            break; // End of stream
        }
        totalBytesRead += bytesRead;
    }

    return totalBytesRead > 0 ? totalBytesRead : -1;
}
```

**참고**  
대부분의 사용 사례에서는 크기를 알 수 없는 스트림에 비동기식 클라이언트 API를 사용하는 것이 좋습니다. 이 접근 방식은 병렬 전송을 사용하고 더 간단한 프로그래밍 인터페이스를 제공합니다. 스트림이 큰 경우 SDK가 스트림 분할을 멀티파트 청크로 처리하기 때문입니다.  
멀티파트가 사용 설정된 표준 S3 비동기식 클라이언트와 AWS CRT 기반 S3 클라이언트 모두이 접근 방식을 구현합니다. 다음 섹션에서는 이 접근 방식의 예제를 보여줍니다.

#### 동기식 API 사용
<a name="s3-stream-upload-unknown-async-client"></a>

`fromInputStream(InputStream inputStream, Long contentLength, ExecutorService executor)`에 대한 `contentLength` 인수의 `null` 값을 제공할 수 있습니다.

**Example AWS CRT 기반 비동기 클라이언트 사용:**  

```
public PutObjectResponse crtClient_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3AsyncClient s3AsyncClient = S3AsyncClient.crtCreate();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor);  // 'null' indicates that the
                                                                                            // content length is unknown.
    CompletableFuture<PutObjectResponse> responseFuture =
            s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body)
                    .exceptionally(e -> {
                        if (e != null){
                            logger.error(e.getMessage(), e);
                        }
                        return null;
                    });

    PutObjectResponse response = responseFuture.join(); // Wait for the response.
    executor.shutdown();
    return response;
}
```

**Example 멀티파트가 사용 설정된 표준 비동기식 클라이언트 사용:**  

```
public PutObjectResponse asyncClient_multipart_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3AsyncClient s3AsyncClient = S3AsyncClient.builder().multipartEnabled(true).build();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor); // 'null' indicates that the
                                                                                           // content length is unknown.
    CompletableFuture<PutObjectResponse> responseFuture =
            s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body)
                    .exceptionally(e -> {
                        if (e != null) {
                            logger.error(e.getMessage(), e);
                        }
                        return null;
                    });

    PutObjectResponse response = responseFuture.join(); // Wait for the response.
    executor.shutdown();
    return response;
}
```

# Amazon S3 미리 서명된 URLs 작업
<a name="examples-s3-presign"></a>

미리 서명된 URLs 사용자가 AWS 자격 증명이나 권한을 가질 필요 없이 프라이빗 S3 객체에 대한 임시 액세스를 제공합니다.

예를 들어 Alice가 S3 객체에 대한 액세스 권한을 가지고 있고 해당 객체에 대한 액세스 권한을 Bob과 일시적으로 공유하려고 할 경우, Alice는 미리 서명된 GET 요청을 생성하여 Bob과 공유할 수 있으므로 Bob은 Alice의 보안 인증에 액세스하지 않고도 객체를 다운로드할 수 있습니다. HTTP GET 요청과 HTTP PUT 요청에 대해 미리 서명된 URL을 생성할 수 있습니다.

## 객체에 대해 미리 서명된 URL을 생성한 다음 다운로드(GET 요청)합니다.
<a name="get-presignedobject"></a>

다음 예시는 두 부분으로 구성되어 있습니다.
+ 1부: Alice가 객체의 미리 서명된 URL을 생성합니다.
+ 2부: Bob은 미리 서명된 URL을 사용하여 객체를 다운로드합니다.

### 1부: URL 생성
<a name="get-presigned-object-part1"></a>

Alice는 이미 S3 버킷에 객체를 보유하고 있으며 다음 코드를 사용하여 Bob이 후속 GET 요청에서 사용할 수 있는 URL 문자열을 생성합니다.

#### 가져오기
<a name="get-presigned-example-imports"></a>

```
import com.example.s3.util.PresignUrlUtils;
import org.slf4j.Logger;
import software.amazon.awssdk.http.HttpExecuteRequest;
import software.amazon.awssdk.http.HttpExecuteResponse;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;
import software.amazon.awssdk.utils.IoUtils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.UUID;
```

```
    /* Create a pre-signed URL to download an object in a subsequent GET request. */
    public String createPresignedGetUrl(String bucketName, String keyName) {
        try (S3Presigner presigner = S3Presigner.create()) {

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

            GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder()
                    .signatureDuration(Duration.ofMinutes(10))  // The URL will expire in 10 minutes.
                    .getObjectRequest(objectRequest)
                    .build();

            PresignedGetObjectRequest presignedRequest = presigner.presignGetObject(presignRequest);
            logger.info("Presigned URL: [{}]", presignedRequest.url().toString());
            logger.info("HTTP method: [{}]", presignedRequest.httpRequest().method());

            return presignedRequest.url().toExternalForm();
        }
    }
```

### 2부: 객체 다운로드
<a name="get-presigned-object-part2"></a>

Bob은 다음 세 가지 코드 옵션 중 하나를 사용하여 객체를 다운로드합니다. 또는 브라우저를 사용하여 GET 요청을 수행할 수도 있습니다.

#### JDK `HttpURLConnection`(v1.1 이후) 사용
<a name="get-presigned-example-useHttpUrlConnection"></a>

```
    /* Use the JDK HttpURLConnection (since v1.1) class to do the download. */
    public byte[] useHttpUrlConnectionToGet(String presignedUrlString) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // Capture the response body to a byte array.

        try {
            URL presignedUrl = new URL(presignedUrlString);
            HttpURLConnection connection = (HttpURLConnection) presignedUrl.openConnection();
            connection.setRequestMethod("GET");
            // Download the result of executing the request.
            try (InputStream content = connection.getInputStream()) {
                IoUtils.copy(content, byteArrayOutputStream);
            }
            logger.info("HTTP response code is " + connection.getResponseCode());

        } catch (S3Exception | IOException e) {
            logger.error(e.getMessage(), e);
        }
        return byteArrayOutputStream.toByteArray();
    }
```

#### JDK `HttpClient`(v11 이후) 사용
<a name="get-presigned-example-useHttpClient"></a>

```
    /* Use the JDK HttpClient (since v11) class to do the download. */
    public byte[] useHttpClientToGet(String presignedUrlString) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // Capture the response body to a byte array.

        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder();
        HttpClient httpClient = HttpClient.newHttpClient();
        try {
            URL presignedUrl = new URL(presignedUrlString);
            HttpResponse<InputStream> response = httpClient.send(requestBuilder
                            .uri(presignedUrl.toURI())
                            .GET()
                            .build(),
                    HttpResponse.BodyHandlers.ofInputStream());

            IoUtils.copy(response.body(), byteArrayOutputStream);

            logger.info("HTTP response code is " + response.statusCode());

        } catch (URISyntaxException | InterruptedException | IOException e) {
            logger.error(e.getMessage(), e);
        }
        return byteArrayOutputStream.toByteArray();
    }
```

#### SDK for Java의 `SdkHttpClient` 사용
<a name="get-presigned-example-useSdkHttpClient"></a>

```
    /* Use the AWS SDK for Java SdkHttpClient class to do the download. */
    public byte[] useSdkHttpClientToGet(String presignedUrlString) {

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // Capture the response body to a byte array.
        try {
            URL presignedUrl = new URL(presignedUrlString);
            SdkHttpRequest request = SdkHttpRequest.builder()
                    .method(SdkHttpMethod.GET)
                    .uri(presignedUrl.toURI())
                    .build();

            HttpExecuteRequest executeRequest = HttpExecuteRequest.builder()
                    .request(request)
                    .build();

            try (SdkHttpClient sdkHttpClient = ApacheHttpClient.create()) {
                HttpExecuteResponse response = sdkHttpClient.prepareRequest(executeRequest).call();
                response.responseBody().ifPresentOrElse(
                        abortableInputStream -> {
                            try {
                                IoUtils.copy(abortableInputStream, byteArrayOutputStream);
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            }
                        },
                        () -> logger.error("No response body."));

                logger.info("HTTP Response code is {}", response.httpResponse().statusCode());
            }
        } catch (URISyntaxException | IOException e) {
            logger.error(e.getMessage(), e);
        }
        return byteArrayOutputStream.toByteArray();
    }
```

GitHub의 [전체 예제](https://github.com/awsdocs/aws-doc-sdk-examples/blob/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/main/java/com/example/s3/GeneratePresignedGetUrlAndRetrieve.java) 및 [테스트](https://github.com/awsdocs/aws-doc-sdk-examples/blob/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/test/java/com/example/s3/presignurl/GeneratePresignedGetUrlTests.java)를 참조하세요.

## 업로드를 위해 미리 서명된 URL을 생성한 다음 파일을 업로드(PUT 요청)합니다.
<a name="put-presignedobject"></a>

다음 예시는 두 부분으로 구성되어 있습니다.
+ 1부: Alice는 객체를 업로드하기 위해 미리 서명된 URL을 생성합니다.
+ 2부: Bob이 미리 서명된 URL을 사용하여 파일을 업로드합니다.

### 1부: URL 생성
<a name="put-presigned-object-part1"></a>

Alice는 이미 S3 버킷을 보유하고 있으며 다음 코드를 사용하여 Bob이 후속 PUT 요청에서 사용할 수 있는 URL 문자열을 생성합니다.

#### 가져오기
<a name="put-presigned-example-imports"></a>

```
import com.example.s3.util.PresignUrlUtils;
import org.slf4j.Logger;
import software.amazon.awssdk.core.internal.sync.FileContentStreamProvider;
import software.amazon.awssdk.http.HttpExecuteRequest;
import software.amazon.awssdk.http.HttpExecuteResponse;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.Map;
import java.util.UUID;
```

```
    /* Create a presigned URL to use in a subsequent PUT request */
    public String createPresignedUrl(String bucketName, String keyName, Map<String, String> metadata) {
        try (S3Presigner presigner = S3Presigner.create()) {

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

            PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
                    .signatureDuration(Duration.ofMinutes(10))  // The URL expires in 10 minutes.
                    .putObjectRequest(objectRequest)
                    .build();


            PresignedPutObjectRequest presignedRequest = presigner.presignPutObject(presignRequest);
            String myURL = presignedRequest.url().toString();
            logger.info("Presigned URL to upload a file to: [{}]", myURL);
            logger.info("HTTP method: [{}]", presignedRequest.httpRequest().method());

            return presignedRequest.url().toExternalForm();
        }
    }
```

### 2부: 파일 객체 업로드
<a name="put-presigned-object-part2"></a>

Bob은 다음 세 가지 코드 옵션 중 하나를 사용하여 파일을 업로드합니다.

#### JDK `HttpURLConnection`(v1.1 이후) 사용
<a name="put-presigned-example-useHttpUrlConnection"></a>

```
    /* Use the JDK HttpURLConnection (since v1.1) class to do the upload. */
    public void useHttpUrlConnectionToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) {
        logger.info("Begin [{}] upload", fileToPut.toString());
        try {
            URL presignedUrl = new URL(presignedUrlString);
            HttpURLConnection connection = (HttpURLConnection) presignedUrl.openConnection();
            connection.setDoOutput(true);
            metadata.forEach((k, v) -> connection.setRequestProperty("x-amz-meta-" + k, v));
            connection.setRequestMethod("PUT");
            OutputStream out = connection.getOutputStream();

            try (RandomAccessFile file = new RandomAccessFile(fileToPut, "r");
                 FileChannel inChannel = file.getChannel()) {
                ByteBuffer buffer = ByteBuffer.allocate(8192); //Buffer size is 8k

                while (inChannel.read(buffer) > 0) {
                    buffer.flip();
                    for (int i = 0; i < buffer.limit(); i++) {
                        out.write(buffer.get());
                    }
                    buffer.clear();
                }
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }

            out.close();
            connection.getResponseCode();
            logger.info("HTTP response code is " + connection.getResponseCode());

        } catch (S3Exception | IOException e) {
            logger.error(e.getMessage(), e);
        }
    }
```

#### JDK `HttpClient`(v11 이후) 사용
<a name="put-presigned-example-useHttpClient"></a>

```
    /* Use the JDK HttpClient (since v11) class to do the upload. */
    public void useHttpClientToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) {
        logger.info("Begin [{}] upload", fileToPut.toString());

        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder();
        metadata.forEach((k, v) -> requestBuilder.header("x-amz-meta-" + k, v));

        HttpClient httpClient = HttpClient.newHttpClient();
        try {
            final HttpResponse<Void> response = httpClient.send(requestBuilder
                            .uri(new URL(presignedUrlString).toURI())
                            .PUT(HttpRequest.BodyPublishers.ofFile(Path.of(fileToPut.toURI())))
                            .build(),
                    HttpResponse.BodyHandlers.discarding());

            logger.info("HTTP response code is " + response.statusCode());

        } catch (URISyntaxException | InterruptedException | IOException e) {
            logger.error(e.getMessage(), e);
        }
    }
```

#### SDK for Java의 `SdkHttpClient` 사용
<a name="put-presigned-example-useSdkHttpClient"></a>

```
    /* Use the AWS SDK for Java V2 SdkHttpClient class to do the upload. */
    public void useSdkHttpClientToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) {
        logger.info("Begin [{}] upload", fileToPut.toString());

        try {
            URL presignedUrl = new URL(presignedUrlString);

            SdkHttpRequest.Builder requestBuilder = SdkHttpRequest.builder()
                    .method(SdkHttpMethod.PUT)
                    .uri(presignedUrl.toURI());
            // Add headers
            metadata.forEach((k, v) -> requestBuilder.putHeader("x-amz-meta-" + k, v));
            // Finish building the request.
            SdkHttpRequest request = requestBuilder.build();

            HttpExecuteRequest executeRequest = HttpExecuteRequest.builder()
                    .request(request)
                    .contentStreamProvider(new FileContentStreamProvider(fileToPut.toPath()))
                    .build();

            try (SdkHttpClient sdkHttpClient = ApacheHttpClient.create()) {
                HttpExecuteResponse response = sdkHttpClient.prepareRequest(executeRequest).call();
                logger.info("Response code: {}", response.httpResponse().statusCode());
            }
        } catch (URISyntaxException | IOException e) {
            logger.error(e.getMessage(), e);
        }
    }
```

GitHub의 [전체 예제](https://github.com/awsdocs/aws-doc-sdk-examples/blob/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/main/java/com/example/s3/GeneratePresignedUrlAndPutFileWithMetadata.java) 및 [테스트](https://github.com/awsdocs/aws-doc-sdk-examples/blob/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/test/java/com/example/s3/presignurl/GeneratePresignedPutUrlTests.java)를 참조하세요.

# Amazon S3를 위한 교차 리전 액세스
<a name="s3-cross-region"></a>

Amazon Simple Storage Service(Amazon S3) 버킷으로 작업하면 일반적으로 버킷의 AWS 리전를 알 수 있습니다. 사용하는 리전전은 S3 클라이언트를 생성할 때 결정됩니다.

하지만 특정 버킷으로 작업해야 하는데 해당 버킷이 S3 클라이언트에 설정된 동일한 리전전에 있는지 알 수 없는 경우가 있습니다.

버킷 리전전을 결정하기 위해 더 많은 호출을 하는 대신 SDK를 사용하여 여러 리전전의 S3 버킷에 액세스할 수 있도록 할 수 있습니다.

## 설정
<a name="s3-cross-region-setup"></a>

SDK `2.20.111` 버전에서 교차 리전 액세스에 대한 지원을 사용할 수 있게 되었습니다. 다음 코드 조각과 같이 Maven 빌드 파일의 `s3` 종속 항목에 대해 이 버전 또는 이후 버전을 사용하세요.

```
<dependency>
  <groupId>software.amazon.awssdk</groupId>
  <artifactId>s3</artifactId>
  <version>2.27.21</version>
</dependency>
```

다음으로 S3 클라이언트를 생성할 때 코드 조각에 표시된 대로 교차 리전 액세스를 활성화하세요. 기본적으로 액세스가 활성화되어 있지 않습니다.

```
S3AsyncClient client = S3AsyncClient.builder()
                                    .crossRegionAccessEnabled(true)
                                    .build();
```

## SDK가 교차 리전 액세스를 제공하는 방법
<a name="s3-cross-region-routing"></a>

`putObject` 메서드를 사용할 때와 같이 요청에서 기존 버킷을 참조하면 SDK가 클라이언트용으로 구성된 리전전에 대한 요청을 시작합니다.

특정 리전전에 버킷이 없는 경우 오류 응답에는 버킷이 있는 실제 리전전이 포함됩니다. 그러면 SDK는 두 번째 요청에서 올바른 리전전을 사용합니다.

동일한 버킷에 대한 향후 요청을 최적화하기 위해 SDK는 이 리전전 매핑을 클라이언트에 캐시합니다.

## 고려 사항
<a name="s3-cross-region-considerations"></a>

교차 리전 버킷 액세스를 활성화하는 경우, 버킷이 클라이언트의 구성된 리전전에 있지 않으면 첫 번째 API 호출 시 지연 시간이 늘어날 수 있다는 점에 유의하세요. 하지만 후속 호출은 캐시된 리전전 정보를 활용하므로 성능이 향상됩니다.

교차 리전 액세스를 활성화해도 버킷 액세스는 영향을 받지 않습니다. 사용자는 버킷이 상주하는 리전전에 상관없이 버킷에 액세스할 수 있는 권한을 부여받아야 합니다.

# 체크섬을 통한 데이터 무결성 보호
<a name="s3-checksums"></a>

Amazon Simple Storage Service(S3)는 객체를 업로드할 때 체크섬을 지정하는 기능을 제공합니다. 체크섬을 지정하면 객체와 함께 저장되며 객체를 다운로드할 때 유효성을 검사할 수 있습니다.

체크섬은 파일을 전송할 때 데이터 무결성을 한층 더 강화합니다. 체크섬을 사용하면 수신된 파일이 원본 파일과 일치하는지 확인하여 데이터 일관성을 확인할 수 있습니다. Amazon S3의 체크섬에 대한 자세한 내용은 [지원되는 알고리즘](https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html#using-additional-checksums)을 포함한 [Amazon Simple Storage Service 사용 설명서](https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html)를 참조하세요.

필요에 가장 적합한 알고리즘을 유연하게 선택하고 SDK가 체크섬을 계산하도록 할 수 있습니다. 또는 지원되는 알고리즘 중 하나를 사용하여 미리 계산된 체크섬 값을 제공할 수 있습니다.

**참고**  
 AWS SDK for Java 2.x의 버전 2.30.0부터 SDK는 업로드에 대한 `CRC32` 체크섬을 자동으로 계산하여 기본 무결성 보호를 제공합니다. 사전 계산된 체크섬 값을 제공하지 않거나 SDK가 체크섬을 계산하는 데 사용해야 하는 알고리즘을 지정하지 않은 경우 SDK는이 체크섬을 계산합니다.   
또한 SDK는 [AWS SDK 및 도구 참조 안내서](https://docs.aws.amazon.com/sdkref/latest/guide/feature-dataintegrity.html)에서 확인할 수 있고 외부에서 설정할 수 있는 데이터 무결성 보호에 대한 전역 설정을 지원합니다.

체크섬은 객체 업로드와 객체 다운로드라는 두 가지 요청 단계로 설명합니다.

## 객체 업로드
<a name="use-service-S3-checksum-upload"></a>

 `putObject` 메서드를 사용하여 객체를 업로드하고 체크섬 알고리즘을 제공하면 SDK가 지정된 알고리즘의 체크섬을 계산합니다.

다음 코드 조각은 `SHA256` 체크섬이 있는 객체를 업로드하라는 요청을 보여줍니다. SDK는 요청을 보내면 `SHA256` 체크섬을 계산하고 객체를 업로드합니다. Amazon S3는 체크섬을 계산하고 SDK에서 제공하는 체크섬과 비교하여 콘텐츠의 무결성을 확인합니다. Amazon S3는 객체와 함께 체크섬을 저장합니다.

```
public void putObjectWithChecksum() {
        s3Client.putObject(b -> b
                .bucket(bucketName)
                .key(key)
                .checksumAlgorithm(ChecksumAlgorithm.SHA256),
            RequestBody.fromString("This is a test"));
}
```

요청에 체크섬 알고리즘을 제공하지 않는 경우 체크섬 동작은 다음 표와 같이 사용하는 SDK 버전에 따라 달라집니다.

**체크섬 알고리즘이 제공되지 않은 경우 체크섬 동작**


| Java SDK 버전 | 체크섬 동작 | 
| --- | --- | 
| 2.30.0 이하 | SDK는 CRC 기반 체크섬을 자동으로 계산하여 요청에 제공하지 않습니다. | 
| 2.30.0 이상 | SDK는 `CRC32` 알고리즘을 사용하여 체크섬을 계산하고 요청에 제공합니다. Amazon S3는 자체 `CRC32` 체크섬을 계산하여 전송의 무결성을 확인하고 이를 SDK에서 제공하는 체크섬과 비교합니다. 체크섬이 일치하면 체크섬이 객체와 함께 저장됩니다. | 

### 미리 계산된 체크섬 값 사용
<a name="use-service-S3-checksum-upload-pre"></a>

요청과 함께 제공되는 사전 계산된 체크섬 값은 SDK의 자동 계산을 비활성화하고 제공된 값을 대신 사용합니다.

다음 예제에서는 사전 계산된 SHA256 체크섬이 있는 요청을 보여줍니다.

```
    public void putObjectWithPrecalculatedChecksum(String filePath) {
        String checksum = calculateChecksum(filePath, "SHA-256");

        s3Client.putObject((b -> b
                .bucket(bucketName)
                .key(key)
                .checksumSHA256(checksum)),
            RequestBody.fromFile(Paths.get(filePath)));
    }
```

Amazon S3에서 체크섬 값이 지정된 알고리즘에 대해 올바르지 않다고 판단하면 서비스는 오류 응답을 반환합니다.

### 멀티파트 업로드
<a name="use-service-S3-checksum-upload-multi"></a>

멀티파트 업로드에 체크섬을 사용할 수도 있습니다.

 Java 2.x용 SDK는 멀티파트 업로드에 체크섬을 사용하는 두 가지 옵션을 제공합니다. 첫 번째 옵션은 `S3TransferManager`를 사용합니다.

다음 전송 관리자 예제는 업로드를 위한 SHA1 알고리즘을 지정합니다.

```
    public void multipartUploadWithChecksumTm(String filePath) {
        S3TransferManager transferManager = S3TransferManager.create();
        UploadFileRequest uploadFileRequest = UploadFileRequest.builder()
            .putObjectRequest(b -> b
                .bucket(bucketName)
                .key(key)
                .checksumAlgorithm(ChecksumAlgorithm.SHA1))
            .source(Paths.get(filePath))
            .build();
        FileUpload fileUpload = transferManager.uploadFile(uploadFileRequest);
        fileUpload.completionFuture().join();
        transferManager.close();
    }
```

업로드에 전송 관리자를 사용할 때 체크섬 알고리즘을 제공하지 않으면 SDK는 `CRC32` 알고리즘을 기반으로 체크섬을 자동으로 계산합니다. SDK는 SDK의 모든 버전에서 이 계산을 수행합니다.

두 번째 옵션은 [`S3Client` API](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html)(또는 [`S3AsyncClient` API](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html))를 사용하여 멀티파트 업로드를 수행합니다. 이 접근법으로 추가 체크섬을 지정하는 경우 업로드를 시작할 때 사용할 알고리즘을 지정해야 합니다. 또한 각 파트 요청에 대한 알고리즘을 지정하고 업로드 후 각 파트에 대해 계산된 체크섬을 제공해야 합니다.

```
    public void multipartUploadWithChecksumS3Client(String filePath) {
        ChecksumAlgorithm algorithm = ChecksumAlgorithm.CRC32;

        // Initiate the multipart upload.
        CreateMultipartUploadResponse createMultipartUploadResponse = s3Client.createMultipartUpload(b -> b
            .bucket(bucketName)
            .key(key)
            .checksumAlgorithm(algorithm)); // Checksum specified on initiation.
        String uploadId = createMultipartUploadResponse.uploadId();

        // Upload the parts of the file.
        int partNumber = 1;
        List<CompletedPart> completedParts = new ArrayList<>();
        ByteBuffer bb = ByteBuffer.allocate(1024 * 1024 * 5); // 5 MB byte buffer

        try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) {
            long fileSize = file.length();
            long position = 0;
            while (position < fileSize) {
                file.seek(position);
                long read = file.getChannel().read(bb);

                bb.flip(); // Swap position and limit before reading from the buffer.
                UploadPartRequest uploadPartRequest = UploadPartRequest.builder()
                    .bucket(bucketName)
                    .key(key)
                    .uploadId(uploadId)
                    .checksumAlgorithm(algorithm) // Checksum specified on each part.
                    .partNumber(partNumber)
                    .build();

                UploadPartResponse partResponse = s3Client.uploadPart(
                    uploadPartRequest,
                    RequestBody.fromByteBuffer(bb));

                CompletedPart part = CompletedPart.builder()
                    .partNumber(partNumber)
                    .checksumCRC32(partResponse.checksumCRC32()) // Provide the calculated checksum.
                    .eTag(partResponse.eTag())
                    .build();
                completedParts.add(part);

                bb.clear();
                position += read;
                partNumber++;
            }
        } catch (IOException e) {
            System.err.println(e.getMessage());
        }

        // Complete the multipart upload.
        s3Client.completeMultipartUpload(b -> b
            .bucket(bucketName)
            .key(key)
            .uploadId(uploadId)
            .multipartUpload(CompletedMultipartUpload.builder().parts(completedParts).build()));
    }
```

[전체 예제 코드](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/main/java/com/example/s3/PerformMultiPartUpload.java) 및 [테스트](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/test/java/com/example/s3/PerformMultiPartUploadTests.java) 코드는 GitHub 코드 예제 저장소에 있습니다.

## 객체 다운로드
<a name="use-service-S3-checksum-download"></a>

[getObject](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#getObject(software.amazon.awssdk.services.s3.model.GetObjectRequest)) 메서드를 사용하여 객체를 다운로드하면, `GetObjectRequest`용 빌더의 `checksumMode` 메서드가 `ChecksumMode.ENABLED`로 설정된 경우. 

다음 스니펫의 요청은 체크섬을 계산하고 값을 비교하여 응답의 체크섬을 검증하도록 SDK에 지시합니다.

```
    public GetObjectResponse getObjectWithChecksum() {
        return s3Client.getObject(b -> b
                        .bucket(bucketName)
                        .key(key)
                        .checksumMode(ChecksumMode.ENABLED))
                .response();
    }
```

**참고**  
체크섬과 함께 객체를 업로드하지 않은 경우 검증이 수행되지 않습니다.

## 기타 체크섬 계산 옵션
<a name="S3-checsum-calculation-options"></a>

**참고**  
전송된 데이터의 데이터 무결성을 확인하고 전송 오류를 식별하려면 사용자가 체크섬 계산 옵션에 대한 SDK 기본 설정을 유지하는 것이 좋습니다. 기본적으로 SDK는 `PutObject` 및 `GetObject`를 포함한 많은 S3 작업에 대해 이 중요한 검사를 추가합니다.

그러나 Amazon S3를 사용하려면 최소한의 체크섬 확인이 필요한 경우 기본 구성 설정을 변경하여 많은 검사를 비활성화할 수 있습니다.

### 필요하지 않은 경우 자동 체크섬 계산 비활성화
<a name="S3-minimize-checksum-calc-global"></a>

`PutObject` 및 `GetObject`와 같이 이를 지원하는 작업에 대해 SDK에서 자동 체크섬 계산을 비활성화할 수 있습니다. 그러나 일부 S3 작업에는 체크섬 계산이 필요하므로 이러한 작업에 대한 체크섬 계산을 비활성화할 수 없습니다.

SDK는 요청의 페이로드와 응답의 페이로드에 대한 체크섬 계산을 위한 별도의 설정을 제공합니다.

다음 목록은 다양한 범위에서 체크섬 계산을 최소화하는 데 사용할 수 있는 설정을 설명합니다.
+ **모든 애플리케이션 범위** - 환경 변수 또는 공유 AWS `config` 및 `credentials` 파일의 프로파일에서 설정을 변경하면 모든 애플리케이션에서 이러한 설정을 사용할 수 있습니다. 애플리케이션 또는 서비스 클라이언트 범위에서 재정의되지 않는 한 이러한 설정은 모든 AWS SDK 애플리케이션의 모든 서비스 클라이언트에 영향을 줍니다.
  + 프로파일에 설정을 추가합니다.

    ```
    [default]
    request_checksum_calculation = WHEN_REQUIRED
    response_checksum_validation = WHEN_REQUIRED
    ```
  + 환경 변수를 추가합니다.

    ```
    AWS_REQUEST_CHECKSUM_CALCULATION=WHEN_REQUIRED
    AWS_RESPONSE_CHECKSUM_VALIDATION=WHEN_REQUIRED
    ```
+ **현재 애플리케이션 범위** - Java 시스템 속성(`aws.requestChecksumCalculation`)을 `WHEN_REQUIRED`로 설정하여 체크섬 계산을 제한할 수 있습니다. 응답에 해당하는 시스템 속성은 `aws.responseChecksumValidation`입니다.

  이러한 설정은 서비스 클라이언트를 만드는 중에 재정의되지 않는 한 애플리케이션의 모든 SDK 서비스 클라이언트에 영향을 줍니다.

  애플리케이션 시작 시 시스템 속성을 설정합니다.

  ```
  import software.amazon.awssdk.core.SdkSystemSetting;
  import software.amazon.awssdk.core.checksums.RequestChecksumCalculation;
  import software.amazon.awssdk.core.checksums.ResponseChecksumValidation;
  import software.amazon.awssdk.services.s3.S3Client;
  
  class DemoClass {
      public static void main(String[] args) {
  
          System.setProperty(SdkSystemSetting.AWS_REQUEST_CHECKSUM_CALCULATION.property(), // Resolves to "aws.requestChecksumCalculation".
                  "WHEN_REQUIRED");
          System.setProperty(SdkSystemSetting.AWS_RESPONSE_CHECKSUM_VALIDATION.property(), // Resolves to "aws.responseChecksumValidation".
                  "WHEN_REQUIRED");
  
          S3Client s3Client = S3Client.builder().build();
  
          // Use s3Client.
      }
  }
  ```
+ **단일 S3 서비스 클라이언트 범위** - 빌더 메서드를 사용하여 최소 체크섬 양을 계산하도록 단일 S3 서비스 클라이언트를 구성할 수 있습니다.

  ```
  import software.amazon.awssdk.core.checksums.RequestChecksumCalculation;
  import software.amazon.awssdk.services.s3.S3Client;
  
  public class RequiredChecksums {
      public static void main(String[] args) {
          S3Client s3 = S3Client.builder()
                  .requestChecksumCalculation(RequestChecksumCalculation.WHEN_REQUIRED)
                  .responseChecksumValidation(ResponseChecksumValidation.WHEN_REQUIRED)
                  .build();
  
          // Use s3Client. 
      }
  // ...
  }
  ```

### 간소화된 MD5 호환성을 위해 `LegacyMd5Plugin` 사용
<a name="S3-checksum-legacy-md5"></a>

버전 2.30.0의 CRC32 체크섬 동작 릴리스와 함께 SDK는 필요한 작업에 대한 MD5 체크섬 계산을 중단했습니다.

S3 작업에 레거시 MD5 체크섬 동작이 필요한 경우 SDK 버전 2.31.32에서 릴리스된 `LegacyMd5Plugin`을 사용할 수 있습니다.

`LegacyMd5Plugin`은 레거시 MD5 체크섬 동작에 의존하는 애플리케이션과의 호환성을 유지해야 할 때 유용합니다. 특히 S3A 파일 시스템 커넥터(Apache Spark, Iceberg)와 함께 사용되는 서드 파티 S3 호환 스토리지 공급자와 함께 작업할 때 유용합니다.

`LegacyMd5Plugin`을 사용하려면 S3 클라이언트 빌더에 추가합니다.

```
// For synchronous S3 client.
S3Client s3Client = S3Client.builder()
                           .addPlugin(LegacyMd5Plugin.create())
                           .build();

// For asynchronous S3 client.
S3AsyncClient asyncClient = S3AsyncClient.builder()
                                       .addPlugin(LegacyMd5Plugin.create())
                                       .build();
```

체크섬이 필요한 작업에 MD5 체크섬을 추가하고 체크섬을 지원하지만 필요하지 않은 작업에 대한 SDK 기본 체크섬 추가를 건너뛰려면 `ClientBuilder` 옵션 `requestChecksumCalculation` 및 `responseChecksumValidation`을 `WHEN_REQUIRED`로 사용할 수 있습니다. 이렇게 하면 체크섬이 필요한 작업에만 SDK 기본 체크섬이 추가됩니다.

```
// Use the `LegacyMd5Plugin` with `requestChecksumCalculation` and `responseChecksumValidation` set to WHEN_REQUIRED.
S3AsyncClient asyncClient = S3AsyncClient.builder()
                                       .addPlugin(LegacyMd5Plugin.create())
                                       .requestChecksumCalculation(RequestChecksumCalculation.WHEN_REQUIRED)
                                       .responseChecksumValidation(ResponseChecksumValidation.WHEN_REQUIRED)
                                       .build();
```

이 구성은 최신 체크섬 알고리즘을 완전히 지원하지는 않지만 특정 작업에 대해 여전히 MD5 체크섬이 필요한 서드 파티 S3 호환 스토리지 시스템으로 작업할 때 특히 유용합니다.

# 성능 S3 클라이언트: AWS CRT 기반 S3 클라이언트 사용
<a name="crt-based-s3-client"></a>

[AWS 공통 런타임(CRT)](https://docs.aws.amazon.com/sdkref/latest/guide/common-runtime.html)을 기반으로 구축된 AWS CRT 기반 S3 클라이언트는 대체 S3 비동기 클라이언트입니다. Amazon S3의 [멀티파트 업로드 API](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html)와 [바이트 범위 가져오기](https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance-guidelines.html#optimizing-performance-guidelines-get-range)를 자동으로 사용하여 향상된 성능과 안정성을 바탕으로 Amazon Simple Storage Service(Amazon S3)와 객체를 주고 받습니다.

 AWS CRT 기반 S3 클라이언트는 네트워크 장애가 발생할 경우 전송 신뢰성을 개선합니다. 전송을 처음부터 다시 시작하지 않고 파일 전송의 실패한 개별 부분을 다시 시도하여 안정성이 향상됩니다.

또한 AWS CRT 기반 S3 클라이언트는 향상된 연결 풀링 및 도메인 이름 시스템(DNS) 로드 밸런싱을 제공하여 처리량도 개선합니다.

SDK의 표준 S3 비동기식 클라이언트 대신 AWS CRT 기반 S3 클라이언트를 사용하고 처리량 향상을 즉시 활용할 수 있습니다.

**중요**  
 AWS CRT 기반 S3 클라이언트는 현재 클라이언트 수준이나 요청 수준에서 [SDK 지표 수집](metrics.md)을 지원하지 않습니다.

**AWS SDK의 CRT 기반 구성 요소**

이 주제에 설명된 AWS CRT 기반* S3* 클라이언트와 AWS CRT 기반 *HTTP* 클라이언트는 SDK의 서로 다른 구성 요소입니다.

**AWS CRT 기반 S3 클라이언트**는 [S3AsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html) 인터페이스를 구현한 것으로, Amazon S3 서비스를 사용하는 데 사용됩니다. 이는 `S3AsyncClient` 인터페이스의 Java 기반 구현의 대안이며 여러 가지 이점을 제공합니다.

[AWS CRT 기반 HTTP 클라이언트](http-configuration-crt.md)는 [SdkAsyncHttpClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/async/SdkAsyncHttpClient.html) 인터페이스를 구현한 것으로, 일반 HTTP 통신에 사용됩니다. 이는 `SdkAsyncHttpClient` 인터페이스의 Netty 구현의 대안이며 여러 가지 이점을 제공합니다.

두 구성 요소 모두 [AWS Common Runtime](https://docs.aws.amazon.com/sdkref/latest/guide/common-runtime.html)의 라이브러리를 사용하지만 AWS CRT 기반 S3 클라이언트는 [aws-c-s3 라이브러리](https://github.com/awslabs/aws-c-s3)를 사용하고 [S3 멀티파트 업로드 API](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html) 기능을 지원합니다. AWS CRT 기반 HTTP 클라이언트는 범용이므로 S3 멀티파트 업로드 API 기능을 지원하지 않습니다.

## AWS CRT 기반 S3 클라이언트를 사용하기 위한 종속성 추가
<a name="crt-based-s3-client-depend"></a>

 AWS CRT 기반 S3 클라이언트를 사용하려면 Maven 프로젝트 파일에 다음 두 가지 종속성을 추가합니다. 예제는 사용하는 최소 버전을 보여 줍니다. Maven 중앙 리포지토리에서 가장 최신 버전의 [s3](https://central.sonatype.com/artifact/software.amazon.awssdk/s3) 및 [aws-crt](https://central.sonatype.com/artifact/software.amazon.awssdk.crt/aws-crt) 아티팩트를 검색하세요.

```
<dependency>
  <groupId>software.amazon.awssdk</groupId>
  <artifactId>s3</artifactId>
  <version>2.27.21</version>
</dependency>
<dependency>
  <groupId>software.amazon.awssdk.crt</groupId>
  <artifactId>aws-crt</artifactId>
  <version>0.30.11</version>
</dependency>
```

## AWS CRT 기반 S3 클라이언트의 인스턴스 생성
<a name="crt-based-s3-client-create"></a>

 다음 코드 조각과 같이 기본 설정을 사용하여 AWS CRT 기반 S3 클라이언트의 인스턴스를 생성합니다.

```
S3AsyncClient s3AsyncClient = S3AsyncClient.crtCreate();
```

클라이언트를 구성하려면 AWS CRT 클라이언트 빌더를 사용합니다. 빌더 메서드를 변경하여 표준 S3 비동기 클라이언트에서 AWS CRT 기반 클라이언트로 전환할 수 있습니다.

```
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;


S3AsyncClient s3AsyncClient = 
        S3AsyncClient.crtBuilder()
                     .credentialsProvider(DefaultCredentialsProvider.create())
                     .region(Region.US_WEST_2)
                     .targetThroughputInGbps(20.0)
                     .minimumPartSizeInBytes(8 * 1025 * 1024L)
                     .build();
```

**참고**  
표준 빌더의 일부 설정은 AWS CRT 클라이언트 빌더에서 현재 지원되지 않을 수 있습니다. `S3AsyncClient#builder()`를 호출하여 표준 빌더를 가져오세요.

## AWS CRT 기반 S3 클라이언트 사용
<a name="crt-based-s3-client-use"></a>

 AWS CRT 기반 S3 클라이언트를 사용하여 Amazon S3 API 작업을 호출합니다. 다음 예제는 AWS SDK for Java를 통해 사용할 수 있는 [PutObject](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html#putObject(java.util.function.Consumer,software.amazon.awssdk.core.async.AsyncRequestBody)) 및 [GetObject](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html#getObject(java.util.function.Consumer,software.amazon.awssdk.core.async.AsyncResponseTransformer)) 작업을 보여줍니다.

```
import software.amazon.awssdk.core.async.AsyncRequestBody;
import software.amazon.awssdk.core.async.AsyncResponseTransformer;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;


S3AsyncClient s3Client = S3AsyncClient.crtCreate();

// Upload a local file to Amazon S3.
PutObjectResponse putObjectResponse = 
      s3Client.putObject(req -> req.bucket(<BUCKET_NAME>)
                                   .key(<KEY_NAME>),
                        AsyncRequestBody.fromFile(Paths.get(<FILE_NAME>)))
              .join();

// Download an object from Amazon S3 to a local file.
GetObjectResponse getObjectResponse = 
      s3Client.getObject(req -> req.bucket(<BUCKET_NAME>)
                                   .key(<KEY_NAME>),
                        AsyncResponseTransformer.toFile(Paths.get(<FILE_NAME>)))
              .join();
```

## 알 수 없는 크기의 스트림 업로드
<a name="crt-stream-unknown-size"></a>

 AWS AWS CRT 기반 S3 클라이언트의 한 가지 중요한 이점은 크기가 알려지지 않은 입력 스트림을 효율적으로 처리할 수 있다는 것입니다. 이는 총 크기를 미리 확인할 수 없는 소스에서 데이터를 업로드해야 할 때 특히 유용합니다.

```
public PutObjectResponse crtClient_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3AsyncClient s3AsyncClient = S3AsyncClient.crtCreate();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor);  // 'null' indicates that the
                                                                                            // content length is unknown.
    CompletableFuture<PutObjectResponse> responseFuture =
            s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body)
                    .exceptionally(e -> {
                        if (e != null){
                            logger.error(e.getMessage(), e);
                        }
                        return null;
                    });

    PutObjectResponse response = responseFuture.join(); // Wait for the response.
    executor.shutdown();
    return response;
}
```

이 기능은 잘못된 콘텐츠 길이 사양으로 인해 객체가 잘리거나 업로드가 실패할 수 있는 기존 업로드의 일반적인 문제를 방지하는 데 도움이 됩니다.

## 구성 제한 사항
<a name="crt-based-s3-client-limitations"></a>

 AWS CRT 기반 S3 클라이언트와 Java 기반 S3 비동기 클라이언트는 성능 엣지를 제공하는 AWS CRT 기반 S3 클라이언트와 [유사한 기능을 제공합니다](examples-s3.md#s3-clients). 그러나 AWS CRT 기반 S3 클라이언트에는 Java 기반 S3 비동기 클라이언트에 있는 구성 설정이 없습니다. 이러한 설정은 다음과 같습니다.
+ *클라이언트 수준 구성:* API 직접 호출 시도 제한 시간, 압축 실행 인터셉터, 지표 게시자, 사용자 지정 실행 속성, 사용자 지정 고급 옵션, 사용자 지정된 예약 실행기 서비스, 사용자 지정 헤더
+ *요청 수준 구성:* 사용자 지정 서명자, API 호출 시도 제한 시간

구성 차이의 전체 목록은 API 참조를 참조하세요.


| Java 기반 S3 비동기식 클라이언트 | AWS CRT 기반 S3 클라이언트 | 
| --- | --- | 
| 클라이언트 수준 구성[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/crt-based-s3-client.html)요청 수준 구성[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/crt-based-s3-client.html) | 클라이언트 수준 구성[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/crt-based-s3-client.html)요청 수준 구성[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/sdk-for-java/latest/developer-guide/crt-based-s3-client.html) | 

# 병렬 전송을 사용하도록 Java 기반 S3 비동기식 클라이언트 구성
<a name="s3-async-client-multipart"></a>

버전 2.27.5부터 표준 Java 기반 S3 비동기식 클라이언트는 자동 병렬 전송(멀티파트 업로드 및 다운로드)을 지원합니다. Java 기반 S3 비동기식 클라이언트를 만들 때 병렬 전송에 대한 지원을 구성합니다.

이 섹션에서는 병렬 전송을 사용하는 방법과 구성을 사용자 지정하는 방법을 보여줍니다.

## `S3AsyncClient`의 인스턴스를 만듭니다.
<a name="s3-async-client-multipart-create"></a>

[빌더](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClientBuilder.html)에서 `multipart*` 메서드를 직접적으로 호출하지 않고 `S3AsyncClient` 인스턴스를 만들면 병렬 전송이 사용되지 않습니다. 다음 각 문은 멀티파트 업로드 및 다운로드를 지원하지 않고 Java 기반 S3 비동기식 클라이언트를 만듭니다.

### 멀티파트 지원 *없이* 만들기
<a name="s3-async-client-mp-off"></a>

**Example**  

```
import software.amazon.awssdk.auth.credentials.ProcessCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;


S3AsyncClient s3Client = S3AsyncClient.create();

S3AsyncClient s3Client2 = S3AsyncClient.builder().build();

S3AsyncClient s3Client3 = S3AsyncClient.builder()
        .credentialsProvider(ProcessCredentialsProvider.builder().build())
        .region(Region.EU_NORTH_1)
        .build();
```

### 멀티파트 지원*으로* 만들기
<a name="s3-async-client-mp-on"></a>

기본 설정으로 병렬 전송을 사용하려면 다음 예제와 같이 빌더에서 `true`를 직접적으로 호출하고 `multipartEnabled`에 전달합니다.

**Example**  

```
S3AsyncClient s3AsyncClient2 = S3AsyncClient.builder()
        .multipartEnabled(true)
        .build();
```

`thresholdInBytes` 및 `minimumPartSizeInBytes` 설정의 기본값은 8MiB입니다.

멀티파트 설정을 사용자 지정하면 다음과 같이 병렬 전송이 자동으로 사용됩니다.

**Example**  

```
import software.amazon.awssdk.services.s3.S3AsyncClient;
import static software.amazon.awssdk.transfer.s3.SizeConstant.MB;


S3AsyncClient s3AsyncClient2 = S3AsyncClient.builder()
        .multipartConfiguration(b -> b
                .thresholdInBytes(16 * MB)
                .minimumPartSizeInBytes(10 * MB))
        .build();
```

## 알 수 없는 크기의 스트림 업로드
<a name="java-async-client-stream-unknown-size"></a>

멀티파트가 사용 설정된 Java 기반 S3 비동기식 클라이언트는 총 크기를 미리 알 수 없는 입력 스트림을 효율적으로 처리할 수 있습니다.

```
public PutObjectResponse asyncClient_multipart_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3AsyncClient s3AsyncClient = S3AsyncClient.builder().multipartEnabled(true).build();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor); // 'null' indicates that the
                                                                                           // content length is unknown.
    CompletableFuture<PutObjectResponse> responseFuture =
            s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body)
                    .exceptionally(e -> {
                        if (e != null) {
                            logger.error(e.getMessage(), e);
                        }
                        return null;
                    });

    PutObjectResponse response = responseFuture.join(); // Wait for the response.
    executor.shutdown();
    return response;
}
```

이 접근 방식은 잘린 객체 또는 실패한 업로드와 같이 잘못된 콘텐츠 길이를 수동으로 지정할 때 발생할 수 있는 문제를 방지합니다.

# Amazon S3 Transfer Manager로 파일 및 디렉터리 전송
<a name="transfer-manager"></a>

Amazon S3 Transfer Manager는 AWS SDK for Java 2.x를 위한 오픈 소스의 고급 파일 전송 유틸리티입니다. 이를 사용하여 Amazon Simple Storage Service(Amazon S3)와 파일 및 디렉터리를 주고받을 수 있습니다.

[AWS CRT 기반 S3 클라이언트](crt-based-s3-client.md) 또는 [표준 Java 기반 S3 비동기식 클라이언트(멀티파트 사용 설정)](s3-async-client-multipart.md)를 기반으로 구축된 S3 Transfer Manager는 [멀티파트 업로드](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html) 및 [바이트 범위 가져오기](https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance-guidelines.html#optimizing-performance-guidelines-get-range)와 같은 성능 개선의 이점을 활용할 수 있습니다.

S3 Transfer Manager를 사용하면 전송 진행 상황을 실시간으로 모니터링하고 나중에 실행하기 위해 전송을 일시 중지할 수도 있습니다.

## 시작하기
<a name="transfer-manager-prerequisites"></a>

### 빌드 파일에 종속성을 추가
<a name="transfer-manager-add-dependency"></a>

향상된 멀티파트 성능으로 S3 Transfer Manager를 사용하려면 필요한 종속성으로 빌드 파일을 구성합니다.

------
#### [ Use the AWS CRT-based S3 client ]

```
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.27.211</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3-transfer-manager</artifactId>
    </dependency>
    <dependency>
        <groupId>software.amazon.awssdk.crt</groupId>
        <artifactId>aws-crt</artifactId>
        <version>0.29.1432</version>
    </dependency>
</dependencies>
```

1 [최신 버전](https://central.sonatype.com/artifact/software.amazon.awssdk/bom). 2[최신 버전](https://central.sonatype.com/artifact/software.amazon.awssdk.crt/aws-crt).

------
#### [ Use the Java-based S3 async client ]

```
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.27.211</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3-transfer-manager</artifactId>
    </dependency>
</dependencies>
```

1 [최신 버전](https://central.sonatype.com/artifact/software.amazon.awssdk/bom).

------

### S3 Transfer Manager 인스턴스를 생성
<a name="transfer-manager-create"></a>

병렬 전송을 활성화하려면 AWS CRT 기반 S3 클라이언트 또는 멀티파트가 활성화된 Java 기반 S3 비동기 클라이언트를 전달해야 합니다. 다음 예제에서는 사용자 지정 설정으로 S3 Transfer Manager를 구성하는 방법을 보여줍니다.

------
#### [ Use the AWS CRT-based S3 client ]

```
        S3AsyncClient s3AsyncClient = S3AsyncClient.crtBuilder()
                .credentialsProvider(DefaultCredentialsProvider.create())
                .region(Region.US_EAST_1)
                .targetThroughputInGbps(20.0)
                .minimumPartSizeInBytes(8 * MB)
                .build();

        S3TransferManager transferManager = S3TransferManager.builder()
                .s3Client(s3AsyncClient)
                .build();
```

------
#### [ Use the Java-based S3 async client ]

`aws-crt` 종속성이 빌드 파일에 포함되지 않은 경우 S3 Transfer Manager는 SDK for Java 2.x에서 사용되는 표준 S3 비동기식 클라이언트를 기반으로 구축됩니다.

**S3 클라이언트의 사용자 지정 구성 - 멀티파트 사용 필요**

```
        S3AsyncClient s3AsyncClient = S3AsyncClient.builder()
                .multipartEnabled(true)
                .credentialsProvider(DefaultCredentialsProvider.create())
                .region(Region.US_EAST_1)
                .build();

        S3TransferManager transferManager = S3TransferManager.builder()
                .s3Client(s3AsyncClient)
                .build();
```

**S3 클라이언트 구성 없음 - 멀티파트 지원이 자동으로 사용 설정됨**

```
S3TransferManager transferManager = S3TransferManager.create();
```

------

## S3 버킷으로 파일을 업로드하려면
<a name="transfer-manager-upload"></a>

다음 예제는 업로드 진행 상황을 기록하는 [LoggingTransferListener](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/progress/LoggingTransferListener.html)의 선택적 사용과 함께 파일 업로드 예제를 보여줍니다.

S3 Transfer Manager를 사용하여 Amazon S3에 파일을 업로드하려면 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/UploadFileRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/UploadFileRequest.html)객체를 `S3TransferManager`의 [uploadFile](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/S3TransferManager.html#uploadFile(software.amazon.awssdk.transfer.s3.model.UploadFileRequest)) 메서드에 전달하세요.

`uploadFile` 메서드에서 반환된 [FileUpload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/FileUpload.html) 객체는 업로드 프로세스를 나타냅니다. 요청이 완료되면 [CompletedFileUpload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedFileUpload.html) 객체에는 업로드에 대한 정보가 포함됩니다.

```
    public void trackUploadFile(S3TransferManager transferManager, String bucketName,
                             String key, URI filePathURI) {
        UploadFileRequest uploadFileRequest = UploadFileRequest.builder()
                .putObjectRequest(b -> b.bucket(bucketName).key(key))
                .addTransferListener(LoggingTransferListener.create())  // Add listener.
                .source(Paths.get(filePathURI))
                .build();

        FileUpload fileUpload = transferManager.uploadFile(uploadFileRequest);

        fileUpload.completionFuture().join();
        /*
            The SDK provides a LoggingTransferListener implementation of the TransferListener interface.
            You can also implement the interface to provide your own logic.

            Configure log4J2 with settings such as the following.
                <Configuration status="WARN">
                    <Appenders>
                        <Console name="AlignedConsoleAppender" target="SYSTEM_OUT">
                            <PatternLayout pattern="%m%n"/>
                        </Console>
                    </Appenders>

                    <Loggers>
                        <logger name="software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener" level="INFO" additivity="false">
                            <AppenderRef ref="AlignedConsoleAppender"/>
                        </logger>
                    </Loggers>
                </Configuration>

            Log4J2 logs the progress. The following is example output for a 21.3 MB file upload.
                Transfer initiated...
                |                    | 0.0%
                |====                | 21.1%
                |============        | 60.5%
                |====================| 100.0%
                Transfer complete!
        */
    }
```

### 가져오기
<a name="transfer-manager-upload-imports"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedFileUpload;
import software.amazon.awssdk.transfer.s3.model.FileUpload;
import software.amazon.awssdk.transfer.s3.model.UploadFileRequest;
import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.UUID;
```

## S3 버킷에서 파일 다운로드
<a name="transfer-manager-download"></a>

다음 예시는 다운로드 예시와 함께 다운로드 진행 상황을 기록하는 [LoggingTransferListener](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/progress/LoggingTransferListener.html)의 선택적 사용을 보여줍니다.

S3 Transfer Manager를 사용하여 S3 버킷에서 객체를 다운로드하려면 [DownloadFileRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/DownloadFileRequest.html) 객체를 만들어 [downloadFile](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/S3TransferManager.html#downloadFile(software.amazon.awssdk.transfer.s3.model.DownloadFileRequest)) 메서드에 전달합니다.

`S3TransferManager`의 `downloadFile` 메서드에서 반환된 [FileDownload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/FileDownload.html) 객체는 파일 전송을 나타냅니다. 다운로드가 완료되면 [CompletedFileDownload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedFileDownload.html)에는 다운로드에 대한 정보에 대한 액세스 권한이 포함됩니다.

```
    public void trackDownloadFile(S3TransferManager transferManager, String bucketName,
                             String key, String downloadedFileWithPath) {
        DownloadFileRequest downloadFileRequest = DownloadFileRequest.builder()
                .getObjectRequest(b -> b.bucket(bucketName).key(key))
                .addTransferListener(LoggingTransferListener.create())  // Add listener.
                .destination(Paths.get(downloadedFileWithPath))
                .build();

        FileDownload downloadFile = transferManager.downloadFile(downloadFileRequest);

        CompletedFileDownload downloadResult = downloadFile.completionFuture().join();
        /*
            The SDK provides a LoggingTransferListener implementation of the TransferListener interface.
            You can also implement the interface to provide your own logic.

            Configure log4J2 with settings such as the following.
                <Configuration status="WARN">
                    <Appenders>
                        <Console name="AlignedConsoleAppender" target="SYSTEM_OUT">
                            <PatternLayout pattern="%m%n"/>
                        </Console>
                    </Appenders>

                    <Loggers>
                        <logger name="software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener" level="INFO" additivity="false">
                            <AppenderRef ref="AlignedConsoleAppender"/>
                        </logger>
                    </Loggers>
                </Configuration>

            Log4J2 logs the progress. The following is example output for a 21.3 MB file download.
                Transfer initiated...
                |=======             | 39.4%
                |===============     | 78.8%
                |====================| 100.0%
                Transfer complete!
        */
    }
```

### 가져오기
<a name="transfer-manager-download-import"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.core.exception.SdkException;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedFileDownload;
import software.amazon.awssdk.transfer.s3.model.DownloadFileRequest;
import software.amazon.awssdk.transfer.s3.model.FileDownload;
import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
```

## 다른 버킷에 Amazon S3 객체를 추가
<a name="transfer-manager-copy"></a>

다음 예는 S3 Transfer Manager로 객체를 복사하는 방법을 보여줍니다.

S3 버킷에서 다른 버킷으로 객체 복사를 시작하려면 기본 [CopyObjectRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model/CopyObjectRequest.html) 인스턴스를 생성하세요.

다음으로, 기본 `CopyObjectRequest` 항목을 S3 Transfer Manager가사용할 수 있는 [CopyRequest로](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CopyRequest.html) 래핑합니다.

`S3TransferManager`의 `copy` 메서드에서 반환된 `Copy` 객체는 복사 프로세스를 나타냅니다. 복사 프로세스가 완료되면 [CompletedCopy](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedCopy.html) 객체에는 응답에 대한 세부 정보가 포함됩니다.

```
    public String copyObject(S3TransferManager transferManager, String bucketName,
            String key, String destinationBucket, String destinationKey) {
        CopyObjectRequest copyObjectRequest = CopyObjectRequest.builder()
                .sourceBucket(bucketName)
                .sourceKey(key)
                .destinationBucket(destinationBucket)
                .destinationKey(destinationKey)
                .build();

        CopyRequest copyRequest = CopyRequest.builder()
                .copyObjectRequest(copyObjectRequest)
                .build();

        Copy copy = transferManager.copy(copyRequest);

        CompletedCopy completedCopy = copy.completionFuture().join();
        return completedCopy.response().copyObjectResult().eTag();
    }
```

**참고**  
S3 Transfer Manager를 사용하여 리전 간 복사를 수행하려면 다음 코드 조각`crossRegionAccessEnabled`과 같이 AWS CRT 기반 S3 클라이언트 빌더에서를 활성화합니다.  

```
S3AsyncClient s3AsyncClient = S3AsyncClient.crtBuilder()
                .crossRegionAccessEnabled(true)
                .build();

S3TransferManager transferManager = S3TransferManager.builder()
                .s3Client(s3AsyncClient)
                .build();
```

### 가져오기
<a name="transfer-manager-copy-import"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.model.CopyObjectRequest;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedCopy;
import software.amazon.awssdk.transfer.s3.model.Copy;
import software.amazon.awssdk.transfer.s3.model.CopyRequest;

import java.util.UUID;
```

## S3 버킷에 로컬 디렉토리 업로드
<a name="transfer-manager-upload_directory"></a>

다음 예는 로컬 디렉터리를 S3에 업로드하는 방법을 보여줍니다.

먼저 `S3TransferManager` 인스턴스의 [uploadDirectory](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/S3TransferManager.html#uploadDirectory(software.amazon.awssdk.transfer.s3.model.UploadDirectoryRequest)) 메서드를 호출하여 [UploadDirectoryRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/UploadDirectoryRequest.html)를 전달합니다.

[DirectoryUpload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/DirectoryUpload.html) 객체는 업로드 프로세스를 나타내며, 요청이 완료되면 [CompletedDirectoryUpload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedDirectoryUpload.html)를 생성합니다. `CompleteDirectoryUpload` 객체에는 전송에 실패한 파일을 포함하여 전송 결과에 대한 정보가 들어 있습니다.

```
    public Integer uploadDirectory(S3TransferManager transferManager,
            URI sourceDirectory, String bucketName) {
        DirectoryUpload directoryUpload = transferManager.uploadDirectory(UploadDirectoryRequest.builder()
                .source(Paths.get(sourceDirectory))
                .bucket(bucketName)
                .build());

        CompletedDirectoryUpload completedDirectoryUpload = directoryUpload.completionFuture().join();
        completedDirectoryUpload.failedTransfers()
                .forEach(fail -> logger.warn("Object [{}] failed to transfer", fail.toString()));
        return completedDirectoryUpload.failedTransfers().size();
    }
```

### 가져오기
<a name="transfer-manager-upload_directory-import"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.services.s3.model.ObjectIdentifier;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedDirectoryUpload;
import software.amazon.awssdk.transfer.s3.model.DirectoryUpload;
import software.amazon.awssdk.transfer.s3.model.UploadDirectoryRequest;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.UUID;
```

## 로컬 디렉터리로 S3 버킷 객체 다운로드
<a name="transfer-manager-download_directory"></a>

다음 예시와 같이 S3 버킷에 있는 객체를 로컬 디렉터리로 다운로드할 수 있습니다.

S3 버킷의 객체를 로컬 디렉토리로 다운로드하려면 먼저 Transfer Manager의 [DownloadDirectory](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/S3TransferManager.html#downloadDirectory(software.amazon.awssdk.transfer.s3.model.DownloadDirectoryRequest)) 메서드를 호출하고 [DownloadDirectoryRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/DownloadDirectoryRequest.html)를 전달하세요.

[DirectoryDownload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/DirectoryDownload.html) 객체는 업로드 프로세스를 나타내며, 요청이 완료되면 [CompletedDirectoryDownload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedDirectoryDownload.html)를 생성합니다. `CompleteDirectoryDownload` 객체에는 전송에 실패한 파일을 포함하여 전송 결과에 대한 정보가 들어 있습니다.

```
    public Integer downloadObjectsToDirectory(S3TransferManager transferManager,
            URI destinationPathURI, String bucketName) {
        DirectoryDownload directoryDownload = transferManager.downloadDirectory(DownloadDirectoryRequest.builder()
                .destination(Paths.get(destinationPathURI))
                .bucket(bucketName)
                .build());
        CompletedDirectoryDownload completedDirectoryDownload = directoryDownload.completionFuture().join();

        completedDirectoryDownload.failedTransfers()
                .forEach(fail -> logger.warn("Object [{}] failed to transfer", fail.toString()));
        return completedDirectoryDownload.failedTransfers().size();
    }
```

### 가져오기
<a name="transfer-manager-download_directory-import"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.model.ObjectIdentifier;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedDirectoryDownload;
import software.amazon.awssdk.transfer.s3.model.DirectoryDownload;
import software.amazon.awssdk.transfer.s3.model.DownloadDirectoryRequest;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
```

## 전체 예제 보기
<a name="transfer-manager-example-location"></a>

이 페이지의 모든 예제에 대한 [전체 코드는 GitHub에 포함](https://github.com/awsdocs/aws-doc-sdk-examples/tree/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager)되어 있습니다.

# S3 이벤트 알림 작업
<a name="examples-s3-event-notifications"></a>

버킷의 활동을 모니터링하는 데 도움이 되도록 Amazon S3는 특정 이벤트가 발생할 때 알림을 보낼 수 있습니다. Amazon S3 사용 설명서에서는 [버킷이 보낼 수 있는 알림](https://docs.aws.amazon.com/AmazonS3/latest/userguide/EventNotifications.html#notification-how-to-overview)에 대한 정보를 얻을 수 있습니다.

SDK for Java를 사용하여 4개의 가능한 대상으로 이벤트를 보내도록 버킷을 설정할 수 있습니다.
+ Amazon Simple Notification Service(Amazon SNS) 주제
+ Amazon Simple Queue Service 대기열
+ AWS Lambda 함수
+ Amazon EventBridge

EventBridge로 이벤트를 전송하도록 버킷을 설정할 때 동일한 이벤트를 여러 대상으로 팬아웃하도록 EventBridge 규칙을 구성할 수 있습니다. 처음 3개 대상 중 하나로 직접 전송하도록 버킷을 구성하는 경우 각 이벤트에 하나의 대상 유형만 지정 가능합니다.

다음 섹션에서는 SDK for Java를 사용하여 Amazon SQS 대기열과 EventBridge라는 2가지 방법으로 S3 이벤트 알림을 전송하도록 버킷을 구성하는 방법을 알아봅니다.

마지막 섹션에서는 S3 이벤트 알림 API를 사용하여 객체 중심 방식으로 알림을 사용하는 방법을 보여줍니다.

## 대상으로 직접 전송하도록 버킷 구성
<a name="s3-event-conf-bucket-direct"></a>

다음 예제에서는 *객체 만들기* 이벤트 또는 객체 *태그 지정* 이벤트가 버킷에서 발생할 때 알림을 보내도록 버킷을 구성합니다.

```
static void processS3Events(String bucketName, String queueArn) {
    // Configure the bucket to send Object Created and Object Tagging notifications to an existing SQS queue.
    s3Client.putBucketNotificationConfiguration(b -> b
            .notificationConfiguration(ncb -> ncb
                    .queueConfigurations(qcb -> qcb
                            .events(Event.S3_OBJECT_CREATED, Event.S3_OBJECT_TAGGING)
                            .queueArn(queueArn)))
                    .bucket(bucketName)
    );
}
```

위에 표시된 코드는 2가지 유형의 이벤트를 수신하도록 하나의 대기열을 설정합니다. 편리하게 `queueConfigurations` 메서드를 사용하면 필요한 경우 여러 대기열 대상을 설정할 수 있습니다. 또한 `notificationConfiguration` 메서드에서 하나 이상의 Amazon SNS 주제 또는 하나 이상의 Lambda 함수와 같은 추가 대상을 설정할 수 있습니다. 다음 코드 조각은 대기열 2개와 대상 유형 3개가 있는 예제를 보여줍니다.

```
s3Client.putBucketNotificationConfiguration(b -> b
                .notificationConfiguration(ncb -> ncb
                        .queueConfigurations(qcb -> qcb
                                .events(Event.S3_OBJECT_CREATED, Event.S3_OBJECT_TAGGING)
                                .queueArn(queueArn), 
                                qcb2 -> qcb2.<...>)
                        .topicConfigurations(tcb -> tcb.<...>)
                        .lambdaFunctionConfigurations(lfcb -> lfcb.<...>))
                        .bucket(bucketName)
        );
```

코드 예제 GitHub 리포지토리에는 S3 이벤트 알림을 대기열로 직접 전송하는 [전체 예제](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/main/java/com/example/s3/ProcessS3EventNotification.java)가 포함되어 있습니다.

## EventBridge로 전송할 버킷 구성
<a name="s3-event-conf-bucket-eventbridge"></a>

다음 예시에서는 EventBridge에 알림을 보내도록 버킷을 구성합니다.

```
public static String setBucketNotificationToEventBridge(String bucketName) {
    // Enable bucket to emit S3 Event notifications to EventBridge.
    s3Client.putBucketNotificationConfiguration(b -> b
            .bucket(bucketName)
            .notificationConfiguration(b1 -> b1
                    .eventBridgeConfiguration(SdkBuilder::build))
    .build());
```

EventBridge로 이벤트를 보내도록 버킷을 구성할 때 EventBridge가 디스패치할 이벤트 유형이나 최종 대상이 아닌 EventBridge 대상을 나타내기만 하면 됩니다. Java SDK의 EventBridge 클라이언트를 사용하여 최종 대상 및 이벤트 유형을 구성합니다.

다음 코드는 *객체 만들기* 이벤트를 주제 및 대기열로 팬아웃하도록 EventBridge를 구성하는 방법을 보여줍니다.

```
   public static String configureEventBridge(String topicArn, String queueArn) {
        try {
            // Create an EventBridge rule to route Object Created notifications.
            PutRuleRequest putRuleRequest = PutRuleRequest.builder()
                    .name(RULE_NAME)
                    .eventPattern("""
                            {
                              "source": ["aws.s3"],
                              "detail-type": ["Object Created"],
                              "detail": {
                                "bucket": {
                                  "name": ["%s"]
                                }
                              }
                            }
                            """.formatted(bucketName))
                    .build();

            // Add the rule to the default event bus.
            PutRuleResponse putRuleResponse = eventBridgeClient.putRule(putRuleRequest)
                    .whenComplete((r, t) -> {
                        if (t != null) {
                            logger.error("Error creating event bus rule: " + t.getMessage(), t);
                            throw new RuntimeException(t.getCause().getMessage(), t);
                        }
                        logger.info("Event bus rule creation request sent successfully. ARN is: {}", r.ruleArn());
                    }).join();

            // Add the existing SNS topic and SQS queue as targets to the rule.
            eventBridgeClient.putTargets(b -> b
                    .eventBusName("default")
                    .rule(RULE_NAME)
                    .targets(List.of (
                            Target.builder()
                                    .arn(queueArn)
                                    .id("Queue")
                                    .build(),
                            Target.builder()
                                    .arn(topicArn)
                                    .id("Topic")
                                    .build())
                            )
                    ).join();
            return putRuleResponse.ruleArn();
        } catch (S3Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        return null;
    }
```

Java 코드에서 EventBridge로 작업하려면 Maven `pom.xml` 파일에 `eventbridge` 아티팩트에 대한 종속성을 추가합니다.

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

코드 예제 GitHub 리포지토리에는 S3 이벤트 알림을 EventBridge로 전송한 다음 주제 및 대기열로 전송하는 [전체 예제](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/main/java/com/example/s3/PutBucketS3EventNotificationEventBridge.java)가 포함되어 있습니다.

## S3 이벤트 알림 API를 사용하여 이벤트 처리
<a name="s3-event-notification-read"></a>

대상이 S3 알림 이벤트를 수신한 후 S3 이벤트 알림 API를 사용하여 객체 중심 방식으로 이를 처리할 수 있습니다. S3 이벤트 알림 API를 사용하여 대상으로 직접 디스패치되는 이벤트 알림([첫 번째 예제](#s3-event-conf-bucket-direct) 참조)을 사용할 수 있지만 EventBridge를 통해 라우팅되는 알림은 사용할 수 없습니다. 버킷에서 EventBridge로 전송된 S3 이벤트 알림에는 S3 이벤트 알림 API가 현재 처리하지 않는 [다른 구조](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html#ev-events-list)가 포함되어 있습니다.

### 종속성 추가
<a name="s3-event-notifications-dep"></a>

S3 이벤트 알림 API는 SDK for Java 2.x의 버전 2.25.11과 함께 릴리스되었습니다.

S3 이벤트 알림 API를 사용하려면 다음 코드 조각과 같이 Maven `pom.xml`에 필요한 종속성 요소를 추가합니다.

```
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.X.X1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3-event-notifications</artifactId>
    </dependency>
</dependencies>
```

1 [최신 버전](https://central.sonatype.com/artifact/software.amazon.awssdk/bom).

### `S3EventNotification` 클래스 사용
<a name="s3-event-notifications-use"></a>

#### JSON 문자열에서 `S3EventNotification` 인스턴스 만들기
<a name="s3-event-notifications-use-from-json"></a>

JSON 문자열을 `S3EventNotification` 객체로 변환하려면 다음 예제와 같이 `S3EventNotification` 클래스의 정적 메서드를 사용합니다.

```
import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification
import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotificationRecord
import software.amazon.awssdk.services.sqs.model.Message; 

public class S3EventNotificationExample {
    ...
    
    void receiveMessage(Message message) {
       // Message received from SQSClient.
       String sqsEventBody = message.body();
       S3EventNotification s3EventNotification = S3EventNotification.fromJson(sqsEventBody);
    
       // Use getRecords() to access all the records in the notification.                                                                                                       
       List<S3EventNotificationRecord> records = s3EventNotification.getRecords();   
    
        S3EventNotificationRecord record = records.stream().findFirst();
        // Use getters on the record to access individual attributes.
        String awsRegion = record.getAwsRegion();
        String eventName = record.getEventName();
        String eventSource = record.getEventSource();                                                                                                   
    }
}
```

이 예제에서 `fromJson` 메서드는 JSON 문자열을 `S3EventNotification` 객체로 변환합니다. JSON 문자열에 누락된 필드가 있으면 해당 Java 객체 필드에 `null` 값이 표시되고 JSON의 추가 필드는 무시됩니다.

이벤트 알림 레코드에 대한 다른 API는 `[S3EventNotificationRecord](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/eventnotifications/s3/model/S3EventNotificationRecord.html)`에 대한 API 참조에서 확인할 수 있습니다.

#### `S3EventNotification` 인스턴스를 JSON 문자열로 변환
<a name="s3-event-notifications-use-to-json"></a>

다음 예제와 같이 `toJson`(또는 `toJsonPretty`) 메서드를 사용하여 `S3EventNotification` 객체를 JSON 문자열로 변환합니다.

```
import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification

public class S3EventNotificationExample {
    ...

    void toJsonString(S3EventNotification event) {

        String json = event.toJson();
        String jsonPretty = event.toJsonPretty();

        System.out.println("JSON: " + json);
        System.out.println("Pretty JSON: " + jsonPretty);
    }
}
```

`GlacierEventData`, `ReplicationEventData`, `IntelligentTieringEventData`, `LifecycleEventData`에 대한 필드는 `null`인 경우 JSON에서 제외됩니다. 다른 `null` 필드는 `null`로 직렬화됩니다.

다음은 S3 객체 태그 지정 이벤트에 대한 `toJsonPretty` 메서드의 출력 예제입니다.

```
{
  "Records" : [ {
    "eventVersion" : "2.3",
    "eventSource" : "aws:s3",
    "awsRegion" : "us-east-1",
    "eventTime" : "2024-07-19T20:09:18.551Z",
    "eventName" : "ObjectTagging:Put",
    "userIdentity" : {
      "principalId" : "AWS:XXXXXXXXXXX"
    },
    "requestParameters" : {
      "sourceIPAddress" : "XXX.XX.XX.XX"
    },
    "responseElements" : {
      "x-amz-request-id" : "XXXXXXXXXXXX",
      "x-amz-id-2" : "XXXXXXXXXXXXX"
    },
    "s3" : {
      "s3SchemaVersion" : "1.0",
      "configurationId" : "XXXXXXXXXXXXX",
      "bucket" : {
        "name" : "amzn-s3-demo-bucket",
        "ownerIdentity" : {
          "principalId" : "XXXXXXXXXXX"
        },
        "arn" : "arn:aws:s3:::XXXXXXXXXX"
      },
      "object" : {
        "key" : "akey",
        "size" : null,
        "eTag" : "XXXXXXXXXX",
        "versionId" : null,
        "sequencer" : null
      }
    }
  } ]
}
```

API를 사용하여 Amazon SQS 대기열에서 수신한 알림으로 작업하는 방법을 보여주는 [전체 예제](https://github.com/awsdocs/aws-doc-sdk-examples/blob/75c3daadf750406156fc87fa30ee499a206b4a36/javav2/example_code/s3/src/main/java/com/example/s3/ProcessS3EventNotification.java#L117)는 GitHub에서 사용할 수 있습니다.

## Java 라이브러리를 사용하여 Lambda에서 S3 이벤트 처리: AWS SDK for Java 2.x 및 `aws-lambda-java-events`
<a name="s3-event-notif-processing-options"></a>

Java 2.x용 SDK를 사용하여 Lambda 함수에서 Amazon S3 이벤트 알림을 처리하는 대신 `[aws-lambda-java-events](https://github.com/aws/aws-lambda-java-libs/tree/main/aws-lambda-java-events)` 버전 3.x.x. AWS 의 `aws-lambda-java-events` 라이브러리를 독립적으로 사용할 수 있으며 자체 종속성 요구 사항이 있습니다. `aws-lambda-java-events` 라이브러리는 Lambda 함수의 S3 이벤트에서만 작동하는 반면, SDK for Java 2.x는 Lambda 함수, Amazon SNS 및 Amazon SQS의 S3 이벤트에서 작동합니다.

두 접근 방식 모두 유사한 API를 사용하여 객체 중심 방식으로 JSON 이벤트 알림 페이로드를 모델링합니다. 다음 표에는 두 접근 방식을 사용하는 데 있어 주목할 만한 차이점이 나와 있습니다.


****  

|  | AWS SDK for Java | aws-lambda-java-events 라이브러리 | 
| --- | --- | --- | 
| 패키지 이름 지정 |  `software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification`  | com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification | 
| RequestHandler 파라미터 |  Lambda 함수의 `RequestHandler` 구현을 작성하여 JSON 문자열을 수신합니다. <pre>import com.amazonaws.services.lambda.runtime.Context;<br />import com.amazonaws.services.lambda.runtime.RequestHandler;<br />import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification;<br /><br />public class Handler implements RequestHandler<String, String> {<br /><br />    @Override<br />        public String handleRequest(String jsonS3Event, Context context) {<br />            S3EventNotification s3Event = S3EventNotification<br />                                             .fromJson(jsonS3Event);<br />            // Work with the s3Event object.        <br />            ...<br />    }<br />}</pre>  | Lambda 함수의 RequestHandler 구현을 작성하여 S3Event 객체를 수신합니다.<pre>import com.amazonaws.services.lambda.runtime.Context;<br />import com.amazonaws.services.lambda.runtime.RequestHandler;<br />import com.amazonaws.services.lambda.runtime.events.S3Event;<br /><br />public class Handler implements RequestHandler<S3Event, String> {<br /><br />    @Override<br />        public String handleRequest(S3Event s3event, Context context) {<br />            // Work with the s3Event object.        <br />            ...<br />    }<br />}</pre> | 
| Maven 종속성 |  <pre><dependencyManagement><br />    <dependencies><br />        <dependency><br />            <groupId>software.amazon.awssdk</groupId><br />            <artifactId>bom</artifactId><br />            <version>2.X.X</version><br />            <type>pom</type><br />            <scope>import</scope><br />        </dependency><br />    </dependencies><br /></dependencyManagement><br /><dependencies><br />    <dependency><br />        <groupId>software.amazon.awssdk</groupId><br />        <artifactId>s3-event-notifications</artifactId><br />    </dependency><br />    <!-- Add other SDK dependencies that you need. --><br /></dependencies></pre>  |  <pre><dependencyManagement><br />    <dependencies><br />        <dependency><br />            <groupId>software.amazon.awssdk</groupId><br />            <artifactId>bom</artifactId><br />            <version>2.X.X</version><br />            <type>pom</type><br />            <scope>import</scope><br />        </dependency><br />    </dependencies><br /></dependencyManagement><br /><dependencies><br />    <!-- The following two dependencies are for the <br />         aws-lambda-java-events library. --><br />    <dependency><br />        <groupId>com.amazonaws</groupId><br />        <artifactId>aws-lambda-java-core</artifactId><br />        <version>1.2.3</version>     <br />    </dependency><br />    <dependency><br />        <groupId>com.amazonaws</groupId><br />        <artifactId>aws-lambda-java-events</artifactId><br />        <version>3.15.0</version><br />    </dependency><br />    <!-- Add other SDK dependencies that you need. --><br /></dependencies></pre>  | 