

# DynamoDB 및 AWS SDK를 사용한 프로그래밍
<a name="Programming"></a>

이 단원에서는 개발자 관련 주제를 다룹니다. 코드 예제를 실행하려면 [이 개발자 안내서의 코드 예시 실행](CodeSamples.md) 단원을 참조하세요.

**참고**  
2017년 12월, AWS는 ATS(Amazon Trust Services)에서 발급된 보안 인증서를 사용하기 위해 모든 Amazon DynamoDB 엔드포인트를 마이그레이션하는 프로세스를 시작했습니다. 자세한 내용은 [DynamoDB와의 SSL/TLS 연결 설정 문제 해결](ats-certs.md) 섹션을 참조하세요.

**Topics**
+ [DynamoDB에 대한 AWS SDK 지원 개요](Programming.SDKOverview.md)
+ [Python과 Boto3를 사용한 Amazon DynamoDB 프로그래밍](programming-with-python.md)
+ [JavaScript를 사용한 Amazon DynamoDB 프로그래밍](programming-with-javascript.md)
+ [AWS SDK for Java 2.x를 사용한 DynamoDB 프로그래밍](ProgrammingWithJava.md)
+ [DynamoDB 관련 오류 처리](Programming.Errors.md)
+ [AWS SDK와 함께 DynamoDB 사용](sdk-general-information-section.md)

# DynamoDB에 대한 AWS SDK 지원 개요
<a name="Programming.SDKOverview"></a>

다음 다이어그램은 AWS SDK를 사용한 Amazon DynamoDB 애플리케이션 프로그래밍의 종합적 개요를 제공합니다.

![\[DynamoDB를 AWS SDK와 함께 사용하기 위한 프로그래밍 모델입니다.\]](http://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/images/SDKSupport.png)


1. 해당 프로그래밍 언어의 AWS SDK를 사용하여 애플리케이션을 작성합니다.

1. 각각의 AWS SDK는 DynamoDB 작업을 위한 하나 이상의 프로그래밍 인터페이스를 제공합니다. 사용 가능한 특정 인터페이스는 사용하는 프로그래밍 언어와 AWS SDK에 따라 달라집니다. 옵션에는 다음이 포함됩니다.
   + [DynamoDB에서 작동하는 하위 수준 인터페이스](Programming.SDKs.Interfaces.md#Programming.SDKs.Interfaces.LowLevel)
   + [DynamoDB에서 작동하는 문서 인터페이스](Programming.SDKs.Interfaces.md#Programming.SDKs.Interfaces.Document)
   + [DynamoDB에서 작동하는 객체 지속성 인터페이스](Programming.SDKs.Interfaces.md#Programming.SDKs.Interfaces.Mapper)
   + [상위 수준 인터페이스](HigherLevelInterfaces.md)

1. AWS SDK가 하위 수준 DynamoDB API와 함께 사용할 HTTP(S) 요청을 구성합니다.

1. AWS SDK가 DynamoDB 엔드포인트에 요청을 보냅니다.

1. DynamoDB가 요청을 실행합니다. 요청이 성공하면 DynamoDB는 HTTP 200 응답 코드(OK)를 반환합니다. 요청이 실패하면 DynamoDB는 HTTP 오류 코드와 오류 메시지를 반환합니다.

1. AWS SDK는 응답을 처리하여 이를 다시 애플리케이션에 전파합니다.

각각의 AWS SDK가 애플리케이션에 제공하는 중요 서비스에는 다음이 포함됩니다.
+ HTTP(S) 요청 서식 설정 및 요청 파라미터 직렬화.
+ 각 요청의 암호화 서명 생성.
+ 요청을 DynamoDB 엔드포인트에 전달하고 DynamoDB에서 응답을 수신.
+ 이러한 응답에서 결과 추출.
+ 오류 발생 시 기본적 재시도 로직 구현.

이들 작업을 위한 코드는 작성할 필요가 없습니다.

**참고**  
설치 지침과 설명서 등 AWS SDK에 대한 자세한 내용은 [Amazon Web Services용 도구](https://aws.amazon.com/tools)를 참조하세요.

## SDK에서 AWS 계정 기반 엔드포인트 지원
<a name="Programming.SDKs.endpoints"></a>

AWS에서는 2024년 9월 4일 AWS SDK for Java V1을 시작으로 DynamoDB용 AWS 계정 기반 엔드포인트에 대한 SDK 지원을 롤아웃합니다. 이러한 새 엔드포인트는 AWS에서 높은 성능과 확장성을 보장하는 데 도움이 됩니다. 업데이트된 SDK는 `https://(account-id).ddb.(region).amazonaws.com` 형식을 가진 새 엔드포인트를 자동으로 사용합니다.

SDK 클라이언트의 단일 인스턴스를 사용하여 여러 계정에 요청을 보내는 경우 애플리케이션에서 연결을 재사용할 기회가 줄어듭니다. AWS에서는 SDK 클라이언트 인스턴스당 더 적은 수의 계정에 연결하도록 애플리케이션을 수정할 것을 권장합니다. 대안은 [https://docs.aws.amazon.com/sdkref/latest/guide/feature-account-endpoints.html](https://docs.aws.amazon.com/sdkref/latest/guide/feature-account-endpoints.html)에 설명된 대로 `ACCOUNT_ID_ENDPOINT_MODE` 설정을 사용하여 SDK 클라이언트가 리전 엔드포인트를 계속 사용하도록 설정하는 것입니다.

# DynamoDB에서 작동하는 프로그래밍 인터페이스
<a name="Programming.SDKs.Interfaces"></a>

각각의 [AWS SDK](https://aws.amazon.com/tools)는 Amazon DynamoDB 작업을 위한 하나 이상의 프로그래밍 인터페이스를 제공합니다. 이들 인터페이스는 단순한 하위 수준 DynamoDB 래퍼부터 객체 지향적인 지속성 계층까지 다양합니다. 사용 가능한 인터페이스는 사용하는 AWS SDK와 프로그래밍 언어에 따라 달라집니다.

![\[DynamoDB와 함께 사용할 수 있도록 다양한 AWS SDK에서 지원되는 프로그래밍 인터페이스입니다.\]](http://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/images/SDKSupport.SDKInterfaces.png)


다음 단원에서는 AWS SDK for Java를 예제로 삼아 사용 가능한 몇 가지 인터페이스를 중점적으로 살펴봅니다. (AWS SDK에 따라 일부 인터페이스는 사용할 수 없습니다.)

**Topics**
+ [DynamoDB에서 작동하는 하위 수준 인터페이스](#Programming.SDKs.Interfaces.LowLevel)
+ [DynamoDB에서 작동하는 문서 인터페이스](#Programming.SDKs.Interfaces.Document)
+ [DynamoDB에서 작동하는 객체 지속성 인터페이스](#Programming.SDKs.Interfaces.Mapper)

## DynamoDB에서 작동하는 하위 수준 인터페이스
<a name="Programming.SDKs.Interfaces.LowLevel"></a>

모든 언어별 AWS SDK는 하위 수준 DynamoDB API 요청과 매우 비슷한 메서드를 사용하여 Amazon DynamoDB용 하위 수준 인터페이스를 제공합니다.

경우에 따라 [데이터 형식 서술자](Programming.LowLevelAPI.md#Programming.LowLevelAPI.DataTypeDescriptors)(예: 문자열의 경우 `N`, 숫자의 경우 `S`)을(를) 사용하여 속성의 데이터 형식을 식별해야 합니다.

**참고**  
하위 수준 인터페이스는 모든 언어별 AWS SDK에서 제공됩니다.

다음 Java 프로그램은 AWS SDK for Java의 하위 수준 인터페이스를 사용합니다.

### 하위 수준 인터페이스 예제
<a name="low-level-example"></a>

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Before running this Java V2 code example, set up your development
 * environment, including your credentials.
 *
 * For more information, see the following documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 *
 * To get an item from an Amazon DynamoDB table using the AWS SDK for Java V2,
 * its better practice to use the
 * Enhanced Client, see the EnhancedGetItem example.
 */
public class GetItem {
    public static void main(String[] args) {
        final String usage = """

                Usage:
                    <tableName> <key> <keyVal>

                Where:
                    tableName - The Amazon DynamoDB table from which an item is retrieved (for example, Music3).\s
                    key - The key used in the Amazon DynamoDB table (for example, Artist).\s
                    keyval - The key value that represents the item to get (for example, Famous Band).
                """;

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

        String tableName = args[0];
        String key = args[1];
        String keyVal = args[2];
        System.out.format("Retrieving item \"%s\" from \"%s\"\n", keyVal, tableName);
        Region region = Region.US_EAST_1;
        DynamoDbClient ddb = DynamoDbClient.builder()
                .region(region)
                .build();

        getDynamoDBItem(ddb, tableName, key, keyVal);
        ddb.close();
    }

    public static void getDynamoDBItem(DynamoDbClient ddb, String tableName, String key, String keyVal) {
        HashMap<String, AttributeValue> keyToGet = new HashMap<>();
        keyToGet.put(key, AttributeValue.builder()
                .s(keyVal)
                .build());

        GetItemRequest request = GetItemRequest.builder()
                .key(keyToGet)
                .tableName(tableName)
                .build();

        try {
            // If there is no matching item, GetItem does not return any data.
            Map<String, AttributeValue> returnedItem = ddb.getItem(request).item();
            if (returnedItem.isEmpty())
                System.out.format("No item found with the key %s!\n", key);
            else {
                Set<String> keys = returnedItem.keySet();
                System.out.println("Amazon DynamoDB table attributes: \n");
                for (String key1 : keys) {
                    System.out.format("%s: %s\n", key1, returnedItem.get(key1).toString());
                }
            }

        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }
}
```

## DynamoDB에서 작동하는 문서 인터페이스
<a name="Programming.SDKs.Interfaces.Document"></a>

많은 AWS SDK는 테이블과 인덱스에서 데이터 영역 작업(생성, 읽기, 업데이트, 삭제)을 수행할 수 있는 문서 인터페이스를 제공합니다. 문서 인터페이스를 사용하면 [데이터 형식 서술자](Programming.LowLevelAPI.md#Programming.LowLevelAPI.DataTypeDescriptors)를 지정할 필요가 없습니다. 데이터 형식은 데이터 자체의 의미론으로 암시됩니다. 이러한 AWS SDK는 JSON 문서를 기본 Amazon DynamoDB 데이터 형식으로, 또 그 반대로 쉽게 변환하는 메서드도 제공합니다.

**참고**  
문서 인터페이스는 AWS SDK for [Java](https://aws.amazon.com/sdk-for-java), [.NET](https://aws.amazon.com/sdk-for-net), [Node.js](https://aws.amazon.com/sdk-for-node-js), [JavaScript SDK](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/)에서 사용할 수 있습니다.

다음 Java 프로그램은 AWS SDK for Java의 문서 인터페이스를 사용합니다. 이 프로그램은 `Table` 테이블을 나타내는 `Music` 객체를 생성한 다음 해당 객체에게 `GetItem`을 사용하여 노래를 검색하라고 요청합니다. 그런 다음 프로그램은 해당 노래가 발표된 연도를 인쇄합니다.

`software.amazon.dynamodb.document.DynamoDB` 클래스는 DynamoDB 문서 인터페이스를 구현합니다. `DynamoDB`가 어떻게 하위 수준 클라이언트(`AmazonDynamoDB`)를 둘러싼 래퍼 역할을 하는지에 유의하세요.

### 문서 인터페이스 예제
<a name="document-level-example"></a>

```
package com.amazonaws.codesamples.gsg;

import software.amazon.dynamodb.AmazonDynamoDB;
import software.amazon.dynamodb.AmazonDynamoDBClientBuilder;
import software.amazon.dynamodb.document.DynamoDB;
import software.amazon.dynamodb.document.GetItemOutcome;
import software.amazon.dynamodb.document.Table;

public class MusicDocumentDemo {

    public static void main(String[] args) {

        AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
        DynamoDB docClient = new DynamoDB(client);

        Table table = docClient.getTable("Music");
        GetItemOutcome outcome = table.getItemOutcome(
                "Artist", "No One You Know",
                "SongTitle", "Call Me Today");

        int year = outcome.getItem().getInt("Year");
        System.out.println("The song was released in " + year);

    }
}
```

## DynamoDB에서 작동하는 객체 지속성 인터페이스
<a name="Programming.SDKs.Interfaces.Mapper"></a>

일부 AWS SDK는 직접 데이터 영역 작업을 수행하지 않는 객체 지속성 인터페이스를 제공합니다. 그 대신 Amazon DynamoDB 테이블 및 인덱스의 항목을 나타내는 객체를 생성하고 이러한 객체와 상호 작용합니다. 이를 통해 데이터베이스 중심 코드가 아니라 객체 중심 코드를 만들 수 있습니다.

**참고**  
객체 지속성 인터페이스는 Java 및 .NET용 AWS SDK에서 제공됩니다. 자세한 내용은 DynamoDB의 [DynamoDB에 대한 높은 수준의 프로그래밍 인터페이스](HigherLevelInterfaces.md)를 참조하세요.

### 객체 지속성 인터페이스 예제
<a name="mapper-level-example"></a>

```
import com.example.dynamodb.Customer;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
import software.amazon.awssdk.enhanced.dynamodb.Key;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.model.GetItemEnhancedRequest;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
```

```
import com.example.dynamodb.Customer;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
import software.amazon.awssdk.enhanced.dynamodb.Key;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.model.GetItemEnhancedRequest;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;

/*
 * Before running this code example, create an Amazon DynamoDB table named Customer with these columns:
 *   - id - the id of the record that is the key. Be sure one of the id values is `id101`
 *   - custName - the customer name
 *   - email - the email value
 *   - registrationDate - an instant value when the item was added to the table. These values
 *                        need to be in the form of `YYYY-MM-DDTHH:mm:ssZ`, such as 2022-07-11T00:00:00Z
 *
 * Also, ensure that you have set up your development environment, including your credentials.
 *
 * For information, see this documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */

public class EnhancedGetItem {
    public static void main(String[] args) {
        Region region = Region.US_EAST_1;
        DynamoDbClient ddb = DynamoDbClient.builder()
                .region(region)
                .build();

        DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
                .dynamoDbClient(ddb)
                .build();

        getItem(enhancedClient);
        ddb.close();
    }

    public static String getItem(DynamoDbEnhancedClient enhancedClient) {
        Customer result = null;
        try {
            DynamoDbTable<Customer> table = enhancedClient.table("Customer", TableSchema.fromBean(Customer.class));
            Key key = Key.builder()
                    .partitionValue("id101").sortValue("tred@noserver.com")
                    .build();

            // Get the item by using the key.
            result = table.getItem(
                    (GetItemEnhancedRequest.Builder requestBuilder) -> requestBuilder.key(key));
            System.out.println("******* The description value is " + result.getCustName());

        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
        return result.getCustName();
    }
}
```

# DynamoDB에 대한 높은 수준의 프로그래밍 인터페이스
<a name="HigherLevelInterfaces"></a>

AWS SDK는 애플리케이션에 Amazon DynamoDB에서 사용할 수 있는 하위 수준의 인터페이스를 제공합니다. 이러한 클라이언트 측 클래스와 메서드는 하위 수준 DynamoDB API에 직접적으로 대응합니다. 그러나 많은 개발자들이 복잡한 데이터 형식을 데이터베이스 테이블의 항목으로 매핑해야 할 때 연결 끊김 현상, 다시 말해 *임피던스 불일치*를 경험합니다. 하위 수준의 데이터베이스 인터페이스를 사용하는 개발자는 객체 데이터를 읽거나 데이터베이스 테이블에 쓰기 위한 메서드를 작성해야 하며 그 반대의 경우도 마찬가지입니다. 각 객체 유형 및 데이터베이스 테이블 조합에 필요한 추가 코드의 양은 엄청날 수 있습니다.

Java 및 .NET용 AWS SDK는 개발을 간소화하기 위해 높은 수준의 추상화를 갖춘 인터페이스를 추가로 제공합니다. 높은 수준의 DynamoDB 인터페이스를 사용하면 프로그램의 객체와 해당 객체의 데이터를 저장하는 데이터베이스 테이블 간의 관계를 정의할 수 있습니다. 이러한 매핑을 정의한 후 `save`, `load` 또는 `delete`와 같은 간단한 객체 메서드를 호출하면 기본 하위 수준 DynamoDB 작업이 사용자를 대신하여 자동으로 호출됩니다. 이를 통해 데이터베이스 중심 코드가 아니라 객체 중심 코드를 만들 수 있습니다.

높은 수준의 DynamoDB 프로그래밍 인터페이스는 Java 및 .NET용 AWS SDK에서 제공됩니다.

**Java**
+ [Java 1.x: DynamoDBMapper](DynamoDBMapper.md)
+ [Java 2.x: DynamoDB 고급형 클라이언트](DynamoDBEnhanced.md)

**.NET**
+ [DynamoDB에서 .NET 문서 모델을 사용하여 작업](DotNetSDKMidLevel.md)
+ [.NET 객체 지속성 모델 및 DynamoDB로 작업](DotNetSDKHighLevel.md)

# Java 1.x: DynamoDBMapper
<a name="DynamoDBMapper"></a>

**참고**  
SDK for Java에는 1.x와 2.x의 두 가지 버전이 있습니다. 1.x의 경우 2024년 1월 12일에 지원 종료가 [발표되었습니다](https://aws.amazon.com/blogs/developer/announcing-end-of-support-for-aws-sdk-for-java-v1-x-on-december-31-2025/). 2025년 12월 31일에 지원이 종료될 예정입니다. 새 개발의 경우 2.x를 사용하는 것이 좋습니다.

AWS SDK for Java에서는 클라이언트 측 클래스를 Amazon DynamoDB 테이블로 매핑할 수 있는 `DynamoDBMapper` 클래스를 제공합니다. `DynamoDBMapper`를 사용하려면 코드에서 DynamoDB 테이블의 항목과 해당하는 객체 인스턴스 간의 관계를 정의합니다. `DynamoDBMapper` 클래스를 사용하면 항목에 대한 다양한 생성, 읽기, 업데이트 및 삭제(CRUD) 작업을 수행하고 테이블에 대한 쿼리 및 스캔을 실행할 수 있습니다.

**Topics**
+ [DynamoDBMapper 클래스](DynamoDBMapper.Methods.md)
+ [Java용 DynamoDBMapper에서 지원되는 데이터 형식](DynamoDBMapper.DataTypes.md)
+ [DynamoDB에 사용되는 Java 주석](DynamoDBMapper.Annotations.md)
+ [DynamoDBMapper의 구성 설정(선택 사항)](DynamoDBMapper.OptionalConfig.md)
+ [DynamoDB 및 버전 번호를 이용한 낙관적 잠금](DynamoDBMapper.OptimisticLocking.md)
+ [DynamoDB에서 임의 데이터 매핑](DynamoDBMapper.ArbitraryDataMapping.md)
+ [DynamoDBMapper 예제](DynamoDBMapper.Examples.md)

**참고**  
`DynamoDBMapper` 클래스는 테이블 생성, 업데이트 또는 삭제를 허용하지 않습니다. 이러한 작업을 위해서는 대신 하위 수준 SDK for Java 인터페이스를 사용해야 합니다.

SDK for Java는 클래스를 테이블로 매핑할 수 있도록 일련의 주석 유형을 제공합니다. 예를 들어, `ProductCatalog` 테이블의 `Id`가 파티션 키인 경우 

```
ProductCatalog(Id, ...)
```

아래 Java 코드와 같이 클라이언트 애플리케이션의 클래스를 `ProductCatalog` 테이블로 매핑할 수 있습니다. 이 코드는 `CatalogItem`이라는 POJO(Plain Old Java Object)를 정의합니다. 여기서는 주석을 사용하여 객체 필드를 DynamoDB 속성 이름에 매핑합니다.

**Example**  

```
package com.amazonaws.codesamples;

import java.util.Set;

import software.amazon.dynamodb.datamodeling.DynamoDBAttribute;
import software.amazon.dynamodb.datamodeling.DynamoDBHashKey;
import software.amazon.dynamodb.datamodeling.DynamoDBIgnore;
import software.amazon.dynamodb.datamodeling.DynamoDBTable;

@DynamoDBTable(tableName="ProductCatalog")
public class CatalogItem {

    private Integer id;
    private String title;
    private String ISBN;
    private Set<String> bookAuthors;
    private String someProp;

    @DynamoDBHashKey(attributeName="Id")
    public Integer getId() { return id; }
    public void setId(Integer id) {this.id = id; }

    @DynamoDBAttribute(attributeName="Title")
    public String getTitle() {return title; }
    public void setTitle(String title) { this.title = title; }

    @DynamoDBAttribute(attributeName="ISBN")
    public String getISBN() { return ISBN; }
    public void setISBN(String ISBN) { this.ISBN = ISBN; }

    @DynamoDBAttribute(attributeName="Authors")
    public Set<String> getBookAuthors() { return bookAuthors; }
    public void setBookAuthors(Set<String> bookAuthors) { this.bookAuthors = bookAuthors; }

    @DynamoDBIgnore
    public String getSomeProp() { return someProp; }
    public void setSomeProp(String someProp) { this.someProp = someProp; }
}
```

위의 코드에서 `@DynamoDBTable` 주석은 `CatalogItem` 클래스를 `ProductCatalog` 테이블로 매핑합니다. 각 클래스 인스턴스는 항목 형태로 이 테이블에 저장할 수 있습니다. 클래스 정의에서 `@DynamoDBHashKey` 주석은 `Id` 속성을 기본 키로 매핑합니다.

기본적으로 클래스 속성은 테이블에서 동일한 이름의 속성으로 매핑됩니다. 예를 들어 `Title` 및 `ISBN` 속성은 테이블에서 동일한 이름의 속성으로 매핑됩니다.

DynamoDB 속성의 이름이 클래스에서 선언된 속성의 이름과 일치하는 경우 `@DynamoDBAttribute` 주석을 선택적으로 사용할 수 있습니다. 이러한 이름이 서로 다르면 이 주석을 `attributeName` 파라미터와 함께 사용하여 이 속성에 대응하는 DynamoDB 속성을 지정합니다.

위 예제에서는 속성 이름이 이전 단계에서 만든 테이블과 정확히 일치하면서 이 가이드의 다른 코드 예제에서도 일관된 속성 이름을 사용할 수 있도록 `@DynamoDBAttribute` 주석이 각 속성에 추가되었습니다.

클래스 정의는 테이블의 어떤 속성에도 매핑되지 않는 속성을 가질 수도 있습니다. `@DynamoDBIgnore` 주석을 추가하여 이러한 속성을 확인할 수 있습니다. 위 예제에서는 `SomeProp` 속성이 `@DynamoDBIgnore` 주석으로 표시되어 있습니다. `CatalogItem` 인스턴스를 테이블에 업로드할 때 `DynamoDBMapper` 인스턴스에는 `SomeProp` 속성이 포함되지 않습니다. 또한 테이블에서 항목을 가져오더라도 매퍼가 이 속성을 반환하지 않습니다.

매핑 클래스를 정의한 후에는 `DynamoDBMapper` 메서드를 사용하여 해당 클래스의 인스턴스를 `Catalog` 테이블의 해당 항목에 쓸 수 있습니다. 다음 코드 예제에서는 이 과정을 보여줍니다.

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();

DynamoDBMapper mapper = new DynamoDBMapper(client);

CatalogItem item = new CatalogItem();
item.setId(102);
item.setTitle("Book 102 Title");
item.setISBN("222-2222222222");
item.setBookAuthors(new HashSet<String>(Arrays.asList("Author 1", "Author 2")));
item.setSomeProp("Test");

mapper.save(item);
```

다음 코드 예제에서는 항목을 검색하고 일부 속성에 액세스하는 방법을 보여줍니다.

```
CatalogItem partitionKey = new CatalogItem();

partitionKey.setId(102);
DynamoDBQueryExpression<CatalogItem> queryExpression = new DynamoDBQueryExpression<CatalogItem>()
    .withHashKeyValues(partitionKey);

List<CatalogItem> itemList = mapper.query(CatalogItem.class, queryExpression);

for (int i = 0; i < itemList.size(); i++) {
    System.out.println(itemList.get(i).getTitle());
    System.out.println(itemList.get(i).getBookAuthors());
}
```

`DynamoDBMapper`는 Java에서 DynamoDB 데이터를 사용하는 직관적이고 자연스러운 방법을 제공합니다. 또한 낙관적 잠금, ACID 트랜잭션, 자동 생성된 파티션 키 및 정렬 키 값, 객체 버전 관리 등 기본적인 여러 기능을 제공합니다.

# DynamoDBMapper 클래스
<a name="DynamoDBMapper.Methods"></a>



`DynamoDBMapper` 클래스는 Amazon DynamoDB API의 진입점입니다. DynamoDB 엔드포인트에 액세스하고 여러 테이블의 데이터에 액세스할 수 있습니다. 또한 항목에 대한 다양한 생성, 읽기, 업데이트 및 삭제(CRUD) 작업을 수행하고 테이블에 대한 쿼리 및 스캔을 실행할 수 있습니다. 이 클래스는 DynamoDB를 작업하기 위한 다음과 같은 메서드를 제공합니다.

해당 Javadoc 설명서는 *AWS SDK for Java API 참조*의 [DynamoDBMapper](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/datamodeling/DynamoDBMapper.html)를 참조하세요.

**Topics**
+ [저장](#DynamoDBMapper.Methods.save)
+ [로드](#DynamoDBMapper.Methods.load)
+ [삭제](#DynamoDBMapper.Methods.delete)
+ [쿼리](#DynamoDBMapper.Methods.query)
+ [queryPage](#DynamoDBMapper.Methods.queryPage)
+ [스캔](#DynamoDBMapper.Methods.scan)
+ [scanPage](#DynamoDBMapper.Methods.scanPage)
+ [parallelScan](#DynamoDBMapper.Methods.parallelScan)
+ [batchSave](#DynamoDBMapper.Methods.batchSave)
+ [batchLoad](#DynamoDBMapper.Methods.batchLoad)
+ [batchDelete](#DynamoDBMapper.Methods.batchDelete)
+ [batchWrite](#DynamoDBMapper.Methods.batchWrite)
+ [transactionWrite](#DynamoDBMapper.Methods.transactionWrite)
+ [transactionLoad](#DynamoDBMapper.Methods.transactionLoad)
+ [count](#DynamoDBMapper.Methods.count)
+ [generateCreateTableRequest](#DynamoDBMapper.Methods.generateCreateTableRequest)
+ [createS3Link](#DynamoDBMapper.Methods.createS3Link)
+ [getS3ClientCache](#DynamoDBMapper.Methods.getS3ClientCache)

## 저장
<a name="DynamoDBMapper.Methods.save"></a>

지정한 객체를 테이블에 저장합니다. 여기서 저장하는 객체가 이 메서드에 유일하게 필요한 파라미터입니다. `DynamoDBMapperConfig` 객체를 사용하여 옵션으로 구성 파라미터를 입력할 수도 있습니다.

동일한 기본 키를 사용하는 항목이 없을 경우에는 이 메서드가 테이블에 새로운 항목을 생성합니다. 그리고, 동일한 기본 키를 사용하는 항목이 있을 경우에는 기존 항목을 업데이트합니다. 파티션 키와 정렬 키가 String 형식이고 `@DynamoDBAutoGeneratedKey`로 주석된 경우 초기화되지 않았으면 랜덤 UUID(Universally Unique Identifier)가 부여됩니다. `@DynamoDBVersionAttribute` 주석이 추가된 버전 필드는 1씩 증가합니다. 또한 버전 필드를 업데이트하거나 키를 생성하는 경우 작업 결과에 따라 전달되는 객체도 업데이트됩니다.

기본적으로 매핑된 클래스 속성에 해당되는 속성만 업데이트됩니다. 항목의 기존 추가 속성은 영향을 받지 않습니다. 하지만 `SaveBehavior.CLOBBER`를 지정할 경우 항목을 강제로 완전히 덮어쓸 수 있습니다.

```
DynamoDBMapperConfig config = DynamoDBMapperConfig.builder()
    .withSaveBehavior(DynamoDBMapperConfig.SaveBehavior.CLOBBER).build();
        
mapper.save(item, config);
```

버전 관리를 활성화하면 클라이언트 측 항목 버전과 서버 측 항목 버전이 일치해야 합니다. 하지만 `SaveBehavior.CLOBBER` 옵션을 사용하면 버전이 일치하지 않아도 괜찮습니다. 버전 관리에 대한 자세한 내용은 [DynamoDB 및 버전 번호를 이용한 낙관적 잠금](DynamoDBMapper.OptimisticLocking.md) 섹션을 참조하십시오.

## 로드
<a name="DynamoDBMapper.Methods.load"></a>

테이블에서 항목을 가져옵니다. 검색할 항목의 기본 키를 제공해야 합니다. `DynamoDBMapperConfig` 객체를 사용하여 옵션으로 구성 파라미터를 입력할 수도 있습니다. 예를 들어 아래 Java 문과 같이 이 메서드를 실행하여 최신 항목 값만 가져오려면 옵션으로 strongly consistent read를 요청하면 됩니다.

```
DynamoDBMapperConfig config = DynamoDBMapperConfig.builder()
    .withConsistentReads(DynamoDBMapperConfig.ConsistentReads.CONSISTENT).build();

CatalogItem item = mapper.load(CatalogItem.class, item.getId(), config);
```

기본적으로 DynamoDB는 최종적으로 일관된 값을 갖는 항목을 반환하기 때문입니다. DynamoDB의 최종 일관성 모델에 대한 자세한 내용은 [DynamoDB 읽기 일관성](HowItWorks.ReadConsistency.md) 단원을 참조하세요.

## 삭제
<a name="DynamoDBMapper.Methods.delete"></a>

항목을 테이블에서 삭제합니다. 이때는 매핑된 클래스의 객체 인스턴스를 전달해야 합니다.

버전 관리를 활성화하면 클라이언트 측 항목 버전과 서버 측 항목 버전이 일치해야 합니다. 하지만 `SaveBehavior.CLOBBER` 옵션을 사용하면 버전이 일치하지 않아도 괜찮습니다. 버전 관리에 대한 자세한 내용은 [DynamoDB 및 버전 번호를 이용한 낙관적 잠금](DynamoDBMapper.OptimisticLocking.md) 섹션을 참조하십시오.

## 쿼리
<a name="DynamoDBMapper.Methods.query"></a>

테이블 또는 보조 인덱스를 쿼리합니다.

포럼 스레드 회신이 저장되는 `Reply` 테이블이 있는 경우 스레드 제목마다 회신 수가 0개 또는 그 이상이 될 수 있습니다. `Reply` 테이블의 기본 키는 `Id` 및 `ReplyDateTime` 필드로 구성됩니다. 여기에서 `Id`는 파티션 키이고, `ReplyDateTime`은 기본 키의 정렬 키입니다.

```
Reply ( Id, ReplyDateTime, ... )
```

`Reply` 클래스와 DynamoDB의 해당 `Reply` 테이블 간 매핑을 생성한 경우 다음 Java 코드는 `DynamoDBMapper`를 사용하여 특정 스레드 제목에 대한 지난 2주 간 모든 회신을 찾습니다.

**Example**  

```
String forumName = "&DDB;";
String forumSubject = "&DDB; Thread 1";
String partitionKey = forumName + "#" + forumSubject;

long twoWeeksAgoMilli = (new Date()).getTime() - (14L*24L*60L*60L*1000L);
Date twoWeeksAgo = new Date();
twoWeeksAgo.setTime(twoWeeksAgoMilli);
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
String twoWeeksAgoStr = df.format(twoWeeksAgo);

Map<String, AttributeValue> eav = new HashMap<String, AttributeValue>();
eav.put(":v1", new AttributeValue().withS(partitionKey));
eav.put(":v2",new AttributeValue().withS(twoWeeksAgoStr.toString()));

DynamoDBQueryExpression<Reply> queryExpression = new DynamoDBQueryExpression<Reply>()
    .withKeyConditionExpression("Id = :v1 and ReplyDateTime > :v2")
    .withExpressionAttributeValues(eav);

List<Reply> latestReplies = mapper.query(Reply.class, queryExpression);
```

쿼리 결과 `Reply` 객체 컬렉션이 반환됩니다.

기본적으로 `query` 메서드는 "지연 로딩된(lazy-loaded)" 컬렉션을 반환합니다. 즉, 처음에는 결과 페이지를 하나만 반환하고, 필요에 따라 서비스를 호출하여 다음 페이지를 반환합니다. 일치하는 항목을 모두 가져오려면 `latestReplies` 컬렉션을 반복합니다.

컬렉션에서 `size()` 메서드를 호출하면 정확한 개수를 제공하기 위해 모든 결과가 로드됩니다. 이로 인해 프로비저닝된 처리량이 많이 사용될 수 있으며, 매우 큰 테이블에서는 JVM의 모든 메모리가 소모될 수도 있습니다.

인덱스를 쿼리하려면 먼저 인덱스를 매퍼 클래스로 모델링해야 합니다. `Reply` 테이블에는 *PostedBy-Message-Index*라는 글로벌 보조 인덱스가 있습니다. 이 인덱스의 파티션 키는 `PostedBy`이고, 정렬 키는 `Message`입니다. 이 인덱스에서 항목의 클래스 정의는 다음과 같을 것입니다.

```
@DynamoDBTable(tableName="Reply")
public class PostedByMessage {
    private String postedBy;
    private String message;

    @DynamoDBIndexHashKey(globalSecondaryIndexName = "PostedBy-Message-Index", attributeName = "PostedBy")
    public String getPostedBy() { return postedBy; }
    public void setPostedBy(String postedBy) { this.postedBy = postedBy; }

    @DynamoDBIndexRangeKey(globalSecondaryIndexName = "PostedBy-Message-Index", attributeName = "Message")
    public String getMessage() { return message; }
    public void setMessage(String message) { this.message = message; }

   // Additional properties go here.
}
```

`@DynamoDBTable` 주석은 이 인덱스가 `Reply` 테이블과 연결되어 있음을 나타냅니다. `@DynamoDBIndexHashKey` 주석은 인덱스의 파티션 키(*PostedBy*)를 표시하고, `@DynamoDBIndexRangeKey`는 인덱스의 정렬 키(*Message*)를 표시합니다.

이제 `DynamoDBMapper`를 사용하여 특정 사용자가 게시한 메시지 하위 집합을 검색하는 인덱스 쿼리를 실행할 수 있습니다. 테이블과 인덱스에 충돌하는 매핑이 없고 매핑이 이미 매퍼에서 만들어진 경우에는 인덱스 이름을 지정할 필요가 없습니다. 매퍼는 프라이머리 키와 정렬 키를 기반으로 추론합니다. 다음 코드에서는 글로벌 보조 인덱스를 쿼리합니다. 글로벌 보조 인덱스는 최종적 일관된 읽기는 지원하지만 강력히 일관된 읽기는 지원하지 않으므로 `withConsistentRead(false)`를 지정해야 합니다.

```
HashMap<String, AttributeValue> eav = new HashMap<String, AttributeValue>();
eav.put(":v1",  new AttributeValue().withS("User A"));
eav.put(":v2",  new AttributeValue().withS("DynamoDB"));

DynamoDBQueryExpression<PostedByMessage> queryExpression = new DynamoDBQueryExpression<PostedByMessage>()
    .withIndexName("PostedBy-Message-Index")
    .withConsistentRead(false)
    .withKeyConditionExpression("PostedBy = :v1 and begins_with(Message, :v2)")
    .withExpressionAttributeValues(eav);

List<PostedByMessage> iList =  mapper.query(PostedByMessage.class, queryExpression);
```

쿼리 결과 `PostedByMessage` 객체 컬렉션이 반환됩니다.

## queryPage
<a name="DynamoDBMapper.Methods.queryPage"></a>

테이블 또는 보조 인덱스를 쿼리하고 일치하는 결과를 단일 페이지로 반환합니다. `query` 메서드에서 그랬듯이 이번에도 파티션 키 값을 비롯해 정렬 키 속성에 적용되는 쿼리 필터를 지정해야 합니다. 그러나 `queryPage`는 첫 번째 데이터 "페이지"만 반환합니다. 다시 말해서 1M를 넘지 않는 범위에서 데이터를 반환합니다.

## 스캔
<a name="DynamoDBMapper.Methods.scan"></a>

전체 테이블 또는 보조 인덱스를 스캔합니다. `FilterExpression`를 지정하여 결과 집합을 필터링할 수도 있습니다(선택 사항).

포럼 스레드 회신이 저장되는 `Reply` 테이블이 있는 경우 스레드 제목마다 회신 수가 0개 또는 그 이상이 될 수 있습니다. `Reply` 테이블의 기본 키는 `Id` 및 `ReplyDateTime` 필드로 구성됩니다. 여기에서 `Id`는 파티션 키이고, `ReplyDateTime`은 기본 키의 정렬 키입니다.

```
Reply ( Id, ReplyDateTime, ... )
```

Java 클래스를 `Reply` 테이블로 매핑한 경우 `DynamoDBMapper`를 사용하여 테이블을 스캔할 수 있습니다. 예를 들어, 다음 Java 코드에서는 전체 `Reply` 테이블을 스캔하여 특정 연도의 회신만 반환합니다.

**Example**  

```
HashMap<String, AttributeValue> eav = new HashMap<String, AttributeValue>();
eav.put(":v1", new AttributeValue().withS("2015"));

DynamoDBScanExpression scanExpression = new DynamoDBScanExpression()
    .withFilterExpression("begins_with(ReplyDateTime,:v1)")
    .withExpressionAttributeValues(eav);

List<Reply> replies =  mapper.scan(Reply.class, scanExpression);
```

기본적으로 `scan` 메서드는 "지연 로딩된(lazy-loaded)" 컬렉션을 반환합니다. 즉, 처음에는 결과 페이지를 하나만 반환하고, 필요에 따라 서비스를 호출하여 다음 페이지를 반환합니다. 일치하는 항목을 모두 가져오려면 `replies` 컬렉션을 반복합니다.

컬렉션에서 `size()` 메서드를 호출하면 정확한 개수를 제공하기 위해 모든 결과가 로드됩니다. 이로 인해 프로비저닝된 처리량이 많이 사용될 수 있으며, 매우 큰 테이블에서는 JVM의 모든 메모리가 소모될 수도 있습니다.

인덱스를 스캔하려면 먼저 인덱스를 매퍼 클래스로 모델링해야 합니다. `Reply` 테이블에 `PostedBy-Message-Index`라는 글로벌 보조 인덱스가 있다고 가정합니다. 이 인덱스의 파티션 키는 `PostedBy`이고, 정렬 키는 `Message`입니다. 이 인덱스에 대한 매퍼 클래스는 [쿼리](#DynamoDBMapper.Methods.query) 단원에 나와 있습니다. 이 클래스는 `@DynamoDBIndexHashKey` 및 `@DynamoDBIndexRangeKey` 주석을 사용하여 인덱스 파티션 키 및 정렬 키를 지정합니다.

다음 코드 예제에서는 `PostedBy-Message-Index`를 스캔합니다. 스캔 필터를 사용하지 않으므로 인덱스의 모든 항목이 반환됩니다.

```
DynamoDBScanExpression scanExpression = new DynamoDBScanExpression()
    .withIndexName("PostedBy-Message-Index")
    .withConsistentRead(false);

    List<PostedByMessage> iList =  mapper.scan(PostedByMessage.class, scanExpression);
    Iterator<PostedByMessage> indexItems = iList.iterator();
```

## scanPage
<a name="DynamoDBMapper.Methods.scanPage"></a>

테이블 또는 보조 인덱스를 스캔하고 일치하는 결과를 단일 페이지로 반환합니다. `scan` 메서드와 마찬가지로 `FilterExpression`을 지정하여 결과 집합을 필터링할 수도 있습니다(선택 사항). 그러나 `scanPage`는 첫 번째 데이터 ‘페이지’만 반환합니다. 다시 말해서 1MB를 넘지 않는 범위에서 데이터를 반환합니다.

## parallelScan
<a name="DynamoDBMapper.Methods.parallelScan"></a>

전체 테이블 또는 보조 인덱스를 병렬 방식으로 스캔합니다. 즉, 결과를 필터링할 스캔 표현식과 함께 테이블의 논리 세그먼트를 다수 지정합니다. `parallelScan`은 스캔 작업을 각 논리 세그먼트마다 하나씩 여러 작업자로 분할합니다. 그러면 작업자가 데이터를 병렬 방식으로 처리하여 결과를 반환합니다.

다음 Java 코드 예제에서는 `Product` 테이블에 대한 병렬 스캔을 수행합니다.

```
int numberOfThreads = 4;

Map<String, AttributeValue> eav = new HashMap<String, AttributeValue>();
eav.put(":n", new AttributeValue().withN("100"));

DynamoDBScanExpression scanExpression = new DynamoDBScanExpression()
    .withFilterExpression("Price <= :n")
    .withExpressionAttributeValues(eav);

List<Product> scanResult = mapper.parallelScan(Product.class, scanExpression, numberOfThreads);
```

## batchSave
<a name="DynamoDBMapper.Methods.batchSave"></a>

`AmazonDynamoDB.batchWriteItem` 메서드를 1회 이상 호출하여 객체를 하나 이상의 테이블에 저장합니다. 이 메서드는 트랜잭션을 보장하지는 않습니다.

다음 Java 코드에서는 두 항목(책)을 `ProductCatalog` 테이블에 저장합니다.

```
Book book1 = new Book();
book1.setId(901);
book1.setProductCategory("Book");
book1.setTitle("Book 901 Title");

Book book2 = new Book();
book2.setId(902);
book2.setProductCategory("Book");
book2.setTitle("Book 902 Title");

mapper.batchSave(Arrays.asList(book1, book2));
```

## batchLoad
<a name="DynamoDBMapper.Methods.batchLoad"></a>

기본 키를 사용하여 다수의 항목을 하나 이상의 테이블에서 가져옵니다.

다음 Java 코드에서는 두 개의 서로 다른 테이블에서 두 개의 항목을 검색합니다.

```
ArrayList<Object> itemsToGet = new ArrayList<Object>();

ForumItem forumItem = new ForumItem();
forumItem.setForumName("Amazon DynamoDB");
itemsToGet.add(forumItem);

ThreadItem threadItem = new ThreadItem();
threadItem.setForumName("Amazon DynamoDB");
threadItem.setSubject("Amazon DynamoDB thread 1 message text");
itemsToGet.add(threadItem);

Map<String, List<Object>> items = mapper.batchLoad(itemsToGet);
```

## batchDelete
<a name="DynamoDBMapper.Methods.batchDelete"></a>

`AmazonDynamoDB.batchWriteItem` 메서드를 1회 이상 호출하여 객체를 하나 이상의 테이블에서 삭제합니다. 이 메서드는 트랜잭션을 보장하지는 않습니다.

다음은 Java 코드에서는 `ProductCatalog` 테이블에서 두 항목(책)을 삭제합니다.

```
Book book1 = mapper.load(Book.class, 901);
Book book2 = mapper.load(Book.class, 902);
mapper.batchDelete(Arrays.asList(book1, book2));
```

## batchWrite
<a name="DynamoDBMapper.Methods.batchWrite"></a>

`AmazonDynamoDB.batchWriteItem` 메서드를 1회 이상 호출하여 객체를 하나 이상의 테이블에 저장하거나 삭제합니다. 이 메서드는 트랜잭션을 보장하거나 버전 관리를 지원하지 않습니다(조건부 업로드 또는 삭제).

다음 Java 코드에서는 새 항목을 `Forum` 테이블에 쓰고, 새 항목을 `Thread` 테이블에 쓰고, `ProductCatalog` 테이블에서 항목을 삭제합니다.

```
// Create a Forum item to save
Forum forumItem = new Forum();
forumItem.setName("Test BatchWrite Forum");

// Create a Thread item to save
Thread threadItem = new Thread();
threadItem.setForumName("AmazonDynamoDB");
threadItem.setSubject("My sample question");

// Load a ProductCatalog item to delete
Book book3 = mapper.load(Book.class, 903);

List<Object> objectsToWrite = Arrays.asList(forumItem, threadItem);
List<Book> objectsToDelete = Arrays.asList(book3);

mapper.batchWrite(objectsToWrite, objectsToDelete);
```

## transactionWrite
<a name="DynamoDBMapper.Methods.transactionWrite"></a>

`AmazonDynamoDB.transactWriteItems` 메서드를 1회 호출하여 객체를 하나 이상의 테이블에 저장하거나 삭제합니다.

트랜잭션별 예외 목록을 보려면 [TransactWriteItems 오류](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html#API_TransactWriteItems_Errors)를 참조하세요.

DynamoDB 트랜잭션과 제공된 원자성, 일관성, 격리성 및 지속성(ACID) 보장에 대한 자세한 내용은 [Amazon DynamoDB Transactions](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transactions.html)를 참조하세요.

**참고**  
 이 메서드는 다음을 지원하지 않습니다.  
[DynamoDBMapperConfig.SaveBehavior](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBMapper.OptionalConfig.html).

다음 Java 코드에서는 트랜잭션 형태로 각 `Forum` 및 `Thread` 테이블에 새 항목을 씁니다.

```
Thread s3ForumThread = new Thread();
s3ForumThread.setForumName("S3 Forum");
s3ForumThread.setSubject("Sample Subject 1");
s3ForumThread.setMessage("Sample Question 1");

Forum s3Forum = new Forum();
s3Forum.setName("S3 Forum");
s3Forum.setCategory("Amazon Web Services");
s3Forum.setThreads(1);

TransactionWriteRequest transactionWriteRequest = new TransactionWriteRequest();
transactionWriteRequest.addPut(s3Forum);
transactionWriteRequest.addPut(s3ForumThread);
mapper.transactionWrite(transactionWriteRequest);
```

## transactionLoad
<a name="DynamoDBMapper.Methods.transactionLoad"></a>

`AmazonDynamoDB.transactGetItems` 메서드를 1회 호출하여 객체를 하나 이상의 테이블에서 로드합니다.

트랜잭션별 예외 목록을 보려면 [TransactGetItems 오류](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactGetItems.html#API_TransactGetItems_Errors)를 참조하세요.

DynamoDB 트랜잭션과 제공된 원자성, 일관성, 격리성 및 지속성(ACID) 보장에 대한 자세한 내용은 [Amazon DynamoDB Transactions](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transactions.html)를 참조하세요.

다음 Java 코드에서는 트랜잭션 형태로 각 `Forum` 및 `Thread` 테이블에서 한 개의 항목을 로드합니다.

```
Forum dynamodbForum = new Forum();
dynamodbForum.setName("DynamoDB Forum");
Thread dynamodbForumThread = new Thread();
dynamodbForumThread.setForumName("DynamoDB Forum");

TransactionLoadRequest transactionLoadRequest = new TransactionLoadRequest();
transactionLoadRequest.addLoad(dynamodbForum);
transactionLoadRequest.addLoad(dynamodbForumThread);
mapper.transactionLoad(transactionLoadRequest);
```

## count
<a name="DynamoDBMapper.Methods.count"></a>

지정한 스캔 표현식을 평가하여 일치하는 항목 수를 반환합니다. 항목 데이터를 따로 반환하지는 않습니다.

## generateCreateTableRequest
<a name="DynamoDBMapper.Methods.generateCreateTableRequest"></a>

DynamoDB 테이블을 나타내는 POJO 클래스를 구문 분석한 다음 해당 테이블의 `CreateTableRequest`를 반환합니다.

## createS3Link
<a name="DynamoDBMapper.Methods.createS3Link"></a>

Amazon S3 객체에 대한 링크를 생성합니다. 이때 버킷 객체의 고유 식별자로서 버킷 이름과 키 이름을 지정해야 합니다.

`createS3Link`를 사용하려면 접근자(getter/setter) 메서드를 정의해야 합니다. 다음 코드 예제에서는 새 속성 및 getter/setter 메서드를 `CatalogItem` 클래스에 추가하여 이를 설명합니다.

```
@DynamoDBTable(tableName="ProductCatalog")
public class CatalogItem {

    ...

    public S3Link productImage;

    ....

    @DynamoDBAttribute(attributeName = "ProductImage")
    public S3Link getProductImage() {
            return productImage;
    }

    public void setProductImage(S3Link productImage) {
        this.productImage = productImage;
    }

...
}
```

다음 Java 코드에서는 `Product` 테이블에 쓸 새 항목을 정의합니다. 코드를 보면 항목에 제품 이미지 링크가 추가되어 있습니다. 이 이미지 데이터는 Amazon S3에 업로드됩니다.

```
CatalogItem item = new CatalogItem();

item.setId(150);
item.setTitle("Book 150 Title");

String amzn-s3-demo-bucket = "amzn-s3-demo-bucket";
String myS3Key = "productImages/book_150_cover.jpg";
item.setProductImage(mapper.createS3Link(amzn-s3-demo-bucket, myS3Key));

item.getProductImage().uploadFrom(new File("/file/path/book_150_cover.jpg"));

mapper.save(item);
```

`S3Link` 클래스는 Amazon S3의 객체를 조작할 수 있는 여러 가지 다른 메서드를 제공합니다. 자세한 내용은 [`S3Link` Javadocs](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/datamodeling/S3Link.html)를 참조하세요.

## getS3ClientCache
<a name="DynamoDBMapper.Methods.getS3ClientCache"></a>

Amazon S3에 액세스할 수 있는 기본 `S3ClientCache`를 반환합니다. `S3ClientCache`는 `AmazonS3Client` 객체를 위한 스마트 맵입니다. 클라이언트가 다수일 때는 `S3ClientCache`가 AWS 리전별로 클라이언트를 구성하는 데 효과적일 뿐만 아니라 필요하다면 새 Amazon S3 클라이언트를 생성할 수도 있습니다.

# Java용 DynamoDBMapper에서 지원되는 데이터 형식
<a name="DynamoDBMapper.DataTypes"></a>

이 단원에서는 Amazon DynamoDB에서 지원되는 기본 Java 데이터 형식, 컬렉션 및 임의의 데이터 형식을 설명합니다.

Amazon DynamoDB는 다음과 같은 기본 Java 데이터 형식 및 기본 래퍼 클래스를 지원합니다.
+ `String`
+ `Boolean`, `boolean`
+ `Byte`, `byte`
+ `Date`([ISO\$18601](http://en.wikipedia.org/wiki/ISO_8601) 밀리초 정밀도 문자열에 따라 UTC로 전환)
+ `Calendar`([ISO\$18601](http://en.wikipedia.org/wiki/ISO_8601) 밀리초 정밀도 문자열에 따라 UTC로 전환)
+ `Long`, `long`
+ `Integer`, `int`
+ `Double`, `double`
+ `Float`, `float`
+ `BigDecimal`
+ `BigInteger`

**참고**  
DynamoDB 이름 지정 규칙 및 지원되는 다양한 데이터 형식에 대한 자세한 내용은 [Amazon DynamoDB에서 지원되는 데이터 형식 및 이름 지정 규칙](HowItWorks.NamingRulesDataTypes.md) 단원을 참조하세요.
DynamoDBMapper에서 빈 이진수 값이 지원됩니다.
AWS SDK for Java 2.x에서 빈 문자열 값이 지원됩니다.  
AWS SDK for Java 1.x에서 DynamoDBMapper는 빈 문자열 속성 값 읽기를 지원하지만, 해당 속성이 요청에서 삭제되므로 빈 문자열 속성 값을 쓰지 않습니다.

DynamoDB는 Java [Set](http://docs.oracle.com/javase/6/docs/api/java/util/Set.html), [List](http://docs.oracle.com/javase/6/docs/api/java/util/List.html) 및 [Map](http://docs.oracle.com/javase/6/docs/api/java/util/Map.html) 컬렉션 형식을 지원합니다. 다음 표에는 이러한 Java 형식이 DynamoDB 형식으로 매핑되는 방법이 요약되어 있습니다.


****  

| Java 형식 | DynamoDB 형식 | 
| --- | --- | 
|  모두 숫자 형식  |  `N`(숫자 형식)  | 
|  문자열  |  `S`(문자열 형식)   | 
|  부울  |  `BOOL`(부울 형식), 0 또는 1  | 
|  ByteBuffer  |  `B`(이진수 형식)  | 
|  날짜  |  `S`(문자열 형식) 날짜 값은 ISO-8601 포맷 문자열로 저장됩니다.  | 
| [Set](http://docs.oracle.com/javase/6/docs/api/java/util/Set.html)(집합) 컬렉션 형식 |  `SS`(문자열 집합) 형식, `NS`(숫자 집합) 형식, 또는 `BS`(이진수 집합) 형식  | 

 `DynamoDBTypeConverter` 인터페이스를 사용하면 자체적인 임의 데이터 형식을 DynamoDB에서 기본적으로 지원되는 데이터 형식으로 매핑할 수 있습니다. 자세한 내용은 [DynamoDB에서 임의 데이터 매핑](DynamoDBMapper.ArbitraryDataMapping.md) 섹션을 참조하세요.

# DynamoDB에 사용되는 Java 주석
<a name="DynamoDBMapper.Annotations"></a>

이 단원에서는 클래스 및 속성을 Amazon DynamoDB의 테이블 및 속성으로 매핑하는 데 사용되는 주석을 설명합니다.

해당 Javadoc 설명서는 [AWS SDK for Java API 참조](https://docs.aws.amazon.com/sdk-for-java/latest/reference/)의 [주석 유형 요약](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/datamodeling/package-summary.html) 단원을 참조하세요.

**참고**  
다음 주석에서 `DynamoDBTable` 및 `DynamoDBHashKey`만 필요합니다.

**Topics**
+ [DynamoDBAttribute](#DynamoDBMapper.Annotations.DynamoDBAttribute)
+ [DynamoDBAutoGeneratedKey](#DynamoDBMapper.Annotations.DynamoDBAutoGeneratedKey)
+ [DynamoDBAutoGeneratedTimestamp](#DynamoDBMapper.Annotations.DynamoDBAutoGeneratedTimestamp)
+ [DynamoDBDocument](#DynamoDBMapper.Annotations.DynamoDBDocument)
+ [DynamoDBHashKey](#DynamoDBMapper.Annotations.DynamoDBHashKey)
+ [DynamoDBIgnore](#DynamoDBMapper.Annotations.DynamoDBIgnore)
+ [DynamoDBIndexHashKey](#DynamoDBMapper.Annotations.DynamoDBIndexHashKey)
+ [DynamoDBIndexRangeKey](#DynamoDBMapper.Annotations.DynamoDBIndexRangeKey)
+ [DynamoDBRangeKey](#DynamoDBMapper.Annotations.DynamoDBRangeKey)
+ [DynamoDBTable](#DynamoDBMapper.Annotations.DynamoDBTable)
+ [DynamoDBTypeConverted](#DynamoDBMapper.Annotations.DynamoDBTypeConverted)
+ [DynamoDBTyped](#DynamoDBMapper.Annotations.DynamoDBTyped)
+ [DynamoDBVersionAttribute](#DynamoDBMapper.Annotations.DynamoDBVersionAttribute)

## DynamoDBAttribute
<a name="DynamoDBMapper.Annotations.DynamoDBAttribute"></a>

속성을 테이블 속성으로 매핑합니다. 기본적으로 각 클래스 속성은 이름이 동일한 항목 속성으로 매핑됩니다. 하지만 이름이 다를 경우에는 이 주석을 사용하여 클래스 속성을 테이블 속성으로 매핑할 수 있습니다. 아래 Java 코드 조각에서는 `DynamoDBAttribute`가 `BookAuthors` 속성을 테이블의 속성 이름으로 매핑하고 있습니다.`Authors`

```
@DynamoDBAttribute(attributeName = "Authors")
public List<String> getBookAuthors() { return BookAuthors; }
public void setBookAuthors(List<String> BookAuthors) { this.BookAuthors = BookAuthors; }
```

`DynamoDBMapper`가 객체를 테이블에 저장할 때는 `Authors`를 속성 이름으로 사용합니다.

## DynamoDBAutoGeneratedKey
<a name="DynamoDBMapper.Annotations.DynamoDBAutoGeneratedKey"></a>

파티션 키 또는 정렬 키 속성을 자동 생성되는 것으로 표시합니다. `DynamoDBMapper`는 이러한 속성을 저장할 때 임의의 [UUID](http://docs.oracle.com/javase/6/docs/api/java/util/UUID.html)를 생성합니다. String 속성만 자동 생성된 키로 표시될 수 있습니다.

다음 예제에서는 자동 생성된 키를 사용하는 방법을 보여줍니다.

```
@DynamoDBTable(tableName="AutoGeneratedKeysExample")
public class AutoGeneratedKeys {
    private String id;
    private String payload;

    @DynamoDBHashKey(attributeName = "Id")
    @DynamoDBAutoGeneratedKey
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }

    @DynamoDBAttribute(attributeName="payload")
    public String getPayload() { return this.payload; }
    public void setPayload(String payload) { this.payload = payload; }

    public static void saveItem() {
        AutoGeneratedKeys obj = new AutoGeneratedKeys();
        obj.setPayload("abc123");

        // id field is null at this point
        DynamoDBMapper mapper = new DynamoDBMapper(dynamoDBClient);
        mapper.save(obj);

        System.out.println("Object was saved with id " + obj.getId());
    }
}
```

## DynamoDBAutoGeneratedTimestamp
<a name="DynamoDBMapper.Annotations.DynamoDBAutoGeneratedTimestamp"></a>

자동으로 타임스탬프를 생성합니다.

```
@DynamoDBAutoGeneratedTimestamp(strategy=DynamoDBAutoGenerateStrategy.ALWAYS)
public Date getLastUpdatedDate() { return lastUpdatedDate; }
public void setLastUpdatedDate(Date lastUpdatedDate) { this.lastUpdatedDate = lastUpdatedDate; }
```

원하는 경우 전략 속성을 제공하여 자동 생성 전략을 정의할 수 있습니다. 기본값은 `ALWAYS`입니다.

## DynamoDBDocument
<a name="DynamoDBMapper.Annotations.DynamoDBDocument"></a>

클래스를 Amazon DynamoDB 문서로 직렬화할 수 있음을 나타냅니다.

예를 들어, JSON 문서를 Map 형식(`M`)의 DynamoDB 속성으로 매핑하려는 경우 다음 코드 예제에서는 Map 유형의 중첩된 속성(Pictures)을 포함하는 항목을 정의합니다.

```
public class ProductCatalogItem {

    private Integer id;  //partition key
    private Pictures pictures;
    /* ...other attributes omitted... */

    @DynamoDBHashKey(attributeName="Id")
    public Integer getId() { return id;}
    public void setId(Integer id) {this.id = id;}

    @DynamoDBAttribute(attributeName="Pictures")
    public Pictures getPictures() { return pictures;}
    public void setPictures(Pictures pictures) {this.pictures = pictures;}

    // Additional properties go here.

    @DynamoDBDocument
    public static class Pictures {
        private String frontView;
        private String rearView;
        private String sideView;

        @DynamoDBAttribute(attributeName = "FrontView")
        public String getFrontView() { return frontView; }
        public void setFrontView(String frontView) { this.frontView = frontView; }

        @DynamoDBAttribute(attributeName = "RearView")
        public String getRearView() { return rearView; }
        public void setRearView(String rearView) { this.rearView = rearView; }

        @DynamoDBAttribute(attributeName = "SideView")
        public String getSideView() { return sideView; }
        public void setSideView(String sideView) { this.sideView = sideView; }

     }
}
```

그러면 다음 예제와 같이 새 `ProductCatalog` 항목을 `Pictures`와 함께 저장할 수 있습니다.

```
ProductCatalogItem item = new ProductCatalogItem();

Pictures pix = new Pictures();
pix.setFrontView("http://example.com/products/123_front.jpg");
pix.setRearView("http://example.com/products/123_rear.jpg");
pix.setSideView("http://example.com/products/123_left_side.jpg");
item.setPictures(pix);

item.setId(123);

mapper.save(item);
```

결과로 얻은 `ProductCatalog` 항목은 다음과 같을 것입니다(JSON 형식).

```
{
  "Id" : 123
  "Pictures" : {
    "SideView" : "http://example.com/products/123_left_side.jpg",
    "RearView" : "http://example.com/products/123_rear.jpg",
    "FrontView" : "http://example.com/products/123_front.jpg"
  }
}
```

## DynamoDBHashKey
<a name="DynamoDBMapper.Annotations.DynamoDBHashKey"></a>

클래스 속성을 테이블의 파티션 키 속성으로 매핑합니다. 속성은 스칼라 문자열, 숫자 또는 이진수 형식 중 하나여야 합니다. 속성은 컬렉션 형식일 수 없습니다.

`ProductCatalog` 테이블이 있고 이 테이블에서 기본 키로 `Id`를 사용하는 경우 다음 Java 코드에서는 `CatalogItem` 클래스를 정의하고 `Id` 태그를 사용하여 해당 `ProductCatalog` 속성을 `@DynamoDBHashKey` 테이블의 기본 키로 매핑합니다.

```
@DynamoDBTable(tableName="ProductCatalog")
public class CatalogItem {
    private Integer Id;
   @DynamoDBHashKey(attributeName="Id")
   public Integer getId() {
        return Id;
   }
   public void setId(Integer Id) {
        this.Id = Id;
   }
   // Additional properties go here.
}
```

## DynamoDBIgnore
<a name="DynamoDBMapper.Annotations.DynamoDBIgnore"></a>

`DynamoDBMapper` 인스턴스에게 연동되어 있는 속성을 무시하라고 지시합니다. 그러면 데이터를 테이블에 저장할 때도 `DynamoDBMapper`는 이 속성을 테이블에 저장하지 않습니다.

 모델링되지 않은 속성의 클래스 필드 또는 getter 메서드에 적용됩니다. 클래스 필드에 주석이 직접 적용되는 경우 해당 getter 및 setter를 동일한 클래스에 선언해야 합니다.

## DynamoDBIndexHashKey
<a name="DynamoDBMapper.Annotations.DynamoDBIndexHashKey"></a>

클래스 속성을 글로벌 보조 인텍스의 파티션 키로 매핑합니다. 속성은 스칼라 문자열, 숫자 또는 이진수 형식 중 하나여야 합니다. 속성은 컬렉션 형식일 수 없습니다.

이 주석은 글로벌 보조 인덱스를 `Query`해야 하는 경우 사용합니다. 이때는 인덱스 이름(`globalSecondaryIndexName`)을 지정해야 합니다. 클래스 속성 이름이 인덱스 파티션 키와 다른 경우 인덱스 속성 이름(`attributeName`)도 지정해야 합니다.

## DynamoDBIndexRangeKey
<a name="DynamoDBMapper.Annotations.DynamoDBIndexRangeKey"></a>

클래스 속성을 글로벌 보조 인덱스 또는 로컬 보조 인덱스의 정렬 키로 매핑합니다. 속성은 스칼라 문자열, 숫자 또는 이진수 형식 중 하나여야 합니다. 속성은 컬렉션 형식일 수 없습니다.

이 주석은 로컬 보조 주석 또는 글로벌 보조 주석을 `Query`해야 하고 인덱스 정렬 키를 사용해 결과를 구체화하고 싶을 때 사용합니다. 이때는 인덱스 이름(`globalSecondaryIndexName` 또는 `localSecondaryIndexName`)을 지정해야 합니다. 그리고 클래스 속성 이름과 인덱스 정렬 키가 다른 경우에는 인덱스 속성 이름(`attributeName`)도 지정해야 합니다.

## DynamoDBRangeKey
<a name="DynamoDBMapper.Annotations.DynamoDBRangeKey"></a>

클래스 속성을 테이블의 정렬 키 속성으로 매핑합니다. 속성은 스칼라 문자열, 숫자 또는 이진수 형식 중 하나여야 합니다. 속성은 컬렉션 형식일 수 없습니다.

기본 키가 복합형(파티션 키 및 정렬 키)일 경우 이 태그를 사용하여 클래스 필드를 정렬 키로 매핑할 수 있습니다. 예를 들어, 포럼 스레드에 대한 회신을 저장하는 `Reply` 테이블이 있는 경우 스레드마다 다수의 댓글을 가질 수 있습니다. 따라서 이 테이블의 기본 키는 `ThreadId` 및 `ReplyDateTime`입니다. `ThreadId`는 파티션 키이고, `ReplyDateTime`은 정렬 키입니다.

다음은 `Reply` 클래스를 정의하여 `Reply` 테이블에 매핑하는 Java 코드입니다. 코드 조각을 보면 `@DynamoDBHashKey` 및 `@DynamoDBRangeKey` 태그를 사용하여 기본 키에 매핑되는 클래스 속성을 식별하고 있습니다.

```
@DynamoDBTable(tableName="Reply")
public class Reply {
    private Integer id;
    private String replyDateTime;

    @DynamoDBHashKey(attributeName="Id")
    public Integer getId() { return id; }
    public void setId(Integer id) { this.id = id; }

    @DynamoDBRangeKey(attributeName="ReplyDateTime")
    public String getReplyDateTime() { return replyDateTime; }
    public void setReplyDateTime(String replyDateTime) { this.replyDateTime = replyDateTime; }

   // Additional properties go here.
}
```

## DynamoDBTable
<a name="DynamoDBMapper.Annotations.DynamoDBTable"></a>

DynamoDB의 대상 테이블을 식별합니다. 예를 들어, 다음 Java 코드는 `Developer` 클래스를 정의하여 DynamoDB의 `People` 테이블에 매핑합니다.

```
@DynamoDBTable(tableName="People")
public class Developer { ...}
```

`@DynamoDBTable` 주석은 상속이 가능합니다. `Developer` 클래스에서 상속되는 새 클래스도 `People` 테이블로 매핑됩니다. 예를 들어 `Lead` 클래스를 `Developer` 클래스에서 상속하여 생성한다고 가정하겠습니다. `Developer` 클래스를 `People` 테이블로 매핑했기 때문에 `Lead` 클래스 객체도 동일한 테이블에 저장됩니다.

`@DynamoDBTable` 또한 재정의가 가능합니다. 기본적으로 `Developer` 클래스에서 상속되는 새 클래스는 모두 동일한 `People` 테이블로 매핑됩니다. 하지만 이 기본 매핑을 재정의할 수도 있습니다. 예를 들어, `Developer` 클래스에서 상속되는 클래스를 생성하는 경우 다음 Java 코드 예제와 같이 `@DynamoDBTable` 주석을 추가하여 해당 클래스를 다른 테이블로 명시적으로 매핑할 수 있습니다.

```
@DynamoDBTable(tableName="Managers")
public class Manager extends Developer { ...}
```

## DynamoDBTypeConverted
<a name="DynamoDBMapper.Annotations.DynamoDBTypeConverted"></a>

속성을 사용자 지정 유형 변환기를 사용하는 것으로 표시하는 주석 `DynamoDBTypeConverter`에 추가 속성을 전달하기 위한 사용자 정의 주석에 작성할 수 있습니다.

 `DynamoDBTypeConverter` 인터페이스를 사용하면 자체적인 임의 데이터 형식을 DynamoDB에서 기본적으로 지원되는 데이터 형식으로 매핑할 수 있습니다. 자세한 내용은 [DynamoDB에서 임의 데이터 매핑](DynamoDBMapper.ArbitraryDataMapping.md) 섹션을 참조하세요.

## DynamoDBTyped
<a name="DynamoDBMapper.Annotations.DynamoDBTyped"></a>

표준 속성 형식 바인딩을 재정의하는 주석 표준 형식에서 해당 형식에 기본 속성 바인딩을 적용할 경우 주석이 필요하지 않습니다.

## DynamoDBVersionAttribute
<a name="DynamoDBMapper.Annotations.DynamoDBVersionAttribute"></a>

낙관적 잠금 버전 번호를 저장하는 클래스 속성을 식별합니다. `DynamoDBMapper`는 새로운 항목을 저장할 때 버전 번호를 이 속성에 할당한 후 항목을 업데이트할 때마다 버전 번호를 올립니다. 여기서는 숫자 스칼라 형식만 지원됩니다. 데이터 형식에 대한 자세한 내용은 [데이터 타입](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes) 단원을 참조하세요. 버전 관리에 대한 자세한 내용은 [DynamoDB 및 버전 번호를 이용한 낙관적 잠금](DynamoDBMapper.OptimisticLocking.md) 섹션을 참조하십시오.

# DynamoDBMapper의 구성 설정(선택 사항)
<a name="DynamoDBMapper.OptionalConfig"></a>

`DynamoDBMapper`의 인스턴스를 생성할 때는 일정한 기본 동작이 있지만 `DynamoDBMapperConfig` 클래스를 사용하면 이러한 기본 동작을 재정의할 수 있습니다.

다음은 사용자 지정 설정으로 `DynamoDBMapper`를 생성하는 코드 조각입니다.

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();

DynamoDBMapperConfig mapperConfig = DynamoDBMapperConfig.builder()
        .withSaveBehavior(DynamoDBMapperConfig.SaveBehavior.CLOBBER)
        .withConsistentReads(DynamoDBMapperConfig.ConsistentReads.CONSISTENT)
        .withTableNameOverride(null)
        .withPaginationLoadingStrategy(DynamoDBMapperConfig.PaginationLoadingStrategy.EAGER_LOADING)
    .build();

DynamoDBMapper mapper = new DynamoDBMapper(client, mapperConfig);
```

자세한 내용은 [AWS SDK for Java API 참조](https://docs.aws.amazon.com/sdk-for-java/latest/reference/)에서 [https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/datamodeling/DynamoDBMapperConfig.html](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/datamodeling/DynamoDBMapperConfig.html)를 참조하세요.

`DynamoDBMapperConfig` 인스턴스에 사용할 수 있는 인수는 다음과 같습니다.
+ `DynamoDBMapperConfig.ConsistentReads` 열거 값:
  + `EVENTUAL` - 매퍼 인스턴스가 최종적으로 일관된 읽기 요청을 사용합니다.
  + `CONSISTENT` - 매퍼 인스턴스가 강력히 일관된 읽기 요청을 사용합니다. 이 옵션 설정은 `load`, `query` 또는 `scan` 작업에 사용할 수 있습니다. 강력히 일관된 읽기는 성능과 결제에 영향을 미칩니다. 자세한 내용은 DynamoDB [제품 세부 정보 페이지](https://aws.amazon.com/dynamodb)를 참조하세요.

  매퍼 인스턴스에 읽기 일관성 설정을 지정하지 않으면 `EVENTUAL`이 기본값으로 설정됩니다.
**참고**  
이 값은 DynamoDBMapper의 `query`, `querypage`, `load` 및 `batch load` 작업에만 적용됩니다.
+ `DynamoDBMapperConfig.PaginationLoadingStrategy` 열거 값 - 매퍼 인스턴스가 `query` 또는`scan`의 결과와 같은 페이지 매긴 데이터 목록을 처리하는 방식을 제어합니다.
  + `LAZY_LOADING` - 매퍼 인스턴스는 가능할 경우 데이터를 로드하고, 로드한 결과를 모두 메모리에 저장합니다.
  + `EAGER_LOADING` - 목록 초기화 직후 매퍼 인스턴스가 데이터를 로드합니다.
  + `ITERATION_ONLY` - 반복자를 사용해야만 목록에서 읽을 수 있습니다. 반복 단계에서는 다음 페이지를 로드하기 전에 이전 결과가 목록에서 사라집니다. 따라서 로드된 결과는 단일 페이지 목록으로만 메모리에 저장합니다. 이는 목록의 반복 횟수가 1회로 제한된다는 것을 의미하기도 합니다. 이러한 전략은 다수의 항목을 처리하면서 메모리 오버헤드를 줄여야 할 때 바람직합니다.

  매퍼 인스턴스에 페이지 매김 로딩 전략을 지정하지 않으면 `LAZY_LOADING`이 기본값으로 설정됩니다.
+ `DynamoDBMapperConfig.SaveBehavior` 열거 값 - 저장 작업 시 매퍼 인스턴스의 속성 처리 방식을 지정합니다.
  + `UPDATE` - 저장 작업 중 모델링된 속성만 모두 업데이트되고 모델링되지 않은 속성은 업데이트되지 않습니다. 기본적인 숫자 형식(byte, int, long)은 0으로 설정됩니다. 그리고 객체 형식은 null로 설정됩니다.
  + `CLOBBER` - 저장 작업 중 모델링되지 않은 속성을 비롯하여 모든 속성을 지우고 교체합니다. 즉, 항목을 삭제했다가 다시 생성합니다. 버전이 지정된 필드 제약 조건 역시 무시됩니다.

   매퍼 인스턴스에 저장 동작을 지정하지 않으면 `UPDATE`가 기본값으로 설정됩니다.
**참고**  
DynamoDBMapper 트랜잭션 작업은 `DynamoDBMapperConfig.SaveBehavior` 열거를 지원하지 않습니다.
+ `DynamoDBMapperConfig.TableNameOverride` - 객체 매퍼 인스턴스에 클래스의 `DynamoDBTable` 주석으로 지정한 테이블 이름은 무시하고 대신에 직접 입력하는 다른 테이블 이름을 사용하도록 지시합니다. 이는 런타임에서 데이터를 여러 테이블로 분할할 때 유용합니다.

필요하다면 작업 단위로 `DynamoDBMapper`의 기본 구성 객체를 재정의할 수도 있습니다.

# DynamoDB 및 버전 번호를 이용한 낙관적 잠금
<a name="DynamoDBMapper.OptimisticLocking"></a>

*낙관적 잠금*은 업데이트 또는 삭제하려는 클라이언트 측 항목이 Amazon DynamoDB의 항목과 동일하도록 하려는 전략입니다. 이 전략을 사용하면 다른 사용자의 쓰기 작업이 자신의 데이터베이스 쓰기 작업을 덮어쓰지 못하도록 보호하며, 그 반대도 마찬가지입니다.

낙관적 잠금 전략에서는 각 항목마다 버전 번호 역할을 하는 속성이 있습니다. 항목을 테이블에서 가져오면 애플리케이션이 해당 항목의 버전 번호를 기록합니다. 이 항목을 업데이트할 수는 있지만 서버 쪽 버전 번호가 바뀌지 않는 경우에 한합니다. 버전 불일치가 있는 경우 사용자가 항목을 수정하기 전에 다른 사람이 해당 항목을 수정했음을 의미합니다. 더 이상 유효하지 않은 항목 버전이 있으므로 업데이트 시도가 실패합니다. 이러한 경우 항목을 검색한 후 해당 항목 업데이트를 시도하여 재시도합니다. 낙관적 잠금을 통해 다른 사람이 수행한 변경 사항을 잘못 덮어쓰지 않게 됩니다. 또한 다른 사람이 사용자의 변경 사항을 잘못 덮어쓰지 않도록 합니다.

자체적인 낙관적 잠금 전략을 구현할 수 있지만 AWS SDK for Java에서는 `@DynamoDBVersionAttribute` 주석을 제공합니다. 테이블 매핑 클래스에서 버전 번호를 저장할 속성을 하나 지정하여 이 주석을 사용해 표시하면 됩니다. 그러면 객체를 저장할 때 DynamoDB 테이블의 해당 항목이 버전 번호를 저장하는 속성을 갖게 됩니다. 처음 객체를 저장할 때 `DynamoDBMapper`가 버전 번호를 할당하고, 이후 항목을 업데이트할 때마다 버전 번호가 일정하게 자동으로 오릅니다. 업데이트 또는 삭제 요청은 클라이언트 측 객체 버전이 DynamoDB 테이블의 해당 항목 버전 번호와 일치해야만 가능합니다.

 `ConditionalCheckFailedException`다음의 경우 이 발생합니다.
+  `@DynamoDBVersionAttribute`를 이용한 낙관적 잠금을 사용하며, 서버의 버전 값이 클라이언트 측의 값과 다를 경우 
+  `DynamoDBMapper`과 함께 `DynamoDBSaveExpression`를 사용하여 데이터를 저장하는 동안 고유의 조건부 제약 조건을 지정하며 이러한 제약 조건이 실패한 경우 

**참고**  
DynamoDB 전역 테이블은 동시 업데이트 간에 "last writer wins" 조정을 사용합니다. 전역 테이블을 사용할 경우 last writer 정책을 우선 적용합니다. 따라서 잠금 전략이 작동하지 않습니다.
`DynamoDBMapper` 트랜잭션 쓰기 작업은 동일한 객체에 대해 `@DynamoDBVersionAttribute` 주석 및 조건 표현식을 지원하지 않습니다. 트랜잭션 쓰기 내 객체에 `@DynamoDBVersionAttribute` 주석이 추가되고 조건식도 있는 경우에는 SdkClientException이 발생합니다.

예를 들어, 다음 Java 코드에서는 몇 가지 속성이 있는 `CatalogItem` 클래스를 정의합니다. `Version` 속성이 `@DynamoDBVersionAttribute` 주석으로 표시되어 있습니다.

**Example**  

```
@DynamoDBTable(tableName="ProductCatalog")
public class CatalogItem {

    private Integer id;
    private String title;
    private String ISBN;
    private Set<String> bookAuthors;
    private String someProp;
    private Long version;

    @DynamoDBHashKey(attributeName="Id")
    public Integer getId() { return id; }
    public void setId(Integer Id) { this.id = Id; }

    @DynamoDBAttribute(attributeName="Title")
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }

    @DynamoDBAttribute(attributeName="ISBN")
    public String getISBN() { return ISBN; }
    public void setISBN(String ISBN) { this.ISBN = ISBN;}

    @DynamoDBAttribute(attributeName = "Authors")
    public Set<String> getBookAuthors() { return bookAuthors; }
    public void setBookAuthors(Set<String> bookAuthors) { this.bookAuthors = bookAuthors; }

    @DynamoDBIgnore
    public String getSomeProp() { return someProp;}
    public void setSomeProp(String someProp) {this.someProp = someProp;}

    @DynamoDBVersionAttribute
    public Long getVersion() { return version; }
    public void setVersion(Long version) { this.version = version;}
}
```

`@DynamoDBVersionAttribute` 주석을 `Long`이나 `Integer` 같이 null이 허용된 형식을 제공하는 기본 래퍼 클래스에서 제공하는 null 형식에 적용할 수 있습니다.

낙관적 잠금 전략은 이러한 `DynamoDBMapper` 메서드에 아래와 같은 영향을 끼칩니다.
+ `save` - 새 항목의 경우 `DynamoDBMapper`는 초기 버전 번호로 1을 할당합니다. 항목을 검색하고, 해당 속성 중 하나 이상을 업데이트하고, 변경 사항 저장을 시도하면 클라이언트 측 버전 번호와 서버 측 버전 번호가 일치하는 경우에만 저장 작업이 성공합니다. 그런 다음 `DynamoDBMapper`가 버전 번호를 자동으로 일정하게 올립니다.
+ `delete` - `delete` 메서드가 객체를 파라미터로 사용하고 `DynamoDBMapper`가 항목을 삭제하기 전에 버전을 검사합니다. 요청 시 `DynamoDBMapperConfig.SaveBehavior.CLOBBER`를 지정하면 버전 검사는 비활성화됩니다.

  `DynamoDBMapper`에서 낙관적 잠금을 내부 구현할 경우에는 DynamoDB가 제공하는 조건부 업데이트와 조건부 삭제 지원을 사용합니다.
+ `transactionWrite` —
  + `Put` - 새 항목의 경우 `DynamoDBMapper`는 초기 버전 번호로 1을 할당합니다. 이후 항목을 가져와 속성을 하나 이상 업데이트한 후 변경 사항을 저장하려고 해도 클라이언트 측과 서버 쪽의 버전 번호가 일치해야만 넣기 작업이 가능합니다. 그런 다음 `DynamoDBMapper`가 버전 번호를 자동으로 일정하게 올립니다.
  + `Update` - 새 항목의 경우 `DynamoDBMapper`는 초기 버전 번호로 1을 할당합니다. 이후 항목을 가져와 속성을 하나 이상 업데이트한 후 변경 사항을 저장하려고 해도 클라이언트 측과 서버 쪽의 버전 번호가 일치해야만 업데이트 작업이 가능합니다. 그런 다음 `DynamoDBMapper`가 버전 번호를 자동으로 일정하게 올립니다.
  + `Delete` - `DynamoDBMapper`는 항목을 삭제하기 전에 버전을 확인합니다. 클라이언트 측과 서버 측 버전 번호가 일치할 경우에만 삭제 작업이 가능합니다.
  + `ConditionCheck` - `@DynamoDBVersionAttribute` 주석은 `ConditionCheck` 작업에 지원되지 않습니다. `ConditionCheck` 항목에 `@DynamoDBVersionAttribute` 주석이 추가될 때 SdkClientException이 발생합니다.

## 낙관적 잠금 비활성화
<a name="DynamoDBMapper.OptimisticLocking.Disabling"></a>

낙관적 잠금은 `DynamoDBMapperConfig.SaveBehavior` 열거 값을 `UPDATE`에서 `CLOBBER`로 변경하면 비활성화할 수 있습니다. 그런 다음 버전 검사를 제외한 `DynamoDBMapperConfig` 인스턴스를 생성하여 모든 요청에 사용하면 됩니다. `DynamoDBMapperConfig.SaveBehavior`와 그 밖에 옵션으로 제공되는 `DynamoDBMapper` 파라미터에 대한 자세한 내용은 [DynamoDBMapper의 구성 설정(선택 사항)](DynamoDBMapper.OptionalConfig.md) 단원을 참조하세요.

또한 잠금 기능을 특정 작업에만 설정할 수도 있습니다. 예를 들어 다음은 `DynamoDBMapper`를 사용하여 카탈로그 항목을 저장하는 Java 코드 조각입니다. 이 조각을 보면 옵션인 `DynamoDBMapperConfig.SaveBehavior` 파라미터를 `DynamoDBMapperConfig` 메서드에 추가하여 `save`를 지정하고 있습니다.

**참고**  
transactionWrite 메서드는 DynamoDBMapperConfig.SaveBehavior 구성을 지원하지 않습니다. transactionWrite의 낙관적 잠금 비활성화는 지원되지 않습니다.

**Example**  

```
DynamoDBMapper mapper = new DynamoDBMapper(client);

// Load a catalog item.
CatalogItem item = mapper.load(CatalogItem.class, 101);
item.setTitle("This is a new title for the item");
...
// Save the item.
mapper.save(item,
    new DynamoDBMapperConfig(
        DynamoDBMapperConfig.SaveBehavior.CLOBBER));
```

# DynamoDB에서 임의 데이터 매핑
<a name="DynamoDBMapper.ArbitraryDataMapping"></a>

지원되는 Java 형식([Java용 DynamoDBMapper에서 지원되는 데이터 형식](DynamoDBMapper.DataTypes.md) 참조) 외에도 애플리케이션에서 직접적으로 Amazon DynamoDB 형식에 매핑되지 않는 형식을 사용할 수 있습니다. 이러한 형식을 매핑하려면 복합 형식을 DynamoDB 지원 형식으로 또는 그 반대로의 변환을 구현하고 `@DynamoDBTypeConverted` 주석을 사용하여 복합 형식의 접근자 메서드에 주석을 추가해야 합니다. 이후 객체를 저장하거나 로드하면 변환기 코드가 데이터를 변환합니다. 그 밖에 복합 형식을 사용하는 모든 작업에서도 유용합니다. 단, 쿼리나 스캔 작업 중 데이터를 비교하면 DynamoDB에 저장된 데이터를 비교하게 됩니다.

아래에서 `CatalogItem` 속성, 즉 `Dimension`을 정의하고 있는 `DimensionType` 클래스를 예로 들어보겠습니다. 이 속성에는 높이, 너비, 두께 등의 항목 차원이 저장됩니다. DynamoDB에서 이 항목 크기를 문자열(8.5x11x.05)로 저장한다고 가정할 때, 다음 예제를 보면 `DimensionType` 객체를 문자열로, 혹은 문자열을 `DimensionType`으로 변환하는 변환기 코드가 나와있습니다.



**참고**  
아래 코드 예제는 [DynamoDB에서 테이블 생성 및 코드 예시에 대한 데이터 로드](SampleData.md) 단원의 지침에 따라 이미 계정의 DynamoDB에 데이터를 로드하였다고 가정한 것입니다.  
다음 예제를 실행하기 위한 단계별 지침은 [Java 코드 예](CodeSamples.Java.md) 섹션을 참조하세요.

**Example**  

```
public class DynamoDBMapperExample {

    static AmazonDynamoDB client;

    public static void main(String[] args) throws IOException {

        // Set the AWS region you want to access.
        Regions usWest2 = Regions.US_WEST_2;
        client = AmazonDynamoDBClientBuilder.standard().withRegion(usWest2).build();

        DimensionType dimType = new DimensionType();
        dimType.setHeight("8.00");
        dimType.setLength("11.0");
        dimType.setThickness("1.0");

        Book book = new Book();
        book.setId(502);
        book.setTitle("Book 502");
        book.setISBN("555-5555555555");
        book.setBookAuthors(new HashSet<String>(Arrays.asList("Author1", "Author2")));
        book.setDimensions(dimType);

        DynamoDBMapper mapper = new DynamoDBMapper(client);
        mapper.save(book);

        Book bookRetrieved = mapper.load(Book.class, 502);
        System.out.println("Book info: " + "\n" + bookRetrieved);

        bookRetrieved.getDimensions().setHeight("9.0");
        bookRetrieved.getDimensions().setLength("12.0");
        bookRetrieved.getDimensions().setThickness("2.0");

        mapper.save(bookRetrieved);

        bookRetrieved = mapper.load(Book.class, 502);
        System.out.println("Updated book info: " + "\n" + bookRetrieved);
    }

    @DynamoDBTable(tableName = "ProductCatalog")
    public static class Book {
        private int id;
        private String title;
        private String ISBN;
        private Set<String> bookAuthors;
        private DimensionType dimensionType;

        // Partition key
        @DynamoDBHashKey(attributeName = "Id")
        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        @DynamoDBAttribute(attributeName = "Title")
        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        @DynamoDBAttribute(attributeName = "ISBN")
        public String getISBN() {
            return ISBN;
        }

        public void setISBN(String ISBN) {
            this.ISBN = ISBN;
        }

        @DynamoDBAttribute(attributeName = "Authors")
        public Set<String> getBookAuthors() {
            return bookAuthors;
        }

        public void setBookAuthors(Set<String> bookAuthors) {
            this.bookAuthors = bookAuthors;
        }

        @DynamoDBTypeConverted(converter = DimensionTypeConverter.class)
        @DynamoDBAttribute(attributeName = "Dimensions")
        public DimensionType getDimensions() {
            return dimensionType;
        }

        @DynamoDBAttribute(attributeName = "Dimensions")
        public void setDimensions(DimensionType dimensionType) {
            this.dimensionType = dimensionType;
        }

        @Override
        public String toString() {
            return "Book [ISBN=" + ISBN + ", bookAuthors=" + bookAuthors + ", dimensionType= "
                    + dimensionType.getHeight() + " X " + dimensionType.getLength() + " X "
                    + dimensionType.getThickness()
                    + ", Id=" + id + ", Title=" + title + "]";
        }
    }

    static public class DimensionType {

        private String length;
        private String height;
        private String thickness;

        public String getLength() {
            return length;
        }

        public void setLength(String length) {
            this.length = length;
        }

        public String getHeight() {
            return height;
        }

        public void setHeight(String height) {
            this.height = height;
        }

        public String getThickness() {
            return thickness;
        }

        public void setThickness(String thickness) {
            this.thickness = thickness;
        }
    }

    // Converts the complex type DimensionType to a string and vice-versa.
    static public class DimensionTypeConverter implements DynamoDBTypeConverter<String, DimensionType> {

        @Override
        public String convert(DimensionType object) {
            DimensionType itemDimensions = (DimensionType) object;
            String dimension = null;
            try {
                if (itemDimensions != null) {
                    dimension = String.format("%s x %s x %s", itemDimensions.getLength(), itemDimensions.getHeight(),
                            itemDimensions.getThickness());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return dimension;
        }

        @Override
        public DimensionType unconvert(String s) {

            DimensionType itemDimension = new DimensionType();
            try {
                if (s != null && s.length() != 0) {
                    String[] data = s.split("x");
                    itemDimension.setLength(data[0].trim());
                    itemDimension.setHeight(data[1].trim());
                    itemDimension.setThickness(data[2].trim());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            return itemDimension;
        }
    }
}
```

# DynamoDBMapper 예제
<a name="DynamoDBMapper.Examples"></a>

AWS SDK for Java에서는 클라이언트 측 클래스를 DynamoDB 테이블로 매핑할 수 있는 `DynamoDBMapper` 클래스를 제공합니다. `DynamoDBMapper`를 사용하려면 코드에서 DynamoDB 테이블의 항목과 해당하는 객체 인스턴스 간의 관계를 정의합니다. `DynamoDBMapper` 클래스를 사용하면 항목에 대한 다양한 생성, 읽기, 업데이트 및 삭제(CRUD) 작업을 수행하고 테이블에 대한 쿼리 및 스캔을 실행할 수 있습니다.

`DynamoDBMapper` 사용 방법에 대한 자세한 내용은 *AWS SDK for Java 1.x 개발자 안내서*에 나와 있는 [AWS SDK for Java 사용에 관한 DynamoDB 예제](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/examples-dynamodb.html)를 참조하시기 바랍니다.

# Java 2.x: DynamoDB 고급형 클라이언트
<a name="DynamoDBEnhanced"></a>

DynamoDB 고급형 클라이언트는 AWS SDK for Java 버전 2(v2)의 일부인 상위 수준 라이브러리로서, 클라이언트 측 클래스를 DynamoDB 테이블에 매핑하는 간단한 방법을 제공합니다. 코드에서 테이블과 해당 모델 클래스 간의 관계를 정의합니다. 이러한 관계를 정의한 다음 DynamoDB의 테이블 또는 항목에 대해 다양한 생성, 읽기, 업데이트 또는 삭제(CRUD) 작업을 직관적으로 수행할 수 있습니다.

고급형 클라이언트를 DynamoDB와 함께 사용하는 방법에 대한 자세한 내용은 [AWS SDK for Java 2.x에서 DynamoDB 고급형 클라이언트 사용](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/dynamodb-enhanced-client.html)을 참조하세요.

# DynamoDB에서 .NET 문서 모델을 사용하여 작업
<a name="DotNetSDKMidLevel"></a>

AWS SDK for .NET은 일부 하위 수준 Amazon DynamoDB 작업을 래핑하여 코딩을 더 간단하게 만드는 문서 모델 클래스를 제공합니다. 이 문서 모델에서 기본 클래스는 `Table` 및 `Document`입니다. `Table` 클래스는 `PutItem`, `GetItem` 및 `DeleteItem`과 같은 데이터 작업 메서드를 제공합니다. 또한 `Query` 및 `Scan` 메서드를 제공합니다. `Document` 클래스는 테이블의 단일 항목을 나타냅니다.

위의 문서 모델 클래스는 `Amazon.DynamoDBv2.DocumentModel` 네임스페이스에서 사용할 수 있습니다.

**참고**  
이 문서 모델 클래스를 사용하여 테이블을 생성, 업데이트 및 삭제할 수 없습니다. 그러나 이 문서 모델은 대부분의 일반적인 데이터 작업을 지원합니다.

**Topics**
+ [지원되는 데이터 유형](#MidLevelAPILimitations.SupportedTypes)

## 지원되는 데이터 유형
<a name="MidLevelAPILimitations.SupportedTypes"></a>

문서 모델은 기본 .NET 데이터 형식 및 컬렉션 데이터 형식 세트를 지원합니다. 모델이 지원하는 기본 데이터 유형은 다음과 같습니다.
+ `bool`
+ `byte` 
+ `char`
+ `DateTime`
+ `decimal`
+ `double`
+ `float`
+ `Guid`
+ `Int16`
+ `Int32`
+ `Int64`
+ `SByte`
+ `string`
+ `UInt16`
+ `UInt32`
+ `UInt64`

다음 표에서는 앞서 나온 .NET 형식을 DynamoDB 형식으로 매핑하는 것을 요약하여 보여 줍니다.


****  

| .NET 기본 유형 | DynamoDB 형식 | 
| --- | --- | 
|  모두 숫자 형식  |  `N`(숫자 형식)  | 
|  모든 문자열 형식  |  `S`(문자열 형식)   | 
|  MemoryStream, byte[]  |  `B`(이진수 형식)   | 
| bool | N(숫자 형식). 0은 false를 나타내고 1은 true를 나타냅니다. | 
| DateTime | S(문자열 형식) DateTime 값은 ISO-8601 형식의 문자열로 저장됩니다. | 
| Guid | S(문자열 형식) | 
| 컬렉션 유형(List, HashSet 및 array) | BS(이진수 세트) 형식, SS(문자열 세트) 형식, 그리고 NS(숫자 세트) 형식 | 

AWS SDK for .NET은 DynamoDB의 부울, null, 목록 및 맵 형식을 .NET 문서 모델 API에 매핑하기 위한 형식을 정의합니다.
+ 부울 형식에 `DynamoDBBool`을 사용합니다.
+ null 형식에 `DynamoDBNull`을 사용합니다.
+ 목록 형식에 `DynamoDBList`를 사용합니다.
+ 맵 형식에 `Document`를 사용합니다.

**참고**  
빈 이진수 값이 지원됩니다.
빈 문자열 값 읽기가 지원됩니다. DynamoDB에 쓰는 동안 문자열 집합 형식의 속성 값에서 빈 문자열 속성 값이 지원됩니다. 문자열 형식의 빈 문자열 속성 값과 목록 또는 맵 형식에 포함된 빈 문자열 값은 쓰기 요청에서 삭제됩니다.

# .NET 객체 지속성 모델 및 DynamoDB로 작업
<a name="DotNetSDKHighLevel"></a>

AWS SDK for .NET은 객체 지속성 모델을 제공하므로 이것으로 클라이언트 측 클래스를 Amazon DynamoDB 테이블에 매핑할 수 있습니다. 그러면 각 객체 인스턴스도 해당 테이블 항목으로 매핑됩니다. 클라이언트 측 객체를 테이블에 저장하기 위해 객체 지속성 모델에서는 DynamoDB에 대한 진입점인 `DynamoDBContext` 클래스를 제공합니다. 이 클래스는 DynamoDB로 연결하는 역할을 하기 때문에 테이블에 액세스하여 다양한 CRUD 작업이 가능할 뿐만 아니라 쿼리를 실행할 수 있습니다.

객체 지속성 모델은 속성 세트를 제공하여 클라이언트 측 클래스를 테이블로 매핑할 수 있으며, 속성/필드를 테이블 속성으로 매핑할 수 있습니다.

**참고**  
객체 지속성 모델은 테이블을 생성, 업데이트 또는 삭제할 수 있는 API를 제공하지 않으며 데이터 작업만 제공합니다. 테이블 생성, 업데이트 및 삭제에는 AWS SDK for .NET 하위 수준 API만 사용할 수 있습니다.

다음 예제에서는 객체 지속성 모델이 작동하는 방법을 보여줍니다. `ProductCatalog` 테이블로 시작합니다. `Id`를 기본 키로 갖고 있습니다.

```
ProductCatalog(Id, ...)
```

`Book` 클래스를 `Title`, `ISBN` 및 `Authors` 속성과 함께 갖고 있는 경우 다음 C\$1 코드 예제와 같이 객체 지속성 모델로 정의된 속성을 추가하여 `Book` 클래스를 `ProductCatalog` 테이블에 매핑할 수 있습니다.

**Example**  

```
[DynamoDBTable("ProductCatalog")]
  public class Book
  {
    [DynamoDBHashKey]
    public int Id { get; set; }

    public string Title { get; set; }
    public int ISBN { get; set; }

    [DynamoDBProperty("Authors")]
    public List<string> BookAuthors { get; set; }

    [DynamoDBIgnore]
    public string CoverPage { get; set; }
  }
```

앞의 예제에서는 `DynamoDBTable` 속성이 `Book` 클래스를 `ProductCatalog` 테이블에 매핑합니다.

객체 지속성 모델은 클래스 속성 및 테이블 속성 간 명시적 매핑과 기본 매핑 모두를 지원합니다.
+ **명시적 매핑** - 속성을 기본 키에 매핑하려면 `DynamoDBHashKey` 및 `DynamoDBRangeKey` 객체 지속성 모델 속성을 사용해야 합니다. 또한 기본이 아닌 키 속성의 경우 클래스의 속성 이름과 이를 매핑하려는 해당 테이블 속성이 같지 않다면 `DynamoDBProperty` 속성을 명시적으로 추가하여 매핑을 정의해야 합니다.

  앞의 예제에서는 `Id` 속성이 같은 이름의 기본 키로 매핑되고 `BookAuthors` 속성이 `Authors` 테이블의 `ProductCatalog` 속성으로 매핑됩니다.
+ **기본 매핑** - 기본적으로 객체 지속성 모델은 클래스 속성을 같은 이름의 테이블 속성으로 매핑합니다.

  앞의 예제에서는 `Title` 및 `ISBN` 속성이 `ProductCatalog` 테이블에 있는 같은 이름의 속성으로 매핑됩니다.

각 클래스 속성을 전부 하나씩 매핑할 필요는 없습니다. `DynamoDBIgnore` 속성을 추가하여 이러한 속성을 확인할 수 있습니다. `Book` 인스턴스를 테이블에 저장하더라도 `DynamoDBContext`에는 `CoverPage` 속성이 포함되지 않습니다. 또한 이러한 책 인스턴스를 가져와도 이 속성은 반환되지 않습니다.

int 및 문자열과 같은 .NET 기본 유형의 속성을 매핑할 수 있습니다. 적절한 변환기를 사용하여 임의 데이터를 DynamoDB 형식 중 하나로 매핑만 한다면 어떤 임의 데이터 형식도 매핑할 수 있습니다. 임의 유형 매핑에 대한 자세한 내용은 [AWS SDK for .NET 객체 지속성 모델을 사용하여 DynamoDB에서 임의 데이터 매핑](DynamoDBContext.ArbitraryDataMapping.md) 단원을 참조하세요.

객체 지속성 모델은 낙관적 잠금을 지원합니다. 따라서 업데이트 작업 동안 업데이트하려는 항목의 최신 복사본을 가질 수 있습니다. 자세한 내용은 [DynamoDB 및 AWS SDK for .NET 객체 지속성 모델을 사용하여 낙관적 잠금 수행](DynamoDBContext.VersionSupport.md) 섹션을 참조하세요.

자세한 내용은 아래 주제를 참조하십시오.

**Topics**
+ [지원되는 데이터 유형](#DotNetDynamoDBContext.SupportedTypes)
+ [.NET 객체 지속성 모델의 DynamoDB 속성](DeclarativeTagsList.md)
+ [.NET 객체 지속성 모델의 DynamoDBContext 클래스](DotNetDynamoDBContext.md)
+ [DynamoDB 및 AWS SDK for .NET 객체 지속성 모델을 사용하여 낙관적 잠금 수행](DynamoDBContext.VersionSupport.md)
+ [AWS SDK for .NET 객체 지속성 모델을 사용하여 DynamoDB에서 임의 데이터 매핑](DynamoDBContext.ArbitraryDataMapping.md)

## 지원되는 데이터 유형
<a name="DotNetDynamoDBContext.SupportedTypes"></a>

객체 지속성 모델은 기본 .NET 데이터 유형 세트와 컬렉션, 그리고 임의 데이터 유형을 지원합니다. 모델이 지원하는 기본 데이터 유형은 다음과 같습니다.
+ `bool`
+ `byte` 
+ `char`
+ `DateTime`
+ `decimal`
+ `double`
+ `float`
+ `Int16`
+ `Int32`
+ `Int64`
+ `SByte`
+ `string`
+ `UInt16`
+ `UInt32`
+ `UInt64`

객체 지속성 모델은 .NET 컬렉션 형식도 지원합니다. `DynamoDBContext`는 구체적인 컬렉션 형식과 단순한 POCO(Plain Old CLR Object)를 변환할 수 있습니다.

다음 표에서는 앞서 나온 .NET 형식을 DynamoDB 형식으로 매핑하는 것을 요약하여 보여 줍니다.


****  

| .NET 기본 유형 | DynamoDB 형식 | 
| --- | --- | 
|  모두 숫자 형식  |  `N`(숫자 형식)  | 
|  모든 문자열 형식  |  `S`(문자열 형식)   | 
|  MemoryStream, byte[]  |  `B`(이진수 형식)   | 
| bool | N(숫자 형식). 0은 false를 나타내고 1은 true를 나타냅니다. | 
| 컬렉션 유형 | BS(이진수 세트) 형식, SS(문자열 세트) 형식, 그리고 NS(숫자 세트) 형식 | 
| DateTime | S(문자열 형식) DateTime 값은 ISO-8601 형식의 문자열로 저장됩니다. | 

객체 지속성 모델은 임의 데이터 형식 또한 지원합니다. 하지만 여러 형식을 DynamoDB 형식으로 매핑하려면 변환기 코드를 입력해야 합니다.

**참고**  
빈 이진수 값이 지원됩니다.
빈 문자열 값 읽기가 지원됩니다. DynamoDB에 쓰는 동안 문자열 집합 형식의 속성 값에서 빈 문자열 속성 값이 지원됩니다. 문자열 형식의 빈 문자열 속성 값과 목록 또는 맵 형식에 포함된 빈 문자열 값은 쓰기 요청에서 삭제됩니다.

# .NET 객체 지속성 모델의 DynamoDB 속성
<a name="DeclarativeTagsList"></a>

이 단원에서는 사용자가 클래스 및 속성을 DynamoDB 테이블 및 속성으로 매핑할 수 있도록 객체 지속성 모델이 제공하는 속성에 대해 설명합니다.

**참고**  
다음 속성에서 `DynamoDBTable` 및 `DynamoDBHashKey`만 필요합니다.

## DynamoDBGlobalSecondaryIndexHashKey
<a name="w2aac17b9c21c23c37b7"></a>

클래스 속성을 글로벌 보조 인텍스의 파티션 키로 매핑합니다. 이 속성은 글로벌 보조 인덱스를 `Query`해야 하는 경우 사용합니다.

## DynamoDBGlobalSecondaryIndexRangeKey
<a name="w2aac17b9c21c23c37b9"></a>

클래스 속성을 글로벌 보조 인텍스의 정렬 키로 매핑합니다. 이 속성은 글로벌 보조 인덱스를 `Query`해야 하거나 인덱스 정렬 키를 사용하여 결과를 구체화하고 싶은 경우에 사용합니다.

## DynamoDBHashKey
<a name="w2aac17b9c21c23c37c11"></a>

클래스 속성을 테이블의 기본 키 파티션 키로 매핑합니다. 기본 키 속성은 컬렉션 형식이 될 수 없습니다.

다음 C\$1 코드 예제에서는 `Book` 클래스를 `ProductCatalog` 테이블로 매핑하고, `Id` 속성을 테이블의 기본 키 파티션 키로 매핑합니다.

```
[DynamoDBTable("ProductCatalog")]
public class Book 
{
    [DynamoDBHashKey]
    public int Id { get; set; }

    // Additional properties go here.
}
```

## DynamoDBIgnore
<a name="w2aac17b9c21c23c37c13"></a>

연결된 속성을 무시해야 함을 나타냅니다. 어떠한 클래스 속성도 저장하지 않으려면 이 속성을 추가하여 `DynamoDBContext`가 객체를 테이블에 저장할 때 이 속성을 포함하지 않도록 지시할 수 있습니다.

## DynamoDBLocalSecondaryIndexRangeKey
<a name="w2aac17b9c21c23c37c15"></a>

클래스 속성을 로컬 보조 인텍스의 정렬 키로 매핑합니다. 이 속성은 로컬 보조 인덱스를 `Query`해야 하거나 인덱스 정렬 키를 사용하여 결과를 구체화하고 싶은 경우에 사용합니다.

## DynamoDBProperty
<a name="w2aac17b9c21c23c37c17"></a>

클래스 속성을 테이블 속성으로 매핑합니다. 클래스 속성을 같은 이름의 테이블 속성으로 매핑할 경우에는 이 속성을 지정할 필요가 없습니다. 하지만 이름이 다를 경우에는 매핑을 제공하는 데 이 태그를 사용할 수 있습니다. 다음 C\$1 문에서는 `DynamoDBProperty`가 `BookAuthors` 속성을 테이블의 `Authors` 속성으로 매핑합니다.

```
[DynamoDBProperty("Authors")]
public List<string> BookAuthors { get; set; }
```

`DynamoDBContext`는 객체 데이터를 해당 테이블에 저장할 때 이 매핑 정보를 사용하여 `Authors` 속성을 생성합니다.

## DynamoDBRenamable
<a name="w2aac17b9c21c23c37c19"></a>

클래스 속성의 대체 이름을 지정합니다. 이것은 클래스 속성 이름이 테이블 속성과 다른 DynamoDB 테이블로 임의 데이터를 매핑하기 위한 사용자 지정 변환기를 쓰고 있을 때 유용합니다.

## DynamoDBRangeKey
<a name="w2aac17b9c21c23c37c21"></a>

클래스 속성을 테이블의 기본 키 정렬 키로 매핑합니다. 테이블에 복합 기본 키(파티션 키 및 정렬 키)가 있는 경우 클래스 매핑에서 `DynamoDBHashKey` 속성과 `DynamoDBRangeKey` 속성을 모두 지정해야 합니다.

예를 들어, 샘플 테이블 `Reply`에는 `Id` 파티션 키 및 `Replenishment` 정렬 키로 구성된 기본 키가 있습니다. 다음 C\$1 코드 예제에서는 `Reply` 클래스를 `Reply` 테이블로 매핑합니다. 클래스 정의는 또한 그 속성 중 두 가지를 기본 키로 매핑하도록 지시합니다.

```
[DynamoDBTable("Reply")]
public class Reply 
{
   [DynamoDBHashKey]
   public int ThreadId { get; set; }
   [DynamoDBRangeKey]
   public string Replenishment { get; set; }
   
   // Additional properties go here.
}
```

## DynamoDBTable
<a name="w2aac17b9c21c23c37c23"></a>

클래스가 매핑하는 대상으로 DynamoDB의 대상 테이블을 식별합니다. 예를 들어, 다음 C\$1 코드 예제에서는 `Developer` 클래스를 DynamoDB의 `People` 테이블로 매핑합니다.

```
[DynamoDBTable("People")]
public class Developer { ...}
```

이 속성은 상속 또는 재정의될 수 있습니다.
+ `DynamoDBTable` 속성은 상속될 수 있습니다. 앞의 예제에서는 `Lead` 클래스에서 상속된 새 클래스 `Developer`를 추가하는 경우 해당 클래스도 `People` 테이블에도 매핑됩니다. `Developer` 객체와 `Lead` 객체 모두 `People` 테이블에 저장됩니다.
+ `DynamoDBTable` 속성 또한 재정의될 수 있습니다. 다음 C\$1 코드 예제에서는 `Manager` 클래스가 `Developer` 클래스에서 상속됩니다. 그러나 `DynamoDBTable` 속성을 명시적으로 추가하면 이 클래스가 다른 테이블(`Managers`)로 매핑됩니다.

  ```
  [DynamoDBTable("Managers")]
  public class Manager : Developer { ...}
  ```

 다음 C\$1 코드 예제와 같이 테이블에 객체를 저장할 때 선택적 파라미터인 `LowerCamelCaseProperties`를 추가하면 속성 이름의 첫 번째 글자를 소문자로 만들도록 DynamoDB에 요청할 수 있습니다.

```
[DynamoDBTable("People", LowerCamelCaseProperties=true)]
public class Developer 
{
    string DeveloperName;
    ...
}
```

`Developer` 클래스의 인스턴스를 저장할 때 `DynamoDBContext`가 `DeveloperName` 속성을 `developerName`으로 저장합니다.

## DynamoDBVersion
<a name="w2aac17b9c21c23c37c25"></a>

항목 버전 번호를 저장하는 클래스 속성을 식별합니다. 버전 관리에 대한 자세한 내용은 [DynamoDB 및 AWS SDK for .NET 객체 지속성 모델을 사용하여 낙관적 잠금 수행](DynamoDBContext.VersionSupport.md) 섹션을 참조하십시오.

# .NET 객체 지속성 모델의 DynamoDBContext 클래스
<a name="DotNetDynamoDBContext"></a>

`DynamoDBContext` 클래스는 Amazon DynamoDB 데이터베이스의 진입점입니다. 이 클래스는 DynamoDB로의 연결을 제공하며, 데이터를 다양한 테이블에 액세스하고 다양한 CRUD 작업을 수행하며 쿼리를 실행할 수 있게 합니다. `DynamoDBContext` 클래스는 다음과 같은 메서드를 제공합니다.

**Topics**
+ [Create​MultiTable​BatchGet](#w2aac17b9c21c23c39b7)
+ [Create​MultiTable​BatchWrite](#w2aac17b9c21c23c39b9)
+ [CreateBatchGet](#w2aac17b9c21c23c39c11)
+ [CreateBatchWrite](#w2aac17b9c21c23c39c13)
+ [삭제](#w2aac17b9c21c23c39c15)
+ [Dispose](#w2aac17b9c21c23c39c17)
+ [Execute​Batch​Get](#w2aac17b9c21c23c39c19)
+ [Execute​Batch​Write](#w2aac17b9c21c23c39c21)
+ [FromDocument](#w2aac17b9c21c23c39c23)
+ [FromQuery](#w2aac17b9c21c23c39c25)
+ [FromScan](#w2aac17b9c21c23c39c27)
+ [Get​Target​Table](#w2aac17b9c21c23c39c29)
+ [Load](#w2aac17b9c21c23c39c31)
+ [쿼리](#w2aac17b9c21c23c39c33)
+ [Save](#w2aac17b9c21c23c39c35)
+ [스캔](#w2aac17b9c21c23c39c37)
+ [ToDocument](#w2aac17b9c21c23c39c39)
+ [DynamoDBContext에 대한 옵션 파라미터 지정](#OptionalConfigParams)

## Create​MultiTable​BatchGet
<a name="w2aac17b9c21c23c39b7"></a>

여러 개의 개별 `MultiTableBatchGet` 객체로 구성된 `BatchGet` 객체를 만듭니다. 이러한 `BatchGet` 객체 각각은 단일 DynamoDB 테이블에서 항목을 가져오는 데 사용됩니다.

테이블에서 항목을 검색하려면 `ExecuteBatchGet` 메서드를 사용하여 `MultiTableBatchGet` 객체를 파라미터로 전달합니다.

## Create​MultiTable​BatchWrite
<a name="w2aac17b9c21c23c39b9"></a>

여러 개의 개별 `MultiTableBatchWrite` 객체로 구성된 `BatchWrite` 객체를 만듭니다. 이러한 `BatchWrite` 객체 각각은 단일 DynamoDB 테이블에서 항목을 쓰거나 삭제하는 데 사용됩니다.

테이블에 쓰려면 `ExecuteBatchWrite` 메서드를 사용하여 `MultiTableBatchWrite` 객체를 파라미터로 전달합니다.

## CreateBatchGet
<a name="w2aac17b9c21c23c39c11"></a>

`BatchGet` 객체를 만들어 테이블에서 여러 개의 항목을 가져올 수 있습니다.

## CreateBatchWrite
<a name="w2aac17b9c21c23c39c13"></a>

`BatchWrite` 객체를 만들어 여러 개의 항목을 테이블에 넣는 데 사용하거나 테이블에서 여러 개의 항목을 삭제하는 데 사용할 수 있습니다.

## 삭제
<a name="w2aac17b9c21c23c39c15"></a>

항목을 테이블에서 삭제합니다. 메서드에는 삭제하려는 항목의 기본 키가 필요합니다. 기본 키 값 또는 기본 키 값을 포함하는 클라이언트 측 객체 중 한 가지를 파라미터로 이 메서드에 제공할 수 있습니다.
+ 클라이언트 측 객체를 파라미터로 지정하고 낙관적 잠금을 활성화했다면, 클라이언트 측과 객체의 서버 쪽 버전이 일치해야만 삭제가 성공적으로 이루어집니다.
+ 기본 키 값만을 파라미터로 지정한 경우, 낙관적 잠금을 활성화했는지의 여부와 상관없이 삭제가 성공적으로 이루어집니다.

**참고**  
백그라운드에서 이 작업을 수행하려면 `DeleteAsync` 메서드를 대신 사용하세요.

## Dispose
<a name="w2aac17b9c21c23c39c17"></a>

관리되거나 관리되지 않는 리소스를 모두 일괄합니다.

## Execute​Batch​Get
<a name="w2aac17b9c21c23c39c19"></a>

모든 `BatchGet` 객체를 `MultiTableBatchGet`에서 처리하여, 하나 또는 그 이상의 테이블에서 데이터를 읽습니다.

**참고**  
백그라운드에서 이 작업을 수행하려면 `ExecuteBatchGetAsync` 메서드를 대신 사용하세요.

## Execute​Batch​Write
<a name="w2aac17b9c21c23c39c21"></a>

모든 `BatchWrite` 객체를 `MultiTableBatchWrite`에서 처리하여, 하나 또는 그 이상의 테이블에 데이터를 쓰거나 삭제합니다.

**참고**  
백그라운드에서 이 작업을 수행하려면 `ExecuteBatchWriteAsync` 메서드를 대신 사용하세요.

## FromDocument
<a name="w2aac17b9c21c23c39c23"></a>

할당된 `Document` 인스턴스에서 `FromDocument` 메서드가 클라이언트 측 클래스의 인스턴스를 반환합니다.

이것은 문서 모델 클래스를 객체 지속성 모델과 함께 사용하여 데이터 작업을 수행할 때 유용합니다. AWS SDK for .NET에 의해 제공되는 문서 모델 클래스에 대한 자세한 내용은 [DynamoDB에서 .NET 문서 모델을 사용하여 작업](DotNetSDKMidLevel.md) 단원을 참조하세요.

`Document` 항목 표시 정보를 포함하는 `doc`라는 이름의 `Forum` 객체가 있는 경우 (이 객체를 구성하는 방법은 이 주제의 후반부에 있는 `ToDocument` 메서드 설명을 참조하세요.) 다음 C\$1 코드 예제와 같이 `FromDocument`를 사용하여 `Forum`에서 `Document` 항목을 검색할 수 있습니다.

**Example**  

```
forum101 = context.FromDocument<Forum>(101);
```

**참고**  
`Document` 객체가 `IEnumerable` 인터페이스를 구현하는 경우에는 `FromDocuments` 메서드를 대신 사용할 수 있습니다. 그러면 `Document`에서 모든 클래스 인스턴스를 반복적으로 처리할 수 있습니다.

## FromQuery
<a name="w2aac17b9c21c23c39c25"></a>

`QueryOperationConfig` 객체에 정의된 쿼리 파라미터로 `Query` 작업을 실행합니다.

**참고**  
백그라운드에서 이 작업을 수행하려면 `FromQueryAsync` 메서드를 대신 사용하세요.

## FromScan
<a name="w2aac17b9c21c23c39c27"></a>

`ScanOperationConfig` 객체에 정의된 스캔 파라미터로 `Scan` 작업을 실행합니다.

**참고**  
백그라운드에서 이 작업을 수행하려면 `FromScanAsync` 메서드를 대신 사용하세요.

## Get​Target​Table
<a name="w2aac17b9c21c23c39c29"></a>

지정된 유형에 대한 대상 테이블을 가져옵니다. 이는 임의 데이터를 DynamoDB 테이블로 매핑하기 위한 사용자 지정 변환기를 작성하고 사용자 지정 데이터 형식과 연결되는 테이블을 확인해야 할 때 유용합니다.

## Load
<a name="w2aac17b9c21c23c39c31"></a>

테이블에서 항목을 가져옵니다. 이 메서드에는 가져오려는 항목의 기본 키만 필요합니다.

기본적으로 DynamoDB는 최종적으로 일관된 값을 갖는 항목을 반환하기 때문입니다. 최종 일관성 모델에 대한 자세한 내용은 [DynamoDB 읽기 일관성](HowItWorks.ReadConsistency.md) 단원을 참조하세요.

`Load` 또는 `LoadAsync` 메서드는 [GetItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_GetItem.html) 작업을 직접 호출합니다. 이 작업을 수행하려면 테이블의 프라이머리 키를 지정해야 합니다. `GetItem`에서는 `IndexName` 파라미터를 무시하므로, 인덱스의 파티션 또는 정렬 키를 사용하여 항목을 로드할 수 없습니다. 따라서 테이블의 프라이머리 키를 사용하여 항목을 로드해야 합니다.

**참고**  
백그라운드에서 이 작업을 수행하려면 `LoadAsync` 메서드를 대신 사용하세요. `LoadAsync` 메서드를 사용하여 DynamoDB 테이블에서 상위 수준의 CRUD 작업을 수행하는 예제를 보려면 다음 예제를 참조하세요.

```
    /// <summary>
    /// Shows how to perform high-level CRUD operations on an Amazon DynamoDB
    /// table.
    /// </summary>
    public class HighLevelItemCrud
    {
        public static async Task Main()
        {
            var client = new AmazonDynamoDBClient();
            DynamoDBContext context = new DynamoDBContext(client);
            await PerformCRUDOperations(context);
        }

        public static async Task PerformCRUDOperations(IDynamoDBContext context)
        {
            int bookId = 1001; // Some unique value.
            Book myBook = new Book
            {
                Id = bookId,
                Title = "object persistence-AWS SDK for.NET SDK-Book 1001",
                Isbn = "111-1111111001",
                BookAuthors = new List<string> { "Author 1", "Author 2" },
            };

            // Save the book to the ProductCatalog table.
            await context.SaveAsync(myBook);

            // Retrieve the book from the ProductCatalog table.
            Book bookRetrieved = await context.LoadAsync<Book>(bookId);

            // Update some properties.
            bookRetrieved.Isbn = "222-2222221001";

            // Update existing authors list with the following values.
            bookRetrieved.BookAuthors = new List<string> { " Author 1", "Author x" };
            await context.SaveAsync(bookRetrieved);

            // Retrieve the updated book. This time, add the optional
            // ConsistentRead parameter using DynamoDBContextConfig object.
            await context.LoadAsync<Book>(bookId, new DynamoDBContextConfig
            {
                ConsistentRead = true,
            });

            // Delete the book.
            await context.DeleteAsync<Book>(bookId);

            // Try to retrieve deleted book. It should return null.
            Book deletedBook = await context.LoadAsync<Book>(bookId, new DynamoDBContextConfig
            {
                ConsistentRead = true,
            });

            if (deletedBook == null)
            {
                Console.WriteLine("Book is deleted");
            }
        }
    }
```

## 쿼리
<a name="w2aac17b9c21c23c39c33"></a>

입력하는 쿼리 파라미터를 기반으로 테이블을 쿼리합니다.

테이블에 복합 기본 키(파티션 키 및 정렬 키)가 있는 경우에만 테이블을 쿼리할 수 있습니다. 쿼리 시에는 파티션 키를 비롯하여 정렬 키에 적용되는 조건을 지정해야 합니다.

DynamoDB의 `Reply` 테이블에 클라이언트 측 `Reply` 클래스가 매핑되어 있는 경우 다음 C\$1 코드 예제에서는 과거 15일 간 게시된 포럼 스레드 회신을 찾기 위해 `Reply` 테이블을 쿼리합니다. `Reply` 테이블에는 `Id` 파티션 키와 `ReplyDateTime` 정렬 키로 구성된 기본 키가 있습니다.

**Example**  

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

string replyId = "DynamoDB#DynamoDB Thread 1"; //Partition key
DateTime twoWeeksAgoDate = DateTime.UtcNow.Subtract(new TimeSpan(14, 0, 0, 0)); // Date to compare.
IEnumerable<Reply> latestReplies = context.Query<Reply>(replyId, QueryOperator.GreaterThan, twoWeeksAgoDate);
```

이를 통해 `Reply` 객체 컬렉션이 반환됩니다.

`Query` 메서드는 "지연 로딩된(lazy-loaded)" `IEnumerable` 컬렉션을 반환합니다. 즉, 처음에는 결과 페이지를 하나만 반환하고, 필요에 따라 서비스를 호출하여 다음 페이지를 반환합니다. 일치하는 항목을 모두 가져오려면 `IEnumerable`을 반복하기만 하면 됩니다.

테이블에 단순 기본 키(파티션 키)가 있는 경우에는 `Query` 메서드를 사용할 수 없습니다. 대신 `Load` 메서드를 사용하면 파티션 키를 입력하여 항목을 가져올 수 있습니다.

**참고**  
백그라운드에서 이 작업을 수행하려면 `QueryAsync` 메서드를 대신 사용하세요.

## Save
<a name="w2aac17b9c21c23c39c35"></a>

지정한 객체를 테이블에 저장합니다. 입력 객체에 지정된 기본 키가 테이블에 존재하지 않는 경우 메서드가 테이블에 새 항목을 추가합니다. 기본 키가 있는 경우 메서드가 기존 항목을 업데이트합니다.

낙관적 잠금이 구성된 경우 항목의 서버 측 버전과 클라이언트 측 버전이 일치하는 경우에만 업데이트가 성공합니다. 자세한 내용은 [DynamoDB 및 AWS SDK for .NET 객체 지속성 모델을 사용하여 낙관적 잠금 수행](DynamoDBContext.VersionSupport.md) 섹션을 참조하세요.

**참고**  
백그라운드에서 이 작업을 수행하려면 `SaveAsync` 메서드를 대신 사용하세요.

## 스캔
<a name="w2aac17b9c21c23c39c37"></a>

전체 테이블 스캔을 수행합니다.

스캔 조건을 지정하여 스캔 결과를 필터링할 수 있습니다. 스캔 조건은 테이블의 어느 속성 상에서든지 평가될 수 있습니다. DynamoDB의 `ProductCatalog` 테이블에 클라이언트 측 `Book` 클래스가 매핑되어 있는 경우 다음 C\$1 코드 예제에서는 테이블을 스캔하고 가격이 0보다 작은 책 항목만 반환합니다.

**Example**  

```
IEnumerable<Book> itemsWithWrongPrice = context.Scan<Book>(
                    new ScanCondition("Price", ScanOperator.LessThan, price),
                    new ScanCondition("ProductCategory", ScanOperator.Equal, "Book")
      );
```

`Scan` 메서드는 "지연 로딩된(lazy-loaded)" `IEnumerable` 컬렉션을 반환합니다. 즉, 처음에는 결과 페이지를 하나만 반환하고, 필요에 따라 서비스를 호출하여 다음 페이지를 반환합니다. 일치하는 항목을 모두 가져오려면 `IEnumerable`을 반복하기만 하면 됩니다.

성능 문제 때문에 테이블 스캔을 피하고 테이블을 쿼리해야 합니다.

**참고**  
백그라운드에서 이 작업을 수행하려면 `ScanAsync` 메서드를 대신 사용하세요.

## ToDocument
<a name="w2aac17b9c21c23c39c39"></a>

클래스 인스턴스에서 `Document` 문서 모델 클래스의 인스턴스가 반환됩니다.

이것은 문서 모델 클래스를 객체 지속성 모델과 함께 사용하여 데이터 작업을 수행할 때 유용합니다. AWS SDK for .NET에 의해 제공되는 문서 모델 클래스에 대한 자세한 내용은 [DynamoDB에서 .NET 문서 모델을 사용하여 작업](DotNetSDKMidLevel.md) 단원을 참조하세요.

샘플 `Forum` 테이블에 클라이언트 측 클래스가 매핑되어 있는 경우 다음 C\$1 코드 예제와 같이 `DynamoDBContext`를 사용하여 `Document` 테이블에서 항목을 `Forum` 객체로 얻을 수 있습니다.

**Example**  

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

Forum forum101 = context.Load<Forum>(101); // Retrieve a forum by primary key.
Document doc = context.ToDocument<Forum>(forum101);
```

## DynamoDBContext에 대한 옵션 파라미터 지정
<a name="OptionalConfigParams"></a>

객체 지속성 모델을 사용하는 경우, `DynamoDBContext`에 대하여 다음과 같은 선택적 파라미터를 지정할 수 있습니다.
+ **`ConsistentRead`** - `Load`, `Query` 또는 `Scan` 작업을 사용하여 데이터를 검색할 때 이 선택적 파라미터를 추가하여 데이터의 최신 값을 요청할 수 있습니다.
+ **`IgnoreNullValues`** - 이 파라미터는 `DynamoDBContext`에 `Save` 작업 동안 속성의 null 값을 무시하도록 지시합니다. 이 파라미터가 false이거나 설정되지 않은 경우 null 값은 특정 속성을 삭제하라는 지시로 해석됩니다.
+ **`SkipVersionCheck`** - 이 파라미터는 항목을 저장하거나 삭제할 때 `DynamoDBContext`에 버전을 비교하지 않도록 지시합니다. 버전 관리에 대한 자세한 내용은 [DynamoDB 및 AWS SDK for .NET 객체 지속성 모델을 사용하여 낙관적 잠금 수행](DynamoDBContext.VersionSupport.md) 섹션을 참조하십시오.
+ ** `TableNamePrefix` **-모든 테이블 이름에서 특정 문자열을 접두사로 사용합니다. 이 파라미터가 null이거나 설정되지 않은 경우, 어떤 접두사도 사용되지 않습니다.
+ `DynamoDBEntryConversion` - 클라이언트가 사용하는 변환 스키마를 지정합니다. 이 파라미터를 버전 V1 또는 V2로 설정할 수 있습니다. 기본 버전은 V1입니다.

  설정한 버전에 따라 이 파라미터의 동작이 달라집니다. 예제:
  + V1에서는 `bool` 데이터 형식이 `N` 숫자 형식으로 변환됩니다. 여기서 0은 false, 1은 true를 나타냅니다. V2에서는 `bool`이 `BOOL`로 변환됩니다.
  + V2에서는 목록과 배열이 HashSet과 함께 그룹화되지 않습니다. 숫자로 구성된 목록과 배열, 문자열 기반 형식, 이진 기반 형식은 `L`(목록) 형식으로 변환됩니다. 이 형식은 목록을 업데이트하기 위해 빈 상태로 보낼 수 있습니다. 이는 빈 목록이 유선으로 전송되지 않는 V1과는 다릅니다.

    V1에서는 목록, HashSet 및 배열과 같은 컬렉션 형식이 동일하게 취급됩니다. 숫자 목록, HashSet 및 배열은 `NS`(숫자 집합) 형식으로 변환됩니다.

  다음 예제에서는 변환 스키마 버전을 V2로 설정하여 .NET 형식과 DynamoDB 데이터 형식 간의 변환 동작을 변경합니다.

  ```
  var config = new DynamoDBContextConfig
  {
      Conversion = DynamoDBEntryConversion.V2
  };
  var contextV2 = new DynamoDBContext(client, config);
  ```

다음 C\$1 예제에서는 앞에 나온 선택적 파라미터 중 두 개인 `ConsistentRead` 및 `SkipVersionCheck`를 지정하여 새 `DynamoDBContext`를 생성합니다.

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
...
DynamoDBContext context =
       new DynamoDBContext(client, new DynamoDBContextConfig { ConsistentRead = true, SkipVersionCheck = true});
```

`DynamoDBContext`에는 이 컨텍스트를 사용하여 보내는 각 요청과 함께 이러한 선택적 파라미터가 포함되어 있습니다.

이러한 파라미터를 `DynamoDBContext` 수준에 설정하는 대신 다음 C\$1 코드 예제와 같이 `DynamoDBContext`를 사용하여 실행하는 개별 작업에 대해 이러한 파라미터를 지정할 수 있습니다. 이 예제는 특정 책 항목을 로드합니다. `DynamoDBContext`의 `Load` 메서드는 선택적 파라미터인 `ConsistentRead` 및 `SkipVersionCheck`를 지정합니다.

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
...
DynamoDBContext context = new DynamoDBContext(client);
Book bookItem = context.Load<Book>(productId,new DynamoDBContextConfig{ ConsistentRead = true, SkipVersionCheck = true });
```

이 경우 `DynamoDBContext`에는 `Get` 요청을 보내는 경우에 한하여 이러한 파라미터가 포함됩니다.

# DynamoDB 및 AWS SDK for .NET 객체 지속성 모델을 사용하여 낙관적 잠금 수행
<a name="DynamoDBContext.VersionSupport"></a>

객체 지속성 모델의 낙관적 잠금 지원을 통해 항목을 업데이트하거나 삭제하기 전에 애플리케이션에 대한 항목 버전과 서버 측 항목 버전이 동일해집니다. 업데이트할 항목을 검색하는 경우 그러나 업데이트를 돌려보내기 전에 다른 애플리케이션이 같은 항목을 업데이트했습니다. 이 경우, 애플리케이션 항목의 오래된 복사본이 남게 됩니다. 낙관적 잠금이 없는 경우, 업데이트를 수행하면 다른 애플리케이션에 의해 생성된 업데이트를 덮어씁니다.

객체 지속성 모델의 낙관적 잠금 기능은 낙관적 잠금을 활성화하는 데 사용할 수 있는 `DynamoDBVersion` 태그를 제공합니다. 이 기능을 사용하려면 버전 번호를 저장하기 위해 클래스에 속성을 추가합니다. 이 속성에 `DynamoDBVersion` 속성을 추가합니다. 처음 객체를 저장할 때 `DynamoDBContext`가 버전 번호를 할당하고, 항목이 업데이트될 때마다 이 값을 증가시킵니다.

업데이트나 삭제 요청은 클라이언트 측 객체 버전이 서버 측의 해당 항목 버전 번호와 일치해야만 성공합니다. 애플리케이션에 오래된 사본이 있는 경우에는 서버에서 최신 버전을 받아야만 항목을 업데이트하거나 삭제할 수 있습니다.

다음 C\$1 코드 예제에서는 객체 지속성 속성으로 `Book` 클래스를 정의하며 해당 클래스를 `ProductCatalog` 테이블로 매핑합니다. `VersionNumber` 속성이 데코레이트된 클래스의`DynamoDBVersion` 속성에는 버전 번호 값이 저장됩니다.

**Example**  

```
[DynamoDBTable("ProductCatalog")]
  public class Book
  {
    [DynamoDBHashKey]   //Partition key
    public int Id { get; set; }
    [DynamoDBProperty]
    public string Title { get; set; }
    [DynamoDBProperty]
    public string ISBN { get; set; }
    [DynamoDBProperty("Authors")]
    public List<string> BookAuthors { get; set; }
    [DynamoDBVersion]
    public int? VersionNumber { get; set; }
  }
```

**참고**  
`DynamoDBVersion` 속성은 `int?`와 같이 null이 허용된 숫자 기본 유형으로만 적용 가능합니다.

낙관적 잠금 전략은 `DynamoDBContext` 작업에 다음과 같은 영향을 끼칩니다.
+ 새로운 항목이 있을 경우, `DynamoDBContext`는 초기 버전 번호를 0으로 할당합니다. 이후 기존 항목을 검색하고 해당 속성을 하나 이상 업데이트한 후 변경 사항을 저장하려고 해도 클라이언트 측 버전 번호와 서버 측 버전 번호가 일치해야만 저장 작업이 성공합니다. `DynamoDBContext`는 버전 번호를 증가시킵니다. 버전 번호를 설정할 필요는 없습니다.
+ 다음 C\$1 코드 예제와 같이 `Delete` 메서드는 기본 키 값 또는 객체를 파라미터로 사용할 수 있는 오버로드를 제공합니다.  
**Example**  

  ```
  DynamoDBContext context = new DynamoDBContext(client);
  ...
  // Load a book.
  Book book = context.Load<ProductCatalog>(111);
  // Do other operations.
  // Delete 1 - Pass in the book object.
  context.Delete<ProductCatalog>(book);
  
  // Delete 2 - Pass in the Id (primary key)
  context.Delete<ProductCatalog>(222);
  ```

  객체를 파라미터로 제공하는 경우 객체 버전이 서버 측 해당 항목 버전과 일치해야만 삭제가 성공합니다. 그러나 기본 키 값을 파라미터로 제공하는 경우 `DynamoDBContext`는 어떤 버전 번호도 알 수 없으며 버전 확인 없이 항목을 삭제합니다.

  단, 낙관적 잠금을 객체 지속성 모델 코드로 내부 구현할 경우에는 DynamoDB의 조건부 업데이트와 조건부 API 작업 삭제를 사용합니다.

## 낙관적 잠금 비활성화
<a name="DotNetDynamoDBContext.DisablingOptimisticLocking"></a>

낙관적 잠금을 비활성화하려면 `SkipVersionCheck` 구성 속성을 사용합니다. `DynamoDBContext`를 만들 때 이 속성을 설정할 수 있습니다. 이 경우 컨텍스트를 사용하여 수행하는 모든 요청에 대해 낙관적 잠금이 비활성화됩니다. 자세한 내용은 [DynamoDBContext에 대한 옵션 파라미터 지정](DotNetDynamoDBContext.md#OptionalConfigParams) 섹션을 참조하세요.

속성을 컨텍스트 수준에 설정하는 대신 다음 C\$1 코드 예제와 같이 특정 작업에 대한 낙관적 잠금을 비활성화할 수 있습니다. 이 예제에서는 컨텍스트를 사용하여 책 항목을 삭제합니다. `Delete` 메서드는 선택적 `SkipVersionCheck` 속성을 true로 설정하여 버전 확인을 비활성화합니다.

**Example**  

```
DynamoDBContext context = new DynamoDBContext(client);
// Load a book.
Book book = context.Load<ProductCatalog>(111);
...
// Delete the book.
context.Delete<Book>(book, new DynamoDBContextConfig { SkipVersionCheck = true });
```

# AWS SDK for .NET 객체 지속성 모델을 사용하여 DynamoDB에서 임의 데이터 매핑
<a name="DynamoDBContext.ArbitraryDataMapping"></a>

지원되는 .NET 형식([지원되는 데이터 유형](DotNetSDKHighLevel.md#DotNetDynamoDBContext.SupportedTypes) 참조) 외에도 애플리케이션에서 직접적으로 Amazon DynamoDB 형식에 매핑되지 않는 형식을 사용할 수 있습니다. 데이터를 임의 형식에서 DynamoDB 형식으로 또는 그 반대로 변환할 수 있는 변환기가 있는 한 객체 지속성 모델은 임의 형식의 데이터 저장을 지원합니다. 객체를 저장하거나 로드하는 동안 변환기 코드가 데이터를 변환합니다.

클라이언트 측에서 어떤 유형도 생성할 수 있습니다. 그러나 테이블에 저장되는 데이터는 DynamoDB 형식 중 하나이며 쿼리 및 스캔이 진행되는 동안 DynamoDB에 저장되어 있는 데이터에 대해 데이터 비교가 수행됩니다.

다음의 C\$1 코드 예제에서는 `Book`, `Id`, `Title` 및 `ISBN` 속성을 사용하여 `Dimension` 클래스를 정의하고 있습니다. `Dimension` 속성은 `DimensionType`, `Height` 및 `Width` 속성을 기술하는 `Thickness`에 포함되어 있습니다. 이 예제 코드는 데이터를 `DimensionType` 및 DynamoDB 문자열 형식 간에 전환하는 변환기 메서드 `ToEntry` 및 `FromEntry`를 제공합니다. 예를 들어, `Book` 인스턴스를 저장할 때 변환기는 "8.5x11x.05"와 같은 책 `Dimension` 문자열을 생성합니다. 책을 검색하면 문자열이 `DimensionType` 인스턴스로 변환됩니다.

이 예제에서는 `Book` 유형을 `ProductCatalog` 테이블로 매핑합니다. 샘플 `Book` 인스턴스를 저장하고, 해당 인스턴스를 검색하고, 해당 차원을 업데이트하고, 업데이트된 `Book`을 다시 저장합니다.



다음 예제를 테스트하기 위한 단계별 지침은 [.NET 코드 예시](CodeSamples.DotNet.md) 단원을 참조하세요.

**Example**  

```
using System;
using System.Collections.Generic;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.Runtime;
using Amazon.SecurityToken;

namespace com.amazonaws.codesamples
{
    class HighLevelMappingArbitraryData
    {
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();

        static void Main(string[] args)
        {
            try
            {
                DynamoDBContext context = new DynamoDBContext(client);

                // 1. Create a book.
                DimensionType myBookDimensions = new DimensionType()
                {
                    Length = 8M,
                    Height = 11M,
                    Thickness = 0.5M
                };

                Book myBook = new Book
                {
                    Id = 501,
                    Title = "AWS SDK for .NET Object Persistence Model Handling Arbitrary Data",
                    ISBN = "999-9999999999",
                    BookAuthors = new List<string> { "Author 1", "Author 2" },
                    Dimensions = myBookDimensions
                };

                context.Save(myBook);

                // 2. Retrieve the book.
                Book bookRetrieved = context.Load<Book>(501);

                // 3. Update property (book dimensions).
                bookRetrieved.Dimensions.Height += 1;
                bookRetrieved.Dimensions.Length += 1;
                bookRetrieved.Dimensions.Thickness += 0.2M;
                // Update the book.
                context.Save(bookRetrieved);

                Console.WriteLine("To continue, press Enter");
                Console.ReadLine();
            }
            catch (AmazonDynamoDBException e) { Console.WriteLine(e.Message); }
            catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
            catch (Exception e) { Console.WriteLine(e.Message); }
        }
    }
    [DynamoDBTable("ProductCatalog")]
    public class Book
    {
        [DynamoDBHashKey] //Partition key
        public int Id
        {
            get; set;
        }
        [DynamoDBProperty]
        public string Title
        {
            get; set;
        }
        [DynamoDBProperty]
        public string ISBN
        {
            get; set;
        }
        // Multi-valued (set type) attribute.
        [DynamoDBProperty("Authors")]
        public List<string> BookAuthors
        {
            get; set;
        }
        // Arbitrary type, with a converter to map it to DynamoDB type.
        [DynamoDBProperty(typeof(DimensionTypeConverter))]
        public DimensionType Dimensions
        {
            get; set;
        }
    }

    public class DimensionType
    {
        public decimal Length
        {
            get; set;
        }
        public decimal Height
        {
            get; set;
        }
        public decimal Thickness
        {
            get; set;
        }
    }

    // Converts the complex type DimensionType to string and vice-versa.
    public class DimensionTypeConverter : IPropertyConverter
    {
        public DynamoDBEntry ToEntry(object value)
        {
            DimensionType bookDimensions = value as DimensionType;
            if (bookDimensions == null) throw new ArgumentOutOfRangeException();

            string data = string.Format("{1}{0}{2}{0}{3}", " x ",
                            bookDimensions.Length, bookDimensions.Height, bookDimensions.Thickness);

            DynamoDBEntry entry = new Primitive
            {
                Value = data
            };
            return entry;
        }

        public object FromEntry(DynamoDBEntry entry)
        {
            Primitive primitive = entry as Primitive;
            if (primitive == null || !(primitive.Value is String) || string.IsNullOrEmpty((string)primitive.Value))
                throw new ArgumentOutOfRangeException();

            string[] data = ((string)(primitive.Value)).Split(new string[] { " x " }, StringSplitOptions.None);
            if (data.Length != 3) throw new ArgumentOutOfRangeException();

            DimensionType complexData = new DimensionType
            {
                Length = Convert.ToDecimal(data[0]),
                Height = Convert.ToDecimal(data[1]),
                Thickness = Convert.ToDecimal(data[2])
            };
            return complexData;
        }
    }
}
```

# 이 개발자 안내서의 코드 예시 실행
<a name="CodeSamples"></a>

AWS SDK는 Amazon DynamoDB에 대한 광범위한 지원을 다음 언어로 제공합니다.
+ [Java](https://aws.amazon.com/sdk-for-java): 
+ [브라우저에서의 JavaScript](https://aws.amazon.com/sdk-for-browser)
+ [.NET](https://aws.amazon.com/sdk-for-net)
+ [Node.js](https://aws.amazon.com/sdk-for-node-js)
+ [PHP](https://aws.amazon.com/sdk-for-php)
+ [Python](https://aws.amazon.com/sdk-for-python)
+ [Ruby](https://aws.amazon.com/sdk-for-ruby)
+ [C\$1\$1](https://aws.amazon.com/sdk-for-cpp)
+ [Go](https://aws.amazon.com/sdk-for-go)
+ [Android](https://aws.amazon.com/mobile/sdk/): 
+ [iOS](https://aws.amazon.com/mobile/sdk/): 

이 개발자 안내서의 코드 예제에서는 다음의 프로그래밍 언어를 사용하여 DynamoDB 작업을 보다 깊이 있게 다룹니다.
+ [Java 코드 예](CodeSamples.Java.md)
+ [.NET 코드 예시](CodeSamples.DotNet.md)

이 연습을 시작하려면 먼저 AWS 계정을 생성하고, 액세스 키 및 보안 키를 얻고, 컴퓨터에서 AWS Command Line Interface(AWS CLI)를 설정해야 합니다. 자세한 내용은 [DynamoDB 설정(웹 서비스)](SettingUp.DynamoWebService.md) 섹션을 참조하세요.

**참고**  
DynamoDB 다운로드 가능 버전을 사용하는 경우, AWS CLI를 사용하여 테이블과 샘플 데이터를 생성해야 합니다. 또한 각 `--endpoint-url` 명령으로 AWS CLI 파라미터를 지정해야 합니다. 자세한 내용은 [로컬 엔드포인트 설정](DynamoDBLocal.UsageNotes.md#DynamoDBLocal.Endpoint) 섹션을 참조하세요.

# DynamoDB에서 테이블 생성 및 코드 예시에 대한 데이터 로드
<a name="SampleData"></a>

DynamoDB에서 테이블을 생성하고, 샘플 데이터 세트를 로드하고, 데이터를 쿼리하고, 데이터를 업데이트하는 방법에 대한 기본 사항은 아래를 참조하세요.
+ [1단계: DynamoDB에 테이블 생성](getting-started-step-1.md)
+ [2단계: DynamoDB 테이블에 데이터 쓰기](getting-started-step-2.md)
+ [3단계: DynamoDB 테이블의 데이터 읽기](getting-started-step-3.md)
+ [4단계: DynamoDB 테이블의 데이터 업데이트](getting-started-step-4.md)

# Java 코드 예
<a name="CodeSamples.Java"></a>

**Topics**
+ [Java: AWS 보안 인증 정보 설정](#CodeSamples.Java.Credentials)
+ [Java: AWS 리전 및 엔드포인트 설정](#CodeSamples.Java.RegionAndEndpoint)

이 개발자 안내서에는 Java 코드 조각과 실행 준비된 프로그램이 포함되어 있습니다. 다음 단원에서 이들 코드 예제를 찾을 수 있습니다.
+ [DynamoDB의 항목 및 속성 작업](WorkingWithItems.md)
+ [DynamoDB의 테이블 및 데이터 작업](WorkingWithTables.md)
+ [DynamoDB에서 테이블 쿼리](Query.md)
+ [DynamoDB에서 테이블 스캔](Scan.md)
+ [DynamoDB에서 보조 인덱스를 사용하여 데이터 액세스 개선](SecondaryIndexes.md)
+ [Java 1.x: DynamoDBMapper](DynamoDBMapper.md)
+ [DynamoDB Streams에 대한 변경 데이터 캡처](Streams.md)

Eclipse와 함께 [AWS Toolkit for Eclipse](https://aws.amazon.com/eclipse/)를 사용하여 빠르게 시작할 수 있습니다. 완벽한 기능의 IDE 외에 자동 업데이트가 포함된 AWS SDK for Java와 사전 구성된 AWS 애플리케이션 개발 템플릿도 포함되어 있습니다.

**Java 코드 예제를 실행하려면(Eclipse 사용)**

1. [Eclipse](http://www.eclipse.org) IDE를 다운로드하고 설치합니다.

1. 를 다운로드하여 설치합니다..[AWS Toolkit for Eclipse](https://aws.amazon.com/eclipse/)

1. Eclipse를 시작하고 **Eclipse** 메뉴에서 **파일**, **새로 만들기**, **기타**를 차례대로 선택합니다.

1. **Select a wizard**에서 **AWS**, **AWS Java Project**, **Next**를 차례대로 선택합니다.

1. **Create an AWS Java**에서 다음을 수행합니다.

   1. **프로젝트 이름**에 프로젝트의 이름을 입력합니다.

   1. **Select Account**의 목록에서 자격 증명 프로필을 선택합니다.

      [AWS Toolkit for Eclipse](https://aws.amazon.com/eclipse/)를 처음 사용하는 경우, **Configure AWS Accounts**(계정 구성)를 선택하여 AWS 보안 인증 정보를 설정합니다.

1. 프로젝트를 생성하려면 **Finish**를 선택합니다.

1. **Eclipse** 메뉴에서 **File**, **New**, **Class**를 차례대로 선택합니다.

1. **Java Class(Java 클래스)**에서 **이름**에 클래스 이름을 입력(실행하려는 코드 예제와 동일한 이름 사용)한 다음 **마침**을 선택하여 클래스를 생성합니다.

1. 설명서 페이지에서 코드 예제를 Eclipse 에디터로 복사합니다.

1. 코드를 실행하려면 Eclipse 메뉴에서 **실행**을 선택합니다.

SDK for Java는 DynamoDB 작업을 위한 스레드 세이프(thread-safe) 클라이언트를 제공합니다. 모범 사례로서 애플리케이션에서 클라이언트 하나를 생성한 후 스레드 간에 재사용해야 합니다.

자세한 내용은 [AWS SDK for Java](https://aws.amazon.com/sdk-for-java) 섹션을 참조하세요.

**참고**  
이 안내서의 코드 예제는 AWS SDK for Java의 최신 버전과 함께 사용해야 합니다.  
AWS Toolkit for Eclipse를 사용하는 경우 SDK for Java에 대해 자동 업데이트를 구성할 수 있습니다. Eclipse에서 이렇게 하려면 **기본 설정**으로 이동하고 **AWS Toolkit**, **AWS SDK for Java**, **Download new SDKs automatically(새 SDK를 자동으로 다운로드)**를 차례로 선택합니다.

## Java: AWS 보안 인증 정보 설정
<a name="CodeSamples.Java.Credentials"></a>

SDK for Java에서는 런타임에 애플리케이션에 AWS 자격 증명을 제공해야 합니다. 이 가이드의 코드 예시에서는 *AWS SDK for Java 개발자 안내서*의 [AWS 보안 인증 정보 설정](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/set-up-creds.html)에 설명된 대로 AWS 보안 인증 정보 파일을 사용한다고 가정합니다.

다음은 `~/.aws/credentials`라는 AWS 자격 증명 파일의 예입니다. 여기서 물결 문자(`~`)는 사용자의 홈 디렉터리입니다.

```
[default]
aws_access_key_id = AWS access key ID goes here
aws_secret_access_key = Secret key goes here
```

## Java: AWS 리전 및 엔드포인트 설정
<a name="CodeSamples.Java.RegionAndEndpoint"></a>

기본적으로 코드 예제는 미국 서부(오레곤) 리전에서 DynamoDB에 액세스합니다. `AmazonDynamoDB` 속성을 수정하여 리전을 변경할 수 있습니다.

다음 코드 예제에서는 새 `AmazonDynamoDB`를 인스턴스화합니다.

```
import software.amazon.dynamodb.AmazonDynamoDBClientBuilder;
import com.amazonaws.regions.Regions;
...
// This client will default to US West (Oregon)
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
.withRegion(Regions.US_WEST_2)
.build();
```

`withRegion` 메서드를 사용하면 사용할 수 있는 어떤 리전의 DynamoDB에 대해서도 코드를 실행할 수 있습니다. 전체 목록은 **Amazon Web Services 일반 참조에서 [AWS 리전 및 엔드포인트](https://docs.aws.amazon.com/general/latest/gr/rande.html#ddb_region)를 참조하세요.

컴퓨터에서 로컬로 DynamoDB를 사용하여 코드 예제를 실행하려면 다음과 같이 엔드포인트를 설정합니다.

### AWS SDK V1
<a name="CodeSamples.Java.RegionAndEndpoint.V1"></a>

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().withEndpointConfiguration(
new AwsClientBuilder.EndpointConfiguration("http://localhost:8000", "us-west-2"))
.build();
```

### AWS SDK V2
<a name="CodeSamples.Java.RegionAndEndpoint.V2"></a>

```
DynamoDbClient client = DynamoDbClient.builder()
    .endpointOverride(URI.create("http://localhost:8000"))
    // The region is meaningless for local DynamoDb but required for client builder validation
    .region(Region.US_EAST_1)
    .credentialsProvider(StaticCredentialsProvider.create(
    AwsBasicCredentials.create("dummy-key", "dummy-secret")))
    .build();
```

# .NET 코드 예시
<a name="CodeSamples.DotNet"></a>

**Topics**
+ [.NET: AWS 보안 인증 정보 설정](#CodeSamples.DotNet.Credentials)
+ [.NET: AWS 리전 및 엔드포인트 설정](#CodeSamples.DotNet.RegionAndEndpoint)

이 안내서에는 .NET 코드 조각과 실행 준비된 프로그램이 포함되어 있습니다. 다음 단원에서 이들 코드 예제를 찾을 수 있습니다.
+ [DynamoDB의 항목 및 속성 작업](WorkingWithItems.md)
+ [DynamoDB의 테이블 및 데이터 작업](WorkingWithTables.md)
+ [DynamoDB에서 테이블 쿼리](Query.md)
+ [DynamoDB에서 테이블 스캔](Scan.md)
+ [DynamoDB에서 보조 인덱스를 사용하여 데이터 액세스 개선](SecondaryIndexes.md)
+ [DynamoDB에서 .NET 문서 모델을 사용하여 작업](DotNetSDKMidLevel.md)
+ [.NET 객체 지속성 모델 및 DynamoDB로 작업](DotNetSDKHighLevel.md)
+ [DynamoDB Streams에 대한 변경 데이터 캡처](Streams.md)

AWS SDK for .NET 및 Toolkit for Visual Studio를 함께 사용하면 빠르게 시작할 수 있습니다.

**.NET 코드 예제를 실행하려면((Visual Studio 사용)**

1. [Microsoft Visual Studio](https://www.visualstudio.com)를 다운로드하여 설치합니다.

1. (선택 사항) [Toolkit for Visual Studio](https://aws.amazon.com/visualstudio/)를 다운로드하여 설치합니다.

1. AWS 보안 인증을 설정합니다. 공유 AWS 자격 증명 파일(`~/.aws/credentials`)에서 자격 증명 프로파일을 구성합니다. 자세한 내용은 **AWS SDK for .NET 개발자 안내서의 [AWS 자격 증명 구성](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-creds.html)을 참조하세요.

1. Visual Studio를 시작합니다. **파일**, **새로 만들기**, **프로젝트**를 선택합니다.

1. **콘솔 앱**을 검색하고 .NET을 대상으로 하는 C\$1 템플릿을 선택한 후 **다음**을 선택합니다. 프로젝트 이름과 위치를 구성한 다음 **생성**을 선택합니다.

1. 프로젝트에 DynamoDB NuGet용 AWS SDK 패키지를 추가하려면 다음을 수행합니다.

   1. Solution Exploer에서 프로젝트의 컨텍스트 메뉴를 열고(마우스 오른쪽 버튼 클릭) **Manage NuGet Packages**를 선택합니다.

   1. NuGet Package Manager에서 **Browse**를 선택합니다.

   1. 검색 상자에 **AWSSDK.DynamoDBv2**를 입력하고 검색이 완료되기를 기다립니다.

   1. **AWSSDK.DynamoDBv2**를 선택한 다음 **설치**를 선택합니다.

1. Visual Studio 프로젝트에서 `Program.cs`를 엽니다. 내용을 실행하려는 설명서 페이지의 코드 예제로 바꿉니다.

1. 코드를 실행하려면 Visual Studio 도구 모음에서 **시작**을 선택합니다.

SDK for .NET은 DynamoDB 작업을 위한 스레드 세이프(thread-safe) 클라이언트를 제공합니다. 모범 사례로서 애플리케이션에서 클라이언트 하나를 생성한 후 스레드 간에 재사용해야 합니다.

자세한 내용은 [AWS SDK for .NET](https://aws.amazon.com/sdk-for-net)을 참조하세요.

**참고**  
이 안내서의 코드 예제는 AWS SDK for .NET의 최신 버전과 함께 사용해야 합니다.

## .NET: AWS 보안 인증 정보 설정
<a name="CodeSamples.DotNet.Credentials"></a>

SDK for .NET에서는 런타임에 애플리케이션에 AWS 자격 증명을 제공해야 합니다. 이 가이드의 코드 예시에서는 *AWS SDK for .NET 개발자 안내서*의 [SDK 스토어 사용](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-creds.html#sdk-store)에 설명된 대로 SDK 스토어를 사용하여 AWS 보안 인증 정보 파일을 관리한다고 가정합니다.

Toolkit for Visual Studio는 계정 수에 제한 없이 다수의 자격 증명 집합을 지원합니다. 각 집합을 *프로필*이라고 부릅니다. Visual Studio는 애플리케이션이 런타임 도중에도 AWS 자격 증명을 찾을 수 있도록 프로젝트의 `App.config` 파일에 항목을 추가합니다.

다음 예제에서는 Toolkit for Visual Studio를 사용하여 새 프로젝트를 만들 때 생성되는 기본 `App.config` 파일을 보여줍니다.

```
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
    <add key="AWSProfileName" value="default"/>
    <add key="AWSRegion" value="us-west-2" />
 </appSettings>
</configuration>
```

런타임에 프로그램은 `AWSProfileName` 항목에 지정된 대로 `default` 자격 증명의 AWS 집합을 사용합니다. AWS 자격 증명 자체는 암호화된 형식으로 SDK 스토어에 보관됩니다. Toolkit for Visual Studio는 Visual Studio 안에서 자격 증명을 모두 관리할 수 있는 그래픽 사용자 인터페이스를 제공합니다. 자세한 내용은 *AWS Toolkit for Visual Studio 사용 설명서*에서 [보안 인증 정보 지정](https://docs.aws.amazon.com/AWSToolkitVS/latest/UserGuide/tkv_setup.html#creds)을 참조하세요.

**참고**  
기본적으로 코드 예제는 미국 서부(오레곤) 리전에서 DynamoDB에 액세스합니다. App.config 파일의 `AWSRegion` 항목을 수정하여 리전을 변경할 수 있습니다. `AWSRegion`은 DynamoDB를 사용할 수 있는 어느 리전으로도 설정할 수 있습니다. 전체 목록은 **Amazon Web Services 일반 참조에서 [AWS 리전 및 엔드포인트](https://docs.aws.amazon.com/general/latest/gr/rande.html#ddb_region)를 참조하세요.

## .NET: AWS 리전 및 엔드포인트 설정
<a name="CodeSamples.DotNet.RegionAndEndpoint"></a>

기본적으로 코드 예제는 미국 서부(오레곤) 리전에서 DynamoDB에 액세스합니다. `AWSRegion` 파일의 `App.config` 항목을 수정하여 리전을 변경할 수 있습니다. 또는 `AmazonDynamoDBClient` 속성을 수정하여 리전을 변경할 수 있습니다.

다음 코드 예제에서는 새 `AmazonDynamoDBClient`를 인스턴스화합니다. 클라이언트가 수정되어 코드가 다른 리전의 DynamoDB에 대해 실행됩니다.

```
AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig();
// This client will access the US East 1 region.
clientConfig.RegionEndpoint = RegionEndpoint.USEast1;
AmazonDynamoDBClient client = new AmazonDynamoDBClient(clientConfig);
```

전체 리전 목록은 **Amazon Web Services 일반 참조에서 [AWS 리전 및 엔드포인트](https://docs.aws.amazon.com/general/latest/gr/rande.html#ddb_region)를 참조하세요.

컴퓨터에서 로컬로 DynamoDB를 사용하여 코드 예제를 실행하려면 다음과 같이 엔드포인트를 설정합니다.

```
AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig();
// Set the endpoint URL
clientConfig.ServiceURL = "http://localhost:8000";
AmazonDynamoDBClient client = new AmazonDynamoDBClient(clientConfig);
```

# DynamoDB 하위 수준 API
<a name="Programming.LowLevelAPI"></a>

Amazon DynamoDB *하위 수준 API*는 DynamoDB의 프로토콜 수준 인터페이스입니다. 이 수준에서는 모든 HTTP(S) 요청이 올바른 형식이어야 하며, 유효한 디지털 서명이 있어야 합니다.

AWS SDK는 사용자를 대신하여 하위 수준 DynamoDB API 요청을 구성하고 DynamoDB의 응답을 처리합니다. 이로써 하위 수준 세부 사항 대신 애플리케이션 로직에 집중할 수 있습니다. 그러나 하위 수준 DynamoDB API의 작동 방식에 대한 기본적 이해는 여전히 도움이 될 수 있습니다.

하위 수준 DynamoDB API에 대한 자세한 내용은 [Amazon DynamoDB API 참조](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/)를 참조하세요.

**참고**  
DynamoDB Streams의 자체 하위 수준 API는 DynamoDB의 그것과 별개이며, AWS SDK가 완벽하게 지원합니다.  
자세한 내용은 [DynamoDB Streams에 대한 변경 데이터 캡처](Streams.md) 섹션을 참조하세요. 하위 수준 DynamoDB Streams API는 [Amazon DynamoDB Streams API 참조](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Operations_Amazon_DynamoDB_Streams.html)를 참조하세요.

하위 수준 DynamoDB API는 JavaScript Object Notation(JSON)을 연결 프로토콜 형식으로 사용합니다. JSON은 데이터를 계층 구조로 나타내므로 데이터 값과 데이터 구조 모두 동시에 전달됩니다. 이름-값 페어는 `name:value` 형식으로 정의됩니다. 데이터 계층은 이름-값 페어의 중첩된 대괄호로 정의됩니다.

DynamoDB는 JSON을 오직 전송 프로토콜로 사용하며 스토리지 형식으로는 사용하지 않습니다. AWS SDK는 JSON을 사용하여 DynamoDB에 데이터를 보내고 DynamoDB는 JSON으로 응답합니다. DynamoDB는 데이터를 JSON 형식으로 지속적으로 저장하지 않습니다.

**참고**  
JSON에 대한 자세한 내용은 `JSON.org` 웹사이트에서 [JSON 소개](http://json.org)를 참조하세요.

**Topics**
+ [요청 형식](#Programming.LowLevelAPI.RequestFormat)
+ [응답 형식](#Programming.LowLevelAPI.ResponseFormat)
+ [데이터 형식 서술자](#Programming.LowLevelAPI.DataTypeDescriptors)
+ [숫자 데이터](#Programming.LowLevelAPI.Numbers)
+ [이진 데이터](#Programming.LowLevelAPI.Binary)

![\[DynamoDB 하위 수준 API 및 AWS SDK가 프로토콜 수준 요청 및 응답을 처리하는 방법입니다.\]](http://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/images/SDKSupport.DDBLowLevelAPI.png)


## 요청 형식
<a name="Programming.LowLevelAPI.RequestFormat"></a>

DynamoDB 하위 수준 API는 HTTP(S) `POST` 요청을 입력으로 받아들입니다. AWS SDK는 이 요청을 구성합니다.

`Pets`(파티션 키) 및 `AnimalType`(정렬 키)으로 구성된 키 스키마가 포함된 `Name`이라는 이름의 테이블이 있는 경우 이 두 가지 속성 모두 `string` 유형입니다. `Pets`에서 항목을 검색하기 위해 AWS SDK는 다음 요청을 구성합니다.

```
POST / HTTP/1.1
Host: dynamodb.<region>.<domain>;
Accept-Encoding: identity
Content-Length: <PayloadSizeBytes>
User-Agent: <UserAgentString>
Content-Type: application/x-amz-json-1.0
Authorization: AWS4-HMAC-SHA256 Credential=<Credential>, SignedHeaders=<Headers>, Signature=<Signature>
X-Amz-Date: <Date> 
X-Amz-Target: DynamoDB_20120810.GetItem

{
    "TableName": "Pets",
    "Key": {
        "AnimalType": {"S": "Dog"},
        "Name": {"S": "Fido"}
    }
}
```

이 요청에 대해 다음을 참조하세요.
+ `Authorization` 헤더에는 DynamoDB가 요청을 인증하는 데 필요한 정보가 포함되어 있습니다. 자세한 내용은 **Amazon Web Services 일반 참조의 [AWS API 요청에 서명](https://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html) 및 [서명 버전 4 서명 프로세스](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html)를 참조하세요.
+ `X-Amz-Target` 헤더에는 DynamoDB 작업의 이름인 `GetItem`이 포함되어 있습니다. (여기에 하위 API 버전(이 경우에는 `20120810`)도 포함됩니다.)
+ 요청의 페이로드(본문)에는 JSON 형식의 작업을 위한 파라미터가 포함되어 있습니다. `GetItem` 작업의 경우, 파라미터는 `TableName` 및 `Key`입니다.

## 응답 형식
<a name="Programming.LowLevelAPI.ResponseFormat"></a>

DynamoDB는 요청을 수신하면 이를 처리하고 응답을 반환합니다. 이전에 나온 요청의 경우 다음 예제와 같이 HTTP(S) 응답 페이로드에 해당 작업 결과가 포함됩니다.

```
HTTP/1.1 200 OK
x-amzn-RequestId: <RequestId>
x-amz-crc32: <Checksum>
Content-Type: application/x-amz-json-1.0
Content-Length: <PayloadSizeBytes>
Date: <Date>
{
    "Item": {
        "Age": {"N": "8"},
        "Colors": {
            "L": [
                {"S": "White"},
                {"S": "Brown"},
                {"S": "Black"}
            ]
        },
        "Name": {"S": "Fido"},
        "Vaccinations": {
            "M": {
                "Rabies": {
                    "L": [
                        {"S": "2009-03-17"},
                        {"S": "2011-09-21"},
                        {"S": "2014-07-08"}
                    ]
                },
                "Distemper": {"S": "2015-10-13"}
            }
        },
        "Breed": {"S": "Beagle"},
        "AnimalType": {"S": "Dog"}
    }
}
```

이때 AWS SDK는 추가 처리를 위해 애플리케이션에 응답 데이터를 반환합니다.

**참고**  
DynamoDB가 요청을 처리할 수 없는 경우 HTTP 오류 코드 및 메시지를 반환합니다. AWS SDK는 이를 예외 형식으로 애플리케이션에 전파합니다. 자세한 내용은 [DynamoDB 관련 오류 처리](Programming.Errors.md) 섹션을 참조하세요.

## 데이터 형식 서술자
<a name="Programming.LowLevelAPI.DataTypeDescriptors"></a>

하위 수준 DynamoDB API 프로토콜에서는 각 속성에 데이터 형식 서술자가 포함되어야 합니다. *데이터 형식 서술자*는 DynamoDB에 각 속성을 해석하는 방법을 알려 주는 토큰입니다.

[요청 형식](#Programming.LowLevelAPI.RequestFormat) 및 [응답 형식](#Programming.LowLevelAPI.ResponseFormat)의 예는 데이터 형식 서술자가 어떻게 사용되는지 보여 줍니다. `GetItem` 요청은 `S` 키 스키마 속성(`Pets` 및 `AnimalType`)에 대해 `Name`를 지정하며, 이는 `string` 유형입니다. `GetItem` 응답에는 `string`(`S`), `number`(`N`), `map`(`M`) 및 `list`(`L`) 형식의 *Pets* 항목이 포함됩니다.

다음은 DynamoDB 데이터 형식 서술자의 전체 목록입니다.
+ **`S`** - 문자열
+ **`N`** - 숫자
+ **`B`** - 이진수
+ **`BOOL`** - 부울
+ **`NULL`** - Null
+ **`M`** - 맵
+ **`L`** - 목록
+ **`SS`** - 문자열 집합
+ **`NS`** - 숫자 집합
+ **`BS`** - 이진수 집합

다음 표에는 각 데이터 유형 설명자의 올바른 JSON 형식이 나와 있습니다. 숫자는 정밀도를 유지하기 위해 문자열로 표시되는 반면, 부울과 null은 기본 JSON 유형을 사용합니다.


| 설명자 | JSON 형식 | 참고 | 
| --- | --- | --- | 
| S | \$1"S": "Hello"\$1 | 값은 JSON 문자열입니다. | 
| N | \$1"N": "123.45"\$1 | 값은 JSON 번호가 아닌 문자열입니다. 이렇게 하면 언어 간 정밀도가 유지됩니다. | 
| B | \$1"B": "dGhpcyBpcyBhIHRlc3Q="\$1 | 값은 base64로 인코딩된 문자열입니다. | 
| BOOL | \$1"BOOL": true\$1 | 값은 문자열이 아닌 JSON 부울(true 또는 false)입니다. | 
| NULL | \$1"NULL": true\$1 | 값은 null을 나타내는 JSON 부울 true입니다. | 
| M | \$1"M": \$1"Name": \$1"S": "Joe"\$1\$1\$1 | 값은 속성 이름-값 페어의 JSON 객체입니다. | 
| L | \$1"L": [\$1"S": "Red"\$1, \$1"N": "5"\$1]\$1 | 값은 속성 값의 JSON 배열입니다. | 
| SS | \$1"SS": ["Red", "Blue"]\$1 | 값은 문자열의 JSON 배열입니다. | 
| NS | \$1"NS": ["1", "2.5"]\$1 | 값은 숫자 문자열의 JSON 배열입니다. | 
| BS | \$1"BS": ["U3Vubnk=", "UmFpbnk="]\$1 | 값은 base64로 인코딩된 문자열의 JSON 배열입니다. | 

**참고**  
 DynamoDB 데이터 형식에 대한 자세한 내용은 [데이터 타입](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes) 단원을 참조하세요.

## 숫자 데이터
<a name="Programming.LowLevelAPI.Numbers"></a>

프로그래밍 언어마다 JSON 지원 수준이 다릅니다. 경우에 따라서는 JSON 문서를 확인하고 구문 분석하는 데 타사 라이브러리를 사용해야 할 수도 있습니다.

일부 타사 라이브러리는 JSON 숫자 형식을 기초로 `int`, `long` 또는 `double` 같은 자체 형식을 제공합니다. 그러나 DynamoDB의 기본 숫자 데이터 형식은 이러한 다른 데이터 형식에 정확히 매핑되지 않으므로 이러한 형식 구분은 충돌을 야기할 수 있습니다. 뿐만 아니라 많은 JSON 라이브러리는 고정 전체 자릿수 숫자 값을 처리하지 않으며, 소수점을 포함하는 숫자열의 double 데이터 형식을 자동으로 유추합니다.

DynamoDB는 이러한 문제를 해결하기 위해 데이터 손실이 없는 단일 숫자 형식을 제공합니다. 또한 사용자의 동의 없이 double 값으로 암시적으로 변환되는 것을 방지하기 위해 DynamoDB는 숫자 값의 데이터 전송에 문자열을 사용합니다. 이러한 접근 방식은 속성 값을 업데이트하는 데 유연성을 제공하는 동시에 "1", "2", "3"을 적절한 순서로 입력하는 등 적절한 정렬 의미 체계를 유지합니다.

애플리케이션에 숫자 전체 자릿수가 중요한 경우, DynamoDB로 전달하기 전에 숫자 값을 문자열로 변환해야 합니다.

## 이진 데이터
<a name="Programming.LowLevelAPI.Binary"></a>

DynamoDB는 이진 속성을 지원합니다. 그러나 JSON은 이진 데이터 인코딩은 기본적으로 지원하지 않습니다. 요청에서 이진 데이터를 보내려면 Base64 형식으로 인코딩해야 합니다. DynamoDB는 요청을 받아 Base64를 다시 이진수로 디코딩합니다.

DynamoDB에서 사용되는 base64 인코딩 체계는 IETF(Internet Engineering Task Force) 웹 사이트의 [RFC 4648](http://tools.ietf.org/html/rfc4648)에 설명되어 있습니다.

# Python과 Boto3를 사용한 Amazon DynamoDB 프로그래밍
<a name="programming-with-python"></a>

이 안내서는 Python을 통해 Amazon DynamoDB를 사용하려는 프로그래머에게 지침을 제공합니다. 다양한 추상화 계층, 구성 관리, 오류 처리, 재시도 정책 제어, 연결 유지 관리 등에 대해 알아봅니다.

**Topics**
+ [Boto 소개](#programming-with-python-about)
+ [Boto 설명서 사용](#programming-with-python-documentation)
+ [클라이언트 및 리소스 추상화 계층 이해](#programming-with-python-client-resource)
+ [batch\$1writer 테이블 리소스 사용](#programming-with-python-batch-writer)
+ [클라이언트 및 리소스 계층을 탐색하는 추가 코드 예시](#programming-with-python-additional-code)
+ [Client 및 Resource 객체가 세션 및 스레드와 상호 작용하는 방식 이해](#programming-with-python-sessions-thread-safety)
+ [Config 객체 사용자 지정](#programming-with-python-config)
+ [오류 처리](#programming-with-python-error-handling)
+ [로깅](#programming-with-python-logging)
+ [이벤트 후크](#programming-with-python-event-hooks)
+ [페이지 매김 및 페이지네이터](#programming-with-python-pagination)
+ [Waiters](#programming-with-python-waiters)

## Boto 소개
<a name="programming-with-python-about"></a>

공식 AWS SDK for Python(일반적으로 **Boto3**라고 함)을 사용하여 Python에서 DynamoDB에 액세스할 수 있습니다. Boto('보토'라고 발음함)라는 이름은 아마존 강에 서식하는 민물 돌고래에서 유래했습니다. Boto3 라이브러리는 2015년에 처음 출시된 이 라이브러리의 세 번째 메이저 버전입니다. Boto3 라이브러리는 DynamoDB뿐만 아니라 모든 AWS 서비스를 지원하므로 상당히 큽니다. 이 오리엔테이션에서는 Boto3에서 DynamoDB와 관련된 부분만 대상으로 합니다.

Boto는 GitHub에서 호스팅되는 오픈 소스 프로젝트로 AWS에서 유지 관리하고 게시합니다. Boto는 [Botocore](https://github.com/boto/botocore)와 [Boto3](https://github.com/boto/boto3)라는 두 개의 패키지로 나뉘어 있습니다.
+ **Botocore**는 하위 수준 기능을 제공합니다. Botocore에서는 클라이언트, 세션, 자격 증명, 구성 및 예외 클래스를 찾을 수 있습니다.
+ **Boto3**는 Botocore를 기반으로 빌드됩니다. Python과 더 유사한 더 높은 수준의 인터페이스를 제공합니다. 특히 DynamoDB 테이블을 리소스로 노출하고 하위 수준의 서비스 지향적 클라이언트 인터페이스에 비해 더 단순하고 세련된 인터페이스를 제공합니다.

이러한 프로젝트는 GitHub에서 호스팅되므로 소스 코드를 보거나 미해결 문제를 추적하거나 직접 문제를 제출할 수 있습니다.

## Boto 설명서 사용
<a name="programming-with-python-documentation"></a>

다음 리소스로 Boto 설명서 사용을 시작하세요.
+ 패키지 설치를 위한 탄탄한 출발점 역할을 하는 [Quickstart 섹션](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html)부터 시작하세요. 아직 설치하지 않은 경우 Boto3를 설치하는 방법에 대한 지침을 보려면 Quickstart 섹션을 방문하세요(Boto3는 AWS Lambda와 같은 AWS 서비스 내에서 자동으로 제공되는 경우가 많습니다).
+ 그 다음에는 설명서의 [DynamoDB 안내서](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/dynamodb.html)를 중점적으로 살펴보세요. 테이블 생성 및 삭제, 항목 조작, 배치 작업 실행, 쿼리 실행, 스캔 수행 등 기본적인 DynamoDB 활동을 수행하는 방법을 보여줍니다. 이 예시에서는 **리소스** 인터페이스를 사용합니다. `boto3.resource('dynamodb')`가 표시되면 상위 수준 **리소스** 인터페이스를 사용하고 있다는 뜻입니다.
+ 안내서를 살펴본 후 [DynamoDB 참조](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html)를 검토할 수 있습니다. 이 랜딩 페이지는 사용 가능한 클래스와 메서드의 전체 목록을 제공합니다. 상단에 `DynamoDB.Client` 클래스가 있습니다. 이 클래스는 모든 컨트롤 플레인 및 데이터 영역 작업에 대한 하위 수준 액세스를 제공합니다. 하단에는 `DynamoDB.ServiceResource` 클래스가 있습니다. 이 클래스는 더 상위 수준의 Python 스타일 인터페이스입니다. 이를 통해 테이블을 만들거나, 테이블 전체에 걸쳐 배치 작업을 수행하거나, 테이블별 작업을 위한 `DynamoDB.ServiceResource.Table` 인스턴스를 확보할 수 있습니다.

## 클라이언트 및 리소스 추상화 계층 이해
<a name="programming-with-python-client-resource"></a>

사용할 두 인터페이스는 **클라이언트** 인터페이스와 **리소스** 인터페이스입니다.
+ 하위 수준 **클라이언트** 인터페이스는 기본 서비스 API에 대한 일대일 매핑을 제공합니다. DynamoDB에서 제공하는 모든 API는 클라이언트를 통해 사용할 수 있습니다. 즉, 클라이언트 인터페이스가 완전한 기능을 제공할 수 있지만 사용하기가 더 복잡한 경우가 많습니다.
+ 상위 수준 **리소스** 인터페이스는 기본 서비스 API의 일대일 매핑을 제공하지 않습니다. 하지만 `batch_writer`와 같이 보다 편리하게 서비스에 액세스할 수 있는 메서드를 제공합니다.

다음은 클라이언트 인터페이스를 사용하여 항목을 삽입하는 예시입니다. 모든 값이 유형(문자열의 경우 'S', 숫자의 경우 'N')을 나타내는 키와 문자열 값으로 구성된 맵으로 전달되는 것을 확인할 수 있습니다. 이를 DynamoDB JSON 형식이라고 합니다.

```
import boto3

dynamodb = boto3.client('dynamodb')

dynamodb.put_item(
    TableName='YourTableName',
    Item={
        'pk': {'S': 'id#1'},
        'sk': {'S': 'cart#123'},
        'name': {'S': 'SomeName'},
        'inventory': {'N': '500'},
        # ... more attributes ...
    }
)
```

다음은 동일한 `PutItem` 작업을 리소스 인터페이스를 사용하여 수행한 것입니다. 데이터 입력은 암시적입니다.

```
import boto3

dynamodb = boto3.resource('dynamodb')

table = dynamodb.Table('YourTableName')

table.put_item(
    Item={
        'pk': 'id#1',
        'sk': 'cart#123',
        'name': 'SomeName',
        'inventory': 500,
        # ... more attributes ...
    }
)
```

필요한 경우 boto3에서 제공하는 `TypeSerializer` 및 `TypeDeserializer` 클래스를 사용하여 일반 JSON과 DynamoDB JSON 간에 변환할 수 있습니다.

```
def dynamo_to_python(dynamo_object: dict) -> dict:
    deserializer = TypeDeserializer()
    return {
        k: deserializer.deserialize(v) 
        for k, v in dynamo_object.items()
    }  
  
def python_to_dynamo(python_object: dict) -> dict:
    serializer = TypeSerializer()
    return {
        k: serializer.serialize(v)
        for k, v in python_object.items()
    }
```

클라이언트 인터페이스를 사용하여 쿼리를 수행하는 방법은 다음과 같습니다. 쿼리를 JSON 구성으로 표현합니다. 잠재적 키워드 충돌을 처리하기 위해 변수 대체가 필요한 `KeyConditionExpression` 문자열을 사용합니다.

```
import boto3

client = boto3.client('dynamodb')

# Construct the query
response = client.query(
    TableName='YourTableName',
    KeyConditionExpression='pk = :pk_val AND begins_with(sk, :sk_val)',
    FilterExpression='#name = :name_val',
    ExpressionAttributeValues={
        ':pk_val': {'S': 'id#1'},
        ':sk_val': {'S': 'cart#'},
        ':name_val': {'S': 'SomeName'},
    },
    ExpressionAttributeNames={
        '#name': 'name',
    }
)
```

리소스 인터페이스를 사용한 동일한 쿼리 작업을 줄이고 단순화할 수 있습니다.

```
import boto3
from boto3.dynamodb.conditions import Key, Attr

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('YourTableName')

response = table.query(
    KeyConditionExpression=Key('pk').eq('id#1') & Key('sk').begins_with('cart#'),
    FilterExpression=Attr('name').eq('SomeName')
)
```

마지막 예로 테이블의 대략적인 크기(테이블에 보관되며 약 6시간마다 업데이트되는 메타데이터)를 구한다고 가정해 보겠습니다. 클라이언트 인터페이스에서 `describe_table()` 작업을 수행하고 반환된 JSON 구조에서 답을 가져옵니다.

```
import boto3

dynamodb = boto3.client('dynamodb')

response = dynamodb.describe_table(TableName='YourTableName')
size = response['Table']['TableSizeBytes']
```

리소스 인터페이스에서 테이블이 설명 작업을 암시적으로 수행하고 데이터를 속성으로 직접 표시합니다.

```
import boto3

dynamodb = boto3.resource('dynamodb')

table = dynamodb.Table('YourTableName')
size = table.table_size_bytes
```

**참고**  
개발에 클라이언트 인터페이스를 사용할지 리소스 인터페이스를 사용할지 고려할 때는 리소스 인터페이스에 신규 기능이 추가되지 않는다는 점을 유의하세요. [리소스 설명서](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/resources.html)에는 "AWS Python SDK 팀은 boto3의 리소스 인터페이스에 신규 기능을 추가할 의도가 없습니다. 기존 인터페이스는 boto3의 수명 주기 동안 계속 작동할 것입니다. 클라이언트 인터페이스를 통해 새로운 서비스 기능에 액세스할 수 있습니다.”라고 명시되어 있습니다.

## batch\$1writer 테이블 리소스 사용
<a name="programming-with-python-batch-writer"></a>

상위 수준 테이블 리소스에서만 사용할 수 있는 편리한 기능 중 하나는 `batch_writer`입니다. DynamoDB는 한 번의 네트워크 요청으로 최대 25개의 넣기 또는 삭제 작업을 허용하는 배치 쓰기 작업을 지원합니다. 이와 같은 배치 작업은 네트워크 왕복을 최소화하여 효율성을 개선합니다.

하위 수준 클라이언트 라이브러리에서는 `client.batch_write_item()` 작업을 사용하여 배치를 실행합니다. 작업을 25개씩 배치로 수동으로 분할해야 합니다. 각 작업 후에는 처리되지 않은 항목 목록도 수신을 요청해야 합니다. 쓰기 작업 중 일부는 성공하지만 다른 작업은 실패할 수 있습니다. 그런 다음 처리되지 않은 항목을 이후 `batch_write_item()` 작업으로 다시 전달해야 합니다. 보일러플레이트 코드가 상당히 많습니다.

[Table.batch\$1writer](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/table/batch_writer.html) 메서드는 객체를 배치로 작성하기 위한 컨텍스트 관리자를 만듭니다. 마치 한 번에 하나씩 항목을 쓰는 것처럼 보이지만 내부적으로는 항목을 버퍼링하고 배치로 전송하는 인터페이스를 제공합니다. 또한 처리되지 않은 항목 재시도를 암시적으로 처리합니다.

```
dynamodb = boto3.resource('dynamodb')

table = dynamodb.Table('YourTableName')

movies = # long list of movies in {'pk': 'val', 'sk': 'val', etc} format
with table.batch_writer() as writer:
    for movie in movies:
        writer.put_item(Item=movie)
```

## 클라이언트 및 리소스 계층을 탐색하는 추가 코드 예시
<a name="programming-with-python-additional-code"></a>

클라이언트와 리소스를 모두 사용하여 다양한 함수의 사용법을 탐색하는 다음 코드 샘플 리포지토리를 참조할 수도 있습니다.
+ [공식 AWS 단일 작업 코드 예시](https://docs.aws.amazon.com/code-library/latest/ug/python_3_dynamodb_code_examples.html) 
+ [공식 AWS 시나리오 지향 코드 예시](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/python)
+ [커뮤니티에서 유지 관리하는 단일 작업 코드 예시](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/examples/SDK/python)

## Client 및 Resource 객체가 세션 및 스레드와 상호 작용하는 방식 이해
<a name="programming-with-python-sessions-thread-safety"></a>

Resource 객체는 스레드로부터 안전하지 않으므로 스레드 또는 프로세스 간에 공유해서는 안 됩니다. 자세한 내용은 [Resource 가이드](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/resources.html#multithreading-or-multiprocessing-with-resources)를 참조하세요.

반면 Client 객체는 특정 고급 기능을 제외하고는 일반적으로 스레드로부터 안전합니다. 자세한 내용은 [Clients 가이드](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/clients.html#multithreading-or-multiprocessing-with-clients)를 참조하세요.

Session 객체는 스레드로부터 안전하지 않습니다. 따라서 다중 스레드 환경에서 Client나 Resource를 만들 때마다 먼저 새 Session을 만든 다음 Session에서 Client 또는 Resource를 만들어야 합니다. 자세한 내용은 [Sessions 가이드](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/session.html#multithreading-or-multiprocessing-with-sessions)를 참조하세요.

`boto3.resource()`를 직접 호출하면 암시적으로 기본 Session을 사용하게 됩니다. 이는 단일 스레드 코드를 작성할 때 편리합니다. 다중 스레드 코드를 작성할 때는 먼저 각 스레드에 대해 새 Session을 생성한 다음 해당 Session에서 리소스를 검색하는 것이 좋습니다.

```
# Explicitly create a new Session for this thread 
session = boto3.Session()
dynamodb = session.resource('dynamodb')
```

## Config 객체 사용자 지정
<a name="programming-with-python-config"></a>

Client 또는 Resource 객체를 구성할 때 선택적으로 명명된 파라미터를 전달하여 동작을 사용자 지정할 수 있습니다. `config`라는 파라미터를 사용하면 다양한 기능을 사용할 수 있습니다. 이 파라미터는 `botocore.client.Config`의 인스턴스이며 [Config에 대한 참조 설명서](https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html)에 사용자가 제어할 수 있도록 노출되는 모든 내용이 나와 있습니다. [Configuration 가이드](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html)는 개요를 알아보기에 좋습니다.

**참고**  
이러한 동작 설정의 대부분은 Session 수준에서, AWS 구성 파일 내에서 또는 환경 변수로 수정할 수 있습니다.

**제한 시간을 위한 Config**

사용자 지정 구성의 용도 중 하나는 네트워킹 동작을 조정하는 것입니다.
+ **connect\$1timeout(float 또는 int)** - 연결을 시도할 때 제한 시간 예외가 발생하기까지의 시간(초)입니다. 기본값은 60초입니다.
+ **read\$1timeout (float 또는 int)** - 연결에서 읽기를 시도할 때 제한 시간 예외가 발생하기까지의 시간(초)입니다. 기본값은 60초입니다.

DynamoDB에서는 60초의 제한 시간은 너무 깁니다. 즉, 일시적인 네트워크 결함으로 인해 재시도하기 전에 클라이언트에게 1분 정도의 지연 시간이 발생한다는 뜻입니다. 다음 코드는 제한 시간을 1초로 단축합니다.

```
import boto3
from botocore.config import Config

my_config = Config(
   connect_timeout = 1.0,
   read_timeout = 1.0
)
dynamodb = boto3.resource('dynamodb', config=my_config)
```

제한 시간에 대한 자세한 설명은 [Tuning AWS Java SDK HTTP request settings for latency-aware DynamoDB applications](https://aws.amazon.com/blogs/database/tuning-aws-java-sdk-http-request-settings-for-latency-aware-amazon-dynamodb-applications/)를 참조하세요. 참고로 Java SDK에는 Python보다 더 많은 제한 시간 구성이 있습니다.

**연결 유지를 위한 Config**

botocore 1.27.84 이상을 사용하는 경우 **TCP 연결 유지**도 제어할 수 있습니다.
+ **tcp\$1keepalive**(bool) - `False`(기본값 `True`)으로 설정된 경우 새 연결을 만들 때 사용하는 TCP 연결 유지 소켓 옵션을 활성화합니다. 이 기능은 botocore 1.27.84부터 사용할 수 있습니다.

TCP 연결 유지를 `True`로 설정하면 평균 지연 시간을 줄일 수 있습니다. 다음은 올바른 botocore 버전을 사용하는 경우 TCP 연결 유지를 조건부로 true로 설정하는 샘플 코드입니다.

```
import botocore
import boto3
from botocore.config import Config
from distutils.version import LooseVersion

required_version = "1.27.84"
current_version = botocore.__version__

my_config = Config(
   connect_timeout = 0.5,
   read_timeout = 0.5
)
if LooseVersion(current_version) > LooseVersion(required_version):
    my_config = my_config.merge(Config(tcp_keepalive = True))

dynamodb = boto3.resource('dynamodb', config=my_config)
```

**참고**  
TCP 연결 유지는 HTTP 연결 유지와 다릅니다. TCP 연결 유지를 사용하면 기본 운영 체제가 소켓 연결을 통해 작은 패킷을 전송하여 연결을 유지하고 연결 해제를 즉시 감지합니다. HTTP 연결 유지를 사용하면 기본 소켓에 구축된 웹 연결이 재사용됩니다. Boto3에서는 HTTP 연결 유지가 항상 활성화되어 있습니다.

유휴 연결을 유지할 수 있는 기간에는 한도가 있습니다. 연결이 유휴 상태이지만 다음 요청에서 이미 설정된 연결을 사용하도록 하려면 주기적으로(예: 1분마다) 요청을 보내는 것을 고려하세요.

**재시도를 위한 Config**

이 구성에서는 원하는 재시도 동작을 지정할 수 있는 **retry**라는 딕셔너리도 허용합니다. SDK에서 오류를 수신하고 오류가 일시적 유형인 경우 SDK 내에서 재시도가 발생합니다. 오류가 내부적으로 재시도되어 결국 성공적인 응답이 나오는 경우 직접 호출하는 코드의 관점에서 보면 오류가 나타나지 않고 지연 시간이 약간 길어질 뿐입니다. 지정할 수 있는 값은 다음과 같습니다.
+ **max\$1attempts** - 단일 요청에서 수행할 수 있는 최대 재시도 횟수를 나타내는 정수입니다. 예를 들어 이 값을 2로 설정하면 초기 요청 이후 요청이 최대 두 번 재시도됩니다. 이 값을 0으로 설정하면 초기 요청 이후 재시도하지 않습니다.
+ **total\$1max\$1attempts** - 단일 요청에서 수행할 수 있는 총 시도 횟수를 나타내는 정수입니다. 여기에는 초기 요청이 포함되므로 값이 1이면 요청이 재시도되지 않음을 나타냅니다. `total_max_attempts`와 `max_attempts`를 모두 제공하는 경우 `total_max_attempts`가 우선합니다. `total_max_attempts`는 `AWS_MAX_ATTEMPTS` 환경 변수 및 `max_attempts` 구성 파일 값에 매핑되기 때문에 `max_attempts`보다 우선하는 것입니다.
+ **mode** - Botocore가 사용해야 하는 재시도 모드의 유형을 나타내는 문자열입니다. 유효값은 다음과 같습니다.
  + **legacy** - 기본 모드입니다. 첫 번째 재시도를 50ms 동안 기다렸다가 기본 계수 2의 지수 백오프를 사용합니다. DynamoDB의 경우 위 값으로 재정의하지 않는 한 모두 합쳐 최대 10회까지 시도합니다.
**참고**  
지수 백오프를 사용할 경우 마지막 시도는 거의 13초간 기다리게 됩니다.
  + **standard** - 다른 AWS SDK와 더 일관적이기 때문에 표준이라는 이름이 지정되었습니다. 첫 번째 재시도를 위해 0ms에서 1,000ms 사이의 임의의 시간 동안 기다립니다. 다시 재시도해야 하는 경우 0ms에서 1,000ms 사이의 또 다른 임의의 시간을 선택하여 2를 곱합니다. 추가 재시도가 필요한 경우 같은 범위에서 임의로 선택한 시간에 4를 곱하는 식으로 반복합니다. 각 대기 시간은 20초로 제한됩니다. 이 모드는 `legacy` 모드보다 더 많이 감지된 장애 조건에 대해 재시도를 수행합니다. DynamoDB의 경우 위 값으로 재정의하지 않는 한 모두 합쳐 최대 3회까지 시도합니다.
  + **adaptive** - 표준 모드의 모든 기능을 포함하고 자동 클라이언트 측 제한도 포함하는 실험적인 재시도 모드입니다. 적응형 속도 제한을 사용하면 SDK가 요청 전송 속도를 늦춰 AWS 서비스의 요청 용량을 더 잘 수용할 수 있습니다. 이 모드는 동작이 변경될 수 있는 임시 모드입니다.

이러한 재시도 모드의 확장된 정의는 [재시도 가이드](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/retries.html) 및 [SDK 참조의 Retry behavior 주제](https://docs.aws.amazon.com/sdkref/latest/guide/feature-retry-behavior.html)에서 확인할 수 있습니다.

다음은 모두 합쳐 최대 3회의 요청(재시도 횟수 2회)을 수행하는 `legacy` 재시도 정책을 명시적으로 사용하는 예시입니다.

```
import boto3
from botocore.config import Config

my_config = Config(
   connect_timeout = 1.0,
   read_timeout = 1.0,
   retries = {
     'mode': 'legacy',
     'total_max_attempts': 3
   }
)
dynamodb = boto3.resource('dynamodb', config=my_config)
```

DynamoDB는 가용성이 높고 지연 시간이 짧은 시스템이므로 기본 제공되는 재시도 정책이 허용하는 것보다 더 공격적으로 재시도 속도를 높이는 것이 좋습니다. Boto3에 의존하여 암시적 재시도를 수행하는 대신 최대 시도 횟수를 0으로 설정하고, 직접 예외를 포착하고, 자체 코드에서 적절하게 재시도함으로써 자체 재시도 정책을 구현할 수 있습니다.

자체 재시도 정책을 관리하는 경우 제한과 오류를 구분하는 것이 좋습니다.
+ **제한**(`ProvisionedThroughputExceededException` 또는 `ThrottlingException`으로 표시됨)은 정상 서비스에서 DynamoDB 테이블 또는 파티션의 읽기 또는 쓰기 용량을 초과했음을 알리는 것입니다. 밀리초가 지날 때마다 약간 더 많은 읽기 또는 쓰기 용량을 사용할 수 있게 되므로 빠르게(예: 50ms마다) 재시도하여 새로 확보된 용량에 액세스하려고 시도할 수 있습니다. 제한을 사용하면 지수 백오프가 특별히 필요하지 않습니다. 제한은 DynamoDB가 반환하기에 가볍고 요청당 요금이 부과되지 않기 때문입니다. 지수 백오프는 이미 가장 오래 기다린 클라이언트 스레드에 더 긴 지연을 할당하여 통계적으로 p50과 p99를 밖으로 확장합니다.
+ **오류**(특히 `InternalServerError` 또는 `ServiceUnavailable`로 표시됨)는 서비스에 일시적인 문제가 있음을 나타냅니다. 테이블 전체에 대한 것일 수도 있고, 읽거나 쓰고 있는 파티션에만 해당될 수도 있습니다. 오류가 발생하면 재시도 전에 더 오래(예: 250ms 또는 500ms) 중단하고 지터를 사용하여 재시도에 시차를 줄 수 있습니다.

**최대 풀 연결을 위한 Config**

마지막으로, 구성을 통해 연결 풀 크기를 제어할 수 있습니다.
+ **max\$1pool\$1connections(int)** - 연결 풀에 유지할 수 있는 최대 연결 수입니다. 값을 설정하지 않으면 기본값 10이 사용됩니다.

이 옵션은 재사용을 위해 풀링된 상태로 유지할 최대 HTTP 연결 수를 제어합니다. Session마다 다른 풀이 보관됩니다. 동일한 Session에서 구축된 클라이언트나 리소스에 대해 스레드가 10개 이상 발생할 것으로 예상되면 스레드가 풀링된 연결을 사용하는 다른 스레드에서 대기하지 않아도 되도록 이 값을 늘리는 것이 좋습니다.

```
import boto3
from botocore.config import Config

my_config = Config(
   max_pool_connections = 20
)

# Setup a single session holding up to 20 pooled connections
session = boto3.Session(my_config)

# Create up to 20 resources against that session for handing to threads
# Notice the single-threaded access to the Session and each Resource
resource1 = session.resource('dynamodb')
resource2 = session.resource('dynamodb')
# etc
```

## 오류 처리
<a name="programming-with-python-error-handling"></a>

AWS 서비스 예외가 Boto3에서 모두 정적으로 정의되어 있지는 않습니다. 이는 AWS 서비스의 오류 및 예외가 매우 다양하고 변경될 수 있기 때문입니다. Boto3는 모든 서비스 예외를 `ClientError`로 래핑하고 세부 정보를 구조화된 JSON으로 노출합니다. 예를 들어, 오류 응답은 다음과 같이 구성될 수 있습니다.

```
{
    'Error': {
        'Code': 'SomeServiceException',
        'Message': 'Details/context around the exception or error'
    },
    'ResponseMetadata': {
        'RequestId': '1234567890ABCDEF',
        'HostId': 'host ID data will appear here as a hash',
        'HTTPStatusCode': 400,
        'HTTPHeaders': {'header metadata key/values will appear here'},
        'RetryAttempts': 0
    }
}
```

다음 코드는 모든 `ClientError` 예외를 포착하고 `Error` 내의 `Code` 문자열 값을 검토하여 취해야 할 조치를 결정합니다.

```
import botocore
import boto3

dynamodb = boto3.client('dynamodb')

try:
    response = dynamodb.put_item(...)

except botocore.exceptions.ClientError as err:
    print('Error Code: {}'.format(err.response['Error']['Code']))
    print('Error Message: {}'.format(err.response['Error']['Message']))
    print('Http Code: {}'.format(err.response['ResponseMetadata']['HTTPStatusCode']))
    print('Request ID: {}'.format(err.response['ResponseMetadata']['RequestId']))

    if err.response['Error']['Code'] in ('ProvisionedThroughputExceededException', 'ThrottlingException'):
        print("Received a throttle")
    elif err.response['Error']['Code'] == 'InternalServerError':
        print("Received a server error")
    else:
        raise err
```

전부는 아니지만 일부 예외 코드는 최상위 수준 클래스로 구체화되었습니다. 이를 직접 처리할 수도 있습니다. Client 인터페이스를 사용하는 경우 이러한 예외는 클라이언트에 동적으로 채워지며 다음과 같이 클라이언트 인스턴스를 사용하여 이러한 예외를 포착할 수 있습니다.

```
except ddb_client.exceptions.ProvisionedThroughputExceededException:
```

Resource 인터페이스를 사용할 때는 `.meta.client`를 사용하여 다음과 같이 리소스에서 기본 Client로 이동해야 예외에 액세스할 수 있습니다.

```
except ddb_resource.meta.client.exceptions.ProvisionedThroughputExceededException:
```

구체화된 예외 유형 목록을 검토하려면 목록을 동적으로 생성하면 됩니다.

```
ddb = boto3.client("dynamodb")
print([e for e in dir(ddb.exceptions) if e.endswith('Exception') or e.endswith('Error')])
```

조건식을 사용하여 쓰기 작업을 수행할 때 표현식이 실패할 경우 오류 응답에서 항목 값을 반환하도록 요청할 수 있습니다.

```
try:
    response = table.put_item(
        Item=item,
        ConditionExpression='attribute_not_exists(pk)',
        ReturnValuesOnConditionCheckFailure='ALL_OLD'
    )
except table.meta.client.exceptions.ConditionalCheckFailedException as e:
    print('Item already exists:', e.response['Item'])
```

오류 처리 및 예외에 대한 추가 정보:
+ [오류 처리에 대한 boto3 가이드](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/error-handling.html)에는 오류 처리 기법에 대한 자세한 정보가 있습니다.
+ [프로그래밍 오류에 대한 DynamoDB 개발자 안내서 섹션](Programming.Errors.md)에는 발생할 수 있는 오류가 나열되어 있습니다.
+ [API 참조의 Common Errors 섹션](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/CommonErrors.html)
+ 각 API 작업의 설명서에는 직접 호출로 인해 발생할 수 있는 오류가 나열되어 있습니다(예: [BatchWriteItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html)).

## 로깅
<a name="programming-with-python-logging"></a>

boto3 라이브러리는 Python의 기본 제공 로깅 모듈과 통합되어 세션 중에 발생하는 일을 추적합니다. 로깅 수준을 제어하기 위해 로깅 모듈을 구성할 수 있습니다.

```
import logging

logging.basicConfig(level=logging.INFO)
```

이렇게 하면 루트 로거가 `INFO` 이상 수준의 메시지를 로깅하도록 구성됩니다. 이 수준보다 덜 심각한 로깅 메시지는 무시됩니다. 로깅 수준은 `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`입니다. 기본값은 `WARNING`입니다.

Boto3의 로거는 계층적입니다. 라이브러리는 각각 라이브러리의 다른 부분에 해당하는 몇 가지 다른 로거를 사용합니다. 각 로거의 동작을 개별적으로 제어할 수 있습니다.
+ **boto3**: boto3 모듈의 기본 로거입니다.
+ **botocore**: botocore 패키지의 메인 로거입니다.
+ **botocore.auth**: 요청에 대한 AWS 서명 생성을 로깅하는 데 사용됩니다.
+ **botocore.credentials**: 자격 증명 가져오기 및 새로 고침 프로세스를 로깅하는 데 사용됩니다.
+ **botocore.endpoint**: 네트워크를 통해 전송되기 전에 요청 생성을 로깅하는 데 사용됩니다.
+ **botocore.hooks**: 라이브러리에서 트리거되는 이벤트를 로깅하는 데 사용됩니다.
+ **botocore.loaders**: AWS 서비스 모델의 일부가 로드될 때 로깅하는 데 사용됩니다.
+ **botocore.parsers**: AWS 서비스 응답을 구문 분석하기 전에 로깅하는 데 사용됩니다.
+ **botocore.retryhandler**: AWS 서비스 요청 재시도 처리를 로깅하는 데 사용됩니다(레거시 모드).
+ **botocore.retries.standard**: AWS 서비스 요청 재시도 처리를 로깅하는 데 사용됩니다(표준 또는 적응형 모드).
+ **botocore.utils**: 라이브러리의 기타 활동을 로깅하는 데 사용됩니다.
+ **botocore.waiter**: 특정 상태에 도달할 때까지 AWS 서비스를 폴링하는 웨이터의 기능을 로깅하는 데 사용됩니다.

다른 라이브러리도 로깅합니다. 내부적으로 boto3는 HTTP 연결 처리를 위해 서드 파티 urllib3를 사용합니다. 지연 시간이 중요한 경우 urllib3가 언제 새 연결을 설정하거나 유휴 연결을 종료하는지 확인하여 풀을 제대로 활용하고 있는지 확인할 수 있습니다.
+ **urllib3.connectionpool**: 연결 풀 처리 이벤트를 로깅하는 데 사용합니다.

다음 코드 스니펫은 엔드포인트 및 연결 풀 활동에 대한 `DEBUG` 로깅과 함께 대부분의 로깅을 `INFO`로 설정합니다.

```
import logging

logging.getLogger('boto3').setLevel(logging.INFO)
logging.getLogger('botocore').setLevel(logging.INFO)
logging.getLogger('botocore.endpoint').setLevel(logging.DEBUG)
logging.getLogger('urllib3.connectionpool').setLevel(logging.DEBUG)
```

## 이벤트 후크
<a name="programming-with-python-event-hooks"></a>

Botocore는 실행의 여러 부분에서 이벤트를 내보냅니다. 이러한 이벤트에 대한 핸들러를 등록하여 이벤트가 발생할 때마다 핸들러가 직접 호출되도록 할 수 있습니다. 이렇게 하면 내부를 수정하지 않고도 botocore의 동작을 확장할 수 있습니다.

예를 들어 애플리케이션의 DynamoDB 테이블에서 `PutItem` 작업이 직접 호출될 때마다 추적하고 싶다고 가정해 보겠습니다. 관련 Session에서 `PutItem` 작업이 간접 호출될 때마다 `'provide-client-params.dynamodb.PutItem'` 이벤트를 등록하여 포착하고 로깅할 수 있습니다. 다음은 그 예입니다.

```
import boto3
import botocore
import logging

def log_put_params(params, **kwargs):
    if 'TableName' in params and 'Item' in params:
        logging.info(f"PutItem on table {params['TableName']}: {params['Item']}")

logging.basicConfig(level=logging.INFO)

session = boto3.Session()
event_system = session.events

# Register our interest in hooking in when the parameters are provided to PutItem
event_system.register('provide-client-params.dynamodb.PutItem', log_put_params)

# Now, every time you use this session to put an item in DynamoDB,
# it will log the table name and item data.
dynamodb = session.resource('dynamodb')
table = dynamodb.Table('YourTableName')
table.put_item(
    Item={
        'pk': '123',
        'sk': 'cart#123',
        'item_data': 'YourItemData',
        # ... more attributes ...
    }
)
```

핸들러 내에서 파라미터를 프로그래밍 방식으로 조작하여 동작을 변경할 수도 있습니다.

```
params['TableName'] = "NewTableName"
```

이벤트에 대한 자세한 내용은 [이벤트에 대한 botocore 설명서](https://botocore.amazonaws.com/v1/documentation/api/latest/topics/events.html) 및 [이벤트에 대한 boto3 설명서](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/events.html)를 참조하세요.

## 페이지 매김 및 페이지네이터
<a name="programming-with-python-pagination"></a>

쿼리 및 스캔과 같은 일부 요청의 경우 단일 요청에서 반환되는 데이터 크기가 제한되므로 후속 페이지를 가져오려면 반복적으로 요청해야 합니다.

`limit` 파라미터를 사용하여 각 페이지에 대해 읽을 항목의 최대 수를 제어할 수 있습니다. 예를 들어, 마지막 10개의 항목을 원하는 경우 `limit`을 사용하여 마지막 10개만 검색할 수 있습니다. 이 제한은 필터링을 적용하기 전에 테이블에서 읽어야 하는 분량이라는 것을 참고하세요. 필터링 후 정확히 10개를 원한다고 지정할 수 있는 방법은 없습니다. 필터링 전의 개수를 제어하고 실제로 10개 항목을 검색한 경우에만 클라이언트 측에서 확인할 수 있습니다. 제한과 관계없이 모든 응답의 최대 크기는 항상 1MB입니다.

응답에 `LastEvaluatedKey`가 포함된 경우 개수 또는 크기 제한에 도달하여 응답이 종료되었음을 나타냅니다. 이 키는 해당 응답에 대해 마지막으로 평가된 키입니다. 이 `LastEvaluatedKey`를 검색하고 후속 직접 호출에 `ExclusiveStartKey`로 전달하여 해당 시작 지점부터 다음 청크를 읽을 수 있습니다. 이것을 반환한 `LastEvaluatedKey`가 없으면 Query 또는 Scan과 일치하는 항목이 더 이상 없음을 의미합니다.

다음은 Resource 인터페이스를 사용하지만 Client 인터페이스의 패턴이 같은 간단한 예시로, 페이지당 최대 100개의 항목을 읽고 모든 항목을 읽을 때까지 반복합니다.

```
import boto3

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('YourTableName')

query_params = {
    'KeyConditionExpression': Key('pk').eq('123') & Key('sk').gt(1000),
    'Limit': 100
}

while True:
    response = table.query(**query_params)

    # Process the items however you like
    for item in response['Items']:
        print(item)

    # No LastEvaluatedKey means no more items to retrieve
    if 'LastEvaluatedKey' not in response:
        break

    # If there are possibly more items, update the start key for the next page
    query_params['ExclusiveStartKey'] = response['LastEvaluatedKey']
```

편의를 위해 boto3에서는 페이지네이터를 사용하여 이 작업을 대신 수행할 수 있습니다. 하지만 이 기능은 Client 인터페이스에서만 작동합니다. 페이지네이터를 사용하도록 다시 작성된 코드는 다음과 같습니다.

```
import boto3

dynamodb = boto3.client('dynamodb')

paginator = dynamodb.get_paginator('query')

query_params = {
    'TableName': 'YourTableName',
    'KeyConditionExpression': 'pk = :pk_val AND sk > :sk_val',
    'ExpressionAttributeValues': {
        ':pk_val': {'S': '123'},
        ':sk_val': {'N': '1000'},
    },
    'Limit': 100
}

page_iterator = paginator.paginate(**query_params)

for page in page_iterator:
    # Process the items however you like
    for item in page['Items']:
        print(item)
```

자세한 내용은 [페이지네이터 가이드](https://botocore.amazonaws.com/v1/documentation/api/latest/topics/events.html) 및 [DynamoDB.Paginator.Query에 대한 API 참조](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/paginator/Query.html)를 확인하세요.

**참고**  
페이지네이터에는 `MaxItems`, `StartingToken`, `PageSize`라는 자체 구성 설정도 있습니다. DynamoDB로 페이지를 매기는 경우 이러한 설정을 무시해야 합니다.

## Waiters
<a name="programming-with-python-waiters"></a>

웨이터는 작업이 완료될 때까지 기다렸다가 진행하는 기능을 제공합니다. 현재는 테이블이 생성되거나 삭제될 때까지 기다리는 것만 지원합니다. 백그라운드에서 웨이터 작업은 20초마다 최대 25회까지 사용자를 대신하여 확인을 수행합니다. 이 작업은 직접 할 수도 있지만, 자동화를 작성할 때는 웨이터를 사용하는 것이 좋습니다.

다음 코드는 특정 테이블이 생성될 때까지 기다리는 방법을 보여줍니다.

```
# Create a table, wait until it exists, and print its ARN
response = client.create_table(...)
waiter = client.get_waiter('table_exists')
waiter.wait(TableName='YourTableName')
print('Table created:', response['TableDescription']['TableArn']
```

자세한 내용은 [웨이터 가이드](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/clients.html#waiters) 및 [웨이터에 대한 참조](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#waiters)를 확인하세요.

# JavaScript를 사용한 Amazon DynamoDB 프로그래밍
<a name="programming-with-javascript"></a>

이 안내서는 JavaScript를 통해 Amazon DynamoDB를 사용하려는 프로그래머에게 지침을 제공합니다. AWS SDK for JavaScript, 사용 가능한 추상화 계층, 연결 구성, 오류 처리, 재시도 정책 정의, 연결 유지 관리 등에 대해 알아봅니다.

**Topics**
+ [AWS SDK for JavaScript 정보](#programming-with-javascript-about)
+ [AWS SDK for JavaScript V3 사용](#programming-with-javascript-using-the-sdk)
+ [JavScript 설명서 액세스](#programming-with-javascript-documentation)
+ [추상화 계층](#programming-with-javascript-abstraction-layers)
+ [마샬 유틸리티 함수 사용](#programming-with-javascript-using-marshall-utility)
+ [항목 읽기](#programming-with-javascript-reading-items)
+ [조건부 쓰기](#programming-with-javascript-conditional-writes)
+ [페이지 매김](#programming-with-javascript-pagination)
+ [구성 지정](#programming-with-javascript-config)
+ [Waiters](#programming-with-javascript-waiters)
+ [오류 처리](#programming-with-javascript-error-handling)
+ [로깅](#programming-with-javascript-logging)
+ [고려 사항](#programming-with-javascript-considerations)

## AWS SDK for JavaScript 정보
<a name="programming-with-javascript-about"></a>

AWS SDK for JavaScript에서는 브라우저 스크립트 또는 Node.js를 사용하여 AWS 서비스에 액세스할 수 있습니다. 이 문서에서는 최신 버전의 SDK(V3)에 중점을 둡니다. AWS SDK for JavaScript V3는 [GitHub에서 호스팅되는 오픈 소스 프로젝트](https://github.com/aws/aws-sdk-js-v3)로 AWS에서 유지 관리합니다. 문제 및 기능 요청은 공개되며 GitHub 리포지토리의 문제 페이지에서 액세스할 수 있습니다.

JavaScript V2는 V3와 비슷하지만 구문 차이가 있습니다. V3는 더 모듈화되어 더 작은 종속성을 더 쉽게 제공할 수 있으며 최고 수준의 TypeScript를 지원합니다. SDK는 최신 버전을 사용하는 것이 좋습니다.

## AWS SDK for JavaScript V3 사용
<a name="programming-with-javascript-using-the-sdk"></a>

노드 패키지 관리자를 사용하여 Node.js 애플리케이션에 SDK를 추가할 수 있습니다. 아래 예는 DynamoDB 작업을 위한 가장 일반적인 SDK 패키지를 추가하는 방법을 보여줍니다.
+ `npm install @aws-sdk/client-dynamodb`
+ `npm install @aws-sdk/lib-dynamodb`
+ `npm install @aws-sdk/util-dynamodb`

패키지를 설치하면 package.json 프로젝트 파일의 종속성 섹션에 대한 참조가 추가됩니다. 최신 ECMAScript 모듈 구문을 사용할 수 있는 옵션이 있습니다. 이 두 가지 접근 방식에 대한 자세한 내용은 고려 사항 섹션을 참조하세요.

## JavScript 설명서 액세스
<a name="programming-with-javascript-documentation"></a>

다음 리소스로 JavaScript 설명서 사용을 시작하세요.
+ 핵심 JavaScript 설명서를 보려면 [개발자 안내서](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/welcome.html)를 참조하세요. 설치 지침은 **설정** 섹션에 있습니다.
+ [API 참조](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/introduction/) 설명서에 액세스하여 사용 가능한 모든 클래스와 메서드를 살펴보세요.
+ SDK for JavaScript는 DynamoDB 외에도 많은 AWS 서비스를 지원합니다. DynamoDB에 대한 구체적인 API 적용 범위를 찾으려면 다음 절차를 따르세요.

  1. **서비스**에서 **DynamoDB 및 라이브러리**를 선택합니다. 이는 하위 수준 클라이언트를 문서화합니다.

  1. **lib-dynamodb**를 선택합니다. 이는 상위 수준 클라이언트를 문서화합니다. 두 클라이언트는 사용자가 사용하기 위해 선택할 수 있는 두 가지 추상화 계층을 나타냅니다. 추상화 계층에 대한 자세한 내용은 아래 섹션을 참조하세요.

## 추상화 계층
<a name="programming-with-javascript-abstraction-layers"></a>

SDK for JavaScript V3에는 하위 수준 클라이언트(`DynamoDBClient`)와 상위 수준 클라이언트(`DynamoDBDocumentClient`)가 있습니다.

**Topics**
+ [하위 수준 클라이언트(`DynamoDBClient`)](#programming-with-javascript-low-level-client)
+ [상위 수준 클라이언트(`DynamoDBDocumentClient`)](#programming-with-javascript-high-level-client)

### 하위 수준 클라이언트(`DynamoDBClient`)
<a name="programming-with-javascript-low-level-client"></a>

하위 수준 클라이언트는 기본 유선 프로토콜에 대한 추가 추상화를 제공하지 않습니다. 통신의 모든 측면을 완벽하게 제어할 수 있지만 추상화가 없으므로 DynamoDB JSON 형식을 사용하여 항목 정의를 제공하는 등의 작업을 수행해야 합니다.

아래 예시에서 볼 수 있듯이 이 형식을 사용할 경우 데이터 유형을 명시적으로 지정해야 합니다. **S는 문자열 값을 나타내고 **N은 숫자 값을 나타냅니다. 회선의 숫자는 정밀도가 떨어지지 않도록 항상 숫자 유형 태그가 지정된 문자열로 전송됩니다. 하위 수준 API 직접 호출에는 `PutItemCommand` 및 `GetItemCommand`와 같은 이름 지정 패턴이 있습니다.

다음은 DynamoDB JSON을 사용하여 `Item`을 정의한 하위 수준 클라이언트를 사용하는 예시입니다.

```
const { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb");

const client = new DynamoDBClient({});

async function addProduct() {
  const params = {
    TableName: "products",
    Item: {
      "id": { S: "Product01" },
      "description": { S: "Hiking Boots" },
      "category": { S: "footwear" },
      "sku": { S: "hiking-sku-01" },
      "size": { N: "9" }
    }
  };

  try {
    const data = await client.send(new PutItemCommand(params));
    console.log('result : ' + JSON.stringify(data));
  } catch (error) {
    console.error("Error:", error);
  }
}
addProduct();
```

### 상위 수준 클라이언트(`DynamoDBDocumentClient`)
<a name="programming-with-javascript-high-level-client"></a>

상위 수준 DynamoDB 문서 클라이언트는 데이터를 수동으로 마샬링할 필요가 없고 표준 JavaScript 객체를 사용하여 직접 읽고 쓸 수 있는 등 편리한 기능을 기본적으로 제공합니다. [`lib-dynamodb` 설명서](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-lib-dynamodb/)에는 장점 목록이 나와 있습니다.

`DynamoDBDocumentClient`를 인스턴스화하려면 하위 수준 `DynamoDBClient`를 구성한 다음 `DynamoDBDocumentClient`로 래핑하세요. 함수 명명 규칙은 두 패키지가 약간 다릅니다. 예를 들어, 하위 수준에서는 `PutItemCommand`를, 상위 수준에서는 `PutCommand`를 사용합니다. 이름이 서로 다르기 때문에 두 함수가 같은 컨텍스트에서 공존할 수 있으므로 동일한 스크립트에서 두 함수를 혼용할 수 있습니다.

```
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, PutCommand } = require("@aws-sdk/lib-dynamodb");

const client = new DynamoDBClient({});

const docClient = DynamoDBDocumentClient.from(client);

async function addProduct() {
  const params = {
    TableName: "products",
    Item: {
      id: "Product01",
      description: "Hiking Boots",
      category: "footwear",
      sku: "hiking-sku-01",
      size: 9,
    },
  };

  try {
    const data = await docClient.send(new PutCommand(params));
    console.log('result : ' + JSON.stringify(data));
  } catch (error) {
    console.error("Error:", error);
  }
}

addProduct();
```

`GetItem`, `Query` 또는 `Scan` 같은 API 작업을 사용하여 항목을 읽을 때 사용 패턴이 일관됩니다.

## 마샬 유틸리티 함수 사용
<a name="programming-with-javascript-using-marshall-utility"></a>

하위 수준 클라이언트를 사용하고 직접 데이터 유형을 마샬링하거나 언마셜링할 수 있습니다. 유틸리티 패키지인 [util-dynamodb](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-util-dynamodb/)에는 JSON을 받아들이고 DynamoDB JSON을 생성하는 `marshall()` 유틸리티 함수와 그 반대의 작업을 수행하는 `unmarshall()` 함수가 있습니다. 다음 예시에서는 `marshall()` 직접 호출로 데이터 마샬링을 처리하는 하위 수준 클라이언트를 사용합니다.

```
const { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb");
const { marshall } = require("@aws-sdk/util-dynamodb");

const client = new DynamoDBClient({});

async function addProduct() {
  const params = {
    TableName: "products",
    Item: marshall({
      id: "Product01",
      description: "Hiking Boots",
      category: "footwear",
      sku: "hiking-sku-01",
      size: 9,
    }),
  };

  try {
    const data = await client.send(new PutItemCommand(params));
  } catch (error) {
    console.error("Error:", error);
  }
}
addProduct();
```

## 항목 읽기
<a name="programming-with-javascript-reading-items"></a>

DynamoDB에서 단일 항목을 읽으려면 `GetItem` API 작업을 사용합니다. `PutItem` 명령과 마찬가지로 하위 수준 클라이언트 또는 상위 수준 Document 클라이언트를 사용할 수 있습니다. 아래 예시는 상위 수준 Document 클라이언트를 사용하여 항목을 가져오는 방법을 보여줍니다.

```
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, GetCommand } = require("@aws-sdk/lib-dynamodb");

const client = new DynamoDBClient({});

const docClient = DynamoDBDocumentClient.from(client);

async function getProduct() {
  const params = {
    TableName: "products",
    Key: {
      id: "Product01",
    },
  };

  try {
    const data = await docClient.send(new GetCommand(params));
    console.log('result : ' + JSON.stringify(data));
  } catch (error) {
    console.error("Error:", error);
  }
}

getProduct();
```

`Query` API 작업을 사용하여 여러 항목을 읽을 수 있습니다. 하위 수준 클라이언트 또는 Document 클라이언트를 사용할 수 있습니다. 아래 예에서는 상위 수준 Document 클라이언트를 사용합니다.

```
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const {
  DynamoDBDocumentClient,
  QueryCommand,
} = require("@aws-sdk/lib-dynamodb");

const client = new DynamoDBClient({});

const docClient = DynamoDBDocumentClient.from(client);

async function productSearch() {
  const params = {
    TableName: "products",
    IndexName: "GSI1",
    KeyConditionExpression: "#category = :category and begins_with(#sku, :sku)",
    ExpressionAttributeNames: {
      "#category": "category",
      "#sku": "sku",
    },
    ExpressionAttributeValues: {
      ":category": "footwear",
      ":sku": "hiking",
    },
  };

  try {
    const data = await docClient.send(new QueryCommand(params));
    console.log('result : ' + JSON.stringify(data));
  } catch (error) {
    console.error("Error:", error);
  }
}

productSearch();
```

## 조건부 쓰기
<a name="programming-with-javascript-conditional-writes"></a>

DynamoDB 쓰기 작업은 쓰기가 진행되려면 true로 평가되어야 하는 논리적 조건식을 지정할 수 있습니다. 조건이 true로 평가되지 않으면 쓰기 작업에서 예외가 발생합니다. 조건 표현식은 항목이 이미 존재하는지 또는 항목의 속성이 특정 제약 조건과 일치하는지 확인할 수 있습니다.

`ConditionExpression = "version = :ver AND size(VideoClip) < :maxsize" `

조건식이 실패할 경우 `ReturnValuesOnConditionCheckFailure`를 사용하여 조건을 충족하지 못한 항목을 오류 응답에 포함하도록 요청하여 문제가 무엇인지 추론할 수 있습니다. 자세한 내용은 [Handle conditional write errors in high concurrency scenarios with Amazon DynamoDB](https://aws.amazon.com/blogs/database/handle-conditional-write-errors-in-high-concurrency-scenarios-with-amazon-dynamodb/)를 참조하세요.

```
try {
      const response = await client.send(new PutCommand({
          TableName: "YourTableName",
          Item: item,
          ConditionExpression: "attribute_not_exists(pk)",
          ReturnValuesOnConditionCheckFailure: "ALL_OLD"
      }));
  } catch (e) {
      if (e.name === 'ConditionalCheckFailedException') {
          console.log('Item already exists:', e.Item);
      } else {
          throw e;
      }
  }
```

JavaScript SDK V3 사용의 다른 측면을 보여주는 추가 코드 예시는 [JavaScript SDK V3 설명서](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/javascript_dynamodb_code_examples.html) 및 [DynamoDB-SDK-Examples GitHub 리포지토리](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/examples/SDK/node.js)에서 확인할 수 있습니다.

## 페이지 매김
<a name="programming-with-javascript-pagination"></a>

**Topics**
+ [`paginateScan` 편의 메서드 사용](#using-the-paginatescan-convenience-method)

`Scan` 또는 `Query`와 같은 읽기 요청은 데이터세트의 여러 항목을 반환할 가능성이 큽니다. `Limit` 파라미터와 함께 `Scan` 또는 `Query`를 수행하는 경우 시스템에서 해당 수의 항목을 읽은 후 부분적인 응답이 전송되므로 추가 항목을 가져오려면 페이지를 매겨야 합니다.

시스템은 요청당 최대 1메가바이트의 데이터만 읽습니다. `Filter` 표현식을 포함하는 경우 시스템은 여전히 디스크에서 최대 1메가바이트의 데이터를 읽지만 해당 메가바이트에서 필터와 일치하는 항목을 반환합니다. 필터 작업 시 한 페이지에 0개의 항목이 반환되어도 검색이 모두 끝나려면 페이지 매김을 더 해야 할 수도 있습니다.

데이터 검색을 계속하려면 응답에서 `LastEvaluatedKey`를 찾아 후속 요청에서 `ExclusiveStartKey` 파라미터로 사용해야 합니다. 이는 다음 예에서 볼 수 있듯이 북마크 역할을 합니다.

**참고**  
샘플은 첫 번째 반복에서와 같이 null `lastEvaluatedKey`를 `ExclusiveStartKey`로 전달하는데 이 동작은 허용됩니다.

`LastEvaluatedKey` 사용 예시:

```
const { DynamoDBClient, ScanCommand } = require("@aws-sdk/client-dynamodb");

const client = new DynamoDBClient({});

async function paginatedScan() {
  let lastEvaluatedKey;
  let pageCount = 0;

  do {
    const params = {
      TableName: "products",
      ExclusiveStartKey: lastEvaluatedKey,
    };

    const response = await client.send(new ScanCommand(params));
    pageCount++;
    console.log(`Page ${pageCount}, Items:`, response.Items);
    lastEvaluatedKey = response.LastEvaluatedKey;
  } while (lastEvaluatedKey);
}

paginatedScan().catch((err) => {
  console.error(err);
});
```

### `paginateScan` 편의 메서드 사용
<a name="using-the-paginatescan-convenience-method"></a>



SDK는 이 작업을 사용자를 대신해서 수행하고 백그라운드에서 반복적인 요청을 수행하는 편의 메서드 `paginateScan` 및 `paginateQuery`를 제공합니다. 표준 `Limit` 파라미터를 사용하여 요청당 읽을 수 있는 최대 항목 수를 지정합니다.

```
const { DynamoDBClient, paginateScan } = require("@aws-sdk/client-dynamodb");

const client = new DynamoDBClient({});

async function paginatedScanUsingPaginator() {
  const params = {
    TableName: "products",
    Limit: 100
  };

  const paginator = paginateScan({client}, params);

  let pageCount = 0;

  for await (const page of paginator) {
    pageCount++;
    console.log(`Page ${pageCount}, Items:`, page.Items);
  }
}

paginatedScanUsingPaginator().catch((err) => {
  console.error(err);
});
```

**참고**  
테이블이 작지 않은 이상 전체 테이블 스캔을 정기적으로 수행하는 것은 권장되지 않는 액세스 패턴입니다.

## 구성 지정
<a name="programming-with-javascript-config"></a>

**Topics**
+ [제한 시간을 위한 Config](#programming-with-javascript-config-timeouts)
+ [연결 유지를 위한 Config](#programming-with-javascript-config-keep-alive)
+ [재시도를 위한 Config](#programming-with-javascript-config-retries)

`DynamoDBClient`를 설정할 때 구성 객체를 생성자에 전달하여 다양한 구성 재정의를 지정할 수 있습니다. 예를 들어, 직접 호출하는 컨텍스트나 사용할 엔드포인트 URL에 아직 알려지지 않은 경우 연결할 리전을 지정할 수 있습니다. 이는 DynamoDB 로컬 인스턴스를 개발 목적으로 대상으로 지정하려는 경우에 유용합니다.

```
const client = new DynamoDBClient({
  region: "eu-west-1",
  endpoint: "http://localhost:8000",
});
```

### 제한 시간을 위한 Config
<a name="programming-with-javascript-config-timeouts"></a>

DynamoDB는 클라이언트와 서버 간 통신을 위해 HTTPS를 사용합니다. `NodeHttpHandler` 객체를 제공하여 HTTP 계층의 일부 측면을 제어할 수 있습니다. 예를 들어, 키 제한 시간 값 `connectionTimeout` 및 `requestTimeout`을 조정할 수 있습니다. `connectionTimeout`은 클라이언트가 연결을 포기하기 전까지 연결을 시도하는 동안 기다리는 최대 시간(밀리초)입니다.

`requestTimeout`은 요청이 전송된 후 클라이언트가 응답을 기다리는 시간(밀리초)입니다. 둘 다 기본값은 0입니다. 즉, 제한 시간이 비활성화되어 있고 응답이 도착하지 않을 경우 클라이언트가 대기하는 시간에는 제한이 없다는 뜻입니다. 네트워크 문제 발생 시 요청에 오류가 발생하여 새 요청을 시작할 수 있도록 제한 시간을 적절한 수준으로 설정해야 합니다. 예제:

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { NodeHttpHandler } from "@smithy/node-http-handler";

const requestHandler = new NodeHttpHandler({
  connectionTimeout: 2000,
  requestTimeout: 2000,
});

const client = new DynamoDBClient({
  requestHandler
});
```

**참고**  
제공된 예시에서는 [Smithy](https://smithy.io/2.0/index.html) 가져오기를 사용합니다. Smithy는 오픈 소스이며 AWS가 유지 관리하며 서비스와 SDK를 정의하는 언어입니다.

제한 시간 값을 구성하는 것 외에도 최대 소켓 수를 설정하여 오리진당 동시 연결 수를 늘릴 수 있습니다. 개발자 안내서에는 [`maxSockets` 파라미터 구성에 대한 세부 정보](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html)가 포함되어 있습니다.

### 연결 유지를 위한 Config
<a name="programming-with-javascript-config-keep-alive"></a>

HTTPS를 사용하는 경우 첫 번째 요청은 보안 연결을 설정하기 위해 항상 주고받는 통신이 필요합니다. HTTP 연결 유지를 사용하면 후속 요청에서 이미 설정된 연결을 재사용할 수 있으므로 요청의 효율성을 높이고 지연 시간을 줄일 수 있습니다. JavaScript V3에서는 HTTP 연결 유지가 기본적으로 활성화되어 있습니다.

유휴 연결을 유지할 수 있는 기간에는 한도가 있습니다. 연결이 유휴 상태이지만 다음 요청에서 이미 설정된 연결을 사용하도록 하려면 주기적으로(예: 1분마다) 요청을 보내는 것을 고려하세요.

**참고**  
참고로 SDK의 이전 V2에서는 연결 유지가 기본적으로 꺼져 있었습니다. 즉, 각 연결은 사용 후 즉시 닫혔습니다. V2를 사용하는 경우 이 설정을 재정의할 수 있습니다.

### 재시도를 위한 Config
<a name="programming-with-javascript-config-retries"></a>

SDK에서 오류 응답을 수신하고 SDK의 판단에 따라 오류를 재개할 수 있는 경우(예: 제한 예외 또는 임시 서비스 예외) SDK는 다시 재시도합니다. 요청이 성공하는 데 시간이 더 오래 걸렸다는 점을 제외하면 호출자 입장에서는 이러한 상황이 눈에 띄지 않게 발생합니다.

SDK for JavaScript V3는 기본적으로 총 3번의 요청을 한 후 포기하고 직접 호출하는 컨텍스트로 오류를 전달합니다. 이러한 재시도 횟수와 빈도를 조정할 수 있습니다.

`DynamoDBClient` 생성자는 시도 횟수를 제한하는 `maxAttempts` 설정을 허용합니다. 아래 예시는 값을 기본값인 3에서 총 5로 올립니다. 0 또는 1로 설정하면 자동 재시도를 원하지 않으며 catch 블록 내에서 재개 가능한 오류를 직접 처리하고 싶다는 뜻입니다.

```
const client = new DynamoDBClient({
  maxAttempts: 5,
});
```

사용자 지정 재시도 전략을 사용하여 재시도 타이밍을 제어할 수도 있습니다. 이렇게 하려면 `util-retry` 유틸리티 패키지를 가져와서 현재 재시도 횟수를 기반으로 재시도 간 대기 시간을 계산하는 사용자 지정 백오프 함수를 만드세요.

아래 예시에서는 첫 번째 시도가 실패할 경우 지연 시간이 15, 30, 90, 360밀리초인 상태에서 최대 5회 시도하도록 되어 있습니다. 사용자 지정 백오프 함수인 ` calculateRetryBackoff`는 재시도 횟수(첫 번째 재시도는 1로 시작)를 수락하여 지연을 계산하고 해당 요청을 기다리는 데 걸리는 시간(밀리초)을 반환합니다.

```
const { ConfiguredRetryStrategy } = require("@aws-sdk/util-retry");

const calculateRetryBackoff = (attempt) => {
  const backoffTimes = [15, 30, 90, 360];
  return backoffTimes[attempt - 1] || 0;
};

const client = new DynamoDBClient({
  retryStrategy: new ConfiguredRetryStrategy(
    5, // max attempts.
    calculateRetryBackoff // backoff function.
  ),
});
```

## Waiters
<a name="programming-with-javascript-waiters"></a>

DynamoDB 클라이언트에는 테이블을 생성, 수정 또는 삭제할 때 테이블 수정이 완료될 때까지 코드가 진행되기를 기다리는 데 사용할 수 있는 두 가지 유용한 [웨이터 함수](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/dynamodb/wait/index.html#cli-aws-dynamodb-wait)가 포함되어 있습니다. 예를 들어 테이블을 배포하고 `waitUntilTableExists` 함수를 호출하면 테이블이 **활성** 상태가 될 때까지 코드가 차단됩니다. 웨이터는 20초마다 `describe-table`을 사용하여 DynamoDB 서비스를 내부적으로 폴링합니다.

```
import {waitUntilTableExists, waitUntilTableNotExists} from "@aws-sdk/client-dynamodb";

… <create table details>

const results = await waitUntilTableExists({client: client, maxWaitTime: 180}, {TableName: "products"});
if (results.state == 'SUCCESS') {
  return results.reason.Table
}
console.error(`${results.state} ${results.reason}`);
```

`waitUntilTableExists` 기능은 테이블 상태를 **활성**으로 표시하는 `describe-table` 명령을 수행할 수 있는 경우에만 제어권을 반환합니다. 이렇게 하면 `waitUntilTableExists`를 사용하여 테이블 생성뿐만 아니라 GSI 인덱스 추가와 같은 수정이 완료될 때까지 기다릴 수 있습니다. 이때 테이블이 **활성** 상태로 돌아가려면 적용하는 데 시간이 다소 걸릴 수 있습니다.

## 오류 처리
<a name="programming-with-javascript-error-handling"></a>

여기의 초기 예시에서는 모든 오류를 광범위하게 찾아냈습니다. 하지만 실제 상황에서는 다양한 오류 유형을 구분하고 보다 정확한 오류 처리를 구현하는 것이 중요합니다.

DynamoDB 오류 응답에는 오류 이름을 비롯한 메타데이터가 포함됩니다. 오류를 발견한 다음 오류 조건에 해당할 수 있는 문자열 이름을 비교하여 처리 방법을 결정할 수 있습니다. 서버 측 오류의 경우 `@aws-sdk/client-dynamodb` 패키지에서 내보낸 오류 유형과 함께 `instanceof` 연산자를 활용하여 오류 처리를 효율적으로 관리할 수 있습니다.

이러한 오류는 모든 재시도를 모두 완료한 후에만 발생한다는 점에 유의해야 합니다. 오류가 재시도되어 결국 성공적인 직접 호출이 발생하는 경우 코드의 관점에서 보면 오류가 나타나지 않고 지연 시간이 약간 길어질 뿐입니다. 재시도는 Amazon CloudWatch 차트에 제한 또는 오류 요청과 같은 실패한 요청으로 표시됩니다. 클라이언트가 최대 재시도 횟수에 도달하면 이를 포기하고 예외가 발생합니다. 이는 클라이언트가 재시도하지 않겠다고 말하는 방식입니다.

다음은 오류를 포착하고 반환된 오류 유형에 따라 조치를 취하는 스니펫입니다.

```
import {
  ResourceNotFoundException
  ProvisionedThroughputExceededException,
  DynamoDBServiceException,
} from "@aws-sdk/client-dynamodb";

try {
  await client.send(someCommand);
} catch (e) {
    if (e instanceof ResourceNotFoundException) {
      // Handle ResourceNotFoundException
    } else if (e instanceof ProvisionedThroughputExceededException) {
      // Handle ProvisionedThroughputExceededException
    } else if (e instanceof DynamoDBServiceException) {
      // Handle DynamoDBServiceException
    } else {
      // Other errors such as those from the SDK
      if (e.name === "TimeoutError") {
        // Handle SDK TimeoutError.
      } else {
        // Handle other errors.
      }
    }
}
```

**DynamoDB 개발자 안내서의 일반적인 오류 문자열은 [DynamoDB 관련 오류 처리](Programming.Errors.md) 섹션을 참조하세요. 특정 API 직접 호출에서 발생할 수 있는 정확한 오류는 해당 API 직접 호출에 대한 설명서(예: [Query API 문서](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html))에서 찾을 수 있습니다.

오류의 메타데이터에는 오류에 따라 추가 속성이 포함됩니다. ` TimeoutError`의 경우 메타데이터에는 아래와 같이 시도한 횟수와 `totalRetryDelay`가 포함됩니다.

```
{
  "name": "TimeoutError",
  "$metadata": {
    "attempts": 3,
    "totalRetryDelay": 199
  }
}
```

자체 재시도 정책을 관리하는 경우 제한과 오류를 구분하는 것이 좋습니다.
+ **제한**(` ProvisionedThroughputExceededException` 또는 `ThrottlingException`으로 표시됨)은 정상 서비스에서 DynamoDB 테이블 또는 파티션의 읽기 또는 쓰기 용량을 초과했음을 알리는 것입니다. 밀리초가 지날 때마다 약간 더 많은 읽기 또는 쓰기 용량을 사용할 수 있게 되므로 빠르게(예: 50ms마다) 재시도하여 새로 확보된 용량에 액세스하려고 시도할 수 있습니다.

   제한을 사용하면 지수 백오프가 특별히 필요하지 않습니다. 제한은 DynamoDB가 반환하기에 가볍고 요청당 요금이 부과되지 않기 때문입니다. 지수 백오프는 이미 가장 오래 기다린 클라이언트 스레드에 더 긴 지연을 할당하여 통계적으로 p50과 p99를 밖으로 확장합니다.
+ **오류**(특히 ` InternalServerError` 또는 `ServiceUnavailable`로 표시됨)는 서비스에 일시적인 문제가 있음을 나타냅니다. 전체 테이블에 문제가 있을 수도 있고, 읽거나 쓰고 있는 파티션에만 문제가 있을 수도 있습니다. 오류가 발생하면 재시도 전에 더 오래(예: 250ms 또는 500ms) 중단하고 지터를 사용하여 재시도에 시차를 줄 수 있습니다.

## 로깅
<a name="programming-with-javascript-logging"></a>

로깅을 켜면 SDK가 수행하는 작업에 대한 자세한 내용을 확인할 수 있습니다. 아래 예시와 같이 `DynamoDBClient`에서 파라미터를 설정할 수 있습니다. 콘솔에 더 많은 로그 정보가 표시되며 여기에는 상태 코드 및 사용된 용량과 같은 메타데이터가 포함됩니다. 터미널 창에서 로컬로 코드를 실행하면 로그가 해당 창에 나타납니다. AWS Lambda에서 코드를 실행하고 Amazon CloudWatch 로그를 설정하면 콘솔 출력이 해당 로그에 기록됩니다.

```
const client = new DynamoDBClient({
  logger: console
});
```

또한 내부 SDK 활동에 연결하여 특정 이벤트가 발생할 때 사용자 지정 로깅을 수행할 수 있습니다. 아래 예시는 클라이언트의 `middlewareStack`을 사용하여 SDK에서 전송되는 각 요청을 가로채고 요청이 발생하는 대로 로깅합니다.

```
const client = new DynamoDBClient({});

client.middlewareStack.add(
  (next) => async (args) => {
    console.log("Sending request from AWS SDK", { request: args.request });
    return next(args);
  },
  {
    step: "build",
    name: "log-ddb-calls",
  }
);
```

`MiddlewareStack`은 SDK 동작을 관찰하고 제어할 수 있는 강력한 후크를 제공합니다. 자세한 내용은 [Introducing Middleware Stack in Modular AWS SDK for JavaScript](https://aws.amazon.com/blogs/developer/middleware-stack-modular-aws-sdk-js/) 블로그를 참조하세요.

## 고려 사항
<a name="programming-with-javascript-considerations"></a>

프로젝트에 AWS SDK for JavaScript를 구현할 때 다음과 같이 추가로 고려해야 할 요소가 있습니다.

**모듈 시스템**  
SDK는 CommonJS와 ES(ECMAScript)라는 두 가지 모듈 시스템을 지원합니다. CommonJS는 `require` 함수를 사용하고 ES는 `import` 키워드를 사용합니다.  

1. **Common JS** – `const { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb");`

1. **ES(ECMAScript**)`import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";` – 
프로젝트 유형에 따라 사용할 모듈 시스템이 결정되며 package.json 파일의 유형 섹션에 명시되어 있습니다. 기본값은 CommonJS입니다. ES 프로젝트를 나타내려면 `"type": "module"`을 사용합니다. CommonJS 패키지 형식을 사용하는 기존 Node.JS 프로젝트가 있는 경우에도 함수 파일 이름을 .mjs 확장자로 지정하여 최신 SDK V3 Import 구문으로 함수를 추가할 수 있습니다. 이렇게 하면 코드 파일을 ES(ECMAScript)로 처리할 수 있습니다.

**비동기식 운영**  
콜백을 사용하고 DynamoDB 작업의 결과를 처리하도록 약속하는 많은 코드 샘플을 보게 될 것입니다. 최신 JavaScript를 사용하면 이러한 복잡성이 더 이상 필요하지 않으며 개발자는 비동기식 작업에 더 간결하고 읽기 쉬운 async/await 구문을 활용할 수 있습니다.

**웹 브라우저 런타임**  
React 또는 React Native로 빌드하는 웹 및 모바일 개발자는 프로젝트에서 SDK for JavaScript를 사용할 수 있습니다. SDK의 초기 V2에서는 웹 개발자가 https://sdk.amazonaws.com/js/에서 호스팅되는 SDK 이미지를 참조하여 전체 SDK를 브라우저에 로드해야 했습니다.  
V3를 사용하면 SDK 설명서의 [Getting started in a browser script](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/getting-started-browser.html) 섹션에 설명된 대로 Webpack을 사용하여 필수 V3 클라이언트 모듈과 모든 필수 JavaScript 함수를 단일 JavaScript 파일로 번들링하고 이를 HTML 페이지의 `<head>`에 있는 스크립트 태그에 추가할 수 있습니다

**DAX 데이터 영역 작업**  
Amazon DynamoDB Streams Accelerator(DAX) 데이터 플레인 작업은 SDK for JavaScript V3에서 지원되지 않습니다.

# AWS SDK for Java 2.x를 사용한 DynamoDB 프로그래밍
<a name="ProgrammingWithJava"></a>

이 프로그래밍 안내서는 Java를 통해 Amazon DynamoDB를 사용하려는 프로그래머에게 지침을 제공합니다. 이 안내서는 추상화 계층, 구성 관리, 오류 처리, 재시도 정책 제어, 연결 유지 관리와 같은 다양한 개념을 다룹니다.

**Topics**
+ [AWS SDK for Java 2.x 소개](#AboutProgrammingWithJavaSDK)
+ [시작하기](#GetStartedProgrammingWithJavaSDK)
+ [SDK for Java 2.x 설명서](#ProgrammingWithJavaUseDoc)
+ [지원되는 인터페이스](#JavaInterfaces)
+ [추가 코드 예시](#AdditionalCodeEx)
+ [동기식 및 비동기식 프로그래밍](#SyncAsyncProgramming)
+ [HTTP 클라이언트](#HttpClients)
+ [구성](#ConfigHttpClient)
+ [오류 처리](#JavaErrorHandling)
+ [AWS 요청 ID](#JavaRequestID)
+ [로깅](#JavaLogging)
+ [페이지 매김](#JavaPagination)
+ [데이터 클래스 주석](#JavaDataClassAnnotation)

## AWS SDK for Java 2.x 소개
<a name="AboutProgrammingWithJavaSDK"></a>

공식 AWS SDK for Java를 사용하여 Java에서 DynamoDB에 액세스할 수 있습니다. SDK for Java에는 1.x와 2.x의 두 가지 버전이 있습니다. 1.x의 경우 2024년 1월 12일에 지원 종료가 [발표되었습니다](https://aws.amazon.com/blogs/developer/announcing-end-of-support-for-aws-sdk-for-java-v1-x-on-december-31-2025/). 2024년 7월 31일에 유지 관리 모드로 전환될 계획이며, 2025년 12월 31일에 지원이 종료될 예정입니다. 새 개발의 경우 2018년에 처음 릴리스된 2.x를 사용하는 것이 좋습니다. 이 안내서는 2.x만을 대상으로 하며 SDK에서 DynamoDB와 관련된 부분에만 초점을 맞춥니다.

AWS SDK에 대한 유지 관리 및 지원에 대한 자세한 내용은 **AWS SDK 및 도구 참조 안내서의 [AWS SDK and Tools maintenance policy](https://docs.aws.amazon.com/sdkref/latest/guide/maint-policy.html) 및 [AWS SDKs and Tools version support matrix](https://docs.aws.amazon.com/sdkref/latest/guide/version-support-matrix.html)를 참조하세요.

AWS SDK for Java 2.x에서 1.x 코드 베이스 상당수를 다시 작성했습니다. SDK for Java 2.x는 Java 8에 도입된 비차단 I/O와 같은 최신 Java 기능을 지원합니다. 또한, SDK for Java 2.x는 플러그형 HTTP 클라이언트 구현에 대한 지원을 추가하여 보다 많은 네트워크 연결 유연성과 구성 옵션을 제공합니다.

SDK for Java 1.x와 비교할 때 SDK for Java 2.x의 눈에 띄는 변화는 새로운 패키지 이름 사용입니다. Java 1.x SDK는 `com.amazonaws` 패키지 이름을 사용하는 반면 Java 2.x SDK는 `software.amazon.awssdk` 패키지 이름을 사용합니다. 마찬가지로 Java 1.x SDK용 Maven 아티팩트는 `com.amazonaws` `groupId`를 사용하는 반면, Java 2.x SDK 아티팩트는 `software.amazon.awssdk` `groupId`를 사용합니다.

**중요**  
AWS SDK for Java 1.x에는 이름이 `com.amazonaws.dynamodbv2`인 DynamoDB 패키지가 있습니다. 패키지 이름의 'v2'는 해당 패키지가 Java 2(J2SE) 용이라는 의미가 아닙니다. 'v2'는 패키지가 하위 수준 API의 [원래 버전](Appendix.APIv20111205.md) 대신 DynamoDB 하위 수준 API의 [두 번째 버전](CurrentAPI.md)을 지원함을 나타냅니다.

### Java 버전 지원
<a name="SupportedJavaVersions"></a>

AWS SDK for Java 2.x는 장기 지원(LTS) [Java 릴리스](https://github.com/aws/aws-sdk-java-v2?tab=readme-ov-file#maintenance-and-support-for-java-versions)를 모두 지원합니다.

## AWS SDK for Java 2.x 시작하기
<a name="GetStartedProgrammingWithJavaSDK"></a>

다음 자습서에서는 [Apache Maven](https://maven.apache.org/)을 사용하여 SDK for Java 2.x에 대한 종속성을 정의하는 방법을 보여줍니다. 또한 이 자습서에서는 사용 가능한 DynamoDB 테이블을 나열하기 위해 DynamoDB에 연결하는 코드를 작성하는 방법도 보여줍니다. 이 안내서의 자습서는 **AWS SDK for Java 2.x 개발자 안내서에 있는 [Get started with the AWS SDK for Java 2.x](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html) 자습서를 기반으로 합니다. Amazon S3 대신 DynamoDB를 직접 호출하도록 이 자습서를 편집했습니다.

**Topics**
+ [1단계: 튜토리얼 설정](#GetStartedJavaSetup)
+ [2단계: 프로젝트 생성](#GetStartedJavaProjectSetup)
+ [3단계: 코드 작성](#GetStartedJavaCode)
+ [4단계: 애플리케이션 빌드 및 실행](#GetStartedRunJava)

### 1단계: 튜토리얼 설정
<a name="GetStartedJavaSetup"></a>

이 튜토리얼을 시작하기 전에 다음이 필요합니다.
+ DynamoDB에 액세스할 수 있는 권한
+ AWS 액세스 포털을 사용하여 AWS 서비스에 대한 SSO(Single Sign-On) 액세스로 구성된 Java 개발 환경

이 자습서를 설정하려면 **AWS SDK for Java 2.x 개발자 안내서의 [설정 개요](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-overview)에 있는 지침을 따르세요. Java SDK에 대한 [SSO(Single Sign-On) 액세스로 개발 환경을 구성](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-credentials)하고 [활성 AWS 액세스 포털 세션](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-login-sso)이 있는 경우 이 자습서의 [2단계](#GetStartedJavaProjectSetup)로 계속 진행합니다.

### 2단계: 프로젝트 생성
<a name="GetStartedJavaProjectSetup"></a>

이 자습서의 프로젝트를 생성하려면 프로젝트 구성 방법에 대한 입력을 요청하는 Maven 명령을 실행합니다. 모든 입력이 입력되고 확인되면 Maven은 `pom.xml` 파일을 생성하여 프로젝트 빌드를 완료하고 스텁 Java 파일을 생성합니다.

1. 터미널 또는 명령 프롬프트 창을 열고 원하는 디렉터리 (예: `Desktop` 또는 `Home` 폴더)로 이동합니다.

1. 터미널에서 다음 명령을 입력하고 **Enter** 키를 누릅니다.

   ```
   mvn archetype:generate \
      -DarchetypeGroupId=software.amazon.awssdk \
      -DarchetypeArtifactId=archetype-app-quickstart \
      -DarchetypeVersion=2.22.0
   ```

1. 각 프롬프트의 두 번째 열에 나열된 값을 입력합니다.    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/ProgrammingWithJava.html)

1. 마지막 값을 입력하면 Maven에서 선택한 항목을 나열합니다. 확인하려면 **Y**를 입력합니다. 아니면 **N**을 입력한 다음 선택 사항을 다시 입력합니다.

Maven은 입력한 `artifactId` 값을 기반으로 이름이 `getstarted`로 지정된 프로젝트 폴더를 만듭니다. `getstarted` 폴더 안에서 검토할 수 있는 `README.md`라는 이름의 파일, `pom.xml` 파일, `src` 디렉터리를 찾습니다.

Maven은 다음과 같은 디렉터리 트리를 만듭니다.

```
getstarted
 ├── README.md
 ├── pom.xml
 └── src
     ├── main
     │   ├── java
     │   │   └── org
     │   │       └── example
     │   │           ├── App.java
     │   │           ├── DependencyFactory.java
     │   │           └── Handler.java
     │   └── resources
     │       └── simplelogger.properties
     └── test
         └── java
             └── org
                 └── example
                     └── HandlerTest.java
 
 10 directories, 7 files
```

다음은 `pom.xml` 프로젝트 파일의 콘텐츠를 보여줍니다.

#### `pom.xml`
<a name="ProjectSetupCollapse2"></a>

`dependencyManagement` 섹션은 AWS SDK for Java 2.x에 대한 종속성을 포함하며 `dependencies` 섹션에는 DynamoDB에 대한 종속성이 있습니다. 이러한 종속성을 지정하면 Maven이 관련 `.jar` 파일을 Java 클래스 경로에 포함하도록 강제합니다. 기본적으로 AWS SDK에는 모든 AWS 서비스에 대한 클래스가 모두 포함되어 있지는 않습니다. DynamoDB의 경우 하위 수준 인터페이스를 사용하면 `dynamodb` 아티팩트에 대한 종속성이 있어야 합니다. 또는 상위 수준 인터페이스를 사용하는 경우 `dynamodb-enhanced` 아티팩트에 종속적이어야 합니다 관련 종속성을 포함하지 않으면 코드가 컴파일되지 않습니다. 프로젝트는 `maven.compiler.source` 및 `maven.compiler.target` 속성의 `1.8` 값 때문에 Java 1.8을 사용합니다.

```
<?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
     <groupId>org.example</groupId>
     <artifactId>getstarted</artifactId>
     <version>1.0-SNAPSHOT</version>
     <packaging>jar</packaging>
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <maven.compiler.source>1.8</maven.compiler.source>
         <maven.compiler.target>1.8</maven.compiler.target>
         <maven.shade.plugin.version>3.2.1</maven.shade.plugin.version>
         <maven.compiler.plugin.version>3.6.1</maven.compiler.plugin.version>
         <exec-maven-plugin.version>1.6.0</exec-maven-plugin.version>
         <aws.java.sdk.version>2.22.0</aws.java.sdk.version> <-------- SDK version picked up from archetype version.
         <slf4j.version>1.7.28</slf4j.version>
         <junit5.version>5.8.1</junit5.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>dynamodb</artifactId>  <-------- DynamoDB dependency
             <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>
 
         <dependency>
             <groupId>software.amazon.awssdk</groupId>
             <artifactId>sso</artifactId> <-------- Required for identity center authentication.
         </dependency>
 
         <dependency>
             <groupId>software.amazon.awssdk</groupId>
             <artifactId>ssooidc</artifactId> <-------- Required for identity center authentication.
         </dependency>
 
         <dependency>
             <groupId>software.amazon.awssdk</groupId>
             <artifactId>apache-client</artifactId> <-------- HTTP client specified.
             <exclusions>
                 <exclusion>
                     <groupId>commons-logging</groupId>
                     <artifactId>commons-logging</artifactId>
                 </exclusion>
             </exclusions>
         </dependency>
 
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
             <version>${slf4j.version}</version>
         </dependency>
 
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-simple</artifactId>
             <version>${slf4j.version}</version>
         </dependency>
 
         <!-- Needed to adapt Apache Commons Logging used by Apache HTTP Client to Slf4j to avoid
         ClassNotFoundException: org.apache.commons.logging.impl.LogFactoryImpl during runtime -->
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>jcl-over-slf4j</artifactId>
             <version>${slf4j.version}</version>
         </dependency>
 
         <!-- Test Dependencies -->
         <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter</artifactId>
             <version>${junit5.version}</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
 
     <build>
         <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
                 <version>${maven.compiler.plugin.version}</version>
             </plugin>
         </plugins>
     </build>
 
 </project>
```

### 3단계: 코드 작성
<a name="GetStartedJavaCode"></a>

다음 코드는 Maven이 생성한 `App` 클래스를 보여줍니다. `main` 메서드는 `Handler` 클래스의 인스턴스를 만든 다음 해당 `sendRequest` 메서드를 호출하는 애플리케이션의 진입점입니다.

#### `App` 클래스
<a name="projectsetup-collapse2"></a>

```
package org.example;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class App {
     private static final Logger logger = LoggerFactory.getLogger(App.class);
 
     public static void main(String... args) {
         logger.info("Application starts");
 
         Handler handler = new Handler();
         handler.sendRequest();
 
         logger.info("Application ends");
     }
 }
```

Maven에서 만든 `DependencyFactory` 클래스에는 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html) 인스턴스를 빌드하고 반환하는 `dynamoDbClient` 팩토리 메서드가 포함되어 있습니다. `DynamoDbClient` 인스턴스는 Apache 기반 HTTP 클라이언트의 인스턴스를 사용합니다. 이는 Maven에서 사용할 HTTP 클라이언트를 묻는 메시지가 표시될 때 사용자가 `apache-client`를 지정했기 때문입니다.

다음 코드는 `DependencyFactory` 클래스를 보여줍니다.

#### DependencyFactory 클래스
<a name="code-collapse2"></a>

```
package org.example;
 
 import software.amazon.awssdk.http.apache.ApacheHttpClient;
 import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
 
 /**
  * The module containing all dependencies required by the {@link Handler}.
  */
 public class DependencyFactory {
 
     private DependencyFactory() {}
 
     /**
      * @return an instance of DynamoDbClient
      */
     public static DynamoDbClient dynamoDbClient() {
         return DynamoDbClient.builder()
                        .httpClientBuilder(ApacheHttpClient.builder())
                        .build();
     }
 }
```

`Handler` 클래스에는 프로그램의 기본 로직이 들어 있습니다. `App` 클래스에서 `Handler` 인스턴스가 생성되면 `DependencyFactory`는 `DynamoDbClient` 서비스 클라이언트를 제공합니다. 코드는 `DynamoDbClient` 인스턴스를 사용하여 DynamoDB를 직접 호출합니다.

Maven은 `TODO` 주석과 함께 다음과 같은 `Handler` 클래스를 생성합니다. 자습서의 다음 단계에서는 *`TODO`* 주석을 코드로 대체합니다.

#### Maven에서 생성한 `Handler` 클래스
<a name="code-collapsible3"></a>

```
package org.example;
 
 import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
 
 
 public class Handler {
     private final DynamoDbClient dynamoDbClient;
 
     public Handler() {
         dynamoDbClient = DependencyFactory.dynamoDbClient();
     }
 
     public void sendRequest() {
         // TODO: invoking the API calls using dynamoDbClient.
     }
 }
```

로직을 채우려면 `Handler` 클래스의 전체 내용을 다음 코드로 바꾸세요. `sendRequest` 메서드가 채워지고 필요한 임포트가 추가됩니다.

#### `Handler` 클래스 구현됨
<a name="code-collapse4"></a>

다음 코드는 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html) 인스턴스를 사용하여 기존 테이블 목록을 검색합니다. 지정된 계정 및 AWS 리전에 대한 테이블이 있는 경우 코드는 `Logger` 인스턴스를 사용하여 이러한 테이블의 이름을 로깅합니다.

```
package org.example;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
 import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse;
 
 
 public class Handler {
     private final DynamoDbClient dynamoDbClient;
 
     public Handler() {
         dynamoDbClient = DependencyFactory.dynamoDbClient();
     }
 
     public void sendRequest() {
         Logger logger = LoggerFactory.getLogger(Handler.class);
 
         logger.info("calling the DynamoDB API to get a list of existing tables");
         ListTablesResponse response = dynamoDbClient.listTables();
 
         if (!response.hasTableNames()) {
             logger.info("No existing tables found for the configured account & region");
         } else {
             response.tableNames().forEach(tableName -> logger.info("Table: " + tableName));
         }
     }
 }
```

### 4단계: 애플리케이션 빌드 및 실행
<a name="GetStartedRunJava"></a>

프로젝트가 생성되고 전체 `Handler` 클래스가 포함된 후 애플리케이션을 빌드하고 실행합니다.

1. AWS IAM Identity Center 세션이 활성화되어 있는지 확인합니다. 확인하려면 AWS Command Line Interface(AWS CLI) 명령 `aws sts get-caller-identity`를 실행하고 응답을 점검하세요. 활성 세션이 없는 경우 [AWS CLI를 사용하여 로그인](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-login-sso)에서 지침을 확인하세요.

1. 터미널 또는 명령 프롬프트 창을 열고 프로젝트 디렉토리 `getstarted`로 이동합니다.

1. 프로젝트를 빌드하려면 다음 명령을 실행합니다.

   ```
   mvn clean package
   ```

1. 애플리케이션을 실행하려면 다음 명령을 사용합니다.

   ```
   mvn exec:java -Dexec.mainClass="org.example.App"
   ```

파일을 확인한 후 객체를 삭제한 다음 버킷을 삭제합니다.

#### Success
<a name="GetStartedSuccessJava"></a>

Maven 프로젝트가 오류 없이 빌드되고 실행되었다면 축하합니다. SDK for Java 2.x를 사용한 첫 Java 애플리케이션 빌드에 성공했습니다.

#### 정리
<a name="GetStartedCleanupJava"></a>

이 자습서를 진행하는 동안 만든 리소스를 정리하려면 `getstarted` 프로젝트 폴더를 삭제합니다.

## AWS SDK for Java 2.x 문서 검토
<a name="ProgrammingWithJavaUseDoc"></a>

[AWS SDK for Java 2.x 개발자 안내서](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/home.html)에서는 전 AWS 서비스에 걸쳐 SDK의 모든 측면을 전체적으로 다룹니다. 다음 주제를 검토하는 것이 좋습니다.
+ [Migrate from version 1.x to 2.x](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/migration.html) - 1.x와 2.x의 차이점에 대한 자세한 설명이 포함되어 있습니다. 이 주제에는 두 주요 버전을 나란히 사용하는 방법에 대한 지침도 포함되어 있습니다.
+ [DynamoDB guide for Java 2.x SDK](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/examples-dynamodb.html) - 테이블 생성, 항목 조작, 항목 검색 등 기본적인 DynamoDB 작업을 수행하는 방법을 보여줍니다. 이 예에서는 하위 수준 인터페이스를 사용합니다. Java에는 [지원되는 인터페이스](#JavaInterfaces) 섹션에 설명된 대로 여러 인터페이스가 있습니다.

**작은 정보**  
이러한 주제를 검토한 후 [AWS SDK for Java 2.x API 참조](https://sdk.amazonaws.com/java/api/latest/)를 북마크하세요. 모든 AWS 서비스를 다루며 기본 API 참조로 사용하는 것이 좋습니다.

## 지원되는 인터페이스
<a name="JavaInterfaces"></a>

AWS SDK for Java 2.x는 원하는 추상화 수준에 따라 다음 인터페이스를 지원합니다.

**Topics**
+ [하위 수준 인터페이스](#LowLevelInterface)
+ [상위 수준 인터페이스](#HighLevelInterface)
+ [문서 인터페이스](#DocumentInterface)
+ [`Query` 예제와 인터페이스 비교](#CompareJavaInterfacesQueryEx)

### 하위 수준 인터페이스
<a name="LowLevelInterface"></a>

하위 수준 인터페이스는 기본 서비스 API에 대한 일대일 매핑을 제공합니다. 이 인터페이스를 통해 모든 DynamoDB API를 사용할 수 있습니다. 즉, 하위 수준 인터페이스가 완전한 기능을 제공할 수 있지만 사용하기가 더 복잡한 경우가 많습니다. 예를 들어, `.s()` 함수를 사용하여 문자열을 저장하고 `.n()` 함수를 사용하여 숫자를 저장합니다. 다음 [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html) 예시는 하위 수준 인터페이스를 사용하여 항목을 삽입합니다.

```
import org.slf4j.*;
import software.amazon.awssdk.http.crt.AwsCrtHttpClient;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;

import java.util.Map;

public class PutItem {

    // Create a DynamoDB client with the default settings connected to the DynamoDB
    // endpoint in the default region based on the default credentials provider chain.
    private static final DynamoDbClient DYNAMODB_CLIENT = DynamoDbClient.create();
    private static final Logger LOGGER = LoggerFactory.getLogger(PutItem.class);

    private void putItem() {
        PutItemResponse response = DYNAMODB_CLIENT.putItem(PutItemRequest.builder()
                .item(Map.of(
                        "pk", AttributeValue.builder().s("123").build(),
                        "sk", AttributeValue.builder().s("cart#123").build(),
                        "item_data", AttributeValue.builder().s("YourItemData").build(),
                        "inventory", AttributeValue.builder().n("500").build()
                        // ... more attributes ...
                ))
                .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
                .tableName("YourTableName")
                .build());
        LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)");
    }
}
```

### 상위 수준 인터페이스
<a name="HighLevelInterface"></a>

AWS SDK for Java 2.x의 상위 수준 인터페이스를 DynamoDB 향상된 클라이언트라고 합니다. 이 인터페이스는 보다 관용적인 코드 작성 경험을 제공합니다.

향상된 클라이언트는 클라이언트 측 데이터 클래스와 해당 데이터를 저장하도록 설계된 DynamoDB 테이블 간에 매핑하는 방법을 제공합니다. 코드에서 테이블과 해당 모델 클래스 간의 관계를 정의합니다. 그러면 SDK를 사용하여 데이터 유형 조작을 관리할 수 있습니다. 향상된 클라이언트에 대한 자세한 내용은 **AWS SDK for Java 2.x 개발자 안내서의 [DynamoDB enhanced client API](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/dynamodb-enhanced-client.html)를 참조하세요.

다음 [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html) 예시에서는 상위 수준 인터페이스를 사용합니다. 이 예제에서는 `YourItem`이라는 `DynamoDbBean`이 `TableSchema`를 만들어 `putItem()` 직접 호출의 입력으로 바로 사용할 수 있도록 합니다.

```
import org.slf4j.*;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.*;
import software.amazon.awssdk.enhanced.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity;

public class DynamoDbEnhancedClientPutItem {
    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<YourItem> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.fromBean(YourItem.class));
    private static final Logger LOGGER = LoggerFactory.getLogger(PutItem.class);

    private void putItem() {
        PutItemEnhancedResponse<YourItem> response = DYNAMODB_TABLE.putItemWithResponse(PutItemEnhancedRequest.builder(YourItem.class)
                .item(new YourItem("123", "cart#123", "YourItemData", 500))
                .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
                .build());
        LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)");
    }

    @DynamoDbBean
    public static class YourItem {

        public YourItem() {}

        public YourItem(String pk, String sk, String itemData, int inventory) {
            this.pk = pk;
            this.sk = sk;
            this.itemData = itemData;
            this.inventory = inventory;
        }

        private String pk;
        private String sk;
        private String itemData;

        private int inventory;

        @DynamoDbPartitionKey
        public void setPk(String pk) {
            this.pk = pk;
        }

        public String getPk() {
            return pk;
        }

        @DynamoDbSortKey
        public void setSk(String sk) {
            this.sk = sk;
        }

        public String getSk() {
            return sk;
        }

        public void setItemData(String itemData) {
            this.itemData = itemData;
        }

        public String getItemData() {
            return itemData;
        }

        public void setInventory(int inventory) {
            this.inventory = inventory;
        }

        public int getInventory() {
            return inventory;
        }
    }
}
```

AWS SDK for Java 1.x에는 자체 상위 수준 인터페이스가 있으며, 이 인터페이스는 주로 기본 클래스 `DynamoDBMapper`에서 참조합니다. AWS SDK for Java 2.x는 `software.amazon.awssdk.enhanced.dynamodb`라는 별도의 패키지(및 Maven 아티팩트)에 게시됩니다. Java 2.x SDK는 주로 기본 클래스 `DynamoDbEnhancedClient`에서 참조합니다.

#### 변경할 수 없는 데이터 클래스를 사용하는 상위 수준 인터페이스
<a name="HighLevelInterfaceImmutableDataClasses"></a>

DynamoDB 향상된 클라이언트 API의 매핑 기능은 변경 불가능한 데이터 클래스와도 함께 작동합니다. 불변 클래스에는 접근자만 포함되며 SDK가 클래스의 인스턴스를 생성하는 데 사용하는 빌더 클래스가 필요합니다. Java의 불변성은 개발자가 부작용이 없는 클래스를 만드는 데 사용할 수 있는 일반적으로 사용되는 스타일입니다. 복잡한 멀티스레드 애플리케이션에서는 이러한 클래스의 동작을 더 잘 예측할 수 있습니다. 변경 불가능한 클래스는 [High-level interface example](#highleveleg)에 나온 대로 `@DynamoDbBean` 주석을 사용하는 대신 빌더 클래스를 입력으로 사용하는 `@DynamoDbImmutable` 주석을 사용합니다.

다음 예시에서는 빌더 클래스 `DynamoDbEnhancedClientImmutablePutItem`을 입력으로 사용하여 테이블 스키마를 생성합니다. 그런 다음 예시는 [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html) API 직접 호출을 위한 입력으로 해당 스키마를 제공합니다.

```
import org.slf4j.*;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity;

public class DynamoDbEnhancedClientImmutablePutItem {
    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<YourImmutableItem> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.fromImmutableClass(YourImmutableItem.class));
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedClientImmutablePutItem.class);

    private void putItem() {
        PutItemEnhancedResponse<YourImmutableItem> response = DYNAMODB_TABLE.putItemWithResponse(PutItemEnhancedRequest.builder(YourImmutableItem.class)
                .item(YourImmutableItem.builder()
                                        .pk("123")
                                        .sk("cart#123")
                                        .itemData("YourItemData")
                                        .inventory(500)
                                        .build())
                .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
                .build());
        LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)");
    }
}
```

다음 예시에서는 변경 불가능한 데이터 클래스를 보여줍니다.

```
@DynamoDbImmutable(builder = YourImmutableItem.YourImmutableItemBuilder.class)
class YourImmutableItem {
    private final String pk;
    private final String sk;
    private final String itemData;
    private final int inventory;
    public YourImmutableItem(YourImmutableItemBuilder builder) {
        this.pk = builder.pk;
        this.sk = builder.sk;
        this.itemData = builder.itemData;
        this.inventory = builder.inventory;
    }

    public static YourImmutableItemBuilder builder() { return new YourImmutableItemBuilder(); }

    @DynamoDbPartitionKey
    public String getPk() {
        return pk;
    }

    @DynamoDbSortKey
    public String getSk() {
        return sk;
    }

    public String getItemData() {
        return itemData;
    }

    public int getInventory() {
        return inventory;
    }

    static final class YourImmutableItemBuilder {
        private String pk;
        private String sk;
        private String itemData;
        private int inventory;

        private YourImmutableItemBuilder() {}

        public YourImmutableItemBuilder pk(String pk) { this.pk = pk; return this; }
        public YourImmutableItemBuilder sk(String sk) { this.sk = sk; return this; }
        public YourImmutableItemBuilder itemData(String itemData) { this.itemData = itemData; return this; }
        public YourImmutableItemBuilder inventory(int inventory) { this.inventory = inventory; return this; }

        public YourImmutableItem build() { return new YourImmutableItem(this); }
    }
}
```

#### 변경 불가능한 데이터 클래스와 서드 파티 보일러플레이트 생성 라이브러리를 사용하는 상위 수준 인터페이스
<a name="ImmutableDataClassesThirdPartyBoilerplateGenLib"></a>

이전 예제에서 언급한 변경 불가능한 데이터 클래스에는 몇 가지 보일러플레이트 코드가 필요합니다. 예를 들어, `Builder` 클래스 외에 데이터 클래스의 게터 및 세터 로직이 필요합니다. [Project Lombok](https://projectlombok.org/)과 같은 서드 파티 라이브러리는 이러한 유형의 보일러플레이트 코드를 생성하는 데 도움이 될 수 있습니다. 대부분의 보일러플레이트 코드를 줄이면 변경 불가능한 데이터 클래스와 AWS SDK를 사용하는 데 필요한 코드의 양을 제한할 수 있습니다. 이를 통해 코드의 생산성과 가독성이 더욱 향상됩니다. **AWS SDK for Java 2.x 자세한 내용은 개발자 안내서의 [Lombok과 같은 타사 라이브러리를 사용](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-use-immut.html#ddb-en-client-use-immut-lombok)을 참조하세요.

다음 예시는 Project Lombok이 DynamoDB 향상된 클라이언트 API를 사용하는 데 필요한 코드를 간소화하는 방법을 보여줍니다.

```
import org.slf4j.*;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity;

public class DynamoDbEnhancedClientImmutableLombokPutItem {

    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<YourImmutableLombokItem> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.fromImmutableClass(YourImmutableLombokItem.class));
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedClientImmutableLombokPutItem.class);

    private void putItem() {
        PutItemEnhancedResponse<YourImmutableLombokItem> response = DYNAMODB_TABLE.putItemWithResponse(PutItemEnhancedRequest.builder(YourImmutableLombokItem.class)
                .item(YourImmutableLombokItem.builder()
                        .pk("123")
                        .sk("cart#123")
                        .itemData("YourItemData")
                        .inventory(500)
                        .build())
                .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
                .build());
        LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)");
    }
}
```

다음 예시에서는 변경 불가능한 데이터 클래스의 변경 불가능한 데이터 객체를 보여줍니다.

```
import lombok.*;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.*;

@Builder
@DynamoDbImmutable(builder = YourImmutableLombokItem.YourImmutableLombokItemBuilder.class)
@Value
public class YourImmutableLombokItem {

    @Getter(onMethod_=@DynamoDbPartitionKey)
    String pk;
    @Getter(onMethod_=@DynamoDbSortKey)
    String sk;
    String itemData;
    int inventory;
}
```

`YourImmutableLombokItem` 클래스는 Project Lombok 및 AWS SDK에서 제공하는 다음과 같은 주석을 사용합니다.
+ [@Builder](https://projectlombok.org/features/Builder) - Project Lombok에서 제공하는 데이터 클래스를 위한 복잡한 빌더 API를 생성합니다.
+ [@DynamoDbImmutable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/annotations/DynamoDbImmutable.html) - `DynamoDbImmutable` 클래스를 AWS SDK에서 제공하는 DynamoDB 매핑 가능한 엔터티 주석으로 식별합니다.
+ [@Value](https://projectlombok.org/features/Value) - `@Data`의 변경할 수 없는 변형입니다. 기본적으로 모든 필드는 비공개 및 최종본으로 설정되고 세터가 생성되지 않습니다. Project Lombok은 이 주석을 제공합니다.

### 문서 인터페이스
<a name="DocumentInterface"></a>

AWS SDK for Java 2.x 문서 인터페이스를 사용하면 데이터 유형 설명자를 지정할 필요가 없습니다. 데이터 형식은 데이터 자체의 의미론으로 암시됩니다. 이 문서 인터페이스는 AWS SDK for Java 1.x, 문서 인터페이스와 비슷하지만, 인터페이스가 새롭게 디자인되었습니다.

다음 [Document interface example](#DocInterfaceEg)는 Document 인터페이스를 사용하여 표현된 `PutItem` 직접 호출을 보여줍니다. 이 예시에서는 EnhancedDocument도 사용합니다. 향상된 문서 API를 사용하여 DynamoDB 테이블에 대해 명령을 실행하려면 먼저 테이블을 문서 테이블 스키마와 연결하여 `DynamoDBTable` 리소스 개체를 생성해야 합니다. 문서 테이블 스키마 빌더에는 기본 인덱스 키와 속성 변환기 제공자가 필요합니다.

`AttributeConverterProvider.defaultProvider()`를 사용하여 기본 유형의 문서 속성을 변환할 수 있습니다. 사용자 지정 `AttributeConverterProvider` 구현으로 전체 기본 동작을 변경할 수 있습니다. 단일 속성의 변환기를 변경할 수도 있습니다. [AWS SDK 및 도구 참조 안내서](https://docs.aws.amazon.com/sdkref/latest/guide/version-support-matrix.html)에는 사용자 지정 변환기를 사용하는 방법에 대한 자세한 내용과 예제가 나와 있습니다. 기본적인 용도는 기본 변환기를 사용할 수 없는 도메인 클래스의 속성을 위한 것입니다. 사용자 지정 변환기를 사용하면 DynamoDB에 쓰거나 읽는 데 필요한 정보를 SDK에 제공할 수 있습니다.

```
import org.slf4j.*;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument;
import software.amazon.awssdk.enhanced.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity;

public class DynamoDbEnhancedDocumentClientPutItem {
    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<EnhancedDocument> DYNAMODB_TABLE =
            ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.documentSchemaBuilder()
                            .addIndexPartitionKey(TableMetadata.primaryIndexName(),"pk", AttributeValueType.S)
                            .addIndexSortKey(TableMetadata.primaryIndexName(), "sk", AttributeValueType.S)
                            .attributeConverterProviders(AttributeConverterProvider.defaultProvider())
                            .build());

    private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedDocumentClientPutItem.class);

    private void putItem() {
        PutItemEnhancedResponse<EnhancedDocument> response = DYNAMODB_TABLE.putItemWithResponse(
                        PutItemEnhancedRequest.builder(EnhancedDocument.class)
                                .item(
                                    EnhancedDocument.builder()
                                            .attributeConverterProviders(AttributeConverterProvider.defaultProvider())
                                            .putString("pk", "123")
                                            .putString("sk", "cart#123")
                                            .putString("item_data", "YourItemData")
                                            .putNumber("inventory", 500)
                                            .build())
                                .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
                                .build());
        LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)");
    }

}
```

다음 유틸리티 메서드를 사용하여 JSON 문서를 기본 Amazon DynamoDB 데이터 유형으로, 또 그 반대로 변환할 수 있습니다.
+ [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html#fromJson(java.lang.String)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html#fromJson(java.lang.String)) – JSON 문자열에서 새로운 EnhancedDocument 인스턴스를 생성합니다.
+ [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html#toJson()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html#toJson()) – 다른 JSON 개체처럼 애플리케이션에서 사용할 수 있도록 문서의 JSON 문자열 표현을 생성합니다.

### `Query` 예제와 인터페이스 비교
<a name="CompareJavaInterfacesQueryEx"></a>

이 섹션에서는 다양한 인터페이스를 사용하여 동일한 [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html) 직접 호출을 표현한 것을 보여줍니다. 이러한 쿼리의 결과를 세밀하게 조정하려면 다음 사항에 유의하세요.
+ DynamoDB는 하나의 특정 파티션 키 값을 대상으로 하므로, 파티션 키를 완전히 지정해야 합니다.
+ 정렬 키에는 카트 항목만 이 쿼리의 대상으로 지정되도록 `begins_with`를 사용하는 키 조건 표현식이 있습니다.
+ 쿼리를 최대 100개의 반환 항목으로 제한하는 데 `limit()`을 사용합니다.
+ `scanIndexForward`를 false로 설정합니다. 결과는 UTF-8 바이트 순으로 반환되며, 이는 일반적으로 숫자가 가장 작은 카트 항목이 먼저 반환됨을 의미합니다. `scanIndexForward`를 false로 설정하면 순서가 반대가 되고 숫자가 가장 큰 카트 항목이 먼저 반환됩니다.
+ 필터를 적용하여 기준과 일치하지 않는 모든 결과를 제거합니다. 필터링되는 데이터는 항목이 필터와 일치하는지와 관계없이 읽기 용량을 소비합니다.

**Example 하위 수준 인터페이스를 사용한 `Query`**  
다음 예제에서는 `keyConditionExpression`를 사용하여 이름이 `YourTableName`인 테이블을 쿼리합니다. 이를 통해 쿼리를 특정 파티션 키 값 및 특정 접두사 값으로 시작하는 정렬 키 값으로 제한합니다. 이러한 키 조건은 DynamoDB에서 읽는 데이터의 양을 제한합니다. 마지막으로 쿼리는 `filterExpression`을 사용하여 DynamoDB에서 검색한 데이터에 필터를 적용합니다.  

```
import org.slf4j.*;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;

import java.util.Map;

public class Query {

    // Create a DynamoDB client with the default settings connected to the DynamoDB 
    // endpoint in the default region based on the default credentials provider chain.
    private static final DynamoDbClient DYNAMODB_CLIENT = DynamoDbClient.builder().build();
    private static final Logger LOGGER = LoggerFactory.getLogger(Query.class);

    private static void query() {
        QueryResponse response = DYNAMODB_CLIENT.query(QueryRequest.builder()
                .expressionAttributeNames(Map.of("#name", "name"))
                .expressionAttributeValues(Map.of(
                    ":pk_val", AttributeValue.fromS("id#1"),
                    ":sk_val", AttributeValue.fromS("cart#"),
                    ":name_val", AttributeValue.fromS("SomeName")))
                .filterExpression("#name = :name_val")
                .keyConditionExpression("pk = :pk_val AND begins_with(sk, :sk_val)")
                .limit(100)
                .scanIndexForward(false)
                .tableName("YourTableName")
                .build());

        LOGGER.info("nr of items: " + response.count());
        LOGGER.info("First item pk: " + response.items().get(0).get("pk"));
        LOGGER.info("First item sk: " + response.items().get(0).get("sk"));
    }
}
```

**Example 문서 인터페이스를 사용한 `Query`**  
다음 예제에서는 문서 인터페이스를 사용하여 이름이 `YourTableName`인 테이블을 쿼리합니다.  

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument;
import software.amazon.awssdk.enhanced.dynamodb.model.*;

import java.util.Map;

public class DynamoDbEnhancedDocumentClientQuery {

    // Create a DynamoDB client with the default settings connected to the DynamoDB 
    // endpoint in the default region based on the default credentials provider chain.
    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<EnhancedDocument> DYNAMODB_TABLE =
            ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.documentSchemaBuilder()
                    .addIndexPartitionKey(TableMetadata.primaryIndexName(),"pk", AttributeValueType.S)
                    .addIndexSortKey(TableMetadata.primaryIndexName(), "sk", AttributeValueType.S)
                    .attributeConverterProviders(AttributeConverterProvider.defaultProvider())
                    .build());
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedDocumentClientQuery.class);

    private void query() {
        PageIterable<EnhancedDocument> response = DYNAMODB_TABLE.query(QueryEnhancedRequest.builder()
                .filterExpression(Expression.builder()
                        .expression("#name = :name_val")
                        .expressionNames(Map.of("#name", "name"))
                        .expressionValues(Map.of(":name_val", AttributeValue.fromS("SomeName")))
                        .build())
                .limit(100)
                .queryConditional(QueryConditional.sortBeginsWith(Key.builder()
                        .partitionValue("id#1")
                        .sortValue("cart#")
                        .build()))
                .scanIndexForward(false)
                .build());

        LOGGER.info("nr of items: " + response.items().stream().count());
        LOGGER.info("First item pk: " + response.items().iterator().next().getString("pk"));
        LOGGER.info("First item sk: " + response.items().iterator().next().getString("sk"));

    }
}
```

**Example 상위 수준 인터페이스를 사용한 `Query`**  
다음 예시는 DynamoDB 향상된 클라이언트 API를 사용하여 이름이 `YourTableName`인 테이블을 쿼리합니다.  

```
import org.slf4j.*;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.*;
import software.amazon.awssdk.enhanced.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;

import java.util.Map;

public class DynamoDbEnhancedClientQuery {

    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<YourItem> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.fromBean(DynamoDbEnhancedClientQuery.YourItem.class));
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedClientQuery.class);

    private void query() {
        PageIterable<YourItem> response = DYNAMODB_TABLE.query(QueryEnhancedRequest.builder()
                .filterExpression(Expression.builder()
                        .expression("#name = :name_val")
                        .expressionNames(Map.of("#name", "name"))
                        .expressionValues(Map.of(":name_val", AttributeValue.fromS("SomeName")))
                        .build())
                .limit(100)
                .queryConditional(QueryConditional.sortBeginsWith(Key.builder()
                        .partitionValue("id#1")
                        .sortValue("cart#")
                        .build()))
                .scanIndexForward(false)
                .build());

        LOGGER.info("nr of items: " + response.items().stream().count());
        LOGGER.info("First item pk: " + response.items().iterator().next().getPk());
        LOGGER.info("First item sk: " + response.items().iterator().next().getSk());
    }

    @DynamoDbBean
    public static class YourItem {

        public YourItem() {}

        public YourItem(String pk, String sk, String name) {
            this.pk = pk;
            this.sk = sk;
            this.name = name;
        }

        private String pk;
        private String sk;
        private String name;

        @DynamoDbPartitionKey
        public void setPk(String pk) {
            this.pk = pk;
        }

        public String getPk() {
            return pk;
        }

        @DynamoDbSortKey
        public void setSk(String sk) {
            this.sk = sk;
        }

        public String getSk() {
            return sk;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }
}
```
**변경할 수 없는 데이터 클래스를 사용하는 상위 수준 인터페이스**  
상위 수준의 변경 불가능한 데이터 클래스를 사용하여 `Query`를 수행하는 경우 엔터티 클래스 `YourItem` 또는 `YourImmutableItem`의 구성을 제외하면 코드는 상위 수준 인터페이스 예제와 동일합니다. 자세한 내용은 [PutItem](#HighLevelImmutableDataClassEg) 예시를 참조하세요.
**변경 불가능한 데이터 클래스와 서드 파티 보일러플레이트 생성 라이브러리를 사용하는 상위 수준 인터페이스**  
상위 수준의 변경 불가능한 데이터 클래스를 사용하여 `Query`를 수행하는 경우 엔터티 클래스 `YourItem` 또는 `YourImmutableLombokItem`의 구성을 제외하면 코드는 상위 수준 인터페이스 예제와 동일합니다. 자세한 내용은 [PutItem](#HighLevelImmutableDataClassEg) 예시를 참조하세요.

## 추가 코드 예시
<a name="AdditionalCodeEx"></a>

SDK for Java 2.x와 함께 DynamoDB를 사용하는 방법에 대한 추가 예제는 다음 코드 예제 리포지토리를 참조하세요.
+ [공식 AWS 단일 작업 코드 예시](https://docs.aws.amazon.com/code-library/latest/ug/java_2_dynamodb_code_examples.html)
+ [커뮤니티에서 유지 관리하는 단일 작업 코드 예시](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/examples/SDK/java)
+ [공식 AWS 시나리오 지향 코드 예시](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/examples/SDK/java)

## 동기식 및 비동기식 프로그래밍
<a name="SyncAsyncProgramming"></a>

AWS SDK for Java 2.x는 DynamoDB와 같은 AWS 서비스에 ****동기식 클라이언트와 비동기식 클라이언트를 모두 제공합니다.

`DynamoDbClient` 및 `DynamoDbEnhancedClient` 클래스는 클라이언트가 서비스로부터 응답을 받을 때까지 스레드의 실행을 차단하는 동기식 메서드를 제공합니다. 이 클라이언트는 비동기식 작업이 필요 없는 경우 DynamoDB와 상호 작용하는 가장 간단한 방법입니다.

`DynamoDbAsyncClient` 및 `DynamoDbEnhancedAsyncClient` 클래스는 즉시 반환하는 비동기식 메서드를 제공하며, 응답을 기다리지 않고 제어 권한을 직접 호출하는 스레드에 넘겨줍니다. 비차단 클라이언트는 몇 개의 스레드에서 높은 동시성을 사용하여 최소한의 컴퓨팅 리소스로 I/O 요청을 효율적으로 처리할 수 있다는 이점이 있습니다. 이를 통해 처리량과 응답성이 향상됩니다.

AWS SDK for Java 2.x는 비차단 I/O에 대한 기본 지원을 사용합니다. AWS SDK for Java 1.x는 비차단 I/O를 시뮬레이션해야 했습니다.

동기식 메서드는 응답이 제공되기 전에 반환하므로, 준비되었을 때 응답을 가져올 방법이 필요합니다. AWS SDK for Java의 비동기식 메서드는 미래의 비동기식 작업 결과를 포함하는 [https://docs.oracle.com/javase/8/docs/api/index.html?java/util/concurrent/CompletableFuture.html](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/concurrent/CompletableFuture.html) 객체를 반환합니다. 이러한 `CompletableFuture` 개체에서 `get()` 또는 `join()`을 직접 호출하면 결과가 나올 때까지 코드가 차단됩니다. 요청과 동시에 직접 호출을 수행하면 일반 동기식 직접 호출과 동작이 비슷합니다.

비동기 프로그래밍에 대한 자세한 내용은 **AWS SDK for Java 2.x 개발자 안내서의 [Use asynchronous programming](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/asynchronous.html)을 참조하세요.

## HTTP 클라이언트
<a name="HttpClients"></a>

모든 클라이언트를 지원하기 위해 AWS 서비스와의 통신을 처리하는 HTTP 클라이언트가 있습니다. 애플리케이션에 가장 적합한 특성을 가진 클라이언트를 선택하여 대체 HTTP 클라이언트를 연결할 수 있습니다. 어떤 것은 더 가볍고 어떤 것은 더 많은 구성 옵션을 제공합니다.

어떤 HTTP 클라이언트는 동기식 사용만 지원하는 반면 어떤 HTTP 클라이언트는 비동기식 사용만 지원합니다. 워크로드에 적합한 HTTP 클라이언트를 선택하는 데 도움이 되는 흐름도는 **AWS SDK for Java 2.x 개발자 안내서의 [HTTP 클라이언트 권장 사항](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration.html#http-clients-recommend)에서 참조하세요.

다음 목록은 가능한 HTTP 클라이언트 중 일부를 보여줍니다.

**Topics**
+ [Apache 기반 HTTP 클라이언트](#ApacheHttpClient)
+ [`URLConnection` 기반 HTTP 클라이언트](#URLConnHttpClient)
+ [Netty 기반 HTTP 클라이언트](#NettyHttpClient)
+ [AWS CRT 기반 HTTP 클라이언트](#AWSCRTHttpClient)

### Apache 기반 HTTP 클라이언트
<a name="ApacheHttpClient"></a>

이 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/apache/ApacheHttpClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/apache/ApacheHttpClient.html) 클래스는 동기식 서비스 클라이언트를 지원합니다. 동기식 사용의 기본 HTTP 클라이언트입니다. `ApacheHttpClient` 클래스 구성에 대한 자세한 내용은 **AWS SDK for Java 2.x 개발자 안내서의 [Configure the Apache-based HTTP client](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-apache.html)에서 참조하세요.

### `URLConnection` 기반 HTTP 클라이언트
<a name="URLConnHttpClient"></a>

[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/urlconnection/UrlConnectionHttpClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/urlconnection/UrlConnectionHttpClient.html) 클래스는 동기식 클라이언트를 위한 또 다른 옵션입니다. Apache 기반 HTTP 클라이언트보다 로드 속도가 빠르지만 기능이 더 적습니다. `UrlConnectionHttpClient` 클래스 구성에 대한 자세한 내용은 **AWS SDK for Java 2.x 개발자 안내서의 [Configure the URLConnection-based HTTP client](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-url.html)에서 참조하세요.

### Netty 기반 HTTP 클라이언트
<a name="NettyHttpClient"></a>

이 `NettyNioAsyncHttpClient` 클래스는 비동기식 클라이언트를 지원합니다. 비동기식으로 사용 시 기본으로 선택됩니다. `NettyNioAsyncHttpClient` 클래스 구성에 대한 자세한 내용은 **AWS SDK for Java 2.x 개발자 안내서의 [Configure the Netty-based HTTP client](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-netty.html)에서 참조하세요.

### AWS CRT 기반 HTTP 클라이언트
<a name="AWSCRTHttpClient"></a>

AWS Common Runtime(CRT) 라이브러리의 최신 `AwsCrtHttpClient` 및 `AwsCrtAsyncHttpClient` 클래스는 동기식 및 비동기식 클라이언트를 지원하는 더 많은 옵션입니다. 다른 HTTP 클라이언트와 비교하여 AWS CRT는 다음을 제공합니다.
+ 더 빠른 SDK 시작 시간
+ 더 작은 메모리 공간
+ 대기 시간 단축
+ 연결 상태 관리
+ DNS 로드 밸런싱

`AwsCrtHttpClient` 및 `AwsCrtAsyncHttpClient` 클래스 구성에 대한 자세한 내용은 *AWS SDK for Java 2.x* 개발자 안내서의 [Configure the AWS CRT-based HTTP clients](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-crt.html)에서 참조하세요.

AWS CRT 기반 HTTP 클라이언트는 기존 애플리케이션의 하위 호환성을 깨뜨릴 수 있으므로, 기본값이 아닙니다. 하지만 DynamoDB의 경우 동기식 및 비동기식 사용에 AWS CRT 기반 HTTP 클라이언트를 사용하는 것이 좋습니다.

AWS CRT 기반 HTTP 클라이언트에 대한 소개는 *AWS* 개발자 도구 블로그의 [Announcing availability of the AWS CRT HTTP Client in the AWS SDK for Java 2.x](https://aws.amazon.com/blogs/developer/announcing-availability-of-the-aws-crt-http-client-in-the-aws-sdk-for-java-2-x/)에서 참조하세요.

## HTTP 클라이언트 구성
<a name="ConfigHttpClient"></a>

클라이언트를 구성할 때 다음과 같은 다양한 구성 옵션을 제공할 수 있습니다.
+ API 직접 호출의 다양한 측면에 대한 제한 시간 설정.
+ TCP 연결 유지 활성화.
+ 오류 발생 시 재시도 정책 제어.
+ [실행 인터셉터](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/interceptors.html) 인스턴스가 수정할 수 있는 실행 속성 지정. 실행 인터셉터는 API 요청 및 응답의 실행을 가로채는 코드를 작성할 수 있습니다. 이를 통해 지표를 게시하고 진행 중인 요청을 수정하는 등의 작업을 수행할 수 있습니다.
+ HTTP 헤더 추가 또는 조작.
+ [클라이언트 측 성능 지표](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/metrics.html) 추적 활성화. 이 기능을 사용하면 애플리케이션의 서비스 클라이언트에 대한 지표를 수집하고 Amazon CloudWatch에서 출력을 분석할 수 있습니다.
+ 비동기식 재시도 및 제한 시간 작업과 같은 일정 예약 작업에 사용할 대체 실행자 서비스 지정.

서비스 클라이언트 `Builder` 클래스에 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.html) 객체를 제공하여 구성을 제어합니다. 다음 섹션의 일부 코드 예시에서 이를 확인할 수 있습니다.

`ClientOverrideConfiguration`은 표준 구성 선택 사항을 제공합니다. 다양한 플러그 가능 HTTP 클라이언트에는 구현별 구성 기능도 있습니다.

**Topics**
+ [제한 시간 구성](#TimeoutConfig)
+ [RetryMode](#RetryMode)
+ [DefaultsMode](#DefaultsMode)
+ [연결 유지 구성](#KeepAliveConfig)

### 제한 시간 구성
<a name="TimeoutConfig"></a>

클라이언트 구성을 조정하여 서비스 직접 호출과 관련된 다양한 제한 시간을 제어할 수 있습니다. DynamoDB는 다른 AWS 서비스에 비해 지연 시간이 더 짧습니다. 따라서 네트워킹 문제가 발생할 경우 빠르게 실패할 수 있도록 이러한 속성을 조정하여 제한 시간 값을 낮추는 것이 좋습니다.

DynamoDB 클라이언트에서 `ClientOverrideConfiguration`을 사용하거나 기본 HTTP 클라이언트 구현에서 세부 구성 옵션을 변경하여 지연 시간 관련 동작을 사용자 지정할 수 있습니다.

`ClientOverrideConfiguration`을 사용하여 다음과 같은 영향력 있는 속성을 구성할 수 있습니다.
+ `apiCallAttemptTimeout` – 포기하고 제한 시간이 초과되기 전에 한 번의 HTTP 요청 시도가 완료되기까지 기다리는 시간입니다.
+ `apiCallTimeout` – 클라이언트가 API 직접 호출을 완전히 실행해야 하는 시간입니다. 여기에는 재시도를 포함한 모든 HTTP 요청으로 구성된 요청 핸들러 실행이 포함됩니다.

AWS SDK for Java 2.x는 연결 제한 시간 및 소켓 제한 시간 등의 일부 제한 시간 옵션에 [기본값](https://github.com/aws/aws-sdk-java-v2/blob/a0c8a0af1fa572b16b5bd78f310594d642324156/http-client-spi/src/main/java/software/amazon/awssdk/http/SdkHttpConfigurationOption.java#L134)을 제공합니다. SDK는 API 직접 호출 제한 시간 또는 개별 API 직접 호출 시도 제한 시간에 기본값을 제공하지 않습니다. 이러한 제한 시간이 `ClientOverrideConfiguration`에 설정되지 않은 경우 SDK는 전체 API 직접 호출 제한 시간 대신 소켓 제한 시간 값을 사용합니다. 소켓 제한 시간의 기본값은 30초입니다.

### RetryMode
<a name="RetryMode"></a>

제한 시간 구성과 관련하여 고려해야 하는 또 다른 구성은 `RetryMode` 구성 객체입니다. 이 구성 객체에는 재시도 동작 모음이 포함되어 있습니다.

SDK for Java 2.x는 다음 재시도 모드를 지원합니다.
+ `legacy` – 명시적으로 변경하지 않는 경우 기본 재시도 모드입니다. 이 재시도 모드는 Java SDK에만 해당됩니다. 최대 3회 재시도 또는 DynamoDB와 같은 서비스의 경우 최대 8회 재시도할 수 있습니다.
+ `standard` - 다른 AWS SDK와 더 일관적이기 때문에 '표준'이라는 이름이 지정되었습니다. 이 모드는 첫 번째 재시도를 위해 0ms에서 1,000ms 사이의 임의의 시간 동안 기다립니다. 다시 재시도해야 하는 경우 이 모드는 0ms에서 1,000ms 사이의 또 다른 임의의 시간을 선택하여 2를 곱합니다. 추가 재시도가 필요한 경우 같은 범위에서 임의로 선택한 시간에 4를 곱하는 식으로 반복합니다. 각 대기 시간은 20초로 제한됩니다. 이 모드는 `legacy` 모드보다 더 많이 감지된 장애 조건에 대해 재시도를 수행합니다. DynamoDB의 경우 [numRetries](#numRetries)로 재정의하지 않는 한 모두 합쳐 최대 3회까지 시도합니다.
+ `adaptive` – `standard` 모드를 기반으로 하며 AWS 요청 비율을 동적으로 제한하여 성공률을 극대화합니다. 이렇게 하면 요청 지연 시간이 길어질 수 있습니다. 예측 가능한 지연 시간이 중요한 경우에는 적응형 재시도 모드를 사용하지 않는 것이 좋습니다.

이러한 재시도 모드의 확장된 정의는 **AWS SDK 및 도구 참조 안내서의 [Retry behavior](https://docs.aws.amazon.com/sdkref/latest/guide/feature-retry-behavior.html)에서 확인할 수 있습니다.

#### 재시도 정책
<a name="RetryPolicies"></a>

모든 `RetryMode` 구성에는 하나 이상의 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryCondition.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryCondition.html) 구성을 기반으로 구축된 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/RetryPolicy.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/RetryPolicy.html)가 있습니다. [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/TokenBucketRetryCondition.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/TokenBucketRetryCondition.html)은 DynamoDB SDK 클라이언트 구현의 재시도 동작에 특히 중요합니다. 이 조건은 토큰 버킷 알고리즘을 사용하여 SDK의 재시도 횟수를 제한합니다. 선택한 재시도 모드에 따라 제한 예외가 `TokenBucket`에서 토큰을 뺄 수도 있고 빼지 않을 수도 있습니다.

클라이언트에서 제한 예외 또는 일시적 서버 오류와 같은 재시도 가능한 오류가 발생하면 SDK는 요청을 자동으로 재시도합니다. 재시도 횟수와 속도를 제어할 수 있습니다.

클라이언트를 구성할 때 다음 파라미터를 지원하는 `RetryPolicy`를 제공할 수 있습니다.
+ `numRetries` – 요청이 실패한 것으로 간주되기 전에 적용해야 하는 최대 재시도 횟수입니다. 사용하는 재시도 모드와 관계없이 기본값은 8입니다.
**주의**  
이 기본값을 변경하려면 신중하게 고려하시기 바랍니다.
+ `backoffStrategy` - 재시도에 적용할 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/BackoffStrategy.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/BackoffStrategy.html)으로, [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/FullJitterBackoffStrategy.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/FullJitterBackoffStrategy.html)이 기본 전략입니다. 이 전략은 현재 재시도 횟수, 기본 지연 및 최대 백오프 시간을 기준으로 추가 재시도 사이에 기하급수적 지연을 수행합니다. 그런 다음 지터를 추가하여 약간의 무작위성을 제공합니다. 기하급수적 지연에 사용되는 기본 지연은 재시도 모드와 관계없이 25ms입니다.
+ `retryCondition` – [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryCondition.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryCondition.html)은 요청을 재시도할지 여부를 결정합니다. 기본적으로 재시도 가능하다고 판단되는 특정 HTTP 상태 코드 및 예외 집합을 재시도합니다. 대부분의 경우 기본 구성이면 충분합니다.

다음 코드는 대체 재시도 정책을 제공합니다. 총 5번의 재시도(총 6번의 요청)를 지정합니다. 첫 번째 재시도는 약 100ms 지연 후에 이루어져야 하며, 각 추가 재시도는 최대 1초 지연까지 기하급수적으로 2배 증가해야 합니다.

```
DynamoDbClient client = DynamoDbClient.builder()
    .overrideConfiguration(ClientOverrideConfiguration.builder()
        .retryPolicy(RetryPolicy.builder()
            .backoffStrategy(FullJitterBackoffStrategy.builder()
                .baseDelay(Duration.ofMillis(100))
                .maxBackoffTime(Duration.ofSeconds(1))
                .build())
            .numRetries(5)
            .build())
        .build())
    .build();
```

### DefaultsMode
<a name="DefaultsMode"></a>

`ClientOverrideConfiguration` 및 `RetryMode`가 관리하지 않는 제한 시간 속성은 일반적으로 `DefaultsMode`를 지정하여 암묵적으로 구성됩니다.

AWS SDK for Java 2.x(버전 2.17.102 이상)에 `DefaultsMode`에 대한 지원이 도입되었습니다. 이 기능은 HTTP 통신 설정, 재시도 동작, 서비스 리전 엔드포인트 설정 및 잠재적인 모든 SDK 관련 구성과 같은 일반적인 구성 가능 설정에 대한 기본값 집합을 제공합니다. 이 기능을 사용하면 일반 사용 시나리오에 맞게 조정된 새 구성 기본값을 얻을 수 있습니다.

기본 모드는 모든 AWS SDK에서 표준화되어 있습니다. SDK for Java 2.x는 다음과 같은 기본 모드를 지원합니다.
+ `legacy` - AWS SDK에 따라 달라지고 `DefaultsMode`가 설정되기 전에 존재했던 기본 설정을 제공합니다.
+ `standard` - 대부분의 시나리오에 최적화되지 않은 기본 설정을 제공합니다.
+ `in-region` - 표준 모드를 기반으로 구축하며 동일한 AWS 리전에서 AWS 서비스를 직접 호출하는 애플리케이션에 맞게 조정된 설정을 포함합니다.
+ `cross-region` - 표준 모드를 기반으로 구축하며 동일한 다른 리전에서 AWS 서비스를 직접 호출하는 애플리케이션에 제한 시간이 긴 설정을 포함합니다.
+ `mobile` - 표준 모드를 기반으로 구축하며 지연 시간이 긴 모바일 애플리케이션에 맞게 조정된 제한 시간이 긴 설정을 포함합니다.
+ `auto`— 표준 모드를 기반으로 구축하며 실험적 기능을 포함합니다. SDK는 런타임 환경을 검색하여 적절한 설정을 자동으로 결정합니다. 자동 감지는 휴리스틱 기반이며 정확도가 100%는 아닙니다. 런타임 환경을 확인할 수 없는 경우 표준 모드가 사용됩니다. 자동 탐지는 [인스턴스 메타데이터와 사용자 데이터](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html)를 쿼리할 수 있으며, 이로 인해 지연이 발생할 수 있습니다. 시작 지연 시간이 애플리케이션에 중요한 경우에는 명시적 지연 시간을 `DefaultsMode`을 대신 선택하는 것이 좋습니다.

다음과 같은 방법으로 기본 모드를 구성할 수 있습니다.
+ `AwsClientBuilder.Builder#defaultsMode(DefaultsMode)`를 통해 클라이언트에게 직접 전달합니다.
+ `defaults_mode` 프로필 파일 속성을 통해 구성 프로필에서 구성합니다.
+ `aws.defaultsMode` 시스템 속성을 통해 전역적으로 구성합니다.
+ `AWS_DEFAULTS_MODE` 환경 변수를 통해 전역적으로 구성합니다.

**참고**  
`legacy` 외의 모든 모드에서는 모범 사례가 발전함에 따라 벤딩 기본값이 변경될 수 있습니다. 따라서 `legacy` 이외의 모드를 사용하는 경우 SDK를 업그레이드할 때 테스트를 수행하는 것이 좋습니다.

**AWS SDK 및 도구 참조 안내서의 [스마트 구성 기본값](https://docs.aws.amazon.com/sdkref/latest/guide/feature-smart-config-defaults.html)은 다양한 기본 모드의 구성 속성 및 기본값 목록을 제공합니다.

애플리케이션의 특성과 애플리케이션이 상호 작용하는 AWS 서비스에 따라 기본 모드 값을 선택합니다.

이러한 값은 다양한 AWS 서비스 선택지를 염두에 두고 구성되었습니다. DynamoDB 테이블과 애플리케이션이 모두 한 리전에 배포되는 일반적인 DynamoDB 배포의 경우 `standard` 기본 모드 중에서 `in-region` 기본 모드가 가장 적합합니다.

**Example 지연 시간이 짧은 직접 호출을 위해 조정된 DynamoDB SDK 클라이언트 구성**  
다음 예시는 지연 시간이 짧을 것으로 예상되는 DynamoDB 직접 호출의 제한 시간을 더 낮은 값으로 조정합니다.  

```
DynamoDbAsyncClient asyncClient = DynamoDbAsyncClient.builder()
    .defaultsMode(DefaultsMode.IN_REGION)
    .httpClientBuilder(AwsCrtAsyncHttpClient.builder())
    .overrideConfiguration(ClientOverrideConfiguration.builder()
        .apiCallTimeout(Duration.ofSeconds(3))
        .apiCallAttemptTimeout(Duration.ofMillis(500))
        .build())
    .build();
```
개별 HTTP 클라이언트 구현을 통해 제한 시간 및 연결 사용 동작을 훨씬 더 세밀하게 제어할 수 있습니다. 예를 들어, AWS CRT 기반 클라이언트의 경우 클라이언트가 사용된 연결의 상태를 능동적으로 모니터링할 수 있도록 `ConnectionHealthConfiguration`을 활성화할 수 있습니다. 자세한 내용은 **AWS SDK for Java 2.x 개발자 안내서의 [AWS CRT 기반 HTTP 클라이언트의 고급 구성](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-crt.html#configuring-the-crt-based-http-client)을 참조하세요.

### 연결 유지 구성
<a name="KeepAliveConfig"></a>

연결 유지를 활성화하면 연결을 재사용하여 지연 시간을 줄일 수 있습니다. 연결 유지에는 HTTP 연결 유지와 TCP 연결 유지라는 두 가지 종류가 있습니다.
+ HTTP 연결 유지는 클라이언트와 서버 간의 HTTPS 연결을 유지하려고 시도하므로 이후 요청에서 해당 연결을 재사용할 수 있습니다. 이렇게 하면 이후 요청 시 무거운 HTTPS 인증을 건너뛸 수 있습니다. HTTP 연결 유지는 모든 클라이언트에서 기본적으로 활성화되어 있습니다.
+ TCP 연결 유지는 기본 운영 체제에 소켓 연결을 통해 작은 패킷을 전송하도록 요청하여 소켓의 연결이 유지되어 있는지 확인하고 연결 해제가 발생하면 즉시 감지하도록 합니다. 이렇게 하면 나중에 요청할 때 연결이 해제된 소켓을 사용하느라 시간을 허비하지 않아도 됩니다. TCP 연결 유지는 모든 클라이언트에서 기본적으로 비활성화되어 있습니다. 다음 코드 예제에서는 각 HTTP 클라이언트에서 이를 활성화하는 방법을 보여줍니다. CRT 기반이 아닌 모든 HTTP 클라이언트에 대해 활성화된 경우 실제 연결 유지 메커니즘은 운영 체제에 따라 달라집니다. 따라서 운영 체제를 통해 추가 TCP 연결 유지 값(예: 제한 시간 및 패킷 수)을 구성해야 합니다. Linux 또는 macOS의 `sysctl`을 사용하거나 Windows의 레지스트리 값을 사용하여 이 작업을 수행할 수 있습니다.

**Example Apache 기반 HTTP 클라이언트에서 TCP 연결 유지를 활성화하는 방법**  

```
DynamoDbClient client = DynamoDbClient.builder()
    .httpClientBuilder(ApacheHttpClient.builder().tcpKeepAlive(true))
    .build();
```

**`URLConnection` 기반 HTTP 클라이언트**  
`URLConnection` 기반 HTTP 클라이언트 [https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html](https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html)을 사용하는 동기식 클라이언트에는 연결 유지를 활성화하는 [메커니즘](https://docs.oracle.com/javase/8/docs/api/java/net/doc-files/net-properties.html)이 없습니다.

**Example Netty 기반 HTTP 클라이언트에서 TCP 연결 유지 활성화**  

```
DynamoDbAsyncClient client = DynamoDbAsyncClient.builder()
    .httpClientBuilder(NettyNioAsyncHttpClient.builder().tcpKeepAlive(true))
    .build();
```

**Example AWS CRT 기반 HTTP 클라이언트에서 TCP 연결 유지 활성화**  
AWS CRT 기반 HTTP 클라이언트를 사용하여 TCP 연결 유지를 활성화하고 기간을 제어할 수 있습니다.  

```
DynamoDbClient client = DynamoDbClient.builder()
    .httpClientBuilder(AwsCrtHttpClient.builder()
    .tcpKeepAliveConfiguration(TcpKeepAliveConfiguration.builder()
        .keepAliveInterval(Duration.ofSeconds(50))
        .keepAliveTimeout(Duration.ofSeconds(5))
        .build()))
    .build();
```
비동기식 DynamoDB 클라이언트를 사용하는 경우 다음 코드와 같이 TCP 연결 유지를 활성화할 수 있습니다.  

```
DynamoDbAsyncClient client = DynamoDbAsyncClient.builder()
    .httpClientBuilder(AwsCrtAsyncHttpClient.builder()
    .tcpKeepAliveConfiguration(TcpKeepAliveConfiguration.builder()
        .keepAliveInterval(Duration.ofSeconds(50))
        .keepAliveTimeout(Duration.ofSeconds(5))
        .build()))
    .build();
```

## 오류 처리
<a name="JavaErrorHandling"></a>

예외 처리와 관련하여 AWS SDK for Java 2.x는 런타임 (확인되지 않은) 예외를 사용합니다.

모든 SDK 예외를 포함하는 기본 예외는 Java 확인되지 않은 `RuntimeException`에서 확장된 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html)입니다. 이를 포착하면 SDK에서 발생하는 모든 예외를 포착할 수 있습니다.

`SdkServiceException`에는 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsServiceException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsServiceException.html)이라는 하위 클래스가 있습니다. 이 하위 클래스는 AWS 서비스와 통신할 때 문제가 발생했음을 나타냅니다. [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html)이라는 하위 클래스가 있는데, 이는 DynamoDB와의 통신에 문제가 있음을 나타냅니다. 이것을 포착하면 DynamoDB와 관련된 모든 예외를 포착할 수 있지만, 다른 SDK 예외는 포착하지 못합니다.

`DynamoDbException` 아래에는 더 구체적인 [예외 유형](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html)이 있습니다. 이러한 예외 유형 중 일부는 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/TableAlreadyExistsException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/TableAlreadyExistsException.html)과 같은 컨트롤 플레인 작업에 적용됩니다. 다른 예외 유형은 데이터 영역 작업에 적용됩니다. 다음은 일반적인 데이터 영역 예외의 예입니다.
+ [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ConditionalCheckFailedException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ConditionalCheckFailedException.html) – 요청에서 false로 평가된 조건을 지정했습니다. 예를 들어 어떤 항목에서 조건부 업데이트 수행을 시도했지만 속성의 실제 값이 조건에서 예상되는 값과 일치하지 않았을 수 있습니다. 이러한 방식으로 실패한 요청은 재시도되지 않습니다.

다른 상황에서는 구체적인 예외가 정의되어 있지 않습니다. 예를 들어 요청이 제한되면 구체적인 `ProvisionedThroughputExceededException`이 발생하고 다른 경우에는 보다 일반적인 `DynamoDbException`이 발생할 수 있습니다. 어느 경우에나 `isThrottlingException()`이 `true`를 반환하는지 확인하여 제한으로 인해 예외가 발생했는지 확인할 수 있습니다.

애플리케이션 요구 사항에 따라 모든 `AwsServiceException` 또는 `DynamoDbException` 인스턴스를 포착할 수 있습니다. 그러나 상황에 따라 다른 동작이 필요한 경우가 많습니다. 상태 확인 실패를 처리하는 논리는 제한을 처리하는 것과 다릅니다. 처리할 예외적인 경로를 정의하고 대체 경로를 테스트해 보세요. 이를 통해 모든 관련 시나리오를 처리할 수 있습니다.

발생할 수 있는 일반적인 오류 목록은 [DynamoDB 관련 오류 처리](Programming.Errors.md) 섹션을 참조하세요. **Amazon DynamoDB API 참조의 [Common Errors](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/CommonErrors.html)도 참조하세요. 또한, API 참조는 각 API 작업(예: [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html) 작업)에서 발생할 수 있는 정확한 오류를 제공합니다. 예외 처리에 대한 자세한 내용은 **AWS SDK for Java 2.x 개발자 안내서의 [Exception handling for the AWS SDK for Java 2.x](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/handling-exceptions.html)를 참조하세요.

## AWS 요청 ID
<a name="JavaRequestID"></a>

각 요청에는 요청 ID가 포함되어 있으며, 이 ID는 AWS Support과 협력하여 문제를 진단하는 경우 유용하게 사용할 수 있습니다. `SdkServiceException`에서 파생된 각 예외에는 요청 ID를 검색하는 데 사용할 수 있는 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html#requestId()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html#requestId()) 메서드가 있습니다.

## 로깅
<a name="JavaLogging"></a>

SDK에서 제공하는 로깅을 사용하면 클라이언트 라이브러리에서 중요한 메시지를 포착하고 심층적으로 디버깅하는 데 모두 유용할 수 있습니다. 로거는 계층적이며 SDK는 `software.amazon.awssdk`를 루트 로거로 사용합니다. 수준은 `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `ALL`, `OFF` 중 하나로 구성할 수 있습니다. 구성된 수준은 해당 로거에 적용되며 로거 계층 구조까지 내려갑니다.

AWS SDK for Java 2.x에서는 로깅에 Simple Logging Façade for Java(SLF4J)를 사용합니다. 이는 다른 로거 주변의 추상화 계층 역할을 하며, 이를 사용하여 원하는 로거를 연결할 수 있습니다. 로거 연결에 대한 지침은 [SLF4J 사용 설명서](https://www.slf4j.org/manual.html)를 참조하세요.

각 로거에는 특정한 동작이 있습니다. 기본적으로 Log4j 2.x 로거는 로그 이벤트를 `System.out`에 추가하고 기본값은 `ERROR` 로그 수준에 추가하는 `ConsoleAppender`를 생성합니다.

SLF4J 내에 포함된 SimpleLogger 로거는 기본적으로 `System.err`을 출력하며 `INFO` 로그 수준을 기본값으로 사용합니다.

모든 프로덕션 배포에서 출력 수량을 제한하면서 SDK 클라이언트 라이브러리에서 중요한 메시지를 포착하려면 `software.amazon.awssdk` 수준을 `WARN`으로 설정하는 것이 좋습니다.

SLF4J가 클래스 경로에서 지원되는 로거를 찾을 수 없는 경우(SLF4J 바인딩 없음) 기본적으로 [무작업 구현](https://www.slf4j.org/codes.html#noProviders)으로 설정됩니다. 이 구현으로 인해 SLF4J가 클래스 경로에서 로거 구현을 찾을 수 없다는 내용의 메시지가 `System.err`에 로깅됩니다. 이러한 상황을 방지하려면 로거 구현을 추가해야 합니다. `org.slf4j.slf4j-simple` 또는 `org.apache.logging.log4j.log4j-slf4j2-imp`와 같은 아티팩트에 Apache Maven `pom.xml`의 종속성을 추가하면 됩니다.

애플리케이션 구성에 로깅 종속성을 추가하는 것을 포함하여 SDK에서 로깅을 구성하는 방법에 대한 자세한 내용은 **AWS SDK for Java 개발자 안내서의 [Logging with the SDK for Java 2.x](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/logging-slf4j.html)를 참조하세요.

`Log4j2.xml` 파일의 다음 구성은 Apache Log4j 2 로거를 사용하는 경우 로깅 동작을 조정하는 방법을 보여줍니다. 이 구성은 루트 로거 수준을 `WARN`으로 설정합니다. 계층의 모든 로거는 `software.amazon.awssdk` 로거를 포함하여 이 로그 수준을 상속합니다.

기본적으로 출력은 `System.out`으로 이동합니다. 다음 예시에서는 여전히 기본 출력 Log4j 어펜더를 재정의하여 맞춤형 Log4j `PatternLayout`을 적용합니다.

**`Log4j2.xml` 구성 파일 예**  
다음 구성은 모든 로거 계층 구조에 대해 `ERROR` 및 `WARN` 수준의 메시지를 콘솔에 로깅합니다.

```
<Configuration status="WARN">
  <Appenders>
    <Console name="ConsoleAppender" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c:%L - %m%n" />
    </Console>
  </Appenders>

  <Loggers>
    <Root level="WARN">
      <AppenderRef ref="ConsoleAppender"/>
    </Root>
  </Loggers>
</Configuration>
```

### AWS 요청 ID 로깅
<a name="JavaReqIDLogging"></a>

문제가 발생한 경우 예외 내에서 요청 ID를 찾을 수 있습니다. 하지만 예외를 생성하지 않는 요청에 대한 요청 ID를 원하면 로깅을 사용하면 됩니다.

`software.amazon.awssdk.request` 로거는 `DEBUG` 수준에서 요청 ID를 출력합니다. 다음 예시는 이전 [configuration example](#Log4j2ConfigEg)를 확장하여 루트 로거 수준을 `ERROR`로, `software.amazon.awssdk`를 `WARN` 수준으로, `software.amazon.awssdk.request`를 `DEBUG` 수준으로 유지합니다. 이러한 수준을 설정하면 요청 ID 및 기타 요청 관련 세부 정보(예: 엔드포인트 및 상태 코드)를 파악하는 데 도움이 됩니다.

```
<Configuration status="WARN">
  <Appenders>
    <Console name="ConsoleAppender" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c:%L - %m%n" />
    </Console>
  </Appenders>

  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="ConsoleAppender"/>
    </Root>
    <Logger name="software.amazon.awssdk" level="WARN" />
    <Logger name="software.amazon.awssdk.request" level="DEBUG" />
  </Loggers>
</Configuration>
```

다음은 로그 출력의 예입니다:

```
2022-09-23 16:02:08 [main] DEBUG software.amazon.awssdk.request:85 - Sending Request: DefaultSdkHttpFullRequest(httpMethod=POST, protocol=https, host=dynamodb.us-east-1.amazonaws.com, encodedPath=/, headers=[amz-sdk-invocation-id, Content-Length, Content-Type, User-Agent, X-Amz-Target], queryParameters=[])
 2022-09-23 16:02:08 [main] DEBUG software.amazon.awssdk.request:85 - Received successful response: 200, Request ID: QS9DUMME2NHEDH8TGT9N5V53OJVV4KQNSO5AEMVJF66Q9ASUAAJG, Extended Request ID: not available
```

## 페이지 매김
<a name="JavaPagination"></a>

[https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html) 및 [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html)과 같은 일부 요청의 경우 단일 요청에서 반환되는 데이터 크기가 제한되므로, 후속 페이지를 가져오려면 반복적으로 요청해야 합니다.

`Limit` 파라미터를 사용하여 각 페이지에 대해 읽을 항목의 최대 수를 제어할 수 있습니다. 예를 들어, `Limit` 파라미터를 사용하여 마지막 10개 항목만 검색할 수 있습니다. 이 제한은 필터링이 적용되기 전에 테이블에서 읽을 항목 수를 지정합니다. 필터링 후 정확히 10개를 원한다고 해서 지정할 수 있는 방법은 없습니다. 필터링 전의 개수를 제어하고 실제로 10개 항목을 검색한 경우에만 클라이언트 측에서 확인할 수 있습니다. 제한과 관계없이 응답의 최대 크기는 항상 1MB입니다.

API 응답에 `LastEvaluatedKey`가 포함될 수 있습니다. 이는 개수 제한 또는 크기 제한에 도달하여 응답이 종료되었음을 나타냅니다. 이 키는 해당 응답에 대해 마지막으로 평가된 키입니다. API와 직접 상호 작용하면 이 `LastEvaluatedKey`를 검색하고 `ExclusiveStartKey`로 후속 직접 호출에 전달하여 해당 시작 지점부터 다음 청크를 읽을 수 있습니다. `LastEvaluatedKey`가 반환되지 않으면 `Query` 또는 `Scan` API 직접 호출과 일치하는 항목이 더 이상 없는 것입니다.

다음 예시에서는 하위 수준 인터페이스를 사용하여 `keyConditionExpression` 파라미터에 따라 항목을 100개로 제한합니다.

```
QueryRequest.Builder queryRequestBuilder = QueryRequest.builder()
        .expressionAttributeValues(Map.of(
                ":pk_val", AttributeValue.fromS("123"),
                ":sk_val", AttributeValue.fromN("1000")))
        .keyConditionExpression("pk = :pk_val AND sk > :sk_val")
        .limit(100)
        .tableName(TABLE_NAME);

while (true) {
    QueryResponse queryResponse = DYNAMODB_CLIENT.query(queryRequestBuilder.build());

    queryResponse.items().forEach(item -> {
        LOGGER.info("item PK: [" + item.get("pk") + "] and SK: [" + item.get("sk") + "]");
    });

    if (!queryResponse.hasLastEvaluatedKey()) {
        break;
    }
    queryRequestBuilder.exclusiveStartKey(queryResponse.lastEvaluatedKey());
}
```

AWS SDK for Java 2.x는 자동으로 다음 결과 페이지를 얻기 위해 여러 서비스 직접 호출을 수행하는 자동 페이지 매김 메서드를 제공함으로써 DynamoDB와의 상호 작용을 단순화할 수 있습니다. 이렇게 하면 코드가 단순해지지만, 페이지를 수동으로 읽으면서 유지할 수 있는 리소스 사용에 대한 일부 제어 기능이 제거됩니다.

DynamoDB 클라이언트에서 사용할 수 있는 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html#queryPaginator(software.amazon.awssdk.services.dynamodb.model.QueryRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html#queryPaginator(software.amazon.awssdk.services.dynamodb.model.QueryRequest)) 및 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html#scanPaginator(software.amazon.awssdk.services.dynamodb.model.ScanRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html#scanPaginator(software.amazon.awssdk.services.dynamodb.model.ScanRequest))와 같은 `Iterable` 메서드를 사용하여 SDK가 페이지 매김을 처리합니다. 이러한 메서드의 반환 유형은 모든 페이지를 반복하는 데 사용할 수 있는 사용자 지정 반복자입니다. SDK는 내부적으로 서비스 직접 호출을 처리합니다. Java Stream API를 사용하여 다음 예제와 같이 `QueryPaginator`의 결과를 처리할 수 있습니다.

```
QueryPublisher queryPublisher =
    DYNAMODB_CLIENT.queryPaginator(QueryRequest.builder()
        .expressionAttributeValues(Map.of(
            ":pk_val", AttributeValue.fromS("123"),
            ":sk_val", AttributeValue.fromN("1000")))
        .keyConditionExpression("pk = :pk_val AND sk > :sk_val")
        .limit(100)
        .tableName("YourTableName")
        .build());

queryPublisher.items().subscribe(item ->
    System.out.println(item.get("itemData"))).join();
```

## 데이터 클래스 주석
<a name="JavaDataClassAnnotation"></a>

Java SDK는 데이터 클래스의 속성에 적용할 수 있는 여러 주석을 제공합니다. 이러한 주석은 SDK가 속성과 상호 작용하는 방식에 영향을 줍니다. 주석을 추가하면 속성이 암시적 원자성 카운터 역할을 하도록 하거나, 자동 생성된 타임스탬프 값을 유지하거나, 항목 버전 번호를 추적할 수 있습니다. 자세한 내용은 [Data class annotations](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)를 참조하세요.

# DynamoDB 관련 오류 처리
<a name="Programming.Errors"></a>

 이 단원에서는 런타임 오류와 그러한 오류를 처리하는 방법을 설명합니다. 또한 Amazon DynamoDB 관련 오류 메시지 및 코드를 설명합니다. 모든 AWS 서비스에 적용되는 일반적인 오류 목록은 [액세스 관리](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/CommonErrors.html)를 참조하세요.

**Topics**
+ [오류 구성 요소](#Programming.Errors.Components)
+ [트랜잭션 오류](#Programming.Errors.TransactionalErrors)
+ [오류 메시지 및 코드](#Programming.Errors.MessagesAndCodes)
+ [애플리케이션에서의 오류 처리](#Programming.Errors.Handling)
+ [오류 재시도 횟수 및 지수 백오프](#Programming.Errors.RetryAndBackoff)
+ [일괄 작업 및 오류 처리](#Programming.Errors.BatchOperations)

## 오류 구성 요소
<a name="Programming.Errors.Components"></a>

프로그램이 요청을 보내면 DynamoDB는 요청 처리를 시도합니다. 요청이 성공하면 DynamoDB는 요청된 작업의 결과와 함께 HTTP 성공 상태 코드(`200 OK`)를 반환합니다.

요청이 실패하면 DynamoDB는 오류를 반환합니다. 오류에는 세 가지 구성 요소가 있습니다.
+ HTTP 상태 코드(예: `400`).
+ 예외 이름(예: `ResourceNotFoundException`).
+ 오류 메시지(예: `Requested resource not found: Table: tablename not found`).

AWS SDK는 사용자가 적절한 조치를 취할 수 있도록 애플리케이션에 오류를 전파합니다. 예를 들어 Java 프로그램에서는 `try-catch`을 처리하기 위해 `ResourceNotFoundException` 로직을 작성할 수 있습니다.

AWS SDK를 사용하지 않는 경우 DynamoDB에서 하위 수준 응답의 콘텐츠를 구문 분석해야 합니다. 다음은 이러한 응답의 예입니다.

```
HTTP/1.1 400 Bad Request
x-amzn-RequestId: LDM6CJP8RMQ1FHKSC1RBVJFPNVV4KQNSO5AEMF66Q9ASUAAJG
Content-Type: application/x-amz-json-1.0
Content-Length: 240
Date: Thu, 15 Mar 2012 23:56:23 GMT

{"__type":"com.amazonaws.dynamodb.v20120810#ResourceNotFoundException",
"message":"Requested resource not found: Table: tablename not found"}
```

## 트랜잭션 오류
<a name="Programming.Errors.TransactionalErrors"></a>

트랜잭션 오류에 대한 자세한 내용은 [DynamoDB의 트랜잭션 충돌 처리](transaction-apis.md#transaction-conflict-handling) 섹션을 참조하세요.

## 오류 메시지 및 코드
<a name="Programming.Errors.MessagesAndCodes"></a>

다음은 DynamoDB가 반환하는 예외 목록을 HTTP 상태 코드를 기준으로 그룹화한 목록입니다. *재시도 가능?*이 *예*라면 동일한 요청을 다시 제출할 수 있습니다. *재시도 가능?*이 *아니요*라면 새로운 요청을 제출하기 전에 클라이언트 측에서 문제를 해결해야 합니다.

### HTTP 상태 코드 400
<a name="Programming.Errors.MessagesAndCodes.http400"></a>

HTTP `400` 상태 코드는 인증 실패, 필수 파라미터 누락, 테이블의 할당 처리량 초과 등 요청에 문제가 있음을 의미합니다. 요청을 다시 제출하기 전에 애플리케이션의 문제를 해결해야 합니다.

**AccessDeniedException **  
메시지: *액세스가 거부되었습니다.*  
클라이언트가 요청에 정확히 서명하지 않았습니다. AWS SDK를 사용 중인 경우 요청에 자동으로 서명됩니다. 그렇지 않은 경우 **AWS 일반 참조의 [서명 버전 4 서명 프로세스](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html)로 이동하세요.  
재시도 가능? 아니요

**ConditionalCheckFailedException**  
메시지: *조건부 요청이 실패했습니다. *   
false로 평가된 조건을 지정했습니다. 예를 들어 어떤 항목에서 조건부 업데이트 수행을 시도했지만 속성의 실제 값이 조건에서 예상되는 값과 일치하지 않았을 수 있습니다.  
재시도 가능? 아니요

**IncompleteSignatureException**  
메시지: *요청 서명이 AWS 표준을 준수하지 않습니다.*  
요청 서명에 일부 필요한 구성 요소가 빠져있습니다. AWS SDK를 사용 중인 경우 요청에 자동으로 서명됩니다. 그렇지 않은 경우 **AWS 일반 참조의 [서명 버전 4 서명 프로세스](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html)로 이동하세요.  
재시도 가능? 아니요

**ItemCollectionSizeLimitExceededException**  
메시지: *그룹 크기 제한이 초과됐습니다.*  
로컬 보조 인덱스가 포함된 테이블에서 동일한 파티션 키 값의 항목 그룹이 최대 크기 제한 10GB를 초과하였습니다. 항목 그룹에 대한 자세한 내용은 [로컬 보조 인덱스의 항목 컬렉션](LSI.md#LSI.ItemCollections) 단원을 참조하세요.  
재시도 가능? 예

**LimitExceededException**  
메시지: *임의 구독자의 작업이 너무 많습니다.*  
동시 제어 플레인 작업이 너무 많습니다. `CREATING`, `DELETING` 또는 `UPDATING` 상태의 누적 테이블 및 인덱스 수는 500을 초과할 수 없습니다.  
재시도 가능? 예

**MissingAuthenticationTokenException**  
메시지: *요청에는 유효한(등록된) AWS 액세스 키 ID가 포함되어야 합니다.*  
요청이 필요한 인증 헤더를 포함하지 않았거나 잘못된 형식입니다. [DynamoDB 하위 수준 API](Programming.LowLevelAPI.md)을(를) 참조하세요.  
재시도 가능? 아니요

**ProvisionedThroughputExceededException**  
메시지: *테이블 또는 하나 이상의 글로벌 보조 인덱스에게 허용되는 최대 프로비저닝된 처리량을 초과하였습니다. 프로비저닝된 처리량과 소비한 처리량의 성능 지표를 보려면 [Amazon CloudWatch 콘솔](https://console.aws.amazon.com/cloudwatch/home)을 여세요.*  
오류에는 스로틀링이 발생한 구체적인 원인을 설명하는 `ThrottlingReason` 필드 목록이 포함되어 있으며, `ResourceType+OperationType+LimitType` 형식(예: `TableReadProvisionedThroughputExceeded`)과 영향을 받은 리소스의 ARN을 따릅니다. 이를 통해 스로틀링이 적용된 리소스(테이블 또는 인덱스), 스로틀링을 유발한 작업 유형(읽기 또는 쓰기), 초과된 특정 한도(이 경우: 프로비저닝된 용량)를 식별할 수 있습니다. 스로틀링 문제 진단 및 해결에 대한 자세한 내용은 [스로틀링 진단](throttling-diagnosing-workflow.md) 섹션을 참조하세요.  
예: 요청량이 너무 높습니다. DynamoDB용 AWS SDK가 이 예외를 수신하는 요청을 자동으로 재시도합니다. 재시도 대기열이 너무 많아 완료하지 못하는 경우를 제외하고 결국 요청이 성공합니다. [오류 재시도 횟수 및 지수 백오프](#Programming.Errors.RetryAndBackoff)를 사용하여 요청 빈도를 줄입니다.  
재시도 가능? 예

**ReplicatedWriteConflictException**  
메시지: *One or more items in this request are being modified by a request in another Region.*  
예: 현재 다른 리전의 요청에 의해 수정 중인 다중 리전 강력한 일관성(MRSC) 글로벌 테이블의 항목에 대해 쓰기 작업이 요청되었습니다.  
재시도 가능? 예

**RequestLimitExceeded**  
메시지: *처리량이 계정에 대한 현재 처리량 한도를 초과합니다. 한도 증가를 요청하려면 AWS Support([https://aws.amazon.com/support](https://aws.amazon.com/support))에 문의하세요*.  
오류에는 스로틀링이 발생한 구체적인 원인을 설명하는 `ThrottlingReason` 필드 목록이 포함되어 있으며, `ResourceType+OperationType+LimitType` 형식(예: `TableWriteAccountLimitExceeded` 또는 `IndexReadAccountLimitExceeded`)과 영향을 받은 리소스의 ARN을 따릅니다. 이를 통해 스로틀된 리소스(테이블 또는 인덱스)가 무엇인지, 어떤 작업 유형(읽기 또는 쓰기)가 스로틀링을 유발했는지, 계정 수준 서비스 할당량을 초과했는지를 확인할 수 있습니다. 스로틀링 문제 진단 및 해결에 대한 자세한 내용은 [스로틀링 진단](throttling-diagnosing-workflow.md) 섹션을 참조하세요.  
예: 온디맨드 요청량이 허용된 계정 처리량을 초과했으며 더 이상 테이블의 크기를 조정할 수 없습니다.  
재시도 가능? 예

**ResourceInUseException**  
메시지: *변경하려는 리소스가 현재 사용 중입니다. *   
예: 기존 테이블을 다시 생성하려고 하거나, 또는 현재 `CREATING` 상태인 테이블을 삭제하려고 했습니다.  
재시도 가능? 아니요

**ResourceNotFoundException **  
메시지: *요청한 리소스를 찾을 수 없습니다.*  
예제: 요청한 테이블이 존재하지 않거나 너무 일찍 `CREATING` 상태가 되었습니다.  
재시도 가능? 아니요

**ThrottlingException**  
메시지: *요청량이 허용 처리량을 초과하였습니다.*  
이 예외는 THROTTLING\$1EXCEPTION 상태 코드와 함께 AmazonServiceException 응답으로 반환됩니다. [제어 플레인](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.API.html#HowItWorks.API.ControlPlane) API 작업을 너무 빠르게 수행하는 경우 이 예외가 반환될 수 있습니다.  
요청 속도가 너무 높으면 온디맨드 모드를 사용하는 테이블의 경우 모든 [데이터 플레인](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.API.html#HowItWorks.API.DataPlane) API 작업에 대해 이 예외가 반환될 수 있습니다. 온디맨드 규모 조정에 대한 자세한 내용은 [초기 처리량 및 규모 조정 속성](on-demand-capacity-mode.md#on-demand-capacity-mode-initial) 섹션을 참조하세요.  
오류에는 스로틀링이 발생한 구체적인 원인을 설명하는 `ThrottlingReason` 필드 목록이 포함되어 있으며, `ResourceType+OperationType+LimitType` 형식(예: `TableReadKeyRangeThroughputExceeded` 또는 `IndexWriteMaxOnDemandThroughputExceeded`)과 영향을 받은 리소스의 ARN을 따릅니다. 이 정보는 어떤 리소스(테이블 또는 인덱스)가 스로틀되고 있는지, 어떤 작업 유형(읽기 또는 쓰기)이 스로틀링을 유발했는지, 초과된 특정 한도(파티션 한도 또는 온디맨드 최대 처리량)를 식별하는 데 도움이 됩니다. 스로틀링 문제 진단 및 해결에 대한 자세한 내용은 [스로틀링 진단](throttling-diagnosing-workflow.md) 섹션을 참조하세요.  
재시도 가능? 예

**UnrecognizedClientException**  
메시지: *액세스 키 ID 또는 보안 토큰이 잘못되었습니다.*  
요청 서명이 잘못되었습니다. 가장 가능성이 높은 원인은 잘못된 AWS 액세스 키 ID 또는 보안 키입니다.  
재시도 가능? 예

**ValidationException**  
메시지: 발생하는 구체적 오류에 따라 다릅니다  
이 오류는 필수 파라미터의 누락, 값의 범위 이탈 또는 일치하지 않는 데이터 형식 등 몇 가지 이유로 발생할 수 있습니다. 오류 메시지에는 요청에서 오류를 일으킨 특정 부분에 대한 정보가 포함됩니다.  
재시도 가능? 아니요

### HTTP 상태 코드 5xx
<a name="Programming.Errors.MessagesAndCodes.http5xx"></a>

HTTP `5xx` 상태 코드는 AWS가 해결해야 하는 문제를 나타냅니다. 이것은 일시적 오류일 수 있으므로 성공할 때까지 요청을 다시 시도할 수 있습니다. 그렇지 않은 경우 [AWS Service Health Dashboard](https://status.aws.amazon.com/)에서 서비스 운영 문제가 있는지 확인하세요.

자세한 내용은 [Amazon DynamoDB에서 HTTP 5xx 오류를 해결하려면 어떻게 해야 하나요?](https://aws.amazon.com/premiumsupport/knowledge-center/dynamodb-http-5xx-errors/)를 참조하세요.

**InternalServerError (HTTP 500)**  
DynamoDB에서 요청을 처리할 수 없습니다.  
재시도 가능? 예  
항목 작업 시 내부 서버 오류가 발생할 수 있습니다. 이러한 오류는 테이블 수명 동안 예상됩니다. 실패한 요청은 즉시 다시 시도할 수 있습니다.  
쓰기 작업에 대한 상태 코드 500을 수신하면 작업이 성공했거나 실패했을 수 있습니다. 쓰기 작업이 `TransactWriteItem` 요청이면 작업을 다시 시도해도 좋습니다. 쓰기 작업이 `PutItem`, `UpdateItem` 또는 `DeleteItem` 같은 단일 항목 쓰기 요청인 경우 작업을 다시 시도하기 전에 애플리케이션이 항목의 상태를 읽어야 합니다. 그리고 [DynamoDB 조건 표현식 CLI 예제](Expressions.ConditionExpressions.md)를 사용하여 이전 작업의 성공 여부와 관계없이 재시도한 후에도 항목이 올바른 상태로 유지되도록 해야 합니다. 멱등성이 쓰기 작업의 요구 사항인 경우 동일한 작업을 수행하려는 여러 시도를 명확하게 하기 위해 `ClientRequestToken`을 자동으로 지정하여 멱등원 요청을 지원하는 [`TransactWriteItem`](transaction-apis.md#transaction-apis-txwriteitems)을 사용하세요.

**ServiceUnavailable (HTTP 503)**  
DynamoDB를 현재 사용할 수 없습니다. (이것은 일시적 상태여야 합니다.)  
재시도 가능? 예

## 애플리케이션에서의 오류 처리
<a name="Programming.Errors.Handling"></a>

애플리케이션의 원활한 실행을 위해서는 오류를 발견하고 대응할 수 있는 로직을 추가해야 합니다. 일반적인 접근법에는 `try-catch` 블록 또는 `if-then` 문의 사용이 포함됩니다.

AWS SDK는 자체적으로 재시도 및 오류 검사를 수행합니다. AWS SDK 사용 중에 오류가 발생하면 해당 오류 코드와 설명이 문제 해결에 도움이 될 수 있습니다.

또한 응답에서 `Request ID`도 확인해야 합니다. 문제 진단을 위해 AWS Support와 협력해야 하는 경우 `Request ID`가 도움이 될 수 있습니다.

## 오류 재시도 횟수 및 지수 백오프
<a name="Programming.Errors.RetryAndBackoff"></a>

DNS 서버, 스위치, 로드 밸런서 등 수많은 네트워크 구성 요소는 요청이 이루어지는 모든 단계에서 오류를 일으킬 수 있습니다. 네트워크 환경에서는 클라이언트 애플리케이션의 재시도 기술이 이러한 오류 응답을 처리하는 데 가장 많이 사용되고 있습니다. 이 기술은 애플리케이션의 안정성을 개선합니다.

AWS SDK는 모두 재시도 로직을 자동으로 구현합니다. 재시도 파라미터를 필요에 따라 수정할 수 있습니다. 예를 들어 오류가 발생할 때 재시도가 허용되지 않는 fail-fast 전략을 필요로 하는 Java 애플리케이션을 고려해 보세요. AWS SDK for Java의 경우, `ClientConfiguration` 클래스를 사용하고 `maxErrorRetry` 값으로 `0`을 입력하면 재시도가 비활성화됩니다. 자세한 내용은 해당 프로그래밍 언어의 AWS SDK 설명서를 참조하세요.

AWS SDK를 사용하지 않을 때는 서버 오류(5xx)가 수신된 최초 요청을 재시도해야 합니다. 하지만 클라이언트 오류(4xx, `ThrottlingException` 또는 `ProvisionedThroughputExceededException` 제외)는 요청 자체를 수정하여 문제를 해결해야만 재시도가 가능합니다. 특정 스로틀링 시나리오를 해결하기 위한 자세한 권장 사항은 [DynamoDB 제한 문제 해결](TroubleshootingThrottling.md) 섹션을 참조하세요.

간단한 재시도 방법 외에도 각각의 AWS SDK는 흐름 제어가 탁월한 지수 백오프 알고리즘을 구현합니다. 지수 백오프의 기본 개념은 오류 응답이 연이어 나올 때마다 재시도 간 대기 시간을 점진적으로 늘린다는 것입니다. 예를 들어 첫 번째 재시도 전에는 최대 50밀리초, 두 번째 재시도 전에는 최대 100밀리초, 그리고 세 번째 전에는 최대 200밀리초를 기다리는 방식입니다. 하지만 1분 후에도 요청이 실패하면 요청량이 아니라 할당 처리량을 초과하는 요청 크기가 원인일 수도 있습니다. 따라서 약 1분 정도에서 멈추도록 최대 재시도 횟수를 설정하세요. 요청이 실패하면 프로비저닝된 처리량 옵션을 살펴보는 것이 좋습니다.

**참고**  
AWS SDK는 자동 재시도 로직과 지수 백오프를 구현합니다.

대부분의 지수 백오프 알고리즘은 지터(임의 지연)를 사용하여 연쇄 충돌을 방지합니다. 이 경우에는 이런 충돌을 방지하는 것이 아니므로 이 난수를 사용할 필요가 없습니다. 하지만 동시 클라이언트를 사용하는 경우에는 지터가 보다 빠른 요청 성공에 도움이 될 수 있습니다. 자세한 내용은 [지수 백오프 및 지터](http://www.awsarchitectureblog.com/2015/03/backoff.html) 블로그 게시물을 참조하세요.

## 일괄 작업 및 오류 처리
<a name="Programming.Errors.BatchOperations"></a>

DynamoDB 하위 수준 API는 읽기와 쓰기에 대한 배치 작업을 지원합니다. `BatchGetItem`은 하나 이상의 테이블에서 항목을 읽고 `BatchWriteItem`은 하나 이상의 테이블에 항목을 추가하거나 삭제합니다. 이 배치 작업은 다른 배치가 아닌 DynamoDB 작업 주위의 래퍼로 구현됩니다. 즉, `BatchGetItem`은 각 항목마다 한 번씩 `GetItem`을 일괄 방식으로 불러옵니다. 마찬가지로 `BatchWriteItem`은 각 항목마다 상황에 맞게 `DeleteItem` 또는 `PutItem`을 일괄 방식으로 불러옵니다.

배치 작업은 처리 도중 개별 요청의 오류를 허용할 수 있습니다. 예를 들어 `BatchGetItem` 요청으로 항목 5개를 읽어온다고 가정하겠습니다. 이때 기본 `GetItem` 요청 일부가 실패하더라도 전체 `BatchGetItem` 작업이 실패하지는 않습니다. 그러나 5개 읽기 작업 모두 실패하면 전체 `BatchGetItem`이 실패합니다.

배치 작업은 실패한 요청 각각에 대한 정보를 반환하므로 사용자가 문제를 진단하고 작업을 재시도할 수 있습니다. `BatchGetItem`의 경우 해당하는 테이블 및 기본 키가 응답의 `UnprocessedKeys` 값으로 반환됩니다. `BatchWriteItem`에서도 비슷한 정보가 `UnprocessedItems`로 반환됩니다.

읽기 또는 쓰기 작업이 실패할 수 있는 가장 큰 원인은 *병목 현상*입니다. `BatchGetItem`일 때는 일괄 요청에 포함된 하나 이상의 테이블에 작업을 지원할 만큼 충분한 읽기 용량이 할당되지 않은 경우입니다. 그리고 `BatchWriteItem`일 때는 하나 이상의 테이블에 충분한 쓰기 용량이 프로비저닝되지 않은 경우입니다.

DynamoDB가 미처리 항목을 반환하면 해당 항목에 대해 배치 작업을 재시도해야 합니다. 하지만 *지수 백오프 알고리즘 사용을 강력히 권장합니다*. 배치 작업을 바로 재시도하면 각 테이블의 병목 현상으로 인해 원인이 된 읽기 또는 쓰기 요청이 다시 실패할 수 있습니다. 하지만 지수 백오프를 사용하여 배치 작업을 지연시키면 일괄에 포함된 각 요청 작업이 성공할 가능성이 훨씬 높습니다.

**참고**  
일부 AWS SDK는 처리되지 않은 항목 재시도를 자동으로 처리하는 상위 수준 클라이언트를 제공하므로 이 재시도 로직을 직접 구현할 필요가 없습니다. 예제:  
**Java** - 배치 작업을 수행할 때 AWS SDK for Java v2의 [DynamoDB Enhanced Client](DynamoDBEnhanced.md)와 v1의 [DynamoDBMapper](DynamoDBMapper.md)가 모두 처리되지 않은 항목을 자동으로 재시도합니다.
**Python** - boto3 Table 리소스 `batch_writer`는 배치 쓰기 작업에 대해 처리되지 않은 항목 재시도를 묵시적으로 처리합니다. 자세한 내용은 [batch\$1writer 테이블 리소스 사용](programming-with-python.md#programming-with-python-batch-writer) 섹션을 참조하세요.
하위 수준 클라이언트 또는 이 동작을 제공하지 않는 SDK를 사용하는 경우 위에서 설명한 대로 재시도 로직을 직접 구현해야 합니다.

# AWS SDK와 함께 DynamoDB 사용
<a name="sdk-general-information-section"></a>

다양한 프로그래밍 언어에 대해 AWS 소프트웨어 개발 키트(SDK)를 사용할 수 있습니다. 각 SDK는 개발자가 선호하는 언어로 애플리케이션을 쉽게 구축할 수 있도록 하는 API, 코드 예제 및 설명서를 제공합니다.


| SDK 설명서 | 코드 예제 | 
| --- | --- | 
| [AWS SDK for C\$1\$1](https://docs.aws.amazon.com/sdk-for-cpp) | [AWS SDK for C\$1\$1 코드 예제](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/cpp) | 
| [AWS CLI](https://docs.aws.amazon.com/cli) | [AWS CLI 코드 예제](https://docs.aws.amazon.com/code-library/latest/ug/cli_2_code_examples.html) | 
| [AWS SDK for Go](https://docs.aws.amazon.com/sdk-for-go) | [AWS SDK for Go 코드 예제](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/gov2) | 
| [AWS SDK for Java](https://docs.aws.amazon.com/sdk-for-java) | [AWS SDK for Java 코드 예제](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2) | 
| [AWS SDK for JavaScript](https://docs.aws.amazon.com/sdk-for-javascript) | [AWS SDK for JavaScript 코드 예제](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javascriptv3) | 
| [AWS SDK for Kotlin](https://docs.aws.amazon.com/sdk-for-kotlin) | [AWS SDK for Kotlin 코드 예제](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/kotlin) | 
| [AWS SDK for .NET](https://docs.aws.amazon.com/sdk-for-net) | [AWS SDK for .NET 코드 예제](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3) | 
| [AWS SDK for PHP](https://docs.aws.amazon.com/sdk-for-php) | [AWS SDK for PHP 코드 예제](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/php) | 
| [AWS Tools for PowerShell](https://docs.aws.amazon.com/powershell) | [AWS Tools for PowerShell 코드 예제](https://docs.aws.amazon.com/code-library/latest/ug/powershell_5_code_examples.html) | 
| [AWS SDK for Python (Boto3)](https://docs.aws.amazon.com/pythonsdk) | [AWS SDK for Python (Boto3) 코드 예제](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/python) | 
| [AWS SDK for Ruby](https://docs.aws.amazon.com/sdk-for-ruby) | [AWS SDK for Ruby 코드 예제](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/ruby) | 
| [AWS SDK for Rust](https://docs.aws.amazon.com/sdk-for-rust) | [AWS SDK for Rust 코드 예제](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/rustv1) | 
| [AWS SDK for SAP ABAP](https://docs.aws.amazon.com/sdk-for-sapabap) | [AWS SDK for SAP ABAP 코드 예제](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/sap-abap) | 
| [AWS SDK for Swift](https://docs.aws.amazon.com/sdk-for-swift) | [AWS SDK for Swift 코드 예제](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/swift) | 

DynamoDB에 대한 구체적인 예는 [AWS SDK를 사용한 DynamoDB용 코드 예제](service_code_examples.md) 섹션을 참조하세요.

**예제 가용성**  
필요한 예제를 찾을 수 없습니까? 이 페이지 하단의 **피드백 제공** 링크를 사용하여 코드 예시를 요청하세요.