

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

# 확장 프로그램을 사용하여 DynamoDB 향상된 클라이언트 작업 사용자 지정
<a name="ddb-en-client-extensions"></a>

DynamoDB 향상된 클라이언트 API는 매핑 작업 이외의 기능을 제공하는 플러그인 확장을 지원합니다. 확장 프로그램은 2가지 후크 메서드를 사용하여 읽기 및 쓰기 작업 중에 데이터를 수정합니다.
+ `beforeWrite()` - 쓰기 작업이 발생하기 전에 수정합니다.
+ `afterRead()` - 읽기 작업이 발생한 후 결과를 수정합니다.

일부 작업(예: 항목 업데이트)은 쓰기와 읽기를 차례로 모두 수행하므로 두 후크 메서드가 모두 직접 호출됩니다.

## 확장 프로그램 로드 방법
<a name="ddb-en-client-extensions-loading"></a>

확장 프로그램은 향상된 클라이언트 빌더에 지정된 순서대로 로드됩니다. 하나의 확장이 이전 확장에 의해 변환된 값에 대해 작동할 수 있으므로 로드 순서가 중요할 수 있습니다.

기본적으로 향상된 클라이언트는 2개의 확장 프로그램을 로드합니다.
+ `[VersionedRecordExtension](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/extensions/VersionedRecordExtension.html)` - 낙관적 잠금 제공
+ `[AtomicCounterExtension](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/extensions/AtomicCounterExtension.html)` - 카운터 속성을 자동으로 증분

향상된 클라이언트 빌더로 기본 동작을 재정의하고 모든 확장 프로그램을 로드할 수 있습니다. 기본 확장자를 원하지 않는 경우에는 none을 지정할 수도 있습니다.

**중요**  
자체 확장을 로드하는 경우 확장 클라이언트는 기본 확장을 로드하지 않습니다. 기본 확장 중 하나가 제공하는 동작을 원하는 경우 해당 확장을 확장 목록에 명시적으로 추가해야 합니다.

다음 예제에서는 `VersionedRecordExtension` 이후 이름이 `verifyChecksumExtension`인 사용자 지정 확장 프로그램을 로드하는 방법을 보여줍니다. 이 예제에서는 `AtomicCounterExtension`가 로드되지 않습니다.

```
DynamoDbEnhancedClientExtension versionedRecordExtension = VersionedRecordExtension.builder().build();

DynamoDbEnhancedClient enhancedClient = 
    DynamoDbEnhancedClient.builder()
                          .dynamoDbClient(dynamoDbClient)
                          .extensions(versionedRecordExtension, verifyChecksumExtension)
                          .build();
```

## 사용 가능한 확장 프로그램 세부 정보 및 구성
<a name="ddb-en-client-extensions-details"></a>

다음 섹션에서는 SDK에서 사용 가능한 각 확장 프로그램에 대한 자세한 정보를 제공합니다.

### `VersionedRecordExtension`을 사용한 낙관적 잠금 구현
<a name="ddb-en-client-extensions-VRE"></a>

`VersionedRecordExtension` 확장 프로그램은 항목이 데이터베이스에 작성될 때 항목 버전 번호를 증분하고 추적하여 낙관적 잠금을 제공합니다. 실제 지속 항목의 버전 번호가 애플리케이션이 마지막으로 읽은 값과 일치하지 않는 경우 쓰기가 실패하도록 하는 조건이 모든 쓰기에 추가됩니다.

#### 구성
<a name="ddb-en-client-extensions-VRE-conf"></a>

항목 버전 번호를 추적하는 데 사용할 속성을 지정하려면 테이블 스키마에서 숫자 속성에 태그를 지정하세요.

다음 코드 조각은 `version` 속성에 항목 버전 번호가 포함되도록 지정합니다.

```
    @DynamoDbVersionAttribute
    public Integer getVersion() {...};
    public void setVersion(Integer version) {...};
```

동일한 정적 테이블 스키마 접근 방식이 다음 코드 조각에 나와 있습니다.

```
    .addAttribute(Integer.class, a -> a.name("version")
                                       .getter(Customer::getVersion)
                                       .setter(Customer::setVersion)
                                        // Apply the 'version' tag to the attribute.
                                       .tags(VersionedRecordExtension.AttributeTags.versionAttribute())
```

#### 작동 방식
<a name="ddb-en-client-extensions-VRE-how-it-works"></a>

