

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

# 미들웨어로 AWS SDK for Go v2 클라이언트 요청 사용자 지정
<a name="middleware"></a>

**주의**  
 클라이언트 요청 파이프라인을 수정하면 형식이 잘못된 또는 유효하지 않은 요청이 발생하거나 예기치 않은 애플리케이션 오류가 발생할 수 있습니다. 이 기능은 SDK 인터페이스에서 기본적으로 제공하지 않는 고급 사용 사례를 위한 것입니다.

 서비스 작업의 [스택](https://pkg.go.dev/github.com/aws/smithy-go/middleware#Stack)에 하나 이상의 미들웨어를 등록하여 AWS SDK for Go 클라이언트 요청을 사용자 지정할 수 있습니다. 스택은 초기화, 직렬화, 빌드, 완료 및 역직렬화와 같은 일련의 단계로 구성됩니다. 각 단계에는 해당 단계의 입력 및 출력 유형에서 작동하는 0개 이상의 미들웨어가 포함되어 있습니다. 다음 다이어그램과 표는 작업의 요청 및 응답이 스택을 통과하는 방법에 대한 개요를 제공합니다.

 ![Middleware](http://docs.aws.amazon.com/ko_kr/sdk-for-go/v2/developer-guide/images/middleware.png) 


|  스택 단계  |  설명  | 
| --- | --- | 
|  초기화  |  입력을 준비하고 필요에 따라 기본 파라미터를 설정합니다. | 
|  직렬화  |  대상 전송 계층에 적합한 프로토콜 형식으로 입력을 직렬화합니다. | 
|  빌드  |  HTTP Content-Length와 같은 직렬화된 입력에 추가 메타데이터를 연결합니다. | 
|  마무리  |  재시도 및 인증을 포함한 최종 메시지 준비입니다(SigV4 서명). | 
|  역직렬화  |  프로토콜 형식의 응답을 구조화된 유형 또는 오류로 역직렬화합니다. | 

 지정된 단계 내의 각 미들웨어에는 미들웨어의 `ID` 메서드에 따라 결정되는 고유 식별자가 있어야 합니다. 미들웨어 식별자는 특정 미들웨어의 인스턴스 하나만 단계에 등록되도록 하고 다른 단계 미들웨어가 해당 미들웨어에 대해 삽입되도록 허용합니다.

 단계의 `Insert` 또는 `Add` 메서드를 사용하여 단계 미들웨어를 연결합니다. `Add`를 사용하여 [middleware.Before](https://pkg.go.dev/github.com/aws/smithy-go/middleware#Before)를 [RelativePosition](https://pkg.go.dev/github.com/aws/smithy-go/middleware#RelativePosition)으로 지정하고 [middleware.After](https://pkg.go.dev/github.com/aws/smithy-go/middleware#After)를 단계 끝에 연결하여 단계 시작 부분에 미들웨어를 연결합니다. `Insert`를 사용하여 다른 단계 미들웨어를 기준으로 미들웨어를 삽입하여 미들웨어를 단계에 연결합니다.

**주의**  
 `Add` 메서드를 사용하여 사용자 지정 단계 미들웨어를 안전하게 삽입해야 합니다. `Insert`를 사용하면 사용자 지정 미들웨어와 삽입하려는 미들웨어 간에 종속성이 생성됩니다. 애플리케이션에 변경 사항이 발생하지 않도록 스택 단계 내의 미들웨어를 불투명한 것으로 간주해야 합니다.

## 사용자 지정 미들웨어 작성
<a name="writing-a-custom-middleware"></a>

 각 스택 단계에는 지정된 단계에 미들웨어를 연결하기 위해 충족해야 하는 인터페이스가 있습니다. 제공된 `{{Step}}MiddlewareFunc` 함수 중 하나를 사용하여 이 인터페이스를 빠르게 충족할 수 있습니다. 다음 표에는 인터페이스를 충족하는 데 사용할 수 있는 단계, 인터페이스 및 헬퍼 함수가 요약되어 있습니다.


|  단계  |  인터페이스  |  Helper 함수  | 
| --- | --- | --- | 
|  초기화  |  [InitializeMiddleware](https://pkg.go.dev/github.com/aws/smithy-go/middleware#InitializeMiddleware)  |  [InitializeMiddlewareFunc](https://pkg.go.dev/github.com/aws/smithy-go/middleware#InitializeMiddlewareFunc)  | 
|  빌드  |  [BuildMiddleware](https://pkg.go.dev/github.com/aws/smithy-go/middleware#BuildMiddleware)  |  [BuildMiddlewareFunc](https://pkg.go.dev/github.com/aws/smithy-go/middleware#BuildMiddlewareFunc)  | 
|  직렬화  |  [SerializeMiddleware](https://pkg.go.dev/github.com/aws/smithy-go/middleware#SerializeMiddleware)  |  [SerializeMiddlewareFunc](https://pkg.go.dev/github.com/aws/smithy-go/middleware#SerializeMiddlewareFunc)  | 
|  마무리  |  [FinalizeMiddleware](https://pkg.go.dev/github.com/aws/smithy-go/middleware#FinalizeMiddleware)  |  [FinalizeMiddlewareFunc](https://pkg.go.dev/github.com/aws/smithy-go/middleware#FinalizeMiddlewareFunc)  | 
|  역직렬화  |  [DeserializeMiddleware](https://pkg.go.dev/github.com/aws/smithy-go/middleware#DeserializeMiddleware)  |  [DeserializeMiddlewareFunc](https://pkg.go.dev/github.com/aws/smithy-go/middleware#DeserializeMiddlewareFunc)  | 

 다음 예제에서는 제공되지 않은 경우 사용자 지정 미들웨어를 작성하여 Amazon S3 `GetObject` API 직접 호출의 버킷 멤버를 채우는 방법을 보여줍니다. 이 미들웨어는 다음 예제에서 참조되어 스택에 단계 미들웨어를 연결하는 방법을 보여줍니다.

```
import "github.com/aws/smithy-go/aws"
import "github.com/aws/smithy-go/middleware"
import "github.com/aws/aws-sdk-go-v2/service/s3"

// ...

var defaultBucket = middleware.InitializeMiddlewareFunc("DefaultBucket", func(
    ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler,
) (
    out middleware.InitializeOutput, metadata middleware.Metadata, err error,
) {
    // Type switch to check if the input is s3.GetObjectInput, if so and the bucket is not set, populate it with
    // our default.
    switch v := in.Parameters.(type) {
    case *s3.GetObjectInput:
        if v.Bucket == nil {
            v.Bucket = aws.String("{{amzn-s3-demo-bucket}}")
        }
    }

    // Middleware must call the next middleware to be executed in order to continue execution of the stack.
    // If an error occurs, you can return to prevent further execution.
    return next.HandleInitialize(ctx, in)
})
```

## 모든 클라이언트에 미들웨어 연결
<a name="attaching-middleware-to-all-clients"></a>

 [aws.Config](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws#Config) 유형의 `APIOptions` 멤버를 사용해 미들웨어를 추가하여 사용자 지정 단계 미들웨어를 모든 클라이언트에 연결할 수 있습니다. 다음 예제에서는 애플리케이션 `aws.Config` 객체를 사용하여 구성된 모든 클라이언트에 `defaultBucket` 미들웨어를 연결합니다.

```
import "context"
import "github.com/aws/aws-sdk-go-v2/aws"
import "github.com/aws/aws-sdk-go-v2/config"
import "github.com/aws/aws-sdk-go-v2/service/s3"
import "github.com/aws/smithy-go/middleware"

// ...

cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
    // handle error
}

cfg.APIOptions = append(cfg.APIOptions, func(stack *middleware.Stack) error {
    // Attach the custom middleware to the beginning of the Initialize step
    return stack.Initialize.Add(defaultBucket, middleware.Before)
})

client := s3.NewFromConfig(cfg)
```

## 특정 작업에 미들웨어 연결
<a name="attaching-middleware-to-a-specific-operation"></a>

 작업에 대한 가변 인수 목록을 사용하여 클라이언트의 `APIOptions` 멤버를 수정하는 방법으로 사용자 정의 단계 미들웨어를 특정 클라이언트 작업에 첨부할 수 있습니다. 다음 예제에서는 `defaultBucket` 미들웨어를 특정 Amazon S3 `GetObject` 작업 간접 호출에 연결합니다.

```
import "context"
import "github.com/aws/aws-sdk-go-v2/aws"
import "github.com/aws/aws-sdk-go-v2/config"
import "github.com/aws/aws-sdk-go-v2/service/s3"
import "github.com/aws/smithy-go/middleware"

// ...

// registerDefaultBucketMiddleware registers the defaultBucket middleware with the provided stack.
func registerDefaultBucketMiddleware(stack *middleware.Stack) error {
    // Attach the custom middleware to the beginning of the Initialize step
    return stack.Initialize.Add(defaultBucket, middleware.Before)
}

// ...

cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
    // handle error
}

client := s3.NewFromConfig(cfg)

object, err := client.GetObject(context.TODO(), &s3.GetObjectInput{
    Key: aws.String("my-key"),
}, func(options *s3.Options) {
    // Register the defaultBucketMiddleware for this operation only
    options.APIOptions = append(options.APIOptions, registerDefaultBucketMiddleware)
})
```

## 스택으로 메타데이터 전달
<a name="passing-metadata-down-the-stack"></a>

 특정 상황에서는 정보 또는 상태를 공유하여 둘 이상의 미들웨어가 동시에 작동해야 할 수 있습니다. [context.Context](https://golang.org/pkg/context/#Context)를 통해 [middleware.WithStackValue](https://pkg.go.dev/github.com/aws/smithy-go/middleware#WithStackValue)를 사용하여 이 메타데이터를 전달할 수 있습니다. `middleware.WithStackValue`는 지정된 키-값 페어를 제공된 컨텍스트에 연결하고 범위를 현재 실행 중인 스택으로 안전하게 제한합니다. 이러한 스택 범위 값은 [middleware.GetStackValue](https://pkg.go.dev/github.com/aws/smithy-go/middleware#GetStackValue)를 사용하고 해당 값을 저장하는 데 사용되는 키를 제공하여 컨텍스트에서 검색할 수 있습니다. 키는 비슷해야 하며 충돌을 방지하려면 자체 유형을 컨텍스트 키로 정의해야 합니다. 다음 예제에서는 두 미들웨어가 `context.Context`를 사용하여 스택에 정보를 전달하는 방법을 보여줍니다.

```
import "context"
import "github.com/aws/smithy-go/middleware"

// ...

type customKey struct {}

func GetCustomKey(ctx context.Context) (v string) {
    v, _ = middleware.GetStackValue(ctx, customKey{}).(string)
    return v
}

func SetCustomKey(ctx context.Context, value string) context.Context {
    return middleware.WithStackValue(ctx, customKey{}, value)
}

// ...

var customInitalize = middleware.InitializeMiddlewareFunc("customInitialize", func(
    ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler,
) (
    out middleware.InitializeOutput, metadata middleware.Metadata, err error,
) {
    ctx = SetCustomKey(ctx, "my-custom-value")
    
    return next.HandleInitialize(ctx, in)
})

var customBuild = middleware.BuildMiddlewareFunc("customBuild", func(
    ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler,
) (
    out middleware.BuildOutput, metadata middleware.Metadata, err error,
) {
    customValue := GetCustomKey(ctx)
    
    // use customValue

    return next.HandleBuild(ctx, in)
})
```

### SDK에서 제공하는 메타데이터
<a name="metadata-provided-by-the-sdk"></a>

 AWS SDK for Go는 제공된 컨텍스트에서 검색할 수 있는 여러 메타데이터 값을 제공합니다. 이러한 값을 사용하여 실행 중인 서비스, 작업 또는 대상 리전에 따라 동작을 수정하는 보다 동적인 미들웨어를 활성화할 수 있습니다. 사용 가능한 몇 가지 키가 아래 표에 나와 있습니다.


|  키  |  리트리버  |  설명  | 
| --- | --- | --- | 
|  ServiceID  |  [GetServiceID](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws/middleware#GetServiceID)  |  실행 중인 스택의 서비스 식별자를 검색합니다. 이를 서비스 클라이언트 패키지의 ServiceID 상수와 비교할 수 있습니다. | 
|  OperationName  |  [GetOperationName](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws/middleware#GetOperationName)  |  실행 중인 스택의 작업 이름을 검색합니다. | 
|  Logger  |  [GetLogger](https://pkg.go.dev/github.com/aws/smithy-go/middleware#GetLogger)  |  미들웨어에서 메시지를 로깅하는 데 사용할 수 있는 로거를 검색합니다. | 

## 스택에 메타데이터 전달
<a name="passing-metadata-up-the-stack"></a>

 [미들웨어 메타데이터](https://pkg.go.dev/github.com/aws/smithy-go/middleware#Metadata)를 사용하여 메타데이터 키와 값 페어를 추가하는 방법으로 스택을 통해 메타데이터를 전달할 수 있습니다. 각 미들웨어 단계는 출력 구조, 메타데이터 및 오류를 반환합니다. 사용자 지정 미들웨어는 단계에서 다음 핸들러를 호출하여 수신한 메타데이터를 반환해야 합니다. 이렇게 하면 다운스트림 미들웨어에 의해 추가된 메타데이터가 서비스 작업을 간접 호출하는 애플리케이션에 전파됩니다. 결과 메타데이터는 `ResultMetadata` 구조 멤버를 통해 작업의 출력 셰이프를 따라 간접 호출 애플리케이션에 액세스할 수 있습니다.

 다음 예제에서는 사용자 지정 미들웨어가 작업 출력의 일부로 반환되는 메타데이터를 추가하는 방법을 보여줍니다.

```
import "context"
import "github.com/aws/aws-sdk-go-v2/service/s3"
import "github.com/aws/smithy-go/middleware"

// ...

type customKey struct{}

func GetCustomKey(metadata middleware.Metadata) (v string) {
    v, _ = metadata.Get(customKey{}).(string)
    return v
}

func SetCustomKey(metadata *middleware.Metadata, value string) {
    metadata.Set(customKey{}, value)
}

// ...

var customInitalize = middleware.InitializeMiddlewareFunc("customInitialize", func (
    ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler,
) (
    out middleware.InitializeOutput, metadata middleware.Metadata, err error,
) {
    out, metadata, err = next.HandleInitialize(ctx, in)
    if err != nil {
        return out, metadata, err
    }
    
    SetCustomKey(&metadata, "my-custom-value")
    
    return out, metadata, nil
})

// ...

client := s3.NewFromConfig(cfg, func (options *s3.Options) {
    options.APIOptions = append(options.APIOptions, func(stack *middleware.Stack) error {
        return stack.Initialize.Add(customInitalize, middleware.After)
    })
})

out, err := client.GetObject(context.TODO(), &s3.GetObjectInput{
    // input parameters
})
if err != nil {
    // handle error
}

customValue := GetCustomKey(out.ResponseMetadata)
```