

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

# AWS SDK for PHP 버전 3에서의 Promise
<a name="guide_promises"></a>

AWS SDK for PHP는 **promise**를 사용하여 비동기 워크플로를 허용하며, 이 비동기성 덕분에 HTTP 요청을 동시에 전송할 수 있습니다. SDK에서 사용하는 promise 사양은 [Promises/A\$1](https://promisesaplus.com/)입니다.

## Promise란 무엇입니까?
<a name="what-is-a-promise"></a>

*promise*는 비동기 작업의 최종 결과를 나타냅니다. promise와 상호 작용하는 기본 방법은 `then` 메서드를 통하는 것입니다. 이 메서드는 promise의 최종 값 또는 promise를 이행할 수 없는 이유를 수신할 콜백을 등록합니다.

AWS SDK for PHP는 promise 구현을 위해 [guzzlehttp/promises](https://github.com/guzzle/promises) Composer 패키지를 이용합니다. Guzzle promise는 차단 및 비차단을 지원하며 비차단 이벤트 루프와 함께 사용할 수 있습니다.

**참고**  
HTTP 요청은 단일 스레드를 사용하여 AWS SDK for PHP에서 동시에 전송됩니다. 이 경우 상태 변경(예: promise 이행 또는 거부)에 응답하면서 하나 이상의 HTTP 요청을 전송하기 위해 비차단 호출이 사용됩니다.

## SDK의 Promise
<a name="promises-in-the-sdk"></a>

Promise는 SDK 전체에서 사용됩니다. 예를 들어 [페이지네이터](guide_paginators.md#async-paginators), [waiter](guide_waiters.md#async-waiters), [명령 풀](guide_commands.md#command-pool), [멀티파트 업로드](s3-multipart-upload.md), [S3 디렉터리/버킷 전송](s3-transfer.md) 등 SDK에서 제공하는 대부분의 상위 수준 추상화에서 프로미스가 사용됩니다.

`Async` 접미사가 있는 메서드를 호출하면 SDK가 제공하는 모든 클라이언트는 promise를 반환합니다. 예를 들어, 다음 코드는 Amazon DynamoDB`DescribeTable` 작업의 결과를 가져오기 위해 promise를 생성하는 방법을 보여 줍니다.

```
$client = new Aws\DynamoDb\DynamoDbClient([
    'region'  => 'us-west-2',
    'version' => 'latest',
]);

// This will create a promise that will eventually contain a result
$promise = $client->describeTableAsync(['TableName' => 'mytable']);
```

`describeTable` 또는 `describeTableAsync`를 호출할 수 있습니다. 이러한 메서드는 클라이언트와 연결된 API 모델 및 `__call` 번호로 구동되는 클라이언트의 magic `version` 메서드입니다. `describeTable` 접미사 없이 `Async`과 같은 메서드를 호출하면 클라이언트는 HTTP 요청을 전송하는 동안 차단하며 `Aws\ResultInterface` 객체를 반환하거나 `Aws\Exception\AwsException`을 발생시킵니다. 작업 이름에 `Async`(예: `describeTableAsync`) 접미사를 붙이면 클라이언트는 결국 `Aws\ResultInterface` 객체를 통해 이행되거나 `Aws\Exception\AwsException`을 통해 거부되는 promise를 생성합니다.

**중요**  
promise가 반환될 때 결과가 이미 도착했거나(예: 모의(mock) 핸들러 사용 시) HTTP 요청이 시작되지 않았을 수 있습니다.

`then` 메서드를 사용하여 콜백을 promise에 등록할 수 있습니다. 이 메서드는 모두 선택적인 `$onFulfilled` 및 `$onRejected`라는 두 개의 콜백을 받습니다. `$onFulfilled` 콜백은 promise가 이행되는 경우에 호출되고 `$onRejected` 콜백은 promise가 거부되는 경우(즉 실패한 경우)에 호출됩니다.

```
$promise->then(
    function ($value) {
        echo "The promise was fulfilled with {$value}";
    },
    function ($reason) {
        echo "The promise was rejected with {$reason}";
    }
);
```

### 동시에 명령 실행
<a name="executing-commands-concurrently"></a>

여러 개의 promise를 동시에 실행하도록 함께 작성할 수 있습니다. SDK를 비차단 이벤트 루프와 통합하거나 여러 promise를 빌드하고 이러한 promise가 동시에 완료될 때까지 대기하여 이렇게 할 수 있습니다.

```
use GuzzleHttp\Promise\Utils;

$sdk = new Aws\Sdk([
    'version' => 'latest',
    'region'  => 'us-east-1'
]);

$s3 = $sdk->createS3();
$ddb = $sdk->createDynamoDb();

$promises = [
    'buckets' => $s3->listBucketsAsync(),
    'tables'  => $ddb->listTablesAsync(),
];

// Wait for both promises to complete.
$results = Utils::unwrap($promises);

// Notice that this method will maintain the input array keys.
var_dump($results['buckets']->toArray());
var_dump($results['tables']->toArray());
```

**참고**  
[CommandPool](guide_commands.md#command-pool)은 여러 API 작업을 동시에 실행하기 위한 더욱 강력한 메커니즘을 제공합니다.

## Promise 연결
<a name="chaining-promises"></a>

promise의 가장 좋은 측면 중 하나는 작성 가능하기 때문에 변환 파이프라인을 생성할 수 있다는 것입니다. Promise는 `then` 콜백을 후속 `then` 콜백과 연결하여 작성됩니다. `then` 메서드의 반환 값은 제공된 콜백의 결과를 기반으로 이행되거나 거부되는 promise입니다.

```
$promise = $client->describeTableAsync(['TableName' => 'mytable']);

$promise
    ->then(
        function ($value) {
            $value['AddedAttribute'] = 'foo';
            return $value;
        },
        function ($reason) use ($client) {
            // The call failed. You can recover from the error here and
            // return a value that will be provided to the next successful
            // then() callback. Let's retry the call.
            return $client->describeTableAsync(['TableName' => 'mytable']);
        }
    )->then(
        function ($value) {
            // This is only invoked when the previous then callback is
            // fulfilled. If the previous callback returned a promise, then
            // this callback is invoked only after that promise is
            // fulfilled.
            echo $value['AddedAttribute']; // outputs "foo"
        },
        function ($reason) {
            // The previous callback was rejected (failed).
        }
    );
```

**참고**  
promise 콜백의 반환 값은 다운스트림 promise에 제공되는 `$value` 인수입니다. 다운스트림 promise 체인에 값을 제공하려는 경우 콜백 함수에서 값을 반환해야 합니다.

### 거부 전송
<a name="rejection-forwarding"></a>

promise가 거부될 때 호출할 콜백을 등록합니다. 콜백에서 예외가 발생하면 promise가 예외를 통해 거부되고 체인의 다음 promise가 예외를 통해 거부됩니다. `$onRejected` 콜백에서 값을 성공적으로 반환하면 `$onRejected` 콜백의 반환 값을 사용하여 promise 체인의 다음 promise가 이행됩니다.

## Promise 대기
<a name="waiting-on-promises"></a>

promise의 `wait` 메서드를 사용하여 promise를 동기적으로 강제 완료할 수 있습니다.

```
$promise = $client->listTablesAsync();
$result = $promise->wait();
```

promise의 `wait` 함수를 호출하는 동안 예외가 발생하면 promise가 예외를 통해 거부되고 예외가 발생합니다.

```
use Aws\Exception\AwsException;

$promise = $client->listTablesAsync();

try {
    $result = $promise->wait();
} catch (AwsException $e) {
    // Handle the error
}
```

이행되지 않은 promise에 대해 `wait`를 호출하면 wait 함수가 트리거되지 않습니다. 이전에 전달한 값만 반환됩니다.

```
$promise = $client->listTablesAsync();
$result = $promise->wait();
assert($result ### $promise->wait());
```

거부된 promise에 대해 `wait`를 호출하면 예외가 발생합니다. 거부 이유가 `\Exception`의 인스턴스인 경우 이유가 발생합니다. 그렇지 않으면 `GuzzleHttp\Promise\RejectionException`이 발생하고 예외의 `getReason` 메서드를 호출하여 이유를 얻을 수 있습니다.

**참고**  
AWS SDK for PHP의 API 작업 호출은 `Aws\Exception\AwsException` 클래스의 하위 클래스를 통해 거부됩니다. 하지만 거부 이유를 변경하는 사용자 지정 미들웨어 때문에 `then` 메서드에 전달된 이유가 다를 수 있습니다.

## Promise 취소
<a name="canceling-promises"></a>

promise의 `cancel()` 메서드를 사용하여 promise를 취소할 수 있습니다. promise가 이미 해결된 경우 `cancel()`을 호출해도 효과가 없습니다. 한 promise를 취소하면 해당 promise와 해당 promise에서 전달을 대기하고 있는 모든 promise가 취소됩니다. 취소된 promise는 `GuzzleHttp\Promise\RejectionException`을 통해 거부됩니다.

## Promise 결합
<a name="combining-promises"></a>

promise를 집계 promise로 결합하여 더욱 정교한 워크플로를 빌드할 수 있습니다. `guzzlehttp/promise` 패키지에는 promise를 결합하는 데 사용할 수 있는 다양한 함수가 포함되어 있습니다.

[namespace-GuzzleHttp.Promise](https://docs.aws.amazon.com/aws-sdk-php/v3/api/namespace-GuzzleHttp.Promise.html)에서 모든 promise 컬렉션 함수에 대한 API 설명서를 참조할 수 있습니다.

### each 및 each\$1limit
<a name="each-and-each-limit"></a>

고정된 풀 크기로 동시에 수행할 `Aws\CommandInterface` 명령이 포함된 작업 대기열이 있는 경우 [CommandPool](guide_commands.md#command-pool)을 사용하세요(명령은 메모리에 있거나 지연 반복기에 의해 생성될 수 있음). `CommandPool`을 사용하면 제공된 반복자가 소진될 때까지 고정된 수의 명령이 동시에 전송됩니다.

`CommandPool`은 동일한 클라이언트가 실행하는 명령에 대해서만 작동합니다. `GuzzleHttp\Promise\each_limit` 함수를 사용하여 다른 클라이언트의 전송 명령을 고정된 풀 크기로 동시에 수행할 수 있습니다.

```
use GuzzleHttp\Promise;

$sdk = new Aws\Sdk([
    'version' => 'latest',
    'region'  => 'us-west-2'
]);

$s3 = $sdk->createS3();
$ddb = $sdk->createDynamoDb();

// Create a generator that yields promises
$promiseGenerator = function () use ($s3, $ddb) {
    yield $s3->listBucketsAsync();
    yield $ddb->listTablesAsync();
    // yield other promises as needed...
};

// Execute the tasks yielded by the generator concurrently while limiting the
// maximum number of concurrent promises to 5
$promise = Promise\each_limit($promiseGenerator(), 5);

// Waiting on an EachPromise will wait on the entire task queue to complete
$promise->wait();
```

### Promise 코루틴
<a name="promise-coroutines"></a>

Guzzle promise 라이브러리의 더욱 강력한 기능 중 하나는 비동기 워크플로 쓰기를 기존 방식의 동기 워크플로 쓰기와 비슷하게 만드는 promise 코루틴을 사용할 수 있다는 것입니다. 실제로 AWS SDK for PHP는 대부분의 상위 수준 추상화에서 코루틴 promise를 사용합니다.

여러 개의 버킷을 생성하고 버킷이 사용 가능하게 되면 버킷에 파일을 업로드하려고 하며, 이 모든 작업이 최대한 빠르게 수행되도록 모두 동시에 수행하려는 경우를 가정해 봅니다. `all()` promise 함수를 사용하여 여러 개의 코루틴 promise를 함께 결합하면 이 작업을 쉽게 수행할 수 있습니다.

```
use GuzzleHttp\Promise;

$uploadFn = function ($bucket) use ($s3Client) {
    return Promise\coroutine(function () use ($bucket, $s3Client) {
        // You can capture the result by yielding inside of parens
        $result = (yield $s3Client->createBucket(['Bucket' => $bucket]));
        // Wait on the bucket to be available
        $waiter = $s3Client->getWaiter('BucketExists', ['Bucket' => $bucket]);
        // Wait until the bucket exists
        yield $waiter->promise();
        // Upload a file to the bucket
        yield $s3Client->putObjectAsync([
            'Bucket' => $bucket,
            'Key'    => '_placeholder',
            'Body'   => 'Hi!'
        ]);
    });
};

// Create the following buckets
$buckets = ['amzn-s3-demo-bucket1', 'amzn-s3-demo-bucket2', 'amzn-s3-demo-bucket3'];
$promises = [];

// Build an array of promises
foreach ($buckets as $bucket) {
    $promises[] = $uploadFn($bucket);
}

// Aggregate the promises into a single "all" promise
$aggregate = Promise\all($promises);

// You can then() off of this promise or synchronously wait
$aggregate->wait();
```