

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

# AWS SDK for PHP 버전 3의 명령 객체
<a name="guide_commands"></a>

는 [명령 패턴을](http://en.wikipedia.org/wiki/Command_pattern) AWS SDK for PHP 사용하여 나중에 HTTP 요청을 전송하는 데 사용할 파라미터와 핸들러를 캡슐화합니다.

## 명령의 암시적 사용
<a name="implicit-use-of-commands"></a>

클라이언트 클래스를 검사하면 API 작업에 해당하는 메서드가 실제로 존재하지 않는 것을 확인할 수 있습니다. 이러한 메서드는 `__call()` magic 메서드를 사용하여 구현됩니다. 이러한 의사 메서드는 실제로 SDK의 명령 객체 사용을 캡슐화하는 바로 가기입니다.

일반적으로 명령 객체와 직접 상호 작용할 필요는 없습니다. `Aws\S3\S3Client::putObject()`와 같은 메서드를 호출하면 SDK는 제공된 파라미터를 기반으로 `Aws\CommandInterface` 객체를 실제로 생성하고, 명령을 실행한 다음, 채워진 `Aws\ResultInterface` 객체를 반환합니다(또는 예외나 오류 발생). 클라이언트의 `Async` 메서드 중 하나(예: `Aws\S3\S3Client::putObjectAsync()`)를 호출하면 비슷한 워크플로가 수행됩니다. 클라이언트는 제공된 파라미터를 기반으로 명령을 생성하고, HTTP 요청을 직렬화한 다음, 요청을 시작하고, promise를 반환합니다.

다음은 기능적으로 동등한 예제입니다.

```
$s3Client = new Aws\S3\S3Client([
    'version' => '2006-03-01',
    'region'  => 'us-standard'
]);

$params = [
    'Bucket' => 'amzn-s3-demo-bucket',
    'Key'    => 'baz',
    'Body'   => 'bar'
];

// Using operation methods creates a command implicitly
$result = $s3Client->putObject($params);

// Using commands explicitly
$command = $s3Client->getCommand('PutObject', $params);
$result = $s3Client->execute($command);
```

## 명령 파라미터
<a name="command-parameters"></a>

모든 명령은 서비스의 API에 속하지 않지만 그 대신 SDK의 동작을 제어하는 몇 가지 특수 파라미터를 지원합니다.

### `@http`
<a name="http"></a>

이 파라미터를 사용하여 기본 HTTP 핸들러가 요청을 실행하는 방식을 미세 조정할 수 있습니다. `@http` 파라미터에 포함시킬 수 있는 옵션은 [“http” 클라이언트 옵션](guide_configuration.md#config-http)을 사용하여 클라이언트를 인스턴스화할 때 설정할 수 있는 옵션과 동일합니다.

```
// Configures the command to be delayed by 500 milliseconds
$command['@http'] = [
    'delay' => 500,
];
```

### `@retries`
<a name="retries"></a>

[“재시도” 클라이언트 옵션](guide_configuration.md#config-retries)과 마찬가지로, `@retries`는 실패한 것으로 간주되기 전에 명령을 재시도할 수 있는 횟수를 제어합니다. 재시도를 비활성화하려면 이 옵션을 `0`으로 설정합니다.

```
// Disable retries
$command['@retries'] = 0;
```

**참고**  
클라이언트에서 재시도를 비활성화한 경우 해당 클라이언트에 전달된 개별 명령에서 재시도를 선택적으로 활성화할 수 없습니다.

## 명령 객체 생성
<a name="creating-command-objects"></a>

클라이언트의 `getCommand()` 메서드를 사용하여 명령을 생성할 수 있습니다. 이 명령은 HTTP 요청을 즉시 실행하거나 전송하지 않으며, 클라이언트의 `execute()` 메서드에 전달될 때만 실행됩니다. 따라서 명령을 실행하기 전에 명령 객체를 수정할 기회가 있습니다.

```
$command = $s3Client->getCommand('ListObjects');
$command['MaxKeys'] = 50;
$command['Prefix'] = 'foo/baz/';
$result = $s3Client->execute($command);

// You can also modify parameters
$command = $s3Client->getCommand('ListObjects', [
    'MaxKeys' => 50,
    'Prefix'  => 'foo/baz/',
]);
$command['MaxKeys'] = 100;
$result = $s3Client->execute($command);
```

## 명령 `HandlerList`
<a name="command-handlerlist"></a>

클라이언트에서 명령을 생성한 경우 클라이언트 `Aws\HandlerList` 객체의 복제가 명령에 제공됩니다. 명령이 클라이언트에서 실행하는 다른 명령에 영향을 미치지 않는 사용자 지정 미들웨어와 핸들러를 사용할 수 있도록 클라이언트 핸들러 목록의 **복제**가 명령에 제공됩니다.

따라서 명령별로 다른 HTTP 클라이언트를 사용할 수 있으며(예: `Aws\MockHandler`) 미들웨어를 통해 명령별로 사용자 지정 동작을 추가할 수 있습니다. 다음 예제에서는 실제 HTTP 요청을 전송하는 대신 `MockHandler`를 사용하여 의사 결과를 생성합니다.

```
use Aws\Result;
use Aws\MockHandler;

// Create a mock handler
$mock = new MockHandler();
// Enqueue a mock result to the handler
$mock->append(new Result(['foo' => 'bar']));
// Create a "ListObjects" command
$command = $s3Client->getCommand('ListObjects');
// Associate the mock handler with the command
$command->getHandlerList()->setHandler($mock);
// Executing the command will use the mock handler, which returns the
// mocked result object
$result = $client->execute($command);

echo $result['foo']; // Outputs 'bar'
```

명령에 사용되는 핸들러를 변경할 수 있을 뿐 아니라, 사용자 지정 미들웨어를 명령에 주입할 수도 있습니다. 다음 예제에서는 핸들러 목록에서 관찰자로 작동하는 `tap` 미들웨어를 사용합니다.

```
use Aws\CommandInterface;
use Aws\Middleware;
use Psr\Http\Message\RequestInterface;

$command = $s3Client->getCommand('ListObjects');
$list = $command->getHandlerList();

// Create a middleware that just dumps the command and request that is
// about to be sent
$middleware = Middleware::tap(
    function (CommandInterface $command, RequestInterface $request) {
        var_dump($command->toArray());
        var_dump($request);
    }
);

// Append the middleware to the "sign" step of the handler list. The sign
// step is the last step before transferring an HTTP request.
$list->append('sign', $middleware);

// Now transfer the command and see the var_dump data
$s3Client->execute($command);
```

## `CommandPool`
<a name="command-pool"></a>

`Aws\CommandPool`을 사용하면 `Aws\CommandInterface` 객체를 산출하는 반복자를 사용하여 명령을 동시에 실행할 수 있습니다. `CommandPool`은 풀의 명령을 반복하는 동안 일정한 수의 명령이 동시에 실행되도록 보장합니다(명령이 완료되면 일정한 풀 크기를 유지하기 위해 추가 명령이 실행됨).

다음은 `CommandPool`을 사용하여 몇 개의 명령만 전송하는 매우 간단한 예제입니다.

```
use Aws\S3\S3Client;
use Aws\CommandPool;

// Create the client
$client = new S3Client([
    'region'  => 'us-standard',
    'version' => '2006-03-01'
]);

$bucket = 'amzn-s3-demo-bucket';
$commands = [
    $client->getCommand('HeadObject', ['Bucket' => $bucket, 'Key' => 'a']),
    $client->getCommand('HeadObject', ['Bucket' => $bucket, 'Key' => 'b']),
    $client->getCommand('HeadObject', ['Bucket' => $bucket, 'Key' => 'c'])
];

$pool = new CommandPool($client, $commands);

// Initiate the pool transfers
$promise = $pool->promise();

// Force the pool to complete synchronously
$promise->wait();
```

이 예제는 `CommandPool`의 성능을 상당히 낮춘 것입니다. 더 복잡한 예제를 살펴보겠습니다. 디스크의 파일을 Amazon S3 버킷에 업로드한다고 가정합니다. 디스크에서 파일 목록을 가져오려면 PHP의 `DirectoryIterator`를 사용할 수 있습니다. 이 반복자는 `SplFileInfo` 객체를 생성합니다. `CommandPool`은 `Aws\CommandInterface` 객체를 산출하는 반복자를 받으므로, `Aws\CommandInterface` 객체를 반환하도록 `SplFileInfo` 객체를 매핑합니다.

```
<?php
require 'vendor/autoload.php';

use Aws\Exception\AwsException;
use Aws\S3\S3Client;
use Aws\CommandPool;
use Aws\CommandInterface;
use Aws\ResultInterface;
use GuzzleHttp\Promise\PromiseInterface;

// Create the client
$client = new S3Client([
    'region'  => 'us-standard',
    'version' => '2006-03-01'
]);

$fromDir = '/path/to/dir';
$toBucket = 'amzn-s3-demo-bucket';

// Create an iterator that yields files from a directory
$files = new DirectoryIterator($fromDir);

// Create a generator that converts the SplFileInfo objects into
// Aws\CommandInterface objects. This generator accepts the iterator that
// yields files and the name of the bucket to upload the files to.
$commandGenerator = function (\Iterator $files, $bucket) use ($client) {
    foreach ($files as $file) {
        // Skip "." and ".." files
        if ($file->isDot()) {
            continue;
        }
        $filename = $file->getPath() . '/' . $file->getFilename();
        // Yield a command that is executed by the pool
        yield $client->getCommand('PutObject', [
            'Bucket' => $bucket,
            'Key'    => $file->getBaseName(),
            'Body'   => fopen($filename, 'r')
        ]);
    }
};

// Now create the generator using the files iterator
$commands = $commandGenerator($files, $toBucket);

// Create a pool and provide an optional array of configuration
$pool = new CommandPool($client, $commands, [
    // Only send 5 files at a time (this is set to 25 by default)
    'concurrency' => 5,
    // Invoke this function before executing each command
    'before' => function (CommandInterface $cmd, $iterKey) {
        echo "About to send {$iterKey}: "
            . print_r($cmd->toArray(), true) . "\n";
    },
    // Invoke this function for each successful transfer
    'fulfilled' => function (
        ResultInterface $result,
        $iterKey,
        PromiseInterface $aggregatePromise
    ) {
        echo "Completed {$iterKey}: {$result}\n";
    },
    // Invoke this function for each failed transfer
    'rejected' => function (
        AwsException $reason,
        $iterKey,
        PromiseInterface $aggregatePromise
    ) {
        echo "Failed {$iterKey}: {$reason}\n";
    },
]);

// Initiate the pool transfers
$promise = $pool->promise();

// Force the pool to complete synchronously
$promise->wait();

// Or you can chain the calls off of the pool
$promise->then(function() { echo "Done\n"; });
```

### `CommandPool` 구성
<a name="commandpool-configuration"></a>

`Aws\CommandPool` 생성자는 다양한 구성 옵션을 받습니다.

**concurrency (callable\$1int)**  
동시에 실행할 최대 명령 수입니다. 동적으로 풀 크기를 조정하려면 함수를 제공합니다. 함수에는 현재 보류 중인 요청 수가 제공되며 이 함수는 새 풀 크기 한도를 나타내는 정수를 반환할 것으로 예상됩니다.

**before (callable)**  
각 명령을 전송하기 전에 호출할 함수입니다. `before` 함수는 명령과 명령의 반복자 키를 받습니다. 명령을 전송하기 전에 `before` 함수에서 필요에 따라 명령을 변형할 수 있습니다.

**fulfilled (callable)**  
promise가 이행될 때 호출할 함수입니다. 결과 객체, 결과가 나온 반복자의 ID, 풀을 단락시켜야 하는 경우 해결하거나 거부할 수 있는 집계 promise가 함수에 제공됩니다.

**rejected (callable)**  
promise가 거부될 때 호출할 함수입니다. `Aws\Exception` 객체, 예외가 나온 반복자의 ID, 풀을 단락시켜야 하는 경우 해결하거나 거부할 수 있는 집계 promise가 함수에 제공됩니다.

### 명령 간 수동 가비지 수집
<a name="manual-garbage-collection-between-commands"></a>

대용량 명령 풀로 인해 메모리 제한에 도달한 경우에는 메모리 제한에 도달했을 때 [PHP 가비지 수집기](https://www.php.net/manual/en/features.gc.php)에서 수집된 순환 참조가 아닌, SDK에서 생성된 순환 참조가 원인일 수 있습니다. 이때는 명령 사이에 수집 알고리즘을 직접 호출하면 제한에 도달하기 전에 순환 참조를 수집할 수 있습니다. 다음 예제는 각 명령을 전송하기 전에 콜백을 사용해 수집 알고리즘을 호출하는 `CommandPool`을 생성하는 것입니다. 단, 가비지 수집기를 호출할 경우 성능 비용이 발생하므로 사용 사례와 환경에 따라 사용하는 것이 좋습니다.

```
$pool = new CommandPool($client, $commands, [
    'concurrency' => 25,
    'before' => function (CommandInterface $cmd, $iterKey) {
        gc_collect_cycles();
    }
]);
```