

# Go에서 Lambda 함수 핸들러 정의
<a name="golang-handler"></a>

Lambda 함수의 *핸들러*는 이벤트를 처리하는 함수 코드의 메서드입니다. 함수가 간접 호출되면 Lambda는 핸들러 메서드를 실행합니다. 함수는 핸들러가 응답을 반환하거나 종료하거나 제한 시간이 초과될 때까지 실행됩니다.

이 페이지에서는 프로젝트 설정, 이름 지정 규칙, 모범 사례를 포함하여 Go에서 Lambda 함수 핸들러를 사용하는 방법을 설명합니다. 이 페이지에는 주문에 대한 정보를 가져와서 텍스트 파일 영수증을 생성하고 해당 파일을 Amazon Simple Storage Service(Amazon S3) 버킷에 넣는 Go Lambda 함수의 예제도 포함되어 있습니다. 함수를 작성한 후 배포하는 방법에 대한 자세한 내용은 [.zip 파일 아카이브를 사용하여 Go Lambda 함수 배포](golang-package.md) 또는 [컨테이너 이미지로 Go Lambda 함수 배포](go-image.md) 섹션을 참조하세요.

**Topics**
+ [Go 핸들러 프로젝트 설정](#golang-handler-setup)
+ [예제 Go Lambda 함수 코드](#golang-example-code)
+ [핸들러 이름 지정 규칙](#golang-handler-naming)
+ [입력 이벤트 객체 정의 및 액세스](#golang-example-input)
+ [Lambda 컨텍스트 객체 액세스 및 사용](#golang-example-context)
+ [Go 핸들러에 대해 유효한 핸들러 서명](#golang-handler-signatures)
+ [핸들러에서 AWS SDK for Go v2 사용](#golang-example-sdk-usage)
+ [환경 변수에 액세스](#golang-example-envvars)
+ [전역 상태 사용](#golang-handler-state)
+ [Go Lambda 함수의 코드 모범 사례](#go-best-practices)

## Go 핸들러 프로젝트 설정
<a name="golang-handler-setup"></a>

[Go](https://golang.org/)에서 작성된 Lambda 함수는 Go 실행 파일로 작성됩니다. 다음 `go mod init` 명령을 사용하여 다른 Go 프로젝트를 초기화하는 것과 동일한 방식으로 Go Lambda 함수 프로젝트를 초기화할 수 있습니다.

```
go mod init example-go
```

여기에서 `example-go`는 모듈 이름입니다. 이를 다른 것으로 대체할 수 있습니다. 이 명령은 프로젝트를 초기화하고 프로젝트의 종속성을 나열하는 `go.mod` 파일을 생성합니다.

`go get` 명령을 사용하여 프로젝트에 외부 종속성을 추가합니다. 예를 들어 Go의 모든 Lambda 함수에 대해서는 Go용 Lambda 프로그래밍 모델을 구현하는 [github.com/aws/aws-lambda-go/lambda](https://github.com/aws/aws-lambda-go/tree/master/lambda) 패키지를 포함해야 합니다. 다음 `go get` 명령을 사용하여 이 패키지를 포함합니다.

```
go get github.com/aws/aws-lambda-go
```

함수 코드는 Go 파일에 있어야 합니다. 다음 예제에서는 이 파일의 이름을 `main.go`로 지정합니다. 이 파일에서는 핸들러 메서드에 핵심 함수 논리와 이 핸들러를 호출하는 `main()` 함수를 구현합니다.

## 예제 Go Lambda 함수 코드
<a name="golang-example-code"></a>

다음 예제 Go Lambda 함수 코드는 주문에 대한 정보를 입력으로 가져와서 텍스트 파일 영수증을 생성하고 해당 파일을 Amazon S3 버킷에 넣습니다.

**Example `main.go` Lambda 함수**  

```
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/s3"
)

type Order struct {
	OrderID string  `json:"order_id"`
	Amount  float64 `json:"amount"`
	Item    string  `json:"item"`
}

var (
	s3Client *s3.Client
)

func init() {
	// Initialize the S3 client outside of the handler, during the init phase
	cfg, err := config.LoadDefaultConfig(context.TODO())
	if err != nil {
		log.Fatalf("unable to load SDK config, %v", err)
	}

	s3Client = s3.NewFromConfig(cfg)
}

func uploadReceiptToS3(ctx context.Context, bucketName, key, receiptContent string) error {
	_, err := s3Client.PutObject(ctx, &s3.PutObjectInput{
		Bucket: &bucketName,
		Key:    &key,
		Body:   strings.NewReader(receiptContent),
	})
	if err != nil {
		log.Printf("Failed to upload receipt to S3: %v", err)
		return err
	}
	return nil
}

func handleRequest(ctx context.Context, event json.RawMessage) error {
	// Parse the input event
	var order Order
	if err := json.Unmarshal(event, &order); err != nil {
		log.Printf("Failed to unmarshal event: %v", err)
		return err
	}

	// Access environment variables
	bucketName := os.Getenv("RECEIPT_BUCKET")
	if bucketName == "" {
		log.Printf("RECEIPT_BUCKET environment variable is not set")
		return fmt.Errorf("missing required environment variable RECEIPT_BUCKET")
	}

	// Create the receipt content and key destination
	receiptContent := fmt.Sprintf("OrderID: %s\nAmount: $%.2f\nItem: %s",
		order.OrderID, order.Amount, order.Item)
	key := "receipts/" + order.OrderID + ".txt"

	// Upload the receipt to S3 using the helper method
	if err := uploadReceiptToS3(ctx, bucketName, key, receiptContent); err != nil {
		return err
	}

	log.Printf("Successfully processed order %s and stored receipt in S3 bucket %s", order.OrderID, bucketName)
	return nil
}

func main() {
	lambda.Start(handleRequest)
}
```

`main.go` 파일은 다음 코드 섹션을 포함하고 있습니다.
+ `package main`: Go에서 `func main()` 함수를 포함하는 패키지의 이름은 항상 `main`이어야 합니다.
+ `import` 블록: 이 블록을 사용하여 Lambda 함수에 필요한 라이브러리를 포함합니다.
+ `type Order struct {}` 블록: 이 Go 구조체에서 예상 입력 이벤트의 형태를 정의합니다.
+ `var ()` 블록: 이 블록을 사용하여 Lambda 함수에 사용할 전역 변수를 정의합니다.
+ `func init() {}`: 이 `init()` 메서드의 [초기화 단계](lambda-runtime-environment.md#runtimes-lifecycle-ib)에서 Lambda가 실행하기를 원하는 모든 코드를 포함합니다.
+ `func uploadReceiptToS3(...) {}`: 기본 `handleRequest` 핸들러 메서드에서 참조하는 도우미 메서드입니다.
+ `func handleRequest(ctx context.Context, event json.RawMessage) error {}`: 기본 애플리케이션 논리를 포함한 **기본 핸들러 메서드**입니다.
+ `func main() {}`: Lambda 핸들러에 필요한 진입점입니다. `lambda.Start()` 메서드에 대한 인수는 기본 핸들러 메서드입니다.

이 함수가 제대로 작동하려면 [실행 역할](lambda-intro-execution-role.md)이 `s3:PutObject` 작업을 허용해야 합니다. 또한 `RECEIPT_BUCKET` 환경 변수를 정의해야 합니다. 성공적으로 호출되면 Amazon S3 버킷은 영수증 파일을 포함합니다.

## 핸들러 이름 지정 규칙
<a name="golang-handler-naming"></a>

Go의 Lambda 함수의 경우에는 핸들러에 어떤 이름이라도 사용할 수 있습니다. 이 예제의 핸들러 메서드 이름은 `handleRequest`입니다. 코드에서 핸들러 값을 참조하려면 `_HANDLER` 환경 변수를 사용합니다.

[.zip 배포 패키지](golang-package.md)를 사용하여 배포된 Go 함수의 경우 함수 코드가 포함된 실행 파일의 이름은 `bootstrap`이어야 합니다. 또한 `bootstrap` 파일은 .zip 파일의 루트에 있어야 합니다. [컨테이너 이미지](go-image.md#go-image-provided)를 사용하여 배포된 Go 함수의 경우 실행 파일에 어떤 이름이라도 사용할 수 있습니다.

## 입력 이벤트 객체 정의 및 액세스
<a name="golang-example-input"></a>

JSON은 Lambda 함수의 가장 일반적인 표준 입력 형식입니다. 이 예제에서 함수는 다음과 유사한 입력을 예상합니다.

```
{
    "order_id": "12345",
    "amount": 199.99,
    "item": "Wireless Headphones"
}
```

Go에서 Lambda 함수로 작업할 때 예상되는 입력 이벤트의 형태를 Go 구조체로 정의할 수 있습니다. 이 예제에서는 `Order`를 나타내는 구조체를 정의합니다.

```
type Order struct {
    OrderID string  `json:"order_id"`
    Amount  float64 `json:"amount"`
    Item    string  `json:"item"`
}
```

이 구조체는 예상 입력 형태와 일치합니다. 구조체를 정의한 후에는 [encoding/json 표준 라이브러리](https://pkg.go.dev/encoding/json)와 호환되는 일반 JSON 유형을 받아들이는 핸들러 서명을 작성할 수 있습니다. 그런 다음 [func Unmarshal](https://golang.org/pkg/encoding/json/#Unmarshal) 함수를 사용하여 이를 구조체로 역직렬화할 수 있습니다. 이는 핸들러의 처음 몇 줄에 설명되어 있습니다.

```
func handleRequest(ctx context.Context, event json.RawMessage) error {
    // Parse the input event
    var order Order
    if err := json.Unmarshal(event, &order); err != nil {
        log.Printf("Failed to unmarshal event: %v", err)
        return err
    ...
}
```

역직렬화 후에는 `order` 변수의 필드에 액세스할 수 있습니다. 예를 들어 `order.OrderID`는 원래 입력에서 `"order_id"`의 값을 검색합니다.

**참고**  
`encoding/json` 패키지는 내보낸 필드에만 액세스할 수 있습니다. 내보내기를 위해서는 이벤트 구문의 필드 이름이 대문자로 표시되어야 합니다.

## Lambda 컨텍스트 객체 액세스 및 사용
<a name="golang-example-context"></a>

Lambda [컨텍스트 객체](golang-context.md)에는 호출, 함수, 실행 환경에 관한 정보가 포함되어 있습니다. 이 예제에서는 핸들러 서명에서 이 변수를 `ctx`로 선언했습니다.

```
func handleRequest(ctx context.Context, event json.RawMessage) error {
    ...
}
```

`ctx context.Context` 입력은 함수 핸들러의 선택적 인수입니다. 허용되는 핸들러 서명에 대한 자세한 내용은 [Go 핸들러에 대해 유효한 핸들러 서명](#golang-handler-signatures) 섹션을 참조하세요.

AWS SDK를 사용하여 다른 서비스를 직접적으로 호출하는 경우 몇 가지 주요 영역에서 컨텍스트 객체가 필요합니다. 예를 들어 SDK 클라이언트를 올바르게 초기화하려면 다음과 같이 컨텍스트 객체를 사용하여 올바른 AWS SDK 구성을 로드할 수 있습니다.

```
// Load AWS SDK configuration using the default credential provider chain
    cfg, err := config.LoadDefaultConfig(ctx)
```

SDK 호출 자체에 컨텍스트 객체가 입력으로 필요할 수 있습니다. 예를 들어 `s3Client.PutObject` 호출은 컨텍스트 객체를 첫 번째 인수로 수락합니다.

```
// Upload the receipt to S3
    _, err = s3Client.PutObject(ctx, &s3.PutObjectInput{
        ...
    })
```

AWS SDK 요청 외에도 함수 모니터링에 컨텍스트 객체를 사용할 수 있습니다. 컨텍스트 객체에 대한 자세한 내용은 [Lambda 컨텍스트 객체를 사용하여 Go 함수 정보 검색](golang-context.md) 섹션을 참조하세요.

## Go 핸들러에 대해 유효한 핸들러 서명
<a name="golang-handler-signatures"></a>

Go에서 Lambda 함수 핸들러를 빌드할 때 몇 가지 옵션들이 있으며 다만 다음과 같은 규칙들을 반드시 준수해야 합니다.
+ 핸들러는 하나의 함수여야 합니다.
+ 핸들러는 0\$12개의 인수를 취할 수 있습니다. 인수가 2개일 경우, 첫 번째 인수는 `context.Context`를 구현해야 합니다.
+ 핸들러는 0\$12개의 인수를 반환할 수 있습니다. 반환 값이 1개일 경우, 이 값은 `error`를 구현해야 합니다. 반환 값이 2개일 경우, 두 번째 값은 `error`를 구현해야 합니다.

다음 목록은 유효한 핸들러 서명을 열거합니다. `TIn` 및 `TOut`은 *encoding/json* 표준 라이브러리와 호환 가능한 유형을 나타냅니다. 이러한 유형들이 역직렬화되는 방식에 관한 자세한 내용은 [func Unmarshal](https://golang.org/pkg/encoding/json/#Unmarshal)을 참조하시기 바랍니다.
+ 

  ```
  func ()
  ```
+ 

  ```
  func () error
  ```
+ 

  ```
  func () (TOut, error)
  ```
+ 

  ```
  func (TIn) error
  ```
+ 

  ```
  func (TIn) (TOut, error)
  ```
+ 

  ```
  func (context.Context) error
  ```
+ 

  ```
  func (context.Context) (TOut, error)
  ```
+ 

  ```
  func (context.Context, TIn) error
  ```
+ 

  ```
  func (context.Context, TIn) (TOut, error)
  ```

## 핸들러에서 AWS SDK for Go v2 사용
<a name="golang-example-sdk-usage"></a>

종종 Lambda 함수를 사용하여 다른 AWS 리소스와 상호 작용하거나 업데이트하는 경우가 있습니다. 이러한 리소스와 상호 작용하는 가장 간단한 방법은 [AWS SDK for Go v2](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2)를 사용하는 것입니다.

**참고**  
AWS SDK for Go(v1)는 유지 관리 모드이며 2025년 7월 31일에 지원이 종료됩니다. 앞으로는 AWS SDK for Go v2만 사용하는 것을 권장합니다.

함수에 SDK 종속성을 추가하려면 필요한 특정 SDK 클라이언트에 `go get` 명령을 사용하세요. 이전의 예제 코드에서는 `config` 라이브러리와 `s3` 라이브러리를 사용했습니다. `go.mod` 및 `main.go ` 파일이 포함된 디렉터리에서 다음 명령을 실행하여 이러한 종속성을 추가합니다.

```
go get github.com/aws/aws-sdk-go-v2/config
go get github.com/aws/aws-sdk-go-v2/service/s3
```

그런 다음 함수의 가져오기 블록에서 그에 따른 종속성을 가져옵니다.

```
import (
    ...
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/s3"
)
```

핸들러에서 SDK를 사용하는 경우에는 클라이언트를 올바른 설정으로 구성하세요. 가장 간단한 방법은 [기본 자격 증명 제공업체 체인](https://docs.aws.amazon.com/sdkref/latest/guide/standardized-credentials.html#credentialProviderChain)을 사용하는 것입니다. 이 예제에서는 이러한 구성을 로드하는 한 가지 방법을 보여줍니다.

```
// Load AWS SDK configuration using the default credential provider chain
    cfg, err := config.LoadDefaultConfig(ctx)
    if err != nil {
        log.Printf("Failed to load AWS SDK config: %v", err)
        return err
    }
```

이러한 구성을 `cfg` 변수에 로드한 후에 해당 변수를 클라이언트 인스턴스에 전달할 수 있습니다. 예제 코드는 다음과 같이 Amazon S3 클라이언트를 인스턴스화합니다.

```
// Create an S3 client
    s3Client := s3.NewFromConfig(cfg)
```

이 예제에서는 `init()` 함수를 간접적으로 간접 호출할 때마다 초기화할 필요가 없도록 함수에서 Amazon S3 클라이언트를 초기화했습니다. 문제는 `init()` 함수에서 Lambda가 컨텍스트 객체에 액세스할 수 없다는 점입니다. 이 문제를 해결하려면 초기화 단계에서 `context.TODO()`와 같은 자리 표시자를 전달할 수 있습니다. 나중에 클라이언트를 사용하여 직접적으로 호출할 때 전체 컨텍스트 객체를 전달합니다. 이 해결 방법은 [AWS SDK 클라이언트 초기화 및 호출에서 컨텍스트 사용](golang-context.md#golang-context-sdk)에도 설명되어 있습니다.

SDK 클라이언트를 구성하고 초기화한 후에는 이를 사용하여 다른 AWS 서비스와 상호 작용할 수 있습니다. 예제 코드는 다음과 같이 Amazon S3 `PutObject` API를 직접적으로 호출합니다.

```
_, err = s3Client.PutObject(ctx, &s3.PutObjectInput{
    Bucket: &bucketName,
    Key:    &key,
    Body:   strings.NewReader(receiptContent),
})
```

## 환경 변수에 액세스
<a name="golang-example-envvars"></a>

핸들러 코드에서 `os.Getenv()` 메서드를 사용하여 모든 [환경 변수](configuration-envvars.md)를 참조할 수 있습니다. 이 예제에서는 다음 코드 줄을 사용하여 정의된 `RECEIPT_BUCKET` 환경 변수를 참조합니다.

```
// Access environment variables
    bucketName := os.Getenv("RECEIPT_BUCKET")
    if bucketName == "" {
        log.Printf("RECEIPT_BUCKET environment variable is not set")
        return fmt.Errorf("missing required environment variable RECEIPT_BUCKET")
    }
```

## 전역 상태 사용
<a name="golang-handler-state"></a>

함수를 간접 호출할 때마다 새 리소스가 생성되지 않도록 하려면 Lambda 함수의 핸들러 코드 외부에서 전역 변수를 선언하고 수정할 수 있습니다. `var` 블록 또는 문에서 이러한 전역 변수를 정의합니다. 또한 핸들러는 [초기화 단계](lambda-runtime-environment.md#runtimes-lifecycle-ib) 동안 실행되는 `init()` 함수를 선언할 수 있습니다. `init` 메서드는 표준 Go 프로그램에서와 마찬가지로 AWS Lambda에서도 동일하게 동작합니다.

## Go Lambda 함수의 코드 모범 사례
<a name="go-best-practices"></a>

Lambda 함수를 구축할 때 코딩 모범 사례를 사용하려면 다음 목록의 지침을 준수하세요.
+ **핵심 로직에서 Lambda 핸들러를 분리합니다.** 이를 통해 단위 테스트를 수행할 수 있는 더 많은 함수를 만들 수 있습니다.
+ **종속성의 복잡성을 최소화합니다.** [실행 환경](lambda-runtime-environment.md) 시작 시 빠르게 로드되는 더 단순한 프레임워크가 권장됩니다.
+ **배포 패키지 크기를 런타임 필요에 따라 최소화합니다.** 이렇게 하면 호출 전에 배포 패키지를 다운로드하고 압축을 풀 때 걸리는 시간이 단축됩니다.

**실행 환경 재사용을 활용하여 함수 성능을 향상시킵니다.** 함수 핸들러 외부에서 SDK 클라이언트 및 데이터베이스 연결을 초기화하고 정적 자산을 `/tmp` 디렉토리에 로컬로 캐시합니다. 동일한 함수 인스턴스에서 처리하는 후속 간접 호출은 이러한 리소스를 재사용할 수 있습니다. 이를 통해 함수 실행 시간을 줄여 비용을 절감합니다.

호출에서 발생할 수 있는 데이터 유출을 방지하려면 실행 환경을 사용하여 사용자 데이터, 이벤트 또는 보안과 관련된 기타 정보를 저장하지 마세요. 함수가 핸들러 내부 메모리에 저장할 수 없는 변경 가능한 상태에 의존하는 경우 각 사용자에 대해 별도의 함수 또는 별도의 함수 버전을 생성하는 것이 좋습니다.

**연결 유지 지시문을 사용하여 지속적인 연결을 유지하세요.** Lambda는 시간이 지남에 따라 유휴 연결을 제거합니다. 함수를 호출할 때 유휴 연결을 재사용하려고 하면 연결 오류가 발생합니다. 지속적인 연결을 유지하려면 런타임과 관련된 연결 유지 지시문을 사용하세요. 예를 들어, [Node.js에서 연결 유지를 이용해 연결 재사용](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-reusing-connections.html)을 참조하세요.

** [환경 변수](configuration-envvars.md)를 사용하여 함수에 운영 파라미터를 전달합니다.** 예를 들어, Amazon S3 버킷에 기록하는 경우 기록하고 있는 버킷 이름을 하드 코딩하는 대신 환경 변수로 구성합니다.

Lambda 함수에서 함수가 자기 자신을 간접적으로 간접 호출하거나 함수를 다시 간접적으로 간접 호출할 수 있는 프로세스를 시작하는 **재귀적 간접 호출을 사용하지 마세요**. 리커시브 코드를 사용할 경우, 의도하지 않은 함수 호출이 증가하고 비용이 상승할 수 있습니다. 의도치 않게 간접 호출이 대량으로 발생하는 경우 함수의 예약된 동시성을 즉시 `0`으로 설정하여 코드를 업데이트하는 동안 해당 함수에 대한 모든 간접 호출을 제한합니다.

Lambda 함수 코드에는 **문서화되지 않은 비공개 API를 사용하지 마세요.** AWS Lambda 관리형 런타임의 경우, Lambda는 주기적으로 보안 및 기능 업데이트를 Lambda의 내부 API에 적용합니다. 이러한 내부 API 업데이트는 이전 버전과 호환되지 않으므로 함수가 이러한 비공개 API에 종속성을 갖는 경우 호출 실패와 같은 의도하지 않은 결과를 초래할 수 있습니다. 공개적으로 사용 가능한 API의 목록은 [API 레퍼런스](https://docs.aws.amazon.com/lambda/latest/api/welcome.html)를 참조하세요.

**멱등성 코드를 작성합니다.** 함수에 멱등성 코드를 작성하면 중복 이벤트가 동일한 방식으로 처리됩니다. 코드는 이벤트를 올바르게 검증하고 중복 이벤트를 정상적으로 처리해야 합니다. 자세한 내용은 [멱등성 Lambda 함수를 만들려면 어떻게 해야 합니까?](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-function-idempotent/) 단원을 참조하십시오.