`VersionedRecordExtension`이 있는 낙관적 잠금 전략은 이러한 `DynamoDbEnhancedClient` 및 `DynamoDbTable` 메서드에 다음과 같은 영향을 끼칩니다.

**`putItem`**  
새 항목에는 초기 버전 값 0이 할당됩니다. `@DynamoDbVersionAttribute(startAt = X)`로 구성할 수 있습니다.

**`updateItem`**  
이후 항목을 검색하여 속성을 하나 이상 업데이트한 후 변경 사항을 저장하려고 해도 클라이언트 측과 서버 측의 버전 번호가 일치해야만 작업이 완료됩니다.  
작업이 완료되면 버전 번호가 자동으로 1씩 증분됩니다. `@DynamoDbVersionAttribute(incrementBy = X)`로 구성할 수 있습니다.

**`deleteItem`**  
`DynamoDbVersionAttribute` 주석은 영향을 미치지 않습니다. 항목을 삭제할 때 조건 표현식을 수동으로 추가해야 합니다.  
다음 예제에서는 조건식을 추가하여 삭제된 항목이 읽은 항목인지 확인합니다. 다음 예제(`recordVersion`)에서는 `@DynamoDbVersionAttribute` 주석이 달린 bean의 속성입니다.  

```
// 1. Read the item and get its current version.
Customer item = customerTable.getItem(Key.builder().partitionValue("someId").build());
// `recordVersion` is the bean's attribute that is annotated with `@DynamoDbVersionAttribute`.
AttributeValue currentVersion = item.getRecordVersion();

// 2. Create conditional delete with the `currentVersion` value.
DeleteItemEnhancedRequest deleteItemRequest =
    DeleteItemEnhancedRequest.builder()
       .key(KEY)
       .conditionExpression(Expression.builder()
           .expression("recordVersion = :current_version_value")
           .putExpressionValue(":current_version_value", currentVersion)
           .build()).build();

customerTable.deleteItem(deleteItemRequest);
```

**`transactWriteItems`**  
+ `addPutItem`: 이 메서드는 `putItem`과 동일한 동작을 갖습니다.
+ `addUpdateItem`: 이 메서드는 `updateItem`과 동일한 동작을 갖습니다.
+ `addDeleteItem`: 이 메서드는 `deleteItem`과 동일한 동작을 갖습니다.

**`batchWriteItem`**  
+ `addPutItem`: 이 메서드는 `putItem`과 동일한 동작을 갖습니다.
+ `addDeleteItem`: 이 메서드는 `deleteItem`과 동일한 동작을 갖습니다.

