

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

# Amazon DocumentDB Java 프로그래밍 설명서
<a name="docdb-java-pg"></a>

이 포괄적인 설명서에서는 MongoDB의 Java 드라이버를 사용하여 Amazon DocumentDB로 작업하기 위한 자세한 안내를 제공하며, 데이터베이스 운영 및 관리의 필수 측면을 다룹니다.

**Topics**
+ [

## 소개
](#java-pg-intro)
+ [

## 사전 조건
](#java-pg-prereqs)
+ [데이터 모델](#java-pg-data-models)
+ [Java 드라이버로 연결](java-pg-connect-mongo-driver.md)
+ [Java를 사용한 CRUD 작업](java-crud-operations.md)
+ [Java를 사용한 인덱스 관리](index-management-java.md)
+ [이벤트 기반 프로그래밍](event-driven-programming.md)

## 소개
<a name="java-pg-intro"></a>

이 설명서에서는 연결부터 시작하여 MongoDB Java 드라이버를 통해 DocumentDB 클러스터에 대한 보안 연결을 설정하는 방법에 대해 설명합니다. 연결 문자열 구성 요소, SSL/TLS 구현, IAM 인증 및 연결 풀링을 비롯한 다양한 연결 옵션과 강력한 오류 처리 전략에 대해 자세히 설명합니다.

이 설명서는 생성, 읽기, 업데이트, 삭제(CRUD) 작업 섹션에서 문서 조작에 대해 철저히 다루며, 단일 작업과 대량 작업을 모두 사용하여 문서를 생성, 읽기, 업데이트 및 삭제하는 방법을 보여줍니다. 필터, 쿼리 및 다양한 작업 옵션의 사용에 대해 설명하고, 오류 처리에 대한 모범 사례를 강조하고, 신뢰성 향상을 위한 재시도 로직을 구현합니다. 또한 이 설명서에서는 인덱스 관리에 대해 광범위하게 다루며 단일 필드, 복합, 희소, 텍스트 인덱스를 비롯한 다양한 인덱스 유형의 생성 및 유지 관리에 대해 자세히 설명합니다. 적절한 인덱스 선택 및 `explain()` 함수 사용을 통해 쿼리 실행 계획을 분석하여 쿼리 성능을 최적화하는 방법을 설명합니다.

마지막 섹션에서는 Amazon DocumentDB의 변경 스트림을 사용한 이벤트 기반 프로그래밍에 중점을 두고 Java 애플리케이션에서 실시간 데이터 변경 모니터링을 구현하는 방법을 보여줍니다. 여기에는 변경 스트림 커서 구현, 연속 작업을 위한 재개 토큰 처리, 과거 데이터 처리를 위한 시간 기반 작업이 포함됩니다. 설명서 전체에서 실용적인 코드 예제와 모범 사례가 제공되므로 Amazon DocumentDB로 강력한 Java 애플리케이션을 구축할 때 매우 유용한 리소스가 됩니다.

## 사전 조건
<a name="java-pg-prereqs"></a>

시작하기 전에 다음 항목이 준비되었는지 확인합니다.
+ 구성된 DocumentDB 클러스터가 있는 AWS 계정입니다. DocumentDB 클러스터 설정에 대한 이 [시작하기 블로그 게시물](https://aws.amazon.com/blogs/database/part-1-getting-started-with-amazon-documentdb-using-amazon-ec2/)을 참조하세요.
+ 설치된 Java Development Kit(JDK)(이 설명서에서는 [Amazon Corretto 21](https://docs.aws.amazon.com/corretto/latest/corretto-21-ug/downloads-list.html)을 사용할 예정임).
+ 종속성 관리를 위한 Maven.

## 이 설명서의 데이터 모델
<a name="java-pg-data-models"></a>

이 설명서의 모든 예제 코드는 ‘Restaurants’ 컬렉션이 있는 ‘ProgGuideData 테스트 데이터베이스에 대한 연결을 가정합니다. 이 설명서의 모든 샘플 코드는 레스토랑 목록 시스템 이하에서 작동합니다. 이 시스템의 문서는 다음과 같습니다.

```
{
    "_id": "ab6ad8f119b5bca3efa2c7ae",
    "restaurantId": "REST-CRT9BL",
    "name": "Thai Curry Palace",
    "description": "Amazing Restaurant, must visit",
    "cuisine": "Thai",
    "address": {
        "street": "914 Park Street",
        "city": "Bryan",
        "state": "AL",
        "zipCode": "96865",
        "location": {
            "type": "Point",
            "coordinates": [-25.4619, 8.389]
        }
    },
    "contact": {
        "phone": "(669) 915-9056 x6657"
    },
    "rating": {
        "average": 3.4,
        "totalReviews": 275
    },
    "priceRange": "$",
    "menu": [{
        "category": "Appetizers",
        "items": [{
            "name": "Buffalo Chicken Wings",
            "price": 13.42
        }]
    }],
    "features": [
        "Private Dining"
    ],
    "isActive": false“ michelin”: {“
        star”: 3,
        “ranking_years”: 4
    }
}
```

CRUD, 인덱스 관리 및 이벤트 기반 프로그래밍을 보여주는 모든 코드 샘플은 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClient.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClient.html) 객체 `dbClient`, [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoDatabase.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoDatabase.html) 객체 `connectionDB`, [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoCollection.html#find()](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoCollection.html#find()) 객체 `collection`이 있다고 가정합니다.

**참고**  
이 설명서의 모든 코드 예제는 MongoDB Java 드라이버 버전 5.3.0으로 테스트되었습니다.

# MongoDB Java 드라이버를 사용하여 Amazon DocumentDB에 연결
<a name="java-pg-connect-mongo-driver"></a>

이 섹션에서는 Java 드라이버를 사용하여 Amazon DocumentDB에 연결하기 위한 단계별 설명서를 제공합니다. 이를 통해 DocumentDB를 Java 애플리케이션에 통합하는 작업을 시작할 수 있습니다.

**Topics**
+ [

## 1단계: 프로젝트 설정
](#step1-set-up)
+ [

## 2단계: 연결 문자열 생성
](#step2-create-connection-string)
+ [

## 3단계: 연결 코드 작성
](#step3-write-connect-code)
+ [

## 4단계: 연결 예외 처리
](#step4-handle-connect-exceptions)
+ [

## 5단계: 코드 실행
](#step5-running-code)
+ [

## 연결 모범 사례
](#java-connect-best-practices)

## 1단계: 프로젝트 설정
<a name="step1-set-up"></a>

1. Maven을 사용하여 java 프로젝트를 생성합니다.

   ```
   mvn archetype:generate -DgroupId=com.docdb.guide -DartifactId=my-docdb-project -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
   ```

1. MongoDB Java 드라이버를 'pom.xml' 파일의 프로젝트에 대한 종속성으로 추가합니다.

   ```
   <dependency>
       <groupId>org.mongodb</groupId>
       <artifactId>mongodb-driver-sync</artifactId> 
       <version>5.3.0</version> 
   </dependency>
   ```

## 2단계: 연결 문자열 생성
<a name="step2-create-connection-string"></a>

Amazon DocumentDB 연결 문자열은 애플리케이션과 DocumentDB 클러스터 간에 연결을 설정하는 데 필수적입니다. 이 문자열은 클러스터 엔드포인트, 포트, 인증 세부 정보 및 다양한 연결 옵션과 같은 중요한 정보를 캡슐화합니다. DocumentDB 연결 문자열을 빌드하려면 일반적으로 기본 형식으로 시작합니다.

```
"mongodb://username:password@cluster-endpoint:port/?[connection options]"
```

‘사용자 이름’ 및 ‘암호’를 실제 자격 증명으로 바꿔야 합니다. AWS Management Console 와를 통해 클러스터의 엔드포인트 및 포트 번호를 찾을 수 있습니다 AWS CLI. 클러스터의 클러스터 엔드포인트를 찾으려면 [클러스터 엔드포인트 찾기](db-cluster-endpoints-find.md) 섹션을 참조하세요. Amazon DocumentDB의 기본 포트는 27017은 입니다.

**연결 문자열 예제**
+ 전송 중 암호화를 사용하여 DocumentDB에 연결하고 읽기 요청이 읽기 복제본으로 이동하고 쓰기 요청이 기본 인스턴스로 이동하도록 합니다.

  ```
  "mongodb://username:password@cluster-endpoint:27017/?tls=true& 
     tlsCAFile=global-bundle.pem& 
     readPreference=secondaryPreferred&
     retryWrites=false"
  ```
+ IAM 인증을 사용하여 DocumentDB에 연결:

  ```
  "mongodb://cluster-endpoint:27017/?tls=true& 
     tlsCAFile=global-bundle.pem& 
     readPreference=secondaryPreferred&
     retryWrites=false&
     authSource=%24external&
     authMechanism=MONGODB-AWS"
  ```

연결 문자열에 사용할 수 있는 다양한 옵션은 다음과 같습니다.
+ [TLS 인증서](#connection-string-tls)
+ [읽기 복제본에서 읽기](#connection-string-read-rep)
+ [쓰기 문제 및 저널링](#connection-string-write-journal)
+ [RetryWrites](#connection-string-retry-writes)
+ [IAM 인증](#connection-string-iam-auth)
+ [연결 풀](#connection-string-pool)
+ [연결 제한 시간 파라미터](#connection-string-timeout)

### TLS 인증서
<a name="connection-string-tls"></a>

**`tls=true|false`** - 이 옵션은 Transport Layer Security(TLS)을 활성화하거나 비활성화합니다. 기본적으로 Amazon DocumentDB 클러스터에서 전송 중 암호화가 활성화되므로 클러스터 수준에서 TLS가 비활성화되지 않는 한 이 옵션의 값은 `true`여야 합니다.

TLS를 사용하는 경우 DocumentDB 클러스터에 대한 연결을 생성할 때 코드는 SSL 인증서를 제공해야 합니다. 클러스터에 안전하게 연결하는 데 필요한 인증서를 다운로드합니다([https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem](https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem)). `global-bundle.pem` 파일을 사용하는 방법에는 두 가지가 있습니다.
+ **옵션 1** - `global-bundle.pem` 파일에서 모든 인증서를 추출하고 Java의 keytool을 사용하여 나중에 코드에서 사용할 수 있는 `.jks` 파일에 저장합니다. 이 작업을 수행하는 방법을 보여주는 스크립트는 [TLS가 활성화된 상태에서 연결](connect_programmatically.md#connect_programmatically-tls_enabled)의 Java 탭을 참조하세요.
+ **옵션 2** - 코드에 `global-bundle.pem` 파일을 동적으로 추가하고, 인 메모리 키 스토어를 빌드하고, `SSLContext`를 사용하여 연결의 일부로 인증서를 제공합니다.

### 읽기 복제본에서 읽기
<a name="connection-string-read-rep"></a>

**`replicaSet=rs0&readPreference=secondaryPreferred`** - 이 두 옵션을 지정하면 모든 읽기 요청이 읽기 복제본으로 라우팅되고 쓰기 요청이 기본 인스턴스로 라우팅됩니다. 연결 문자열에서 `replicaSet=rs0`을 사용하면 MongoDB 드라이버가 클러스터 토폴로지의 자동으로 업데이트된 보기를 유지 관리할 수 있으므로 인스턴스가 추가되거나 제거될 때 애플리케이션이 현재 노드 구성의 가시성을 유지할 수 있습니다. 이러한 옵션을 제공하지 않거나 `readPreference=primary`를 지정하지 않으면 모든 읽기 및 쓰기가 기본 인스턴스로 전송됩니다. `readPreference`에 대한 더 많은 옵션은 [읽기 기본 설정 옵션](how-it-works.md#durability-consistency-isolation) 섹션을 참조하세요.

### 쓰기 문제 및 저널링
<a name="connection-string-write-journal"></a>

쓰기 문제에 따라 쓰기 작업을 위해 데이터베이스에서 요청된 승인 수준이 결정됩니다. MongoDB 드라이버는 쓰기 문제 및 저널 파일을 조정할 수 있는 옵션을 제공합니다. Amazon DocumentDB는 쓰기 문제 및 저널을 설정할 것으로 예상하지 않으며 `w` 및 `j`(`writeConcern` 및 `journal`)에 대해 전송된 값을 무시합니다. DocumentDB는 항상 `writeConcern`: `majority` 및 `journal`: `true`를 사용하여 데이터를 기록하므로 클라이언트에 승인을 보내기 전에 대부분의 노드에 쓰기가 안정적으로 기록됩니다.

### RetryWrites
<a name="connection-string-retry-writes"></a>

**`retryWrites=false`** - DocumentDB는 재시도 가능한 쓰기를 지원하지 않으므로 이 속성은 항상 `false`로 설정해야 합니다.

### IAM 인증
<a name="connection-string-iam-auth"></a>

**`authSource=%24external` 및 `authMechanism=MONGODB-AWS`** -이 두 파라미터는를 사용하여 인증하는 데 사용됩니다 AWS Identity and Access Management. IAM 인증은 현재 인스턴스 기반 클러스터 버전 5.0에서만 사용할 수 있습니다. 자세한 내용은 [IAM 자격 증명을 사용한 인증](iam-identity-auth.md) 단원을 참조하십시오.

### 연결 풀
<a name="connection-string-pool"></a>

연결 풀링에 사용할 수 있는 옵션은 다음과 같습니다.
+ **`maxPoolSize`** - 풀에서 생성할 수 있는 최대 연결 수를 설정합니다. 모든 연결이 사용 중이고 새 요청이 들어오면 연결을 사용할 수 있을 때까지 기다립니다. MongoDB Java 드라이버의 기본값은 100입니다.
+ **`minPoolSize`** - 풀에서 항상 유지해야 하는 최소 연결 수를 나타냅니다. MongoDB Java 드라이버의 기본값은 0입니다.
+ **`maxIdleTimeMS`** - 연결을 닫고 제거하기 전에 풀에서 연결을 유휴 상태로 유지할 수 있는 시간을 결정합니다. MongoDB Java 드라이버의 기본값은 100밀리초입니다.
+ **`waitQueueTimeoutMS`** - 풀이 최대 크기일 때 스레드가 연결을 사용할 수 있을 때까지 기다려야 하는 시간을 구성합니다. 이 시간 내에 연결을 사용할 수 없는 경우 예외가 발생합니다. MongoDB Java 드라이버의 기본값은 120,000밀리초(2분)입니다.

### 연결 제한 시간 파라미터
<a name="connection-string-timeout"></a>

제한 시간은 작업 또는 연결 시도가 실패로 간주되기까지 걸릴 수 있는 시간을 제한하는 메커니즘입니다. 무기한 대기를 방지하고 리소스 할당을 관리하는 데 사용할 수 있는 제한 시간 파라미터는 다음과 같습니다.
+ **`connectTimeoutMS`** - 드라이버가 클러스터에 대한 연결을 설정하기 위해 기다리는 시간을 구성합니다. 기본값은 10,000밀리초(10초)입니다.
+ **`socketTimeoutMS`** - 드라이버가 쓰기 작업이 아닌 작업을 위해 서버의 응답을 기다리는 시간을 지정합니다. 기본값은 0입니다(제한 시간 없음 또는 무한).
+ **`serverSelectionTimeoutMS`** - 드라이버가 클러스터에서 사용할 수 있는 서버를 찾기 위해 기다리는 시간을 지정합니다. 이 설정의 기본값은 30초이며 장애 조치 중에 새 기본 인스턴스를 선택하기에 충분합니다.

## 3단계: 연결 코드 작성
<a name="step3-write-connect-code"></a>

다음 코드 예제에서는 Amazon DocumentDB에 TLS를 연결하는 방법을 보여줍니다.
+ Java [https://docs.oracle.com/javase/8/docs/api/java/security/KeyStore.html](https://docs.oracle.com/javase/8/docs/api/java/security/KeyStore.html) 및 [`SSLContext`>](https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/SSLContext.html) 객체를 생성합니다.
+ 또한 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/ConnectionString.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/ConnectionString.html) 객체에 전달하여 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoClientSettings.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoClientSettings.html) 객체를 생성합니다. TLS 연결을 만들려면 `MongoClientSettings` 객체를 사용하여 `connectionstring` 및 `sslcontext`를 바인딩해야 합니다.
+ [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClients.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClients.html)를 사용하여 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClient.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClient.html) 객체를 가져옵니다.

```
public static MongoClient makeDbConnection(String dbName, String DbUserName, String DbPassword,
    String DbClusterEndPoint, String keyStorePass) throws Exception {
    MongoClient connectedClient;
    String connectionOptions = "?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false";
    String connectionUrl = "mongodb://" + DbUserName + ":" + DbPassword + "@" + DbClusterEndPoint + ":27017/" +
        dbName + connectionOptions;

    try {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        try (FileInputStream fis = new FileInputStream("src/main/resources/certs/truststore.jks")) {
            trustStore.load(fis, keyStorePass.toCharArray());
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(trustStore);

            SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
            sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
            ConnectionString connectionString = new ConnectionString(connectionUrl);
            MongoClientSettings settings = MongoClientSettings.builder()
                .applyConnectionString(connectionString)
                .applyToSslSettings(builder - > {
                    builder.enabled(true);
                    builder.context(sslContext);
                })
                .build();
            connectedClient = MongoClients.create(settings);
        }
        return connectedClient;
    } catch (MongoException e5) {
        throw new RuntimeException(e5);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
```

## 4단계: 연결 예외 처리
<a name="step4-handle-connect-exceptions"></a>

Java 애플리케이션에서 DocumentDB로 작업할 때 강력하고 안정적인 데이터베이스 작업을 유지하려면 연결 예외 처리가 중요합니다. 이러한 예외를 적절하게 관리하면 문제를 신속하게 진단하는 데 도움이 될 뿐만 아니라 애플리케이션이 임시 네트워크 중단 또는 서버 가용성을 정상적으로 처리할 수 있으므로 안정성과 사용자 경험이 향상됩니다. 연결 설정과 관련된 몇 가지 중요한 예외는 다음과 같습니다.
+ **`MongoException`** - 일반적인 예외는 보다 구체적인 예외가 적용되지 않는 다양한 시나리오에서 발행될 수 있습니다. 이 예외는 모든 MongoDB 예외를 일반적으로 포착하므로 다른 모든 특정 예외 이후에 처리되어야 합니다.
+ **`MongoTimeoutException`** - 작업 시간이 초과되는 경우에 발생합니다. 존재하지 않는 클러스터 엔드포인트를 쿼리하는 경우를 예로 들 수 있습니다.
+ **`MongoSocketException`** - 네트워크 관련 문제가 있는 경우에 발생합니다. 작업 중 갑작스럽게 네트워크 연결이 해제되는 경우를 예로 들 수 있습니다.
+ **`MongoSecurityException`** - 인증이 실패하는 경우에 발생합니다. 잘못된 자격 증명으로 연결하는 경우를 예로 들 수 있습니다.
+ **`MongoConfigurationException`** - 클라이언트 구성에 오류가 있는 경우에 발생합니다. 잘못된 연결 문자열을 사용하는 경우를 예로 들 수 있습니다.

## 5단계: 코드 실행
<a name="step5-running-code"></a>

다음 코드 샘플은 Amazon DocumentDB 연결을 생성하고 모든 데이터베이스를 인쇄합니다.

```
public static void TestConnection() {
    try (MongoClient mongoClient = makeDbConnection(DATABASE_NAME, DB_USER_NAME, DB_PASSWORD, DB_CLUSTER_ENDPOINT, KEYSTORE_PASSWORD)) {
        List < String > databases = mongoClient.listDatabaseNames().into(new ArrayList < > ());
        System.out.println("Databases: " + databases);
    } catch (MongoException e) {
        System.err.println("MongoDB error: " + e.getMessage());
        throw new RuntimeException(e);
    }
}
```

## 연결 모범 사례
<a name="java-connect-best-practices"></a>

다음은 MongoDB Java 드라이버를 사용하여 Amazon DocumentDB에 연결할 때 고려해야 할 모범 사례입니다.
+ 클라이언트가 더 이상 리소스를 릴리스할 필요가 없는 경우 항상 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClient.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClient.html)를 닫습니다.
+ 예외를 적절하게 처리하고 적절한 오류 로깅을 구현합니다.
+ 환경 변수 또는 AWS Secrets Manager 를 사용하여 사용자 이름 및 암호와 같은 민감한 정보를 저장합니다.

# Java를 사용하여 Amazon DocumentDB에서 CRUD 작업 수행
<a name="java-crud-operations"></a>

이 섹션에서는 MongoDB Java 드라이버를 사용하여 Amazon DocumentDB에서 생성, 읽기, 업데이트, 삭제(CRUD) 작업을 수행하는 방법을 설명합니다.

**Topics**
+ [

## DocumentDB 컬렉션에 문서 생성 및 삽입
](#creating-inserting)
+ [

## DocumentDB 컬렉션에서 데이터 읽기 및 검색
](#reading-retrieving)
+ [

## DocumentDB 컬렉션의 기존 문서 업데이트
](#updating-documents)
+ [

## DocumentDB 컬렉션에서 문서 제거
](#deleting-documents)
+ [

## 재시도 로직을 사용한 오류 처리
](#error-handling)

## DocumentDB 컬렉션에 문서 생성 및 삽입
<a name="creating-inserting"></a>

Amazon DocumentDB에 문서를 삽입하면 컬렉션에 새 데이터를 추가할 수 있습니다. 요구 사항과 작업 중인 데이터의 양에 따라 삽입을 수행하는 방법에는 여러 가지가 있습니다. 컬렉션에 개별 문서를 삽입하는 가장 기본적인 메서드는 `insertOne()`입니다. 한 번에 여러 문서를 삽입하는 경우 `insertMany()` 메서드를 사용하여 단일 작업으로 문서 배열을 추가할 수 있습니다. DocumentDB 컬렉션에 많은 문서를 삽입하는 또 다른 메서드는 `bulkWrite()`입니다. 이 설명서에서는 DocumentDB 컬렉션에서 문서를 생성하는 모든 메서드에 대해 설명합니다.

**`insertOne()`**

먼저 Amazon DocumentDBB 컬렉션에 개별 문서를 삽입하는 방법을 살펴보겠습니다. 단일 문서 삽입은 `insertOne()` 메서드를 사용하여 수행됩니다. 이 메서드는 삽입을 위해 [BsonDocument](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/bson/org/bson/BsonDocument.html)를 가져와 새로 삽입된 문서의 객체 ID를 가져오는 데 사용할 수 있는 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/result/InsertOneResult.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/result/InsertOneResult.html) 객체를 반환합니다. 아래 예제 코드는 컬렉션에 하나의 레스토랑 문서를 삽입하는 작업을 보여줍니다.

```
Document article = new Document()
    .append("restaurantId", "REST-21G145")
    .append("name", "Future-proofed Intelligent Bronze Hat")
    .append("cuisine", "International")
    .append("rating", new Document()
        .append("average", 1.8)
        .append("totalReviews", 267))
    .append("features", Arrays.asList("Outdoor Seating", "Live Music"));

try {
    InsertOneResult result = collection.insertOne(article);
    System.out.println("Inserted document with the following id: " + result.getInsertedId());
} catch (MongoWriteException e) {
    // Handle duplicate key or other write errors
    System.err.println("Failed to insert document: " + e.getMessage());
    throw e;
} catch (MongoException e) {
    // Handle other MongoDB errors
    System.err.println("MongoDB error: " + e.getMessage());
    throw e;
}
```

`insertOne()`을 사용할 때는 적절한 오류 처리를 포함해야 합니다. 예를 들어 위 코드에서 “`restaurantId`”에는 고유한 인덱스가 있으므로 이 코드를 다시 실행하면 다음과 같은 `MongoWriteException`이 발생합니다.

```
Failed to insert document: Write operation error on server docdbCluster.docdb.amazonaws.com:27017. 
Write error: WriteError{code=11000, message='E11000 duplicate key error collection: Restaurants index: restaurantId_1', details={}}.
```

**insertMany()**

컬렉션에 많은 문서를 삽입하는 데 사용되는 기본 메서드는 insertMany() 및 `bulkWrite()`입니다.

`insertMany()` 메서드는 단일 작업으로 여러 문서를 삽입하는 가장 간단한 방법입니다. 문서 목록을 받아 컬렉션에 삽입합니다. 이 메서드는 서로 독립적이며 특별한 처리 또는 혼합 작업이 필요하지 않은 새 문서 배치를 삽입할 때 적합합니다. 다음 코드는 파일에서 JSON 문서를 읽고 컬렉션에 삽입하는 방법을 보여줍니다. `insertMany()` 함수는 삽입된 모든 문서의 ID를 가져오는 데 사용할 수 있는 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/result/InsertManyResult.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/result/InsertManyResult.html)`InsertManyResult` 객체를 반환합니다.

```
// Read JSON file content
String content = new String(Files.readAllBytes(Paths.get(jsonFileName)));
JSONArray jsonArray = new JSONArray(content);

// Convert JSON articles to Documents
List < Document > restaurants = new ArrayList < > ();
for (int i = 0; i < jsonArray.length(); i++) {
    JSONObject jsonObject = jsonArray.getJSONObject(i);
    Document doc = Document.parse(jsonObject.toString());
    restaurants.add(doc);
}
//insert documents in collection
InsertManyResult result = collection.insertMany(restaurants);

System.out.println("Count of inserted documents: " + result.getInsertedIds().size());
```

**[https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/bulk/package-summary.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/bulk/package-summary.html)**

`bulkWrite()` 메서드를 사용하여 단일 배치에서 여러 쓰기 작업(삽입, 업데이트, 삭제)을 수행할 수 있습니다. 다른 문서를 업데이트하는 동안 일부 문서를 삽입하는 등 단일 배치에서 다양한 유형의 작업을 수행해야 하는 경우 `bulkWrite()`를 사용할 수 있습니다. `bulkWrite()`는 순서가 지정된 배치 쓰기와 순서가 지정되지 않은 쓰기의 두 가지 유형을 지원합니다.
+ *순서가 지정된 작업 * - (기본값) Amazon DocumentDB는 쓰기 작업을 순차적으로 처리하고 오류가 처음 발생하면 중지합니다. 이후 작업이 이전 작업에 의존하는 경우와 같이 작업 순서가 중요한 경우에 유용합니다. 그러나 정렬된 작업은 일반적으로 정렬되지 않은 작업보다 느립니다. 순서가 지정된 작업의 경우 첫 번째 오류 시 배치가 중지되어 일부 작업이 처리되지 않을 수 있는 경우를 해결해야 합니다.
+ *순서가 지정되지 않은 작업 * - Amazon DocumentDB를 사용하면 데이터베이스에서 삽입을 단일 실행으로 처리할 수 있습니다. 문서 하나에 오류가 발생하는 경우 작업은 나머지 문서로 계속됩니다. 대량의 데이터를 삽입할 때 특히 유용하며 중복 키로 인해 일부 문서가 실패할 수 있는 데이터 마이그레이션 또는 대량 가져오기와 같은 일부 오류를 허용할 수 있습니다. 순서가 지정되지 않은 작업의 경우 일부 작업은 성공하고 일부 작업은 실패하는 부분적인 성공 시나리오를 해결해야 합니다.

`bulkWrite()` 메서드를 사용할 때는 몇 가지 필수 클래스가 필요합니다. 먼저 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/WriteModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/WriteModel.html) 클래스는 모든 쓰기 작업에 대한 기본 클래스 역할을 하며 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/InsertOneModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/InsertOneModel.html), [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/UpdateOneModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/UpdateOneModel.html), [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/UpdateManyModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/UpdateManyModel.html), [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/DeleteOneModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/DeleteOneModel.html), [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/DeleteManyModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/DeleteManyModel.html)과 같은 특정 구현을 통해 다양한 유형의 작업을 처리합니다.

[https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/BulkWriteOptions.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/BulkWriteOptions.html) 클래스는 순서가 지정된/순서가 지정되지 않은 실행 설정 또는 문서 검증 우회와 같은 대량 작업의 동작을 구성하는 데 필요합니다. [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/bulk/BulkWriteResult.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/bulk/BulkWriteResult.html) 클래스는 삽입, 업데이트 및 삭제된 문서 수를 포함하여 실행 결과에 대한 자세한 정보를 제공합니다.

오류 처리의 경우 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoBulkWriteException.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoBulkWriteException.html) 클래스는 대량 작업 중 발생하는 모든 장애에 대한 정보를 포함하는 반면, [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/bulk/BulkWriteError.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/bulk/BulkWriteError.html) 클래스는 개별 작업 실패에 대한 특정 세부 정보를 제공하므로 매우 중요합니다. 다음 코드는 단일 `bulkWrite()` 메서드 호출 실행 내에서 문서 목록을 삽입하고 단일 문서를 업데이트 및 삭제하는 예를 보여줍니다. 또한 코드는 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/BulkWriteOptions.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/BulkWriteOptions.html) 및 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/bulk/BulkWriteResult.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/bulk/BulkWriteResult.html) 작업 방법과 `bulkWrite()` 작업의 적절한 오류 처리를 보여줍니다.

```
List < WriteModel < Document >> bulkOperations = new ArrayList < > ();
// get list of 10 documents representing 10 restaurants
List < Document > restaurantsToInsert = getSampleData();

for (Document doc: restaurantsToInsert) {
    bulkOperations.add(new InsertOneModel < > (doc));
}
// Update operation
bulkOperations.add(new UpdateOneModel < > (
    new Document("restaurantId", "REST-Y2E9H5"),
    new Document("", new Document("stats.likes", 20))
    .append("", new Document("rating.average", 4.5))));
// Delete operation
bulkOperations.add(new DeleteOneModel < > (new Document("restaurantId", "REST-D2L431")));

// Perform bulkWrite operation
try {
    BulkWriteOptions options = new BulkWriteOptions()
        .ordered(false); // Allow unordered inserts

    BulkWriteResult result = collection.bulkWrite(bulkOperations, options);

    System.out.println("Inserted: " + result.getInsertedCount());
    System.out.println("Updated: " + result.getModifiedCount());
    System.out.println("Deleted: " + result.getDeletedCount());
} catch (MongoBulkWriteException e) {
    System.err.println("Bulk write error occurred: " + e.getMessage());
    // Log individual write errors
    for (BulkWriteError error: e.getWriteErrors()) {
        System.err.printf("Error at index %d: %s (Code: %d)%n", error.getIndex(), error.getMessage(),
            error.getCode());

        // Log the problematic document
        Document errorDoc = new Document(error.getDetails());
        if (errorDoc != null) {
            System.err.println("Problematic document: " + errorDoc);
        }
    }
} catch (Exception e) {
    System.err.println("Error during bulkWrite: " + e.getMessage());
}
```

**재시도 가능한 쓰기**

MongoDB와 달리 Amazon DocumentDB는 재시도 가능한 쓰기를 지원하지 않습니다. 따라서 특히 네트워크 문제 또는 임시 서비스 불가능을 처리하기 위해 애플리케이션에서 사용자 지정 재시도 로직을 구현해야 합니다. 일반적으로 잘 구현된 재시도 전략에는 재시도 간의 지연을 늘리고 총 재시도 횟수를 제한하는 작업이 포함됩니다. 오류 처리로 재시도 로직을 빌드하는 코드 샘플은 아래의 [재시도 로직을 사용한 오류 처리](#error-handling) 섹션을 참조하세요.

## DocumentDB 컬렉션에서 데이터 읽기 및 검색
<a name="reading-retrieving"></a>

Amazon DocumentDB에서 문서를 쿼리하는 작업은 데이터를 정확하게 검색하고 조작할 수 있는 몇 가지 주요 구성 요소를 중심으로 수행합니다. [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoCollection.html#find()](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoCollection.html#find()) 메서드는 MongoDB Java 드라이버의 기본 쿼리 API입니다. 결과를 필터링, 정렬 및 프로젝션하는 다양한 옵션을 통해 복잡한 데이터를 검색할 수 있습니다. `find()` 메서드 외에도 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Filters.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Filters.html) 및 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/FindIterable.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/FindIterable.html)은 MongoDB Java 드라이버에서 쿼리 작업을 위한 구성 요소를 제공하는 두 가지 다른 기본 구성 요소입니다.

`Filters` 클래스는 쿼리 필터를 구성하기 위한 능숙한 API를 제공하는 MongoDB Java 드라이버의 유틸리티 클래스입니다. 이 클래스는 다양한 쿼리 조건을 나타내는 `Bson` 객체의 인스턴스를 생성하는 정적 팩토리 메서드를 제공합니다. 가장 일반적으로 사용되는 메서드에는 균등 비교를 위한 `eq()`, 숫자 비교를 위한 `gt()`, `lt()`, `gte()`, `lte()`, 여러 조건 결합을 위한 `and()`, `or()`, 배열 멤버십 테스트를 위한 `in()`, `nin()`, 패턴 일치를 위한 `regex()` 등이 있습니다. 클래스는 유형이 안전하도록 설계되었으며 원시 문서 기반 쿼리에 비해 더 나은 컴파일 시간 확인을 제공하므로 Java 애플리케이션에서 DocumentDB 쿼리를 구성하는 데 선호되는 접근 방식입니다. 오류 처리는 견고하며 잘못된 필터 구성에 대해 명확한 예외가 발생합니다.

`FindIterable`은 `find()` 메서드의 결과를 처리하도록 설계된 특수 인터페이스입니다. 쿼리 실행을 구체화하고 제어하기 위한 풍부한 메서드 세트를 제공하여 메서드 체인을 위한 능숙한 API를 제공합니다. 인터페이스에는 반환된 문서 수 제한을 위한 `limit()`, 페이지 매김을 위한 `skip()`, 결과 순서 지정을 위한 `sort()`, 특정 필드 선택을 위한 `projection()`, 인덱스 선택을 위한 `hint()`와 같은 필수 쿼리 수정 메서드가 포함되어 있습니다. `FindIterable`의 배치, 건너뛰기 및 제한 작업은 데이터베이스에서 문서를 검색하고 처리하는 방법을 제어하는 데 도움이 되는 필수 페이지 매김 및 데이터 관리 도구입니다.

일괄 처리(`batchSize`)는 DocumentDB가 단일 네트워크 왕복으로 클라이언트에 반환하는 문서 수를 제어합니다. 배치 크기를 설정하면 DocumentDB는 일치하는 모든 문서를 한 번에 반환하지 않고 대신 지정된 배치 크기의 그룹으로 반환합니다.

건너뛰기를 사용하면 결과의 시작점을 오프셋하여 일치 항목을 반환하기 전에 지정된 수의 문서를 건너뛰도록 DocumentDB에 지시할 수 있습니다. 예를 들어 `skip(20)`은 처음 20개의 일치하는 문서를 우회합니다. 결과의 후속 페이지를 검색하려는 페이지 매김 시나리오에서 일반적으로 사용됩니다.

제한은 쿼리에서 반환할 수 있는 총 문서 수를 제한합니다. `limit(n)`을 지정하면 데이터베이스에 일치하는 항목이 더 있더라도 DocumentDB가 'n' 문서를 반환한 후 문서 반환을 중지합니다.

`FindIterable`은 Amazon DocumentDB에서 문서를 검색할 때 반복자와 커서 패턴을 모두 지원합니다. `FindIterable`을 반복자로 사용하면 문서를 느리게 로드할 수 있고 애플리케이션에서 요청할 때만 문서를 가져올 수 있다는 이점이 있습니다. 반복자 사용의 또 다른 이점은 클러스터에 대한 연결을 유지할 책임이 없으므로 연결을 명시적으로 닫을 필요가 없다는 점입니다.

`FindIterable`은 Amazon DocumentDB 쿼리로 작업할 때 커서 패턴을 사용할 수 있는 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoCursor.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoCursor.html) 지원도 제공합니다. `MongoCursor`는 데이터베이스 작업 및 리소스 관리를 제어하는 MongoDB Java 드라이버별 구현입니다. `AutoCloseable` 인터페이스를 구현하여 리소스 사용 try-with-resources 블록을 통해 명시적 리소스 관리를 가능하게 합니다. 이 블록은 데이터베이스 연결을 올바르게 닫고 서버 리소스를 확보하는 데 매우 중요합니다. 기본적으로 커서는 10분 후에 시간 초과되며 DocumentDB는 이 시간 초과 동작을 변경할 수 있는 옵션을 제공하지 않습니다. 배치 데이터로 작업할 때는 커서 시간이 초과되기 전에 다음 데이터 배치를 검색해야 합니다. `MongoCursor`를 사용할 때 한 가지 주요 고려 사항은 리소스 유출을 방지하기 위해 명시적으로 닫아야 한다는 것입니다.

이 섹션에서는 `find()`, `Filters`, `FindIterable`에 대한 몇 가지 예제가 나와 있습니다.

다음 코드 예제에서는 `find()`를 사용하여 “restaurantId” 필드를 통해 단일 문서를 검색하는 방법을 보여줍니다.

```
Document filter = new Document("restaurantId", "REST-21G145");
Document result = collection.find(filter).first();
```

`Filters`를 사용하면 컴파일 시간 오류 검사를 개선할 수 있지만 java 드라이버를 사용하면 `find()` 메서드에서 직접 `Bson` 필터를 지정할 수 있습니다. 다음 예제 코드는 `Bson` 문서를 `find()`에 전달합니다.

```
result = collection.find(new Document("$and", Arrays.asList(
    new Document("rating.totalReviews", new Document("$gt", 1000)),
    new Document("priceRange", "$$"))))
```

다음 예제 코드는 `find()`에서 `Filters` 클래스를 사용하는 몇 가지 예를 보여줍니다.

```
FindIterable < Document > results;

// Exact match
results = collection.find(Filters.eq("name", "Thai Curry Palace"));

// Not equal
results = collection.find(Filters.ne("cuisine", "Thai"));

// find an element in an array
results = collection.find(Filters.in("features", Arrays.asList("Private Dining")));

// Greater than
results = collection.find(Filters.gt("rating.average", 3.5));

// Between (inclusive)
results = collection.find(Filters.and(
    Filters.gte("rating.totalReviews", 100),
    Filters.lte("rating.totalReviews", 200)));
// AND
results = collection.find(Filters.and(
    Filters.eq("cuisine", "Thai"),
    Filters.gt("rating.average", 4.5)));

// OR
results = collection.find(Filters.or(
    Filters.eq("cuisine", "Thai"),
    Filters.eq("cuisine", "American")));


// All document where the Field exists
results = collection.find(Filters.exists("michelin"));

// Regex
results = collection.find(Filters.regex("name", Pattern.compile("Curry", Pattern.CASE_INSENSITIVE)));

// Find all document where the array contain the list of value regardless of its order
results = collection.find(Filters.all("features", Arrays.asList("Private Dining", "Parking")));

// Array size
results = collection.find(Filters.size("features", 4));
```

다음 예제에서는 `FindIterable` 객체에서 `sort()`, `skip()`, `limit()`, `batchSize()` 작업을 연결하는 방법을 보여줍니다. 이러한 작업이 제공되는 순서는 쿼리 성능에 영향을 미칩니다. 이러한 작업의 순서가 `sort()`, `projection()`, `skip()`, `limit()`, `batchSize()` 순이 되는 것이 가장 좋은 방법입니다.

```
FindIterable < Document > results = collection.find(Filters.gt("rating.totalReviews", 1000))
    // Sorting
    .sort(Sorts.orderBy(
        Sorts.descending("address.city"),
        Sorts.ascending("cuisine")))
    // Field selection
    .projection(Projections.fields(
        Projections.include("name", "cuisine", "priceRange"),
        Projections.excludeId()))

    // Pagination
    .skip(20)
    .limit(10)
    .batchSize(2);
```

다음 코드 예제는 `FindIterable`에서 반복자를 생성하는 방법을 보여줍니다. Java의 `forEach` 구문을 사용하여 결과 세트를 순회합니다.

```
collection.find(Filters.eq("cuisine", "American")).forEach(doc -> System.out.println(doc.toJson()));
```

마지막 `find()` 코드 예제에서는 문서 검색에 `cursor()`를 사용하는 방법을 보여줍니다. 코드가 시도 블록을 종료할 때 커서가 닫히도록 시도 블록에 커서를 생성합니다.

```
try (MongoCursor < Document > cursor = collection.find(Filters.eq("cuisine", "American"))
    .batchSize(25)
    .cursor()) {
    while (cursor.hasNext()) {
        Document doc = cursor.next();
        System.out.println(doc.toJson());
    }
} // Cursor automatically closed
```

## DocumentDB 컬렉션의 기존 문서 업데이트
<a name="updating-documents"></a>

Amazon DocumentDB는 기존 문서를 수정하고 존재하지 않을 때 새 문서를 삽입할 수 있는 유연하고 강력한 메커니즘을 제공합니다. MongoDB Java 드라이버는 단일 문서 업데이트를 위한 `updateOne()`, 여러 문서 업데이트를 위한 `updateMany()`, 전체 문서 교체를 위한 `replaceOne()` 등 여러 가지 업데이트 메서드를 제공합니다. 이 세 가지 메서드 외에도 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Updates.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Updates.html), [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/UpdateOptions.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/UpdateOptions.html) 및 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/result/UpdateResult.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/result/UpdateResult.html)는 MongoDB Java 드라이버의 업데이트 작업을 위한 구성 요소를 제공하는 다른 기본 구성 요소입니다.

MongoDB Java 드라이버의 `Updates` 클래스는 업데이트 연산자를 생성하기 위한 정적 팩토리 메서드를 제공하는 유틸리티 클래스입니다. 유형이 안전하고 읽기 가능한 방식으로 업데이트 작업을 구성하기 위한 기본 빌더 역할을 합니다. `set()`, `unset()`, `inc()`와 같은 기본 메서드를 사용하면 문서를 직접 수정할 수 있습니다. 여러 업데이트 작업을 원자적으로 실행하여 데이터 일관성을 보장할 수 있는 `Updates.combine()` 메서드를 사용하여 여러 작업을 결합하면 이 클래스의 성능이 분명해집니다.

`UpdateOptions`는 문서 업데이트 작업에 필수적인 사용자 지정 기능을 제공하는 MongoDB의 Java 드라이버에 있는 강력한 구성 클래스입니다. 이 클래스의 두 가지 중요한 측면은 업데이트 작업에 대한 업서트 및 배열 필터 지원을 제공한다는 점입니다. `upsert(true)`를 통해 활성화된 업서트 기능을 사용하면 업데이트 작업 중에 일치하는 문서를 찾을 수 없는 경우 새 문서를 생성할 수 있습니다. `arrayFilters()`를 통해 업데이트 작업은 특정 기준을 충족하는 배열 요소를 정확하게 업데이트할 수 있습니다.

MongoDB Java 드라이버의 `UpdateResult`는 업데이트 작업의 결과를 자세히 설명하는 피드백 메커니즘을 제공합니다. 이 클래스는 업데이트 기준과 일치하는 문서 수(`matchedCount`), 실제로 수정된 문서 수(`modifiedCount`), 업서트된 문서에 대한 정보(`upsertedId`)의 세 가지 주요 지표를 캡슐화합니다. 이러한 지표를 이해하는 것은 적절한 오류 처리, 업데이트 작업 확인 및 애플리케이션의 데이터 일관성 유지에 필수적입니다.

### 단일 문서 업데이트 및 교체
<a name="update-single-doc"></a>

DocumentDB에서는 updateOne() 메서드를 사용하여 단일 문서를 업데이트할 수 있습니다. 이 메서드는 업데이트할 문서를 식별하기 위해 일반적으로 `Filters` 클래스에서 제공하는 필터 파라미터, 업데이트할 필드를 결정하는 `Updat`e 파라미터, 업데이트에 대한 다양한 옵션을 설정하는 선택적 `UpdateOptions` 파라미터를 사용합니다. `updateOne()` 메서드를 사용하면 선택 기준과 일치하는 첫 번째 문서만 업데이트됩니다. 다음 예제 코드는 한 문서의 단일 필드를 업데이트합니다.

```
collection.updateOne(Filters.eq("restaurantId", "REST-Y2E9H5"),
    Updates.set("name", "Amazing Japanese sushi"));
```

한 문서에서 여러 필드를 업데이트하려면 다음 예제와 같이 `updateOne()` 및 `Update.combine()`을 함께 사용합니다. 이 예제에서는 문서의 배열에 항목을 추가하는 방법도 보여줍니다.

```
List<Bson> updates = new ArrayList<>();
// Basic field updates
updates.add(Updates.set("name", "Shanghai Best"));
// Array operations
updates.add(Updates.addEachToSet("features", Arrays.asList("Live Music")));
// Counter updates
updates.add(Updates.inc("rating.totalReviews", 10));
// Combine all updates
Bson combinedUpdates = Updates.combine(updates);
// Execute automic update with one call
collection.updateOne(Filters.eq("restaurantId","REST-1J83NH"), combinedUpdates);
```

다음 코드 예제에서는 데이터베이스에서 문서를 업데이트하는 방법을 보여줍니다. 지정된 문서가 없는 경우 작업은 자동으로 새 문서로 삽입합니다. 이 코드는 `UpdateResult` 객체를 통해 사용 가능한 지표를 사용하는 방법도 보여줍니다.

```
Bson filter = Filters.eq("restaurantId", "REST-0Y9GL0");
Bson update = Updates.set("cuisine", "Indian");
// Upsert operation
UpdateOptions options = new UpdateOptions().upsert(true);
UpdateResult result = collection.updateOne(filter, update, options);

if (result.getUpsertedId() != null) {
   	System.out.println("Inserted document with _id: " + result.getUpsertedId());
} else {
    	System.out.println("Updated " + result.getModifiedCount() + " document(s)");
}
```

다음 코드 예제에서는 개별 필드를 업데이트하는 대신 `replaceOne()` 메서드를 사용하여 기존 문서를 새 문서로 완전히 대체하는 방법을 보여줍니다. `replaceOne()` 메서드는 원본의 `_id` 필드만 보존하여 전체 문서를 덮어씁니다. 여러 문서가 필터 기준과 일치하는 경우 처음 발견된 문서만 대체됩니다.

```
Document newDocument = new Document()
                .append("restaurantId", "REST-0Y9GL0")
                .append("name", "Bhiryani Adda")
                .append("cuisine", "Indian")
                .append("rating", new Document()
                        .append("average", 4.8)
                        .append("totalReviews", 267))
                .append("features", Arrays.asList("Outdoor Seating", "Live Music"));

UpdateResult result = collection.replaceOne(
                    Filters.eq("restaurantId", "REST-0Y9GL0"),
                    newDocument);
System.out.printf("Modified %d document%n", result.getModifiedCount());
```

### 여러 문서 업데이트
<a name="update-multiple-docs"></a>

컬렉션에서 여러 문서를 동시에 업데이트하는 메서드에는 두 가지가 있습니다. `updateMany()` 메서드를 사용하거나 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/UpdateManyModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/UpdateManyModel.html) 및 `bulkWrite()` 메서드를 함께 사용할 수 있습니다. `updateMany()` 메서드는 필터 파라미터를 사용하여 업데이트할 문서를 선택하고, `Update` 파라미터를 사용하여 업데이트할 필드를 식별하며, 선택적 `UpdateOptions` 파라미터를 사용하여 업데이트 옵션을 지정합니다.

다음 예제 코드는 `updateMany()` 메서드의 사용 방법을 보여줍니다.

```
Bson filter = Filters.and(
    Filters.in("features", Arrays.asList("Private Dining")),
    Filters.eq("cuisine", "Thai"));
UpdateResult result1 = collection.updateMany(filter, Updates.set("priceRange", "$$$"));
```

다음 예제 코드는 동일한 업데이트를 사용하는 `bulkWrite()` 메서드를 보여줍니다.

```
BulkWriteOptions options = new BulkWriteOptions().ordered(false);
List < WriteModel < Document >> updates = new ArrayList < > ();
Bson filter = Filters.and(
    Filters.in("features", Arrays.asList("Private Dining")),
    Filters.eq("cuisine", "Indian"));
Bson updateField = Updates.set("priceRange", "$$$");
updates.add(new UpdateManyModel < > (filter, updateField));
BulkWriteResult result = collection.bulkWrite(updates, options);
System.out.printf("Modified %d document%n", result.getModifiedCount());
```

## DocumentDB 컬렉션에서 문서 제거
<a name="deleting-documents"></a>

MongoDB Java 드라이버는 단일 문서를 제거하기 위한 `deleteOne()` 및 특정 기준과 일치하는 여러 문서를 제거하기 위한 `deleteMany()`를 제공합니다. 업데이트와 마찬가지로 삭제 작업을 `bulkWrite()` 메서드와 함께 사용할 수도 있습니다. `deleteOne()` 및 `deleteMany()` 모두 삭제된 문서 수를 포함하여 작업 결과에 대한 정보를 제공하는 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/result/DeleteResult.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/result/DeleteResult.html) 객체를 반환합니다. 다음은 `deleteMany()`를 사용하여 여러 문서를 제거하는 예입니다.

```
Bson filter = Filters.and(
    Filters.eq("cuisine", "Thai"),
    Filters.lt("rating.totalReviews", 50));
DeleteResult result = collection.deleteMany(filter);
System.out.printf("Deleted %d document%n", result.getDeletedCount());
```

## 재시도 로직을 사용한 오류 처리
<a name="error-handling"></a>

Amazon DocumentDB에 대한 강력한 오류 처리 전략은 오류 분류를 재시도 가능(예: 네트워크 제한 시간, 연결 문제) 및 재시도 불가능(예: 인증 실패, 잘못된 쿼리)으로 구현해야 합니다. 재시도해야 하는 오류로 인한 작업 실패의 경우 각 재시도와 최대 재시도 간에 시간 지연을 구현해야 합니다. CRUD 작업은 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoException.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoException.html) 및 해당 하위 클래스를 포착하는 try-catch 블록에 있어야 합니다. 또한 운영 가시성을 위해 오류 모니터링 및 로깅을 포함해야 합니다. 다음은 재시도 오류 처리를 구현하는 방법을 보여주는 샘플 코드입니다.

```
int MAX_RETRIES = 3;
int INITIAL_DELAY_MS = 1000;
int retryCount = 0;

while (true) {
    try {
        crud_operation(); //perform crud that will throw MongoException or one of its subclass
        break;
    } catch (MongoException e) {
        if (retryCount < MAX_RETRIES) {
            retryCount++;
            long delayMs = INITIAL_DELAY_MS * (long) Math.pow(2, retryCount - 1);
            try {
                TimeUnit.MILLISECONDS.sleep(delayMs);
            } catch (InterruptedException t) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("Retry interrupted", t);
            }
            continue;
        } else
            throw new RuntimeException("Crud operation failed", e);
    }
}
```

# Java를 사용한 Amazon DocumentDB의 인덱스 관리
<a name="index-management-java"></a>

인덱스를 사용하면 Amazon DocumentDB 컬렉션에서 데이터를 효율적으로 검색할 수 있습니다. 인덱스가 없으면 DocumentDB는 컬렉션의 모든 문서를 스캔하여 지정된 쿼리를 충족하는 결과를 반환해야 합니다. 이 주제에서는 MongoDB Java 드라이버를 사용하여 인덱스를 생성, 삭제 및 나열하는 방법에 대한 정보를 제공합니다. 또한 쿼리에서 특정 인덱스가 사용되고 있는지 확인하는 방법과 특정 인덱스를 사용하도록 Amazon DocumentDB에 힌트를 제공하는 방법도 설명합니다.

**Topics**
+ [인덱스 생성](#creating-indexes)
+ [인덱스 삭제](#dropping-indes)
+ [인덱스 선택 결정 및 인덱스 힌트 제공](#w2aac43b9b7c17c13)

Amazon DocumentDB는 다양한 유형의 인덱스를 지원합니다. 지원되는 모든 인덱스에 대한 포괄적인 개요는 이 [블로그 게시물](https://aws.amazon.com/blogs/database/how-to-index-on-amazon-documentdb-with-mongodb-compatibility/)을 참조하세요.

## Java를 사용하여 인덱스 생성
<a name="creating-indexes"></a>

MongoDB Java 드라이버를 사용하여 Amazon DocumentDB에서 인덱스를 생성하는 두 가지 메커니즘은 `runCommand()`를 통한 방법과 단일 인덱스의 경우 `createIndex()` 메서드 또는 여러 인덱스의 경우 `createIndexes()` 메서드를 통한 방법입니다. `createIndex()` 및 `createIndexes()` 메서드를 사용하는 한 가지 이유는 인덱스 생성과 관련된 특정 오류를 포착하여 오류 처리를 개선할 수 있기 때문입니다. `runCommand()`에서 이러한 방법을 사용하는 또 다른 이유는 MongDB Java 드라이버가 인덱스 생성 및 조작을 위한 풍부한 지원 클래스 세트를 제공하기 때문입니다. 이러한 지원 클래스는 `createIndex()` 또는 `createIndexes()` 메서드를 사용하는 경우에만 사용할 수 있습니다. 다음 세 가지 지원 클래스가 있습니다.
+ **[https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Indexes.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Indexes.html)** - 이 클래스는 다양한 유형의 인덱스를 생성하기 위한 정적 팩토리 메서드를 제공하는 유틸리티 클래스 역할을 합니다. 복잡한 인덱스 정의를 생성하는 프로세스를 간소화하며 일반적으로 다른 인덱스 관련 클래스와 함께 사용됩니다.
+ **[https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html)** - 이 클래스는 인덱스 키 정의 및 해당 옵션을 모두 캡슐화하는 기본 클래스입니다. 인덱싱할 항목(키)과 인덱싱 방법( 옵션)을 결합한 전체 인덱스 사양을 나타냅니다. 이 클래스는 `createIndexes()` 메서드에 전달할 수 있는 인덱스 사양 모음을 정의할 수 있기 때문에 여러 인덱스를 동시에 생성할 때 특히 유용합니다.
+ **[https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexOptions.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexOptions.html)** - 인덱스 동작을 사용자 지정하기 위한 다양한 메서드를 제공하는 포괄적인 구성 클래스입니다. 여기에는 고유 인덱스, 희소 인덱스, 만료 시간(TTL) 및 부분 필터 표현식에 대한 설정이 포함됩니다. 메서드 체인을 통해 백그라운드 인덱스 구축 및 고유한 제약 조건과 같은 여러 옵션을 구성할 수 있습니다.

**단일 인덱스 생성**

이 예제에서는 백그라운드에서 `createIndex(`) 메서드를 사용하여 단일 인덱스를 생성하는 방법을 보여줍니다. 백그라운드 및 포그라운드 인덱스 생성을 이해하려면 [인덱스 빌드 유형](managing-indexes.md#index-build-types) 섹션을 참조하세요. 다음 코드 예제에서는 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexOptions.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexOptions.html)를 사용하여 백그라운드에서 이름이 “unique\$1restaurantId\$1idx”인 고유 인덱스를 생성합니다. 그러면 이 `IndexOptions` 객체가 `createIndex()` 메서드로 전달됩니다.

```
collection.createIndex(
    Indexes.ascending("restaurantId"),
    new IndexOptions()
        .unique(true)
        .name("unique_restaurantId_idx")
        .background(true));
```

**여러 인덱스 생성**

이 예제에서는 `createIndexes()` 메서드를 사용하여 여러 인덱스를 생성합니다. 먼저 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html) 객체를 사용하여 각 인덱스에 대한 옵션을 빌드한 다음 `IndexModel` 객체 목록을 `createIndexes()` 메서드에 전달합니다. 다음 코드 예제에서는 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Indexes.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/Indexes.html) 유틸리티 클래스를 사용하여 복합 인덱스를 생성하는 방법을 보여줍니다. 이 클래스는 오름차순 또는 내림차순 정렬 순서를 사용하여 인덱스를 생성할지 여부를 지정하는 데에도 사용됩니다. 여러 인덱스를 생성한 후 `listIndexes()` 메서드를 호출하여 인덱스 생성을 확인합니다.

```
// Single Field Index on cuisine
IndexModel singleIndex = new IndexModel(
    Indexes.ascending("cuisine"),
    new IndexOptions().name("cuisine_idx"));

// Compound Index
IndexModel compoundIndex = new IndexModel(
    Indexes.compoundIndex(
        Indexes.ascending("address.state"),
        Indexes.ascending("priceRange")),
    new IndexOptions().name("location_price_idx"));

// Build a list of IndexModel for the indexes
List < IndexModel > indexes = Arrays.asList(
    singleIndex,
    compoundIndex
);

collection.createIndexes(indexes);

// Verify created indexes
collection.listIndexes().forEach(index - > System.out.println("Created index: " + index.toJson()));
```

**희소 인덱스 및 부분 인덱스 생성**

이 예제에서는 각 인덱스 유형에 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/IndexModel.html)을 생성하여 희소 인덱스와 부분 인덱스를 생성합니다.

```
// Sparse Index Model, this will identify only those documents that have a
// michelin star rating
IndexModel sparseIndex = new IndexModel(
    Indexes.ascending("michelin.star"),
    new IndexOptions()
    .name("michelin_sparse_idx")
    .sparse(true));

// Partial Index Model where the restaurant is active and has a rating of 4 and above
IndexModel partialIndex = new IndexModel(
    Indexes.ascending("rating.average"),
    new IndexOptions()
    .name("high_rated_active_idx")
    .partialFilterExpression(
        Filters.and(
            Filters.eq("isActive", true),
            Filters.gte("rating.average", 4.0))));
```

**텍스트 인덱스 생성**

이 예제에서는 텍스트 인덱스를 생성하는 방법을 보여줍니다. 컬렉션에는 하나의 텍스트 인덱스만 허용되지만 하나의 텍스트 인덱스는 여러 필드를 포함하는 복합 인덱스일 수 있습니다. 텍스트 인덱스에서 여러 필드를 사용하는 경우 인덱스의 각 필드에 가중치를 할당할 수도 있습니다. 배열 필드의 텍스트 인덱스는 Amazon DocumentDB에서 지원되지 않으며 복합 텍스트 인덱스에 최대 30개의 필드를 사용할 수 있지만 세 개의 필드만 가중치를 할당할 수 있습니다.

```
IndexModel textIndex = new IndexModel(
    new Document()
    .append("name", "text")
    .append("description", "text")
    .append("cuisine", "text"),
    new IndexOptions()
    .name("restaurant_text_idx")
    .weights(new Document()
        .append("name", 10) // Restaurant name gets highest weight
        .append("description", 5) // Description get medium weight
        .append("cuisine", 2) // Cuisine type gets low weight
    ));

collection.createIndex(textIndex.getKeys(), textIndex.getOptions());
```

**`runCommand()`를 사용하여 인덱스 생성**

Amazon DocumentDB는 병렬 인덱스 생성을 지원하여 인덱스를 생성하는 데 걸리는 시간을 줄입니다. 병렬 인덱싱은 여러 동시 작업자를 사용합니다. 인덱스 생성에 사용되는 기본 작업자는 2명입니다. 이 [블로그 게시물](https://aws.amazon.com/blogs/database/unlock-the-power-of-parallel-indexing-in-amazon-documentdb/)에서는 병렬 인덱싱에 대한 심층적인 설명을 제공합니다. 현재 MongDB Java 드라이버는 `createIndex()` 또는 `createIndexes()`를 사용할 때 작업자 옵션 지정을 지원하지 않으므로 작업자를 지정하는 유일한 방법은 `runCommand`를 통하는 것입니다. 다음 코드 예제에서는 `runCommand`를 사용하여 작업자를 4명으로 늘리는 인덱스를 생성하는 방법을 보여줍니다.

```
Document command = new Document("createIndexes", "Restaurants")
    .append("indexes", Arrays.asList(
        new Document("key", new Document("name", 1))
        .append("name", "restaurant_name_idx")
        .append("workers", 4) // Specify number of workers
    ));

Document commendResult = connectedDB.runCommand(command);
```

## 인덱스 삭제
<a name="dropping-indes"></a>

MongoDB Java 드라이버는 인덱스를 삭제하는 여러 방법을 제공하여 다양한 시나리오와 기본 설정에 맞게 조정할 수 있습니다. 이름이나 키 사양별으로 인덱스를 삭제하거나 모든 인덱스를 한 번에 삭제할 수 있습니다. 컬렉션 객체에서 메서드 `dropIndex()` 및 `dropIndexes()`를 호출하여 인덱스를 삭제할 수 있습니다. 이름별로 인덱스를 삭제할 때는 특히 복합 또는 자동 생성된 인덱스의 경우 항상 직관적이지 않을 수 있는 올바른 인덱스 이름을 사용해야 합니다. 존재하지 않는 인덱스를 삭제하려고 하면 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoCommandException.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/MongoCommandException.html)이 발생합니다. `default _id` 인덱스는 컬렉션 내에서 문서 고유성을 보장하기 때문에 삭제할 수 없습니다.

다음 코드 예제에서는 인덱스가 생성되는 필드 이름을 제공하거나 모든 인덱스를 삭제하여 인덱스를 삭제하는 방법을 보여줍니다.

```
String indexName = "unique_restaurantId_idx";
Document keys = new Document("cuisine", 1);
// Drop index by name
collection.dropIndex(indexName);
            
// Drop index by keys
collection.dropIndex(keys);
            
// Drop all indexes
collection.dropIndexes();
```

여러 키를 사용하여 인덱스를 삭제할 때는 지정된 모든 키가 포함된 복합 인덱스가 있고 키 순서가 올바른지 확인합니다. 위의 인덱스 생성 예제 코드는 "cuisine" 및 기능에 대한 복합 키를 보여줍니다. 해당 복합 키를 삭제하려고 시도하지만 해당 순서가 생성에 사용된 순서가 아닌 경우 MongoCommnadException 오류는 다음과 같이 발생합니다.

```
Document keys = new Document("features", 1)
    .append("cuisine", 1);
try {
    // Drop index by keys
    collection.dropIndex(keys);
    System.out.println("Successfully dropped index with keys: " + keys.toJson());

} catch (MongoCommandException commErr) {
    System.out.println("Error dropping index: " + commErr.getErrorMessage());
    throw new RuntimeException("MongoCommandException was thrown while dropping index", commErr);
}
```

다음 오류가 표시됩니다.

```
Error dropping index: Cannot drop index: index not found.
Tests run: 3, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.819 sec <<< FAILURE!
com.amazon.docdb.guide.DocDBGuideTest.testindexGuide()  Time elapsed: 0.817 sec  <<< FAILURE!
org.opentest4j.AssertionFailedError: Unexpected exception thrown: java.lang.RuntimeException: MongoCommandException was thrown while dropping index
```

## 인덱스 선택 결정 및 인덱스 힌트 제공
<a name="w2aac43b9b7c17c13"></a>

Amazon DocumentDB에서 설명 기능을 사용하는 작업은 쿼리 성능 및 인덱스 사용량을 이해하는 데 매우 중요합니다. 쿼리를 실행할 때 `explain()` 메서드를 추가하여 사용 중인 인덱스를 포함하여 쿼리 계획에 대한 자세한 정보를 얻을 수 있습니다. `explain()` 출력은 쿼리 실행 단계, 검사된 문서 수, 각 단계에 소요된 시간에 대한 인사이트를 제공합니다. 이 정보는 특정 인덱스가 효과적으로 사용되고 있는지 또는 쿼리가 다른 인덱스 구조의 이점을 누릴 수 있는지 식별하는 데 매우 중요합니다.

`explain()` 메서드는 `find()` 메서드와 함께 연결할 수 있습니다. `explain()` 메서드는 `explain()`에서 반환되는 세부 수준을 결정하는 선택적 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/ExplainVerbosity.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/ExplainVerbosity.html) 열거형을 사용할 수 있습니다. 현재 DocumentDB에서는 `EXECUTION_STATS` 및 `QUERY_PLANNER` 열거자만 지원됩니다. 다음 코드 예제에서는 특정 쿼리에 대한 쿼리 플래너를 가져오는 방법을 보여줍니다.

```
// Query we want to analyze
Document query = new Document()
    .append("cuisine", "Thai")
    .append("rating.average", new Document("$gte", 4.0));


Document allPlansExplain = collection.find(query).explain(ExplainVerbosity.QUERY_PLANNER);
System.out.println("All Plans Explain:\n" + allPlansExplain.toJson());
```

쿼리 플래너 세부 수준에 대해 다음 JSON 문서가 반환됩니다.

```
{
  "queryPlanner": {
    "plannerVersion": 1,
    "namespace": "ProgGuideData.Restaurants",
    "winningPlan": {
      "stage": "IXSCAN",
      "indexName": "cuisine_idx",
      "direction": "forward"
    }
  },
  "serverInfo": {
    "host": "guidecluster3",
    "port": 27017,
    "version": "5.0.0"
  },
  "ok": 1,
  "operationTime": {
    "$timestamp": {
      "t": 1739221668,
      "i": 1
    }
  }
}
```

Amazon DocumentDB가 특정 인덱스를 사용하도록 영향을 미치거나 강제하는 몇 가지 옵션이 있습니다. `hint()` 및 `hintString()` 메서드를 사용하면 쿼리에 사용할 인덱스를 명시적으로 지정하여 쿼리 최적화 프로그램의 기본 인덱스 선택 동작을 재정의할 수 있습니다. DocumentDB의 쿼리 최적화 프로그램은 일반적으로 인덱스 선택에 적합한 선택이지만 왜곡된 데이터를 처리하거나 인덱스 성능을 테스트하는 등 `hint()` 또는 `hintString()`을 통해 특정 인덱스를 강제 적용하는 경우 유용할 수 있습니다.

다음 코드 예제에서는 위 코드에서 실행된 동일한 쿼리에 복합 인덱스 “cuisine\$1features\$1idx”를 강제로 사용합니다.

```
// Query we want to analyze
Document query = new Document()
    .append("cuisine", "Thai")
    .append("rating.average", new Document("$gte", 4.0));

List < Document > queryDocs = new ArrayList < > ();
collection.find(query).hintString("cuisine_features_idx").forEach(doc - > queryDocs.add(doc));
```

# Amazon DocumentDB 및 Java를 사용한 이벤트 기반 프로그래밍
<a name="event-driven-programming"></a>

Amazon DocumentDB의 컨텍스트에서 이벤트 기반 프로그래밍은 데이터베이스 변경이 후속 비즈니스 로직 및 프로세스를 트리거하는 기본 이벤트 생성기 역할을 하는 강력한 아키텍처 패턴을 나타냅니다. DocumentDB 컬렉션에 레코드가 삽입, 업데이트 또는 삭제되면 이러한 변경 사항은 다양한 다운스트림 프로세스, 알림 또는 데이터 동기화 작업을 자동으로 시작하는 이벤트 역할을 합니다. 이 패턴은 여러 애플리케이션 또는 서비스가 데이터 변경에 실시간으로 대응해야 하는 최신 분산 시스템에서 특히 유용합니다. DocumentDB에서 이벤트 기반 프로그래밍을 구현하는 주요 메커니즘은 변경 스트림입니다.

**참고**  
이 설명서에서는 작업 중인 컬렉션에서 변경 스트림을 활성화했다고 가정합니다. 컬렉션에서 변경 스트림을 활성화하는 방법은 [Amazon DocumentDB에서 변경 스트림 사용](change_streams.md) 섹션을 참조하세요.

**Java 애플리케이션에서 변경 스트림 작업**

MongoDB의 Java 드라이버에 있는 `watch()` 메서드는 Amazon DocumentDB에서 실시간 데이터 변경을 모니터링하는 기본 메커니즘입니다. `watch()` 메서드는 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClient.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClient.html), [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoDatabase.html#watch(com.mongodb.client.ClientSession,java.lang.Class)](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoDatabase.html#watch(com.mongodb.client.ClientSession,java.lang.Class)) 및 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoCollection.html#watch()](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoCollection.html#watch()) 객체에 의해 호출될 수 있습니다.

`watch()` 메서드는 업데이트 시 전체 문서 조회, 신뢰성을 위한 재개 토큰과 타임스탬프 제공, 변경 사항 필터링을 위한 파이프라인 집계 단계 등 다양한 구성 옵션을 지원하는 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)) 인스턴스를 반환합니다.

[https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp))은 핵심 Java 인터페이스 `Iterable`을 구현하며 `forEach()`와 함께 사용할 수 있습니다. `forEach()`를 사용하여 이벤트를 캡처하려면 변경된 이벤트를 처리하는 `forEach()`에 콜백 함수를 전달합니다. 다음 코드 조각은 컬렉션에서 변경 스트림을 열어 변경 이벤트 모니터링을 시작하는 방법을 보여줍니다.

```
ChangeStreamIterable < Document > iterator = collection.watch();
iterator.forEach(event - > {
    System.out.println("Received a change: " + event);
});
```

모든 변경 이벤트를 순회하는 또 다른 방법은 클러스터와의 연결을 유지하고 새 변경 이벤트가 발생할 때마다 지속적으로 수신하는 커서를 여는 것입니다. 변경 스트림 커서를 가져오려면 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)) 객체의 `cursor()` 메서드를 사용합니다. 다음 코드 예제에서는 커서를 사용하여 변경 이벤트를 모니터링하는 방법을 보여줍니다.

```
try (MongoChangeStreamCursor < ChangeStreamDocument < Document >> cursor = collection.watch().cursor()) {
    System.out.println(cursor.tryNext());
}
```

가장 좋은 방법은 try-with-resource 문에서 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoChangeStreamCursor.html#getResumeToken()](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/MongoChangeStreamCursor.html#getResumeToken())를 생성하거나 커서를 수동으로 닫는 것입니다. [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp))에서 `cursor()` 메서드를 호출하면 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/changestream/ChangeStreamDocument.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/changestream/ChangeStreamDocument.html) 객체를 통해 생성된 `MongoChangeStreamCursor`가 반환됩니다.

[https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/changestream/ChangeStreamDocument.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/changestream/ChangeStreamDocument.html) 클래스는 스트림의 개별 변경 이벤트를 나타내는 중요한 구성 요소입니다. 여기에는 작업 유형(삽입, 업데이트, 삭제, 교체), 문서 키, 네임스페이스 정보, 사용 가능한 경우 전체 문서 콘텐츠를 포함하여 각 수정에 대한 자세한 정보가 포함되어 있습니다. 클래스는 변경 유형을 결정하는 `getOperationType()`, 전체 문서 상태에 액세스하는 `getFullDocument()`, 수정된 문서를 식별하는 `getDocumentKey()` 등 변경 이벤트의 다양한 측면에 액세스하는 방법을 제공합니다.

[https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/changestream/ChangeStreamDocument.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/changestream/ChangeStreamDocument.html) 객체는 두 가지 중요한 정보인 재개 토큰과 변경 이벤트 시간을 제공합니다.

DocumentDB 변경 스트림에서 토큰 재개 및 시간 기반 작업은 연속성을 유지하고 과거 데이터 액세스를 관리하기 위한 중요한 메커니즘을 제공합니다. 재개 토큰은 각 변경 이벤트에 대해 생성된 고유 식별자로, 애플리케이션이 연결 해제 또는 실패 후 특정 지점에서 변경 스트림 처리를 다시 시작할 수 있도록 허용하는 북마크 역할을 합니다. 변경 스트림 커서가 생성되면 `resumeAfter()` 옵션을 통해 이전에 저장된 재개 토큰을 사용하여 처음부터 시작하거나 이벤트를 손실하는 대신 스트림이 중단된 위치에서 계속될 수 있습니다.

변경 스트림의 시간 기반 작업은 변경 이벤트 모니터링의 시작점을 관리하기 위한 다양한 접근 방식을 제공합니다. `startAtOperationTime()` 옵션을 사용하면 특정 타임스탬프에서 또는 그 이후에 발생한 변경 사항을 볼 수 있습니다. 이러한 시간 기반 기능은 과거 데이터 처리, 특정 시점으로 복구 또는 시스템 간 동기화가 필요한 시나리오에서 특히 유용합니다.

다음 코드 예제에서는 삽입 문서와 연결된 이벤트를 검색하고 재개 토큰을 캡처한 다음 해당 토큰을 제공하여 삽입 이벤트 후 이벤트 모니터링을 시작합니다. 이벤트는 업데이트 이벤트와 연결되어 업데이트가 발생한 클러스터 시간을 가져오고 해당 타임스탬프를 추가 처리를 위한 시작점으로 사용합니다.

```
BsonDocument resumeToken;
BsonTimestamp resumeTime;

try (MongoChangeStreamCursor < ChangeStreamDocument < Document >> cursor = collection.watch().cursor()) {
    System.out.println("****************** Insert Document *******************");
    ChangeStreamDocument < Document > insertChange = cursor.tryNext();
    resumeToken = insertChange.getResumeToken();
    printJson(cursor.tryNext());
}
try (MongoChangeStreamCursor < ChangeStreamDocument < Document >> cursor = collection.watch()
    .resumeAfter(resumeToken)
    .cursor()) {
    System.out.println("****************** Update Document *******************");
    ChangeStreamDocument < Document > insertChange = cursor.tryNext();
    resumeTime = insertChange.getClusterTime();
    printJson(cursor.tryNext());
}
try (MongoChangeStreamCursor < ChangeStreamDocument < Document >> cursor = collection.watch()
    .startAtOperationTime(resumeTime)
    .cursor()) {
    System.out.println("****************** Delete Document *******************");
    printJson(cursor.tryNext());
  }
```

기본적으로 변경 사항 업데이트 이벤트에는 전체 문서가 포함되지 않으며 변경된 내용만 포함됩니다. 업데이트된 전체 문서에 액세스해야 하는 경우 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-sync/com/mongodb/client/ChangeStreamIterable.html#startAtOperationTime(org.bson.BsonTimestamp)) 객체에서 `fullDocument()` 메서드를 호출할 수 있습니다. 업데이트 이벤트에 대해 전체 문서를 반환하도록 요청하면 스트림 변경 호출이 이루어진 시점에 존재하는 문서가 반환된다는 점에 유의하세요.

이 메서드는 [https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/changestream/FullDocument.html](https://mongodb.github.io/mongo-java-driver/5.3/apidocs/mongodb-driver-core/com/mongodb/client/model/changestream/FullDocument.html) 열거형을 파라미터로 사용합니다. 현재 Amazon DocumentDB는 DEFAULT 및 `UPDATE_LOOKUP` 값만 지원합니다. 다음 코드 조각은 변경 사항을 감시하기 시작할 때 업데이트 이벤트에 대한 전체 문서를 요청하는 방법을 보여줍니다.

```
try (MongoChangeStreamCursor < ChangeStreamDocument < Document >> cursor = collection.watch().fullDocument(FullDocument.UPDATE_LOOKUP).cursor())
```