**참고**  
DynamoDB 전역 테이블은 동시 업데이트 간에 [‘최종 라이터 우선 적용’ 조정](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/V2globaltables_HowItWorks.html#V2globaltables_HowItWorks.consistency-modes)을 사용하며, DynamoDB는 최종 라이터를 확인하기 위해 노력합니다. 전역 테이블을 사용하는 경우 이 '최종 라이터 우선 적용' 정책은 모든 복제본이 결국 DynamoDB에서 결정한 최종 쓰기를 기반으로 수렴되므로 잠금 전략이 예상대로 작동하지 않을 수 있음을 의미합니다.

#### 사용 해제 방법
<a name="ddb-en-client-extensions-VRE-how-to-disable"></a>

낙관적 잠금을 사용 해제하려면 `@DynamoDbVersionAttribute` 주석을 사용하지 않습니다.

### `AtomicCounterExtension`을 사용하여 카운터 구현
<a name="ddb-en-client-extensions-ACE"></a>

`AtomicCounterExtension` 확장 프로그램은 레코드가 데이터베이스에 기록될 때마다 태그가 지정된 숫자 속성을 증분시킵니다. 시작 및 증분 값을 지정할 수 있습니다. 값을 지정하지 않으면 시작 값은 0으로 설정되고 속성 값은 1씩 증가합니다.

#### 구성
<a name="ddb-en-client-extensions-ACE-conf"></a>

어떤 속성이 카운터인지 지정하려면 테이블 스키마에서 `Long` 유형의 속성에 태그를 지정하세요.

다음 코드 조각은 `counter` 속성의 기본 시작 및 증분 값 사용을 보여줍니다.

```
    @DynamoDbAtomicCounter
    public Long getCounter() {...};
    public void setCounter(Long counter) {...};
```

정적 테이블 스키마 접근 방식이 다음 코드 조각에 나와 있습니다. 원자 카운터 확장은 시작 값 10을 사용하고 레코드가 기록될 때마다 값을 5씩 증가시킵니다.

```
    .addAttribute(Integer.class, a -> a.name("counter")
                                       .getter(Customer::getCounter)
                                       .setter(Customer::setCounter)
                                        // Apply the 'atomicCounter' tag to the attribute with start and increment values.
                                       .tags(StaticAttributeTags.atomicCounter(10L, 5L))
```

### `AutoGeneratedTimestampRecordExtension`을 사용하여 타임스탬프 추가
<a name="ddb-en-client-extensions-AGTE"></a>

`AutoGeneratedTimestampRecordExtension` 확장 프로그램은 항목이 데이터베이스에 성공적으로 기록될 때마다 `[Instant](https://docs.oracle.com/javase/8/docs/api/java/time/Instant.html)` 유형의 태그가 지정된 속성을 현재 타임스탬프로 자동 업데이트합니다. 이 확장은 기본적으로 로드되지 않습니다.

#### 구성
<a name="ddb-en-client-extensions-AGTE-conf"></a>

현재 타임스탬프로 업데이트할 속성을 지정하려면 테이블 스키마에서 `Instant` 속성에 태그를 지정하세요.

`lastUpdate` 속성은 다음 코드 조각에서 확장 프로그램의 동작 대상입니다. 속성이 `Instant` 유형이어야 한다는 요구 사항에 유의하세요.

```
    @DynamoDbAutoGeneratedTimestampAttribute
    public Instant getLastUpdate() {...}
    public void setLastUpdate(Instant lastUpdate) {...}
```

동일한 정적 테이블 스키마 접근 방식이 다음 코드 조각에 나와 있습니다.

```
     .addAttribute(Instant.class, a -> a.name("lastUpdate")
                                        .getter(Customer::getLastUpdate)
                                        .setter(Customer::setLastUpdate)
                                        // Applying the 'autoGeneratedTimestamp' tag to the attribute.
                                        .tags(AutoGeneratedTimestampRecordExtension.AttributeTags.autoGeneratedTimestampAttribute())
```

### AutoGeneratedUuidExtension을 사용하여 UUID 생성
<a name="ddb-en-client-extensions-AGUE"></a>

`AutoGeneratedUuidExtension` 확장 프로그램은 새 레코드가 데이터베이스에 기록될 때 속성에 대한 고유한 UUID(범용 고유 식별자)를 생성합니다. Java JDK [UUID.randomUUID()](https://docs.oracle.com/javase/8/docs/api/java/util/UUID.html#randomUUID--) 메서드를 사용하고 `java.lang.String` 유형의 속성에 적용됩니다. 이 확장은 기본적으로 로드되지 않습니다.

#### 구성
<a name="ddb-en-client-extensions-AGUE-conf"></a>

`uniqueId` 속성은 다음 코드 조각에서 확장 프로그램의 동작 대상입니다.

```
    @AutoGeneratedUuidExtension
    public String getUniqueId() {...}
    public void setUniqueId(String uniqueId) {...}
```

동일한 정적 테이블 스키마 접근 방식이 다음 코드 조각에 나와 있습니다.

```
     .addAttribute(String.class, a -> a.name("uniqueId")
                                        .getter(Customer::getUniqueId)
                                        .setter(Customer::setUniqueId)
                                        // Applying the 'autoGeneratedUuid' tag to the attribute.
                                        .tags(AutoGeneratedUuidExtension.AttributeTags.autoGeneratedUuidAttribute())
```

확장 프로그램이 `putItem` 메서드에 한해 UUID를 채우고 `updateItem` 메서드에는 채우지 않도록 하려면 다음 코드 조각과 같이 [업데이트 동작](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/UpdateBehavior.html) 주석을 추가합니다.

```
    @AutoGeneratedUuidExtension
    @DynamoDbUpdateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS)
    public String getUniqueId() {...}
    public void setUniqueId(String uniqueId) {...}
```

정적 테이블 스키마 접근 방식을 사용하는 경우 다음과 같은 동등한 코드를 사용합니다.

```
     .addAttribute(String.class, a -> a.name("uniqueId")
                                        .getter(Customer::getUniqueId)
                                        .setter(Customer::setUniqueId)
                                        // Applying the 'autoGeneratedUuid' tag to the attribute.
                                        .tags(AutoGeneratedUuidExtension.AttributeTags.autoGeneratedUuidAttribute(),
                                              StaticAttributeTags.updateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS))
```