

# Lambda@Edge를 사용하여 엣지에서 사용자 지정
<a name="lambda-at-the-edge"></a>

Lambda@Edge는 AWS Lambda의 확장된 기능입니다. Lambda@Edge는 Amazon CloudFront를 통해 전달되는 콘텐츠를 사용자 지정하는 함수를 실행할 수 있게 해 주는 컴퓨팅 서비스입니다. 미국 동부(버지니아 북부) AWS 리전 한 곳에서 Lambda 콘솔을 사용하여 Node.js 또는 Python 함수를 작성할 수 있습니다.

함수를 만든 후에 Lambda 콘솔 또는 CloudFront 콘솔에 트리거를 추가하여 서버를 프로비저닝하거나 관리하지 않고도 뷰어에 더 가까운 AWS 위치에서 함수를 실행합니다. 선택적으로 Lambda 및 CloudFront API 작업을 사용하여 프로그래밍 방식으로 함수와 트리거를 설정할 수 있습니다.

Lambda@Edge는 하루 몇 번의 요청에서 초당 수천 개의 요청으로 자동 확장됩니다. 오리진 서버가 아니라 최종 사용자에게 가까운 AWS 위치에서 요청을 처리하므로 지연 시간이 크게 단축되고 사용자 경험이 상당히 개선됩니다.

**참고**  
Lambda@Edge는 gRPC 요청에서 지원되지 않습니다. 자세한 내용은 [CloudFront 배포에 gRPC 사용](distribution-using-grpc.md) 섹션을 참조하시기 바랍니다.

**Topics**
+ [Lambda@Edge가 요청 및 응답을 처리하는 방법](lambda-edge-event-request-response.md)
+ [Lambda@Edge 사용 방법](lambda-edge-ways-to-use.md)
+ [Lambda@Edge 함수 시작(콘솔)](lambda-edge-how-it-works.md)
+ [Lambda@Edge에 대한 IAM 권한 및 역할 설정](lambda-edge-permissions.md)
+ [Lambda@Edge 함수 작성 및 생성](lambda-edge-create-function.md)
+ [Lambda@Edge 함수에 대한 트리거 추가](lambda-edge-add-triggers.md)
+ [Lambda@Edge 함수 테스트 및 디버깅](lambda-edge-testing-debugging.md)
+ [Lambda@Edge 함수 및 복제본 삭제](lambda-edge-delete-replicas.md)
+ [Lambda@Edge 이벤트 구조](lambda-event-structure.md)
+ [요청 및 응답 작업 수행](lambda-generating-http-responses.md)
+ [Lambda@Edge 예제 함수](lambda-examples.md)

# Lambda@Edge가 요청 및 응답을 처리하는 방법
<a name="lambda-edge-event-request-response"></a>

CloudFront 배포를 Lambda@Edge 함수와 연결하면 CloudFront가 CloudFront 엣지 로케이션에서 요청 및 응답을 가로챕니다. 다음과 같은 CloudFront 이벤트가 발생할 때 Lambda 함수를 실행할 수 있습니다.
+ CloudFront가 최종 사용자의 요청을 수신할 때(최종 사용자 요청)
+ CloudFront가 오리진에 요청을 전달하기 전(오리진 요청)
+ CloudFront가 오리진의 응답을 수신할 때(오리진 응답)
+ CloudFront가 최종 사용자에게 응답을 반환하기 전(최종 사용자 응답)

AWS WAF를 사용하는 경우, Lambda@Edge 최종 사용자 요청은 AWS WAF 규칙이 적용된 후에 실행됩니다.

자세한 내용은 [요청 및 응답 작업 수행](lambda-generating-http-responses.md) 및 [Lambda@Edge 이벤트 구조](lambda-event-structure.md)(을)를 참조하세요.

# Lambda@Edge 사용 방법
<a name="lambda-edge-ways-to-use"></a>

다음 예와 같이 Amazon CloudFront 배포에서 Lambda@Edge 처리는 여러 용도로 사용됩니다.
+ Lambda 함수는 사용자가 A/B 테스트를 위해 사이트의 다양한 버전을 볼 수 있도록 쿠키를 검사하고 URL을 다시 작성합니다.
+ CloudFront는 `User-Agent` 헤더를 확인하여 사용 중인 디바이스를 기반으로 디바이스에 대한 정보를 포함하여 최종 사용자에게 다양한 객체를 반환할 수 있습니다. 예를 들어, CloudFront는 디바이스의 화면 크기에 따라 다른 이미지를 반환할 수 있습니다. 마찬가지로 함수는 `Referer` 헤더 값을 고려하여 CloudFront가 사용 가능한 최저 해상도의 이미지를 봇에 반환하게 할 수 있습니다.
+ 아니면 다른 기준으로 쿠키를 검사할 수 있습니다. 예를 들어, 의류를 판매하는 소매 웹 사이트에서 사용자가 재킷에 대해 선택한 색상을 표시하기 위해 쿠키를 사용하는 경우 Lambda 함수는 CloudFront가 선택된 색상으로 재킷 이미지를 반환하도록 요청을 변경할 수 있습니다.
+ Lambda 함수는 CloudFront 최종 사용자 요청 또는 오리진 요청 이벤트가 발생할 때 HTTP 응답을 생성할 수 있습니다.
+ 함수는 헤더 또는 권한 부여 토큰을 검사하고, CloudFront가 오리진으로 요청을 전달하기 전에 헤더를 삽입하여 콘텐츠에 대한 액세스 권한을 제어할 수 있습니다.
+ 또한 Lambda 함수는 외부 리소스에 대한 네트워크 호출을 생성하여 사용자 자격 증명을 확인하거나 추가 콘텐츠를 가져와 응답을 사용자 지정할 수 있습니다.

예제 코드를 포함한 자세한 내용은 [Lambda@Edge 예제 함수](lambda-examples.md) 섹션을 참조하세요.

콘솔의 Lambda@Edge 설정에 대한 자세한 내용은 [자습서: 기본 Lambda@Edge 함수 만들기(콘솔)](lambda-edge-how-it-works-tutorial.md) 섹션을 참조하세요.

# Lambda@Edge 함수 시작(콘솔)
<a name="lambda-edge-how-it-works"></a>

Lambda@Edge에서 CloudFront 트리거를 사용하여 Lambda 함수를 간접 호출할 수 있습니다. CloudFront 배포를 Lambda 함수와 연결하면 CloudFront가 CloudFront 엣지 로케이션에서 [요청 및 응답을 가로채고](https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html) 함수를 실행합니다. Lambda 함수로 보안을 강화하거나 뷰어와 더 관련성 있는 정보가 표시되도록 사용자 지정하여 성능을 높일 수 있습니다.

다음 목록은 CloudFront에서 Lambda 함수를 생성 및 사용하는 방법에 대한 기본적인 개요를 제공합니다.

**개요: CloudFront를 통한 Lambda 함수 생성 및 사용**

1. 미국 동부(버지니아 북부) 리전에 Lambda 함수를 생성합니다.

1. 번호가 지정된 함수 버전을 저장하고 게시합니다.

   함수를 변경하려면 미국 동부(버지니아 북부)에서 \$1LATEST 버전의 함수를 편집해야 합니다. 그런 다음 번호가 지정된 새 버전을 게시하고 CloudFront와 연동되도록 설정합니다.

1. 함수를 CloudFront 배포 및 캐시 동작에 연결합니다. 그런 다음 함수를 실행시키는 하나 이상의 CloudFront 이벤트(*트리거*)를 지정합니다. 예를 들어, CloudFront가 최종 사용자의 요청을 받으면 함수가 실행되도록 하는 트리거를 생성할 수 있습니다.

1. 트리거를 생성하면 Lambda가 해당 함수를 전 세계의 AWS 위치에 복제를 생성합니다.

**작은 정보**  
자세한 내용은 [함수 생성 및 업데이트](lambda-edge-create-function.md), [이벤트 구조](lambda-event-structure.md)와 [CloudFront 트리거 추가](lambda-edge-add-triggers.md)를 참조하세요. 또한 [Lambda@Edge 예제 함수](lambda-examples.md)에서 다양한 아이디어와 코드 샘플도 다룹니다.

단계별 자습서는 다음 주제를 참조하세요.

**Topics**
+ [자습서: 기본 Lambda@Edge 함수 만들기(콘솔)](lambda-edge-how-it-works-tutorial.md)

# 자습서: 기본 Lambda@Edge 함수 만들기(콘솔)
<a name="lambda-edge-how-it-works-tutorial"></a>

이 자습서에서는 CloudFront에서 실행되는 예제 Node.js 함수를 만들고 구성하여 Lambda@Edge를 시작하는 방법을 보여줍니다. 이 예시에서는 CloudFront가 파일을 검색할 때 HTTP 보안 헤더를 응답에 추가합니다. (이를 통해 웹 사이트의 보안 및 개인 정보 보호를 개선할 수 있음)

이 자습서에는 자체 웹 사이트가 필요하지 않습니다. 그러나 자체 Lambda@Edge 솔루션을 생성하는 경우 비슷한 단계를 따르고 동일한 옵션 중에서 선택합니다.

**Topics**
+ [1단계: AWS 계정 가입](#lambda-edge-how-it-works-tutorial-AWS)
+ [2단계: CloudFront 배포 생성](#lambda-edge-how-it-works-tutorial-cloudfront)
+ [3단계: 함수 생성](#lambda-edge-how-it-works-tutorial-create-function)
+ [4단계: 함수를 실행할 CloudFront 트리거 추가](#lambda-edge-how-it-works-tutorial-add-trigger)
+ [5단계: 함수 실행 확인](#lambda-edge-how-it-works-tutorial-verify)
+ [6단계: 문제 해결](#lambda-edge-how-it-works-tutorial-troubleshoot)
+ [7단계: 예제 리소스 정리](#lambda-edge-how-it-works-tutorial-cleanup-resources)
+ [관련 정보](#lambda-edge-how-it-works-tutorial-resources)

## 1단계: AWS 계정 가입
<a name="lambda-edge-how-it-works-tutorial-AWS"></a>

아직 계정에 가입하지 않았다면 AWS 계정에 가입하세요. 자세한 내용은 [AWS 계정에 가입](setting-up-cloudfront.md#sign-up-for-aws) 단원을 참조합니다.

## 2단계: CloudFront 배포 생성
<a name="lambda-edge-how-it-works-tutorial-cloudfront"></a>

예제 Lambda@Edge 함수를 만들려면 먼저 콘텐츠를 제공할 오리진이 있는 CloudFront 환경부터 갖춰야 합니다.

이 예제에서는 Amazon S3 버킷을 배포의 오리진으로 사용하는 CloudFront 배포를 만듭니다. 사용할 환경이 이미 있는 경우 이 단계를 건너뛸 수 있습니다.<a name="lambda-edge-how-it-works-tutorial-cf-proc"></a>

**Amazon S3 오리진을 사용하여 CloudFront 배포를 생성하려면**

1. 이미지 파일 등 샘플 콘텐츠가 될 파일 한두 개로 Amazon S3 버킷을 만듭니다. [Amazon S3에 콘텐츠 업로드](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.html#GettingStartedUploadContent)의 단계를 따르면 됩니다. 버킷의 객체에 대한 퍼블릭 읽기 액세스를 허용하는 권한을 설정해야 합니다.

1. [CloudFront 웹 배포 생성](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.html#GettingStartedCreateDistribution)의 단계에 따라 CloudFront 배포를 만들고 오리진으로 S3 버킷을 추가합니다. 이미 배포가 있다면 버킷을 그 배포의 오리진으로 추가할 수 있습니다.
**작은 정보**  
배포 ID를 기록해 둡니다. 이 자습서 후반부에서 함수의 CloudFront 트리거를 추가할 때 드롭다운 목록에서 배포 ID(예: `E653W22221KDDL`)를 선택해야 합니다.

## 3단계: 함수 생성
<a name="lambda-edge-how-it-works-tutorial-create-function"></a>

이 단계에서는 Lambda 콘솔에 있는 청사진 템플릿에서 Lambda 함수를 생성합니다. 이 함수는 CloudFront 배포의 보안 헤더를 업데이트하는 코드를 추가합니다.<a name="lambda-edge-how-it-works-tutorial-create-function-blueprint-proc"></a>

**Lambda 함수를 생성하려면**

1. AWS Management Console에 로그인한 다음 AWS Lambda에서 [https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/) 콘솔을 엽니다.
**중요**  
현재 **US-East-1(버지니아 북부)** AWS 리전(**us-east-1**)에 있는지 확인합니다. 이 리전에 있어야 Lambda@Edge 함수를 만들 수 있습니다.

1. **함수 생성**을 선택합니다.

1. **함수 생성** 페이지에서 **청사진 사용**을 선택한 다음 검색 필드에 **cloudfront**를 입력하여 CloudFront 청사진을 필터링합니다.
**참고**  
CloudFront 청사진은 **US-East-1(버지니아 북부)** 리전(**us-east-1**)에서만 사용 가능합니다.

1. 함수에 대한 템플릿으로 **HTTP 응답 헤더 수정** 청사진을 선택합니다.

1. 다음과 같이 함수에 대한 정보를 입력합니다.
   + **함수 이름** - 함수의 이름을 입력합니다.
   + **실행 역할** - 함수에 대한 권한 설정 방법을 선택합니다. 권장되는 기본 Lambda@Edge 권한 정책 템플릿을 사용하려면 **AWS 정책 템플릿에서 새 역할 생성**을 선택합니다.
   + **역할 이름** - 정책 템플릿이 생성하는 역할 이름을 입력합니다.
   + **정책 템플릿** - CloudFront 청사진을 함수의 기본으로 선택했으므로 Lambda는 정책 템플릿 **기본 Lambda@Edge 권한**을 자동으로 추가합니다. 이 정책 템플릿은 CloudFront가 전 세계 CloudFront 로케이션에서 Lambda 함수를 실행할 수 있도록 하는 실행 역할 권한을 추가합니다. 자세한 내용은 [Lambda@Edge에 대한 IAM 권한 및 역할 설정](lambda-edge-permissions.md) 섹션을 참조하세요.

1. 페이지 하단에서 **함수 생성**을 선택합니다.

1. **Lambda@Edge에 배포 창**이 나타나면 **취소**를 선택합니다. (이 자습서에서는 함수를 Lambda@Edge에 배포하기 전에 함수 코드를 수정해야 합니다.)

1. 아래로 스크롤하여 페이지의 **코드 소스** 섹션으로 이동합니다.

1. 템플릿 코드를 오리진에서 반환하는 보안 헤더를 수정하는 함수로 바꿉니다. 예를 들면 다음과 비슷한 코드를 사용할 수 있습니다.

   ```
   'use strict';
   export const handler = (event, context, callback) => {
   
       //Get contents of response
       const response = event.Records[0].cf.response;
       const headers = response.headers;
   
       //Set new headers
       headers['strict-transport-security'] = [{key: 'Strict-Transport-Security', value: 'max-age= 63072000; includeSubdomains; preload'}];
       headers['content-security-policy'] = [{key: 'Content-Security-Policy', value: "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'"}];
       headers['x-content-type-options'] = [{key: 'X-Content-Type-Options', value: 'nosniff'}];
       headers['x-frame-options'] = [{key: 'X-Frame-Options', value: 'DENY'}];
       headers['x-xss-protection'] = [{key: 'X-XSS-Protection', value: '1; mode=block'}];
       headers['referrer-policy'] = [{key: 'Referrer-Policy', value: 'same-origin'}];
   
       //Return modified response
       callback(null, response);
   };
   ```

1. **파일**을 선택하여 **저장**하고 업데이트된 코드를 저장합니다.

1. **배포(Deploy)**를 선택합니다.

다음 섹션으로 이동하여 함수를 실행할 CloudFront 트리거를 추가합니다.

## 4단계: 함수를 실행할 CloudFront 트리거 추가
<a name="lambda-edge-how-it-works-tutorial-add-trigger"></a>

이제 보안 헤더를 업데이트할 Lambda 함수가 생성되었으므로, 그 함수를 실행하여 오리진에서 해당 배포에 대해 CloudFront로 보내는 모든 응답에 헤더를 추가할 CloudFront 트리거를 구성해야 합니다.<a name="lambda-edge-how-it-works-tutorial-add-trigger-proc"></a>

**함수의 CloudFront 트리거를 구성하려면**

1. Lambda 콘솔의 **함수 개요** 페이지에서 해당 함수에 대한 함수 개요 페이지에서 **트리거 추가**를 선택합니다.

1. **트리거 구성**에서 **CloudFront**를 선택합니다.

1. **Lambda@Edge에 배포**를 선택합니다.

1. **Lambda@Edge 배포** 창에서 **CloudFront 트리거 구성** 아래에 다음 정보를 입력합니다.
   + **배포** - 함수와 연결할 CloudFront 배포 ID입니다. 드롭다운 목록에서 배포 ID를 선택합니다.
   + **캐시 동작** - 트리거에 사용할 캐시 동작입니다. 이 예제에서는 **\$1**로 설정된 값을 그대로 둡니다. 이 값은 해당 배포의 기본 캐시 동작을 의미합니다. 자세한 내용은 [캐시 동작 설정](DownloadDistValuesCacheBehavior.md) 주제에서 [모든 배포 설정 참조](distribution-web-values-specify.md) 섹션을 참조하세요.
   + **CloudFront 이벤트** - 언제 함수를 실행할지 지정하는 트리거입니다. 여기서는 CloudFront가 오리진에서 응답을 반환할 때마다 보안 헤더 함수가 실행되도록 하겠습니다. 드롭다운 목록에서 **오리진 응답**을 선택합니다. 자세한 내용은 [Lambda@Edge 함수에 대한 트리거 추가](lambda-edge-add-triggers.md) 섹션을 참조하세요.

1. **Lambda@Edge로의 배포 확인**의 확인란을 선택합니다.

1. **배포**를 선택하여 트리거를 추가하고 함수를 전 세계 AWS 위치에 복제합니다.

1. 함수가 복제될 때까지 기다립니다. 일반적으로 몇 분 정도 걸립니다.

    [CloudFront 콘솔로 이동](https://console.aws.amazon.com/cloudfront/v4/home)하여 해당 배포를 보고 복제가 완료되었는지 확인할 수 있습니다. 배포 상태가 **배포 중**에서 날짜 및 시간으로 변경될 때까지 기다립니다. 이는 함수가 복제되었음을 의미합니다. 함수가 작동하는지 확인하려면 다음 단원의 단계를 따릅니다.

## 5단계: 함수 실행 확인
<a name="lambda-edge-how-it-works-tutorial-verify"></a>

Lambda 함수를 만들고 CloudFront 배포에 대해 이를 실행하는 트리거를 구성했으니 이제 이 함수가 원하는 결과를 달성하는지 확인해야 합니다. 이 예제에서는 CloudFront가 반환하는 HTTP 헤더를 확인하여 보안 헤더가 추가되었는지 알아봅니다.<a name="lambda-edge-how-it-works-tutorial-verify-proc"></a>

**Lambda@Edge 함수가 보안 헤더를 추가하는지 확인하려면**

1. 브라우저에서 S3 버킷에 있는 파일의 URL을 입력합니다. 예를 들면 `https://d111111abcdef8.cloudfront.net/image.jpg`와 비슷한 URL을 사용할 수 있습니다.

   파일 URL에 사용할 CloudFront 도메인 이름에 대한 자세한 내용은 [CloudFront에서 파일에 대한 URL 형식 사용자 지정](LinkFormat.md) 단원을 참조하세요.

1. 브라우저에서 웹 개발자 도구 모음을 엽니다. 예를 들어 Chrome 브라우저 창에서는 컨텍스트(마우스 오른쪽 버튼 클릭) 메뉴를 연 다음 **Inspect(검사)**를 선택합니다.

1. **네트워크** 탭을 선택합니다.

1. 페이지를 새로 고쳐 이미지를 확인한 다음 왼쪽 창에서 HTTP 요청을 선택합니다. HTTP 헤더가 별도의 창에 표시됩니다.

1. HTTP 헤더 목록을 살펴보면서 원하는 보안 헤더가 목록에 포함되어 있는지 확인합니다. 예를 들어 다음 스크린샷에 표시된 것과 비슷한 헤더가 보일 것입니다.  
![\[원하는 보안 헤더가 강조 표시된 HTTP 헤더 목록.\]](http://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/images/lambda-at-edge-security-headers-list.png)

추가한 보안 헤더가 헤더 목록에 있으면 완료된 것입니다. 첫 번째 Lambda@Edge 함수가 생성되었습니다. CloudFront에서 오류를 반환하거나 다른 문제가 있는 경우에는 계속해서 다음 단계로 넘어가 문제를 해결하세요.

## 6단계: 문제 해결
<a name="lambda-edge-how-it-works-tutorial-troubleshoot"></a>

CloudFront가 오류를 반환하거나 원하는 보안 헤더를 추가하지 않는 경우, CloudWatch Logs를 조사하여 함수의 실행 상태를 확인할 수 있습니다. 함수를 실행한 위치와 가장 가까운 AWS 위치에 저장된 로그를 사용해야 합니다.

예를 들어, 런던에서 파일을 볼 때는 CloudWatch 콘솔에서 리전을 유럽(런던)으로 변경해야 합니다.<a name="lambda-edge-how-it-works-tutorial-cloudwatch-proc"></a>

**Lambda@Edge 함수에 대한 CloudWatch 로그를 검사하려면**

1. AWS Management Console에 로그인하고 [https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/)에서 CloudWatch 콘솔을 엽니다.

1. **리전**을 브라우저에서 파일을 볼 때 표시되는 위치로 변경합니다. 이는 함수가 실행되고 있는 리전입니다.

1. 왼쪽 창에서 **로그**를 선택하여 해당 배포의 로그를 봅니다.

자세한 내용은 [Amazon CloudWatch를 사용한 CloudFront 지표 모니터링](monitoring-using-cloudwatch.md) 단원을 참조하세요.

## 7단계: 예제 리소스 정리
<a name="lambda-edge-how-it-works-tutorial-cleanup-resources"></a>

이 자습서에 사용할 용도로만 Amazon S3 버킷과 CloudFront 배포를 만들었다면 할당한 AWS 리소스를 삭제하여 더 이상 요금이 발생하지 않도록 하세요. AWS 리소스를 삭제한 뒤에는 추가한 콘텐츠를 이용할 수 없게 됩니다.

**업무**
+ [S3 버킷 삭제](#lambda-edge-how-it-works-tutorial-delete-bucket) 
+ [Lambda 함수 삭제](#lambda-edge-how-it-works-tutorial-delete-function)
+ [CloudFront 배포 삭제](#lambda-edge-how-it-works-tutorial-delete-distribution)

### S3 버킷 삭제
<a name="lambda-edge-how-it-works-tutorial-delete-bucket"></a>

Amazon S3 버킷을 삭제하기 전에 버킷에 대한 로깅이 비활성화되어 있는지 확인하세요. 그렇지 않으면 삭제해도 AWS가 해당 버킷에 계속 로그를 기록합니다.<a name="lambda-edge-how-it-works-tutorial-delete-bucket-proc"></a>

**버킷에 대한 로깅 사용 중지**

1. [https://console.aws.amazon.com/s3/](https://console.aws.amazon.com/s3/)에서 Amazon S3 콘솔을 엽니다.

1. 버킷을 선택한 다음, **속성**를 선택합니다.

1. **속성**에서 **로깅**을 선택합니다.

1. **사용(Enabled)** 확인란의 선택을 취소합니다.

1. **저장**을 선택합니다.

이제 버킷을 삭제할 수 있습니다. 자세한 내용은 *Amazon Simple Storage Service Console 사용 설명서*의 [ 버킷 삭제](https://docs.aws.amazon.com/AmazonS3/latest/userguide/delete-bucket.html)를 참조하십시오.

### Lambda 함수 삭제
<a name="lambda-edge-how-it-works-tutorial-delete-function"></a>

Lambda 함수 연결 및 선택적으로 함수 자체를 삭제하는 지침은 [Lambda@Edge 함수 및 복제본 삭제](lambda-edge-delete-replicas.md) 단원을 참조합니다.

### CloudFront 배포 삭제
<a name="lambda-edge-how-it-works-tutorial-delete-distribution"></a>

CloudFront 배포를 삭제하기 전에 반드시 배포를 비활성화해야 합니다. 비활성화된 배포가 작동하지 않아 요금이 발생하지 않습니다. 언제든지 비활성화된 배포를 활성화할 수 있습니다. 비활성화된 배포를 삭제한 뒤에는 사용할 수 없습니다.<a name="lambda-edge-how-it-works-tutorial-delete-distribution-proc"></a>

**CloudFront 배포를 사용하지 않도록 설정하고 삭제하려면**

1. 에서 CloudFront 콘솔을 엽니다[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)

1. 사용 중지하려는 배포를 선택한 후 **사용 중지**를 선택합니다.

1. 확인 메시지가 표시되면 **예, 사용 중지**를 선택합니다.

1. 사용 중지된 배포를 선택한 후 **삭제**를 선택합니다.

1. 확인 메시지가 나타나면 **예, 삭제합니다**를 선택합니다.

## 관련 정보
<a name="lambda-edge-how-it-works-tutorial-resources"></a>

Lambda@Edge 함수의 작동 방식에 대한 기본적인 내용을 배웠습니다. 이제 다음 자료를 읽고 자세히 알아보십시오.
+ [Lambda@Edge 예제 함수](lambda-examples.md)
+ [Lambda@Edge 설계 모범 사례](https://aws.amazon.com/blogs/networking-and-content-delivery/lambdaedge-design-best-practices/)
+ [Lambda@Edge로 대기 시간 감소 및 컴퓨팅 작업을 엣지로 이전](https://aws.amazon.com/blogs/networking-and-content-delivery/reducing-latency-and-shifting-compute-to-the-edge-with-lambdaedge/)

# Lambda@Edge에 대한 IAM 권한 및 역할 설정
<a name="lambda-edge-permissions"></a>

Lambda@Edge를 구성하려면 AWS Lambda에 대한 다음 IAM 권한과 역할이 있어야 합니다.
+ [IAM 권한](#lambda-edge-permissions-required) - 이러한 권한을 통해 Lambda 함수를 생성하고 CloudFront 배포와 연결할 수 있습니다.
+ [Lambda 함수 실행 역할](#lambda-edge-permissions-function-execution)(IAM 역할) – Lambda 서비스 보안 주체가 이 역할을 맡아 함수를 실행합니다.
+ [Lambda@Edge 서비스 연결 역할](#using-service-linked-roles-lambda-edge) - 서비스 연결 역할은 특정 AWS 서비스가 Lambda 함수를 AWS 리전에 복제하고 CloudWatch가 CloudFront 로그 파일을 사용할 수 있도록 합니다.

## Lambda@Edge 함수를 CloudFront 배포와 연결하는 데 필요한 IAM 권한
<a name="lambda-edge-permissions-required"></a>

사용자가 Lambda 함수를 CloudFront 배포와 연결하려면 Lambda에 필요한 IAM 권한 외에 다음과 같은 권한이 필요합니다.
+ `lambda:GetFunction` – 함수가 포함된 `.zip` 파일을 다운로드하기 위한 미리 서명된 URL과 Lambda 함수에 대한 구성 정보를 가져오기 위한 권한을 부여합니다.
+ `lambda:EnableReplication*` – Lambda 복제 서비스가 함수 코드 및 구성을 가져올 수 있도록 리소스 정책에 대한 권한을 부여합니다.
+ `lambda:DisableReplication*` – Lambda 복제 서비스가 함수를 삭제할 수 있도록 리소스 정책에 대한 권한을 부여합니다.
**중요**  
`lambda:EnableReplication*` 및 `lambda:DisableReplication*` 작업 끝에 별표(`*`)를 추가해야 합니다.
+ 리소스의 경우, 다음 예시와 같이 CloudFront 이벤트가 발생할 때 실행할 함수 버전의 ARN을 지정합니다.

  `arn:aws:lambda:us-east-1:123456789012:function:TestFunction:2`
+ `iam:CreateServiceLinkedRole` – Lambda@Edge가 CloudFront에서 Lambda 함수를 복제하는 데 사용하는 서비스 연결 역할을 생성하기 위한 권한을 부여합니다. Lambda@Edge를 처음으로 구성한 후 서비스 연결 역할이 자동으로 생성됩니다. Lambda@Edge를 사용하는 다른 배포에는 이 권한을 추가할 필요가 없습니다.

  
+ `cloudfront:UpdateDistribution` 또는 `cloudfront:CreateDistribution` – 배포를 업데이트 또는 생성하기 위한 권한을 부여합니다.

자세한 내용은 다음 항목을 참조하세요.
+ [Amazon CloudFront용 Identity and Access Management](security-iam.md)
+ **AWS Lambda 개발자 안내서의 [Lambda 리소스 액세스 권한](https://docs.aws.amazon.com/lambda/latest/dg/intro-permission-model.html#lambda-intro-execution-role)

## 서비스 보안 주체에 대한 함수 실행 역할
<a name="lambda-edge-permissions-function-execution"></a>

함수를 실행할 때 `lambda.amazonaws.com` 및 `edgelambda.amazonaws.com` 서비스 보안 주체가 맡을 수 있는 IAM 역할을 생성해야 합니다.

**작은 정보**  
Lambda 콘솔에서 함수를 생성할 때 AWS 정책 템플릿을 사용하여 새로운 실행 역할을 생성할 수 있습니다. 이 단계는 함수를 실행하는 데 필요한 Lambda@Edge 권한을 *자동으로* 추가합니다. [자습서의 5단계: 간단한 Lambda@Edge 함수 생성](lambda-edge-how-it-works-tutorial.md#lambda-edge-how-it-works-tutorial-create-function)을 참조하세요.

IAM 역할 수동 생성에 대한 자세한 내용은 **IAM 사용 설명서의 [역할 생성 및 정책 연결(콘솔)](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions_create-policies.html)을 참조하세요.

**Example 예시: 역할 신뢰 정책**  
IAM 콘솔의 **신뢰 관계** 탭에서 이 역할을 추가할 수 있습니다. **권한** 탭 아래에 이 정책을 추가하지 마세요.    
****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement": [
      {
         "Effect": "Allow",
         "Principal": {
            "Service": [
               "lambda.amazonaws.com",
               "edgelambda.amazonaws.com"
            ]
         },
         "Action": "sts:AssumeRole"
      }
   ]
}
```

실행 역할에 부여해야 하는 권한에 대한 자세한 내용은 **AWS Lambda 개발자 안내서의 [Lambda 리소스 액세스 권한](https://docs.aws.amazon.com/lambda/latest/dg/intro-permission-model.html#lambda-intro-execution-role)을 참조하세요.

**참고**  
기본적으로 CloudFront 이벤트가 Lambda 함수를 트리거할 때마다 데이터는 CloudWatch Logs에 기록됩니다. 이러한 로그를 사용하려면 실행 역할에 CloudWatch Logs에 데이터를 기록할 권한이 있어야 합니다. 사전 정의된 AWSLambdaBasicExecutionRole을 사용하여 실행 역할에 대한 권한을 부여할 수 있습니다.  
CloudWatch Logs에 대한 자세한 내용은 [엣지 함수 로그](edge-functions-logs.md) 섹션을 참조하세요.
Lambda 함수 코드가 S3 버킷에서 객체를 읽는 것처럼 다른 AWS 리소스에 액세스하는 경우 실행 역할에는 해당 작업을 수행할 수 있는 권한이 필요합니다.

## Lambda@Edge의 서비스 연결 역할
<a name="using-service-linked-roles-lambda-edge"></a>

Lambda@Edge는 IAM [서비스 연결 역할](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_terms-and-concepts.html#iam-term-service-linked-role)을 사용합니다. 서비스 연결 역할은 서비스에 직접 연결된 고유한 유형의 IAM 역할입니다. 서비스 연결 역할은 해당 서비스에서 사전 정의하며 서비스에서 사용자를 대신하여 다른 AWS 서비스를 호출하기 위해 필요한 모든 권한을 포함합니다.

Lambda@Edge는 다음 IAM 서비스 연결 역할을 사용합니다.
+ **AWSServiceRoleForLambdaReplicator** - Lambda@Edge에서는 이 역할을 사용해 Lambda@Edge가 AWS 리전에 함수를 복제하도록 합니다.

  CloudFront에 Lambda@Edge 트리거를 처음 추가할 때 AWSServiceRoleForLambdaReplicator라는 역할이 자동으로 생성되어 Lambda@Edge가 AWS 리전에 함수를 복제할 수 있도록 합니다. Lambda@Edge 함수를 사용하려면 이 역할이 필요합니다. AWSServiceRoleForLambdaReplicator 역할에 대한 ARN은 다음 예시와 같을 수 있습니다.

  `arn:aws:iam::123456789012:role/aws-service-role/replicator.lambda.amazonaws.com/AWSServiceRoleForLambdaReplicator`
+ **AWSServiceRoleForCloudFrontLogger** – CloudFront는 이 역할을 사용하여 CloudWatch에 로그 파일을 푸시합니다. 로그 파일을 사용하여 Lambda@Edge 검증 오류를 디버깅할 수 있습니다.

  AWSServiceRoleForCloudFrontLogger 역할은 CloudFront에서 Lambda@Edge 오류 로그 파일을 CloudWatch로 푸시하도록 허용하기 위해 Lambda@Edge 함수 연결을 추가한 경우 자동으로 생성됩니다. AWSServiceRoleForCloudFrontLogger 역할의 ARN의 모양은 다음과 같습니다.

  `arn:aws:iam::account_number:role/aws-service-role/logger.cloudfront.amazonaws.com/AWSServiceRoleForCloudFrontLogger`

서비스 연결 역할이 있으면 필요한 권한을 수동으로 추가할 필요가 없으므로 Lambda@Edge를 설정 및 사용하기가 쉽습니다. 서비스 연결 역할의 권한은 Lambda@Edge에서 정의하며, Lambda@Edge만이 그 역할을 맡을 수 있습니다. 정의된 권한에는 신뢰 정책과 권한 정책이 포함됩니다. 권한 정책은 다른 어떤 IAM 엔터티에도 연결할 수 없습니다.

서비스 연결 역할을 삭제하려면 먼저 연결된 CloudFront 또는 Lambda@Edge 리소스를 제거해야 합니다. 그러면 활성 리소스에 액세스하는 데 필요한 서비스 연결 역할을 제거하지 않고 Lambda@Edge 리소스를 보호할 수 있습니다.

서비스 연결 역할에 대한 자세한 내용은 [CloudFront 서비스 연결 역할](security_iam_service-with-iam.md#security_iam_service-with-iam-roles-service-linked)를 참조하세요.

### Lambda@Edge의 서비스 연결 역할 권한
<a name="slr-permissions-lambda-edge"></a>

Lambda@Edge는 **AWSServiceRoleForLambdaReplicator** 및 **AWSServiceRoleForCloudFrontLogger**이라는 서비스 연결 역할을 사용합니다. 다음 단원에서는 이러한 각 역할에 대한 권한을 설명합니다.

**Contents**
+ [Lambda Replicator의 서비스 연결 역할 권한](#slr-permissions-lambda-replicator)
+ [CloudFront Logger에 대한 서비스 연결 역할 권한](#slr-permissions-cloudfront-logger)

#### Lambda Replicator의 서비스 연결 역할 권한
<a name="slr-permissions-lambda-replicator"></a>

이 서비스 연결 역할을 통해 Lambda는 Lambda@Edge 함수를 AWS 리전에 복제할 수 있습니다.

AWSServiceRoleForLambdaReplicator 서비스 연결 역할은 역할을 수임하기 위해 `replicator.lambda.amazonaws.com`서비스를 신뢰합니다.

역할 권한 정책에서는 Lambda@Edge가 지정된 리소스에서 다음 작업을 완료할 수 있도록 허용합니다.
+ `lambda:CreateFunction`​의 `arn:aws:lambda:*:*:function:*`
+ `lambda:DeleteFunction`​의 `arn:aws:lambda:*:*:function:*`
+ `lambda:DisableReplication`​의 `arn:aws:lambda:*:*:function:*`
+ `iam:PassRole`​의 `all AWS resources`
+  `cloudfront:ListDistributionsByLambdaFunction`​의 `all AWS resources`

#### CloudFront Logger에 대한 서비스 연결 역할 권한
<a name="slr-permissions-cloudfront-logger"></a>

이 서비스 연결 역할을 사용하면 Lambda@Edge 검증 오류를 디버깅하기 위해 CloudFront에서 CloudWatch로 로그 파일을 푸시할 수 있습니다.

AWSServiceRoleForCloudFrontLogger 서비스 연결 역할은 역할을 수임하기 위해 `logger.cloudfront.amazonaws.com`서비스를 신뢰합니다.

역할 권한 정책에서는 Lambda@Edge가 지정된 `arn:aws:logs:*:*:log-group:/aws/cloudfront/*` 리소스에서 다음 작업을 완료할 수 있도록 허용합니다.
+ `logs:CreateLogGroup` ``
+ `logs:CreateLogStream`
+ `logs:PutLogEvents`

IAM 엔터티(예: 사용자, 그룹 또는 역할)가 Lambda@Edge 서비스 연결 역할을 삭제할 수 있도록 권한을 구성해야 합니다. 자세한 내용은 *IAM 사용 설명서*의 [서비스 연결 역할 권한](https://docs.aws.amazon.com/IAM/latest/UserGuide/using-service-linked-roles.html#service-linked-role-permissions)을 참조하세요.

### Lambda@Edge의 서비스 연결 역할 생성
<a name="create-slr-lambda-edge"></a>

일반적으로 Lambda@Edge에 대한 서비스 연결 역할은 수동으로 생성하지 않습니다. 다음 시나리오에서 서비스는 역할을 자동으로 생성합니다.
+ 트리거를 처음으로 생성할 때, 이 서비스에서는 AWSServiceRoleForLambdaReplicator 역할이 없는 경우 해당 역할을 생성합니다. 이 역할을 통해 Lambda는 Lambda@Edge 함수를 AWS 리전에 복제할 수 있습니다.

  서비스 연결 역할을 삭제하는 경우, 배포에서 Lambda@Edge의 새 트리거를 추가할 때 이 역할이 다시 생성됩니다.
+ Lambda@Edge 연결이 있는 CloudFront 배포를 업데이트 또는 생성할 때, 이 서비스에서는 AWSServiceRoleForCloudFrontLogger라는 역할이 없는 경우 해당 역할을 생성합니다. 이 정책은 CloudFront에서 로그 파일을 CloudFront로 푸시할 수 있도록 허용합니다.

  서비스 연결 역할을 삭제했다 하더라도, Lambda@Edge 연결이 있는 CloudFront 배포를 업데이트 또는 생성하는 경우 해당 역할이 다시 생성됩니다.

이러한 서비스 연결 역할을 수동으로 만들어야 하는 경우 다음 AWS Command Line Interface(AWS CLI) 명령을 실행합니다.

**AWSServiceRoleForLambdaReplicator 역할을 만들려면**
+ 다음 명령을 실행합니다.

  ```
  aws iam create-service-linked-role --aws-service-name replicator.lambda.amazonaws.com
  ```

**AWSServiceRoleForCloudFrontLogger 역할을 만들려면**
+ 다음 명령을 실행합니다.

  ```
  aws iam create-service-linked-role --aws-service-name logger.cloudfront.amazonaws.com
  ```

### Lambda@Edge 서비스 연결 역할 편집
<a name="edit-slr-lambda-edge"></a>

Lambda@Edge에서는 AWSServiceRoleForLambdaReplicator 또는 AWSServiceRoleForCloudFrontLogger 서비스 연결 역할을 편집할 수 없습니다. 서비스에서 서비스 연결 역할을 만든 후에는 다양한 엔터티가 역할을 참조할 수 있기 때문에 역할 이름을 변경할 수 없습니다. 그러나 IAM을 사용하여 역할 설명을 편집할 수는 있습니다. 자세한 내용은 *IAM 사용 설명서*의 [서비스 연결 역할 편집](https://docs.aws.amazon.com/IAM/latest/UserGuide/using-service-linked-roles.html#edit-service-linked-role)을 참조하세요.

### Lambda@Edge 서비스 연결 역할에 대해 지원되는 AWS 리전
<a name="slr-regions-lambda-edge"></a>

CloudFront는 다음 AWS 리전에서 Lambda@Edge에 대한 서비스 연결 역할 사용을 지원합니다.
+ 미국 동부(버지니아 북부) – `us-east-1`
+ 미국 동부(오하이오) – `us-east-2`
+ 미국 서부(캘리포니아 북부) – `us-west-1`
+ 미국 서부(오리건) – `us-west-2`
+ 아시아 태평양(뭄바이) – `ap-south-1`
+ 아시아 태평양(서울) – `ap-northeast-2`
+ 아시아 태평양(싱가포르) – `ap-southeast-1`
+ 아시아 태평양(시드니) – `ap-southeast-2`
+ 아시아 태평양(도쿄) – `ap-northeast-1`
+ 유럽(프랑크푸르트) – `eu-central-1`
+ 유럽(아일랜드) – `eu-west-1`
+ 유럽(런던) – `eu-west-2`
+ 남아메리카(상파울루) – `sa-east-1`

# Lambda@Edge 함수 작성 및 생성
<a name="lambda-edge-create-function"></a>

Lambda@Edge를 사용하려면 AWS Lambda 함수에 대한 코드를 *작성*해야 합니다. Lambda@Edge 함수를 작성하는 데 도움이 되도록 다음 리소스를 참조하세요.
+  [Lambda@Edge 이벤트 구조](lambda-event-structure.md) – Lambda@Edge와 함께 사용할 이벤트 구조를 이해합니다.
+ [Lambda@Edge 예제 함수](lambda-examples.md) – A/B 테스트 및 HTTP 리디렉션 생성 등의 예시 함수입니다.

Lambda@Edge에서 Node.js 또는 Python을 사용하는 프로그래밍 AWS 리전에서 Lambda를 사용하는 것과 동일합니다. 자세한 내용은 **AWS Lambda 개발자 안내서의 [Node.js를 사용하여 Lambda 함수 작성](https://docs.aws.amazon.com/lambda/latest/dg/lambda-nodejs.html) 또는 [Python을 사용하여 Lambda 함수 작성](https://docs.aws.amazon.com/lambda/latest/dg/lambda-python.html)을 참조하세요.

Lambda@Edge 함수에 `callback` 파라미터를 포함하고 요청 또는 응답 이벤트에 해당되는 객체를 반환합니다.
+ **요청 이벤트** - 응답에 `cf.request` 객체를 포함합니다.

  응답을 생성하는 경우 응답에 `cf.response` 객체를 포함합니다. 자세한 내용은 [요청 트리거에서 HTTP 응답 생성](lambda-generating-http-responses.md#lambda-generating-http-responses-in-requests) 단원을 참조하세요.
+ **응답 이벤트** - 응답에 `cf.response` 객체를 포함합니다.

코드를 직접 작성하거나 예제 중 하나를 사용한 후 Lambda에서 함수를 생성합니다. 함수를 생성하거나 기존 함수를 편집하려면 다음 주제를 참조하세요.

**Topics**
+ [Lambda@Edge 함수 생성](lambda-edge-create-in-lambda-console.md)
+ [Lambda 함수 편집](lambda-edge-edit-function.md)

 Lambda에서 함수를 생성한 후에는 *triggers*라고 하는 특정 CloudFront 이벤트를 기반으로 함수를 실행하도록 Lambda를 설정합니다. 자세한 내용은 [Lambda@Edge 함수에 대한 트리거 추가](lambda-edge-add-triggers.md) 섹션을 참조하세요.

# Lambda@Edge 함수 생성
<a name="lambda-edge-create-in-lambda-console"></a>

CloudFront 이벤트를 기반으로 한 Lambda 함수를 실행하도록 AWS Lambda를 설정하려면 다음 절차를 수행합니다.<a name="lambda-edge-create-function-procedure"></a>

**Lambda@Edge 함수를 생성하려면**

1. AWS Management Console에 로그인하고 AWS Lambdahttps://console.aws.amazon.com/lambda/[에서 ](https://console.aws.amazon.com/lambda/) 콘솔을 엽니다.

1. 이미 하나 이상의 Lambda 함수가 있는 경우에는 **함수 생성**을 선택합니다.

   아무 함수도 없는 경우 **Get Started Now**(지금 시작하기)를 선택합니다.

1. 페이지 상단의 리전 목록에서 **미국 동부(버지니아 북부)(US East (N. Virginia))**를 선택합니다.

1. 자체 코드를 사용하여 함수를 생성하거나 CloudFront 청사진으로 시작하는 함수를 생성합니다.
   + 자체 코드를 사용하여 함수를 생성하려면 **새로 작성**을 선택합니다.
   + CloudFront용 청사진 목록을 표시하려면 필터 필드에 **cloudfront**를 입력한 다음, **Enter** 키를 누릅니다.

     사용할 청사진을 찾은 경우 청사진 이름을 선택합니다.

1. **기본 정보** 섹션에서 다음 값을 지정합니다.

   1. **이름** – 함수의 이름을 입력합니다.

   1. **역할** - 빠르게 시작하려면 **템플릿에서 새 역할 만들기**를 선택합니다. **기존 역할 선택** 또는 **사용자 지정 역할 생성**을 선택한 다음 지시에 따라 이 섹션의 정보를 작성할 수도 있습니다.

   1. **역할 이름** – 역할의 이름을 입력합니다.

   1. **정책 템플릿** – **기본 Edge Lambda 권한**을 선택합니다.

1. 4단계에서 **새로 작성**을 선택한 경우 7단계로 건너뜁니다.

   4단계에서 청사진을 선택한 경우 **cloudfront** 단원에서 이 함수를 CloudFront 배포 및 CloudFront 이벤트의 캐시와 연결하는 트리거를 하나 생성할 수 있습니다. 이때 함수 생성 시 트리거가 없도록 **제거**를 선택하는 것이 좋습니다. 그런 다음, 나중에 트리거를 추가할 수 있습니다.
**작은 정보**  
트리거를 추가하기 전에 함수를 테스트하고 디버깅하는 것이 좋습니다. 지금 트리거를 추가하면 함수가 생성되어 전 세계의 AWS 위치로 복제를 완료하고 난 후 해당 배포가 배포되는 즉시 함수가 실행됩니다.

1. **함수 생성**을 선택합니다.

   Lambda는 함수의 두 버전인 \$1LATEST와 Version 1을 생성합니다. \$1LATEST 버전만 편집할 수 있지만 콘솔에 처음에는 Version 1이 표시됩니다.

1. 함수를 편집하려면 페이지 위쪽의, 함수 ARN 아래에서 **Version 1**을 선택합니다. 그런 다음, **버전** 탭에서 **\$1LATEST**를 선택합니다. 함수에서 나간 후 다시 돌아오면 버튼 레이블이 **한정자**로 바뀌어 있습니다.

1. **구성** 탭에서 해당하는 **코드 입력 유형**을 선택합니다. 그런 다음, 프롬프트의 메시지를 따라 코드를 편집하거나 업로드합니다.

1. **실행 시간**에서 함수 코드를 기반으로 값을 선택합니다.

1. **태그** 섹션에서 해당하는 태그를 추가합니다.

1. **작업**을 선택한 다음, **Publish new version**(새 버전 게시)을 선택합니다.

1. 새 함수 버전의 설명을 입력합니다.

1. [**Publish**]를 선택합니다.

1. 함수를 테스트하고 디버깅합니다. Lambda 콘솔 테스트에 대한 자세한 내용은 AWS Lambda 개발자 안내서**에서 [콘솔을 사용하여 Lambda 함수 호출](https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html#get-started-invoke-manually)을 참조하세요.

1. CloudFront 이벤트에 대해 함수를 실행할 준비가 되면 다른 버전을 게시하고 함수를 편집하여 트리거를 추가합니다. 자세한 내용은 [Lambda@Edge 함수에 대한 트리거 추가](lambda-edge-add-triggers.md) 섹션을 참조하세요.

# Lambda 함수 편집
<a name="lambda-edge-edit-function"></a>

Lambda@Edge 함수를 생성한 후 Lambda 콘솔을 사용하여 편집할 수 있습니다.

**참고**  
원래 버전은 \$1LATEST라고 표시되어 있습니다.
\$1LATEST 버전만 편집할 수 있습니다.
\$1LATEST 버전을 편집할 때마다 번호가 지정된 새 버전을 게시해야 합니다.
\$1LATEST에 대한 트리거는 생성할 수 없습니다.
새 버전의 함수를 게시하면 Lambda는 이전 버전의 트리거를 새 버전으로 자동 복사하지 않습니다. 새 버전에 대한 트리거를 다시 생성해야 합니다.
CloudFront 이벤트에 대한 트리거를 함수에 추가할 때 동일한 함수의 이전 버전에 대해 동일한 배포, 캐시 동작 및 이벤트에 대한 트리거가 이미 존재하면 Lambda는 이전 버전에서 트리거를 삭제합니다.
트리거를 추가하는 등 CloudFront 배포를 업데이트한 후에는 변경 사항이 엣지 로케이션으로 전파될 때까지 기다려야만 트리거에서 지정한 함수가 작동합니다.<a name="lambda-edge-edit-function-procedure"></a>

**Lambda 함수를 편집하려면**

1. AWS Management Console에 로그인하고 AWS Lambdahttps://console.aws.amazon.com/lambda/[에서 ](https://console.aws.amazon.com/lambda/) 콘솔을 엽니다.

1. 페이지 상단의 리전 목록에서 **미국 동부(버지니아 북부)(US East (N. Virginia))**를 선택합니다.

1. 함수 목록에서 함수의 이름을 선택합니다.

   기본 설정 사용 시, \$1LATEST 버전이 콘솔에 표시됩니다. 이전 버전을 볼 수 있지만(**한정자** 선택) \$1LATEST만 편집할 수 있습니다.

1. **코드(Code)** 탭의 **코드 입력 유형(Code entry type)**에서 브라우저에서 코드를 편집하거나, .zip 파일을 업로드하거나, Amazon S3에서 파일을 업로드하도록 선택합니다.

1. **저장** 또는 **저장 및 테스트**를 선택합니다.

1. **작업** 및 **Publish new version**(새 버전 게시)을 차례대로 선택합니다.

1. **\$1LATEST의 새 버전 게시** 대화 상자에서 새 버전 설명을 입력합니다. 이 설명은 자동으로 생성된 버전 번호와 함께 버전 목록에 표시됩니다.

1. [**Publish**]를 선택합니다.

   새 버전은 자동으로 최신 버전이 됩니다. 버전 번호는 페이지 왼쪽 상단 모서리의 **버전**에 표시됩니다.
**참고**  
함수에 트리거를 아직 추가하지 않았다면 [Lambda@Edge 함수에 대한 트리거 추가](lambda-edge-add-triggers.md) 섹션을 참조하세요.

1. **트리거** 탭을 선택합니다.

1. **트리거 추가**를 선택합니다.

1. **트리거 추가(Add trigger)** 대화 상자에서 점선으로 된 상자를 선택한 다음, **CloudFront**를 선택합니다.
**참고**  
함수에 대해 하나 이상의 트리거를 이미 만든 경우 CloudFront가 기본 서비스입니다.

1. 다음 값을 지정하여 Lambda 함수를 실행할 시기를 나타냅니다.

   1. **배포 ID** – 트리거를 추가할 배포의 ID를 선택합니다.

   1. **캐시 동작** – 함수를 실행할 객체를 지정하는 캐시 동작을 선택합니다.

   1. **CloudFront 이벤트** – 함수를 실행시키는 CloudFront 이벤트를 선택합니다.

   1. **트리거 활성화 및 복제** – 이 확인란을 선택하면 Lambda가 전 세계 AWS 리전으로 함수를 복제합니다.

1. **제출**을 선택합니다.

1. 이 함수에 대한 트리거를 추가하려면 10\$113단계를 반복합니다.

Lambda 콘솔에서 함수를 테스트하고 디버깅하는 방법에 대한 자세한 내용은 AWS Lambda 개발자 안내서**에서 [콘솔을 사용하여 Lambda 함수 호출](https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html#get-started-invoke-manually)을 참조하세요.

CloudFront 이벤트에 대해 함수를 실행할 준비가 되면 다른 버전을 게시하고 함수를 편집하여 트리거를 추가합니다. 자세한 내용은 [Lambda@Edge 함수에 대한 트리거 추가](lambda-edge-add-triggers.md) 섹션을 참조하세요.

# Lambda@Edge 함수에 대한 트리거 추가
<a name="lambda-edge-add-triggers"></a>

Lambda@Edge 트리거는 CloudFront 배포, 캐시 동작, 그리고 함수 실행을 유도하는 이벤트를 하나로 조합한 것입니다. 예를 들어, 최종 사용자가 해당 배포에 설정된 특정 캐시 동작을 CloudFront에 요청하면 함수가 실행되도록 하는 트리거를 생성할 수 있습니다. 하나 이상의 CloudFront 트리거를 지정할 수 있습니다.

**작은 정보**  
CloudFront 배포를 생성할 때 다른 요청을 수신할 때 응답하는 방법을 CloudFront에게 알려주는 설정을 지정합니다. 기본 설정을 배포에 대한 *기본 캐시 동작*이라고 합니다. CloudFront가 특정한 상황(예를 들어, 특정 파일 유형에 대한 요청을 수신할 때)에서 응답하는 방법을 정의하는 추가 캐시 동작을 설정할 수 있습니다. 자세한 내용은 [캐시 동작 설정](DownloadDistValuesCacheBehavior.md) 섹션을 참조하세요.

Lambda 함수를 처음 생성할 때는 트리거를 *하나*만 지정할 수 있습니다. 나중에 Lambda 콘솔을 사용하거나 CloudFront 콘솔에서 배포를 편집하여 동일한 함수에 더 많은 트리거를 추가할 수 있습니다.
+ Lambda 콘솔을 사용하는 방법은 동일한 CloudFront 배포에 대한 함수에 트리거를 더 추가하려는 경우에 적합합니다.
+ CloudFront 콘솔을 사용하는 방법은 업데이트하려는 배포를 쉽게 찾을 수 있기 때문에 여러 배포에 대한 트리거를 추가하려는 경우에 더 적합할 수 있습니다. 또한 동시에 다른 CloudFront 설정을 업데이트할 수 있습니다.

**Topics**
+ [Lambda@Edge 함수를 트리거할 수 있는 CloudFront 이벤트](lambda-cloudfront-trigger-events.md)
+ [함수를 트리거할 이벤트를 선택합니다.](lambda-how-to-choose-event.md)
+ [Lambda@Edge 함수에 트리거 추가](lambda-edge-add-triggers-console.md)

# Lambda@Edge 함수를 트리거할 수 있는 CloudFront 이벤트
<a name="lambda-cloudfront-trigger-events"></a>

특정한 CloudFront 이벤트가 발생하면 Lambda 함수가 실행되도록 하는 트리거(연결)를 Amazon CloudFront 배포의 각 캐시 동작에 대해 4개까지 추가할 수 있습니다. CloudFront 트리거는 아래 그림의 네 가지 CloudFront 이벤트 중 하나를 토대로 합니다.

![\[Lambda 함수의 CloudFront 트리거 이벤트가 CloudFront와 통합되는 방법을 보여 주는 개념 그림.\]](http://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/images/cloudfront-events-that-trigger-lambda-functions.png)


Lambda@Edge 함수를 트리거하는 데 사용할 수 있는 CloudFront 이벤트는 다음과 같습니다.

**최종 사용자 요청**  
이 함수는 CloudFront가 최종 사용자로부터 요청을 수신하면 실행되며, 요청된 객체가 CloudFront 캐시에 있는지를 확인합니다.  
다음과 같은 경우에는 함수가 실행되지 않습니다.  
+ 사용자 지정 오류 페이지를 가져오는 경우.
+ CloudFront가 자동으로 HTTP 요청을 HTTPS로 리디렉션하는 경우([뷰어 프로토콜 정책](DownloadDistValuesCacheBehavior.md#DownloadDistValuesViewerProtocolPolicy) 값이 **Redirect HTTP to HTTPS**일 경우).

**오리진 요청**  
이 함수는 CloudFront가 오리진으로 요청을 전달할 *경우에만* 실행됩니다. 요청된 객체가 CloudFront 캐시에 저장되어 있으면 함수는 실행되지 않습니다.

**오리진 응답**  
이 함수는 CloudFront가 오리진으로부터 응답을 수신한 후 실행되어 응답의 객체를 캐싱합니다. 오리진에서 오류가 반환되는 경우에도 함수는 실행됩니다.  
다음과 같은 경우에는 함수가 실행되지 않습니다.  
+ 요청된 파일이 CloudFront 캐시에 있고 만료되지 않은 경우.
+ 오리진 요청 이벤트가 트리거한 함수로부터 응답이 생성되는 경우.

**최종 사용자 응답**  
요청된 파일을 최종 사용자에게 반환하기 전에 함수가 실행됩니다. 이때 함수는 해당 파일이 이미 CloudFront 캐시에 있는지 여부와 상관없이 실행됩니다.  
다음과 같은 경우에는 함수가 실행되지 않습니다.  
+ 오리진이 400 이상의 HTTP 상태 코드 반환하는 경우
+ 사용자 지정 오류 페이지가 반환될 때.
+ 최종 사용자 요청 이벤트가 트리거한 함수로부터 응답이 생성되는 경우.
+ CloudFront가 자동으로 HTTP 요청을 HTTPS로 리디렉션하는 경우([뷰어 프로토콜 정책](DownloadDistValuesCacheBehavior.md#DownloadDistValuesViewerProtocolPolicy) 값이 **Redirect HTTP to HTTPS**일 경우).

한 캐시 동작에 여러 트리거를 추가하는 경우, 이를 사용하여 동일한 함수를 실행하거나 트리거마다 다른 함수를 실행할 수 있습니다. 또한 복수의 배포에 동일한 함수를 연결할 수도 있습니다.

**참고**  
CloudFront 이벤트가 Lambda 함수의 실행을 트리거하면 CloudFront가 계속되기 **전에 함수가 완료되어야 합니다.  
예를 들어, CloudFront 최종 사용자 요청 이벤트에 따라 Lambda 함수가 트리거되면 CloudFront는 Lambda 함수 실행이 완료될 때까지 최종 사용자에게 응답을 반환하거나 오리진에 요청을 전달하지 않습니다.  
다시 말해, Lambda 함수를 트리거하는 각 요청은 요청 지연 시간을 증가시키므로 최대한 빨리 함수가 실행되도록 해야 합니다.

# 함수를 트리거할 이벤트를 선택합니다.
<a name="lambda-how-to-choose-event"></a>

Lambda 함수를 트리거하는 데 사용할 CloudFront 이벤트를 결정할 때 다음 사항을 고려해야 합니다.

**CloudFront에서 Lambda 함수가 변경한 객체를 캐싱하고자 함**  
CloudFront가 Lambda 함수로 수정된 객체를 캐싱하여 다음에 해당 객체가 요청될 경우 엣지 로케이션의 객체에 CloudFront가 서비스를 제공할 수 있게 하려면 **오리진 요청 또는 **오리진 응답 이벤트를 사용합니다.  
그러면 오리진에 대한 로드가 감소하고, 이후 요청의 지연 시간이 단축되며, 이후 요청에서 Lambda@Edge를 호출하는 비용이 절감됩니다.  
예를 들어, 오리진에 의해 반환되는 객체의 헤더를 추가, 제거 또는 변경하고 CloudFront에서 해당 결과를 캐싱하게 하려는 경우 오리진 응답 이벤트를 사용합니다.

**모든 요청에 대해 함수를 실행하고자 함**  
CloudFront가 배포에 대해 수신하는 모든 요청에서 함수를 실행하려면 **뷰어 요청 또는 **뷰어 응답 이벤트를 사용합니다.  
오리진 요청 및 오리진 응답 이벤트는 요청된 객체가 엣지 로케이션에 캐싱되지 않고 CloudFront가 오리진으로 요청을 전달하는 경우에만 발생합니다.

**함수가 캐시 키를 변경하고자 함**  
함수가 캐싱의 기준으로 사용되는 값을 변경하게 하려면 **뷰어 요청 이벤트를 사용합니다.  
예를 들어, 함수가 URL을 변경하여 경로에 언어 약자를 포함시키는 경우(예를 들어, 사용자가 드롭다운 목록에서 언어를 선택했기 때문) 최종 사용자 요청 이벤트를 사용합니다.  
+ **최종 사용자 요청 내 URL** - https://example.com/en/index.html
+ **요청이 독일 내 IP 주소로부터 온 경우의 URL** - https://example.com/de/index.html
쿠키 또는 요청 헤더를 기준으로 캐싱하는 경우에도 최종 사용자 요청 이벤트를 사용합니다.  
함수가 쿠키 또는 헤더를 변경하는 경우 CloudFront가 요청의 해당 부분을 오리진으로 전달하도록 구성합니다. 자세한 내용은 다음 항목을 참조하세요.  
+ [쿠키 기반의 콘텐츠 캐싱](Cookies.md)
+ [요청 헤더 기반의 콘텐츠 캐싱](header-caching.md)

**함수가 오리진의 응답에 영향을 미침**  
함수가 오리진의 응답에 영향을 미치는 방식으로 요청을 변경하려면 **오리진 요청 이벤트를 사용합니다.  
일반적으로 대부분의 뷰어 요청 이벤트는 오리진으로 전달되지 않습니다. CloudFront는 이미 엣지 캐시에 저장된 객체를 사용하여 요청에 응답합니다. 함수가 오리진 요청 이벤트를 기준으로 요청을 변경하는 경우 CloudFront가 변경된 오리진 요청에 대한 응답을 캐싱합니다.

# Lambda@Edge 함수에 트리거 추가
<a name="lambda-edge-add-triggers-console"></a>

Lambda@Edge 함수에 트리거를 추가하기 위해 AWS Lambda 콘솔 또는 Amazon CloudFront 콘솔을 사용할 수 있습니다.

**중요**  
번호가 매겨진 함수 버전에 대해서만 트리거를 생성할 수 있습니다(**\$1LATEST** 제외).

------
#### [ Lambda console ]<a name="lambda-edge-add-triggers-procedure"></a>

**Lambda@Edge 함수에 CloudFront 이벤트에 대한 트리거를 추가하려면**

1. AWS Management Console에 로그인하고 AWS Lambdahttps://console.aws.amazon.com/lambda/[에서 ](https://console.aws.amazon.com/lambda/) 콘솔을 엽니다.

1. 페이지 상단의 리전 목록에서 **미국 동부(버지니아 북부)(US East (N. Virginia))**를 선택합니다.

1. **함수** 페이지에서 트리거를 추가할 함수의 이름을 선택합니다.

1. **함수 개요** 페이지에서 **버전** 탭을 선택합니다.

1. 트리거를 추가할 버전을 선택합니다.

   버전을 선택한 후 버튼의 이름이 **버전: \$1LATEST** 또는 **버전:** *버전 번호*로 변경됩니다.

1. **트리거** 탭을 선택합니다.

1. **트리거 추가**를 선택합니다.

1. **트리거 구성**에서 **소스 선택**을 선택하고 **cloudfront**를 입력한 다음 **CloudFront**를 선택합니다.
**참고**  
하나 이상의 트리거를 이미 만든 경우 CloudFront가 기본 서비스입니다.

1. 다음 값을 지정하여 Lambda 함수를 실행할 시기를 나타냅니다.

   1. **배포** – 트리거를 추가할 배포를 선택합니다.

   1. **캐시 동작** – 함수를 실행할 객체를 지정하는 캐시 동작을 선택합니다.
**참고**  
캐시 동작에 `*`를 지정하는 경우 Lambda 함수는 기본 캐시 동작으로 배포됩니다.

   1. **CloudFront 이벤트** – 함수를 실행시키는 CloudFront 이벤트를 선택합니다.

   1. **본문 포함** – 함수의 요청 본문에 액세스하려는 경우 이 확인란을 선택합니다.

   1. **Lambda@Edge로 배포 확인** - 이 확인란을 선택하면 AWS Lambda가 전 세계 AWS 리전으로 함수를 복제합니다.

1. **추가**를 선택합니다.

   업데이트된 CloudFront 배포가 배포되면 함수가 지정된 CloudFront 이벤트에 대한 요청을 처리하기 시작합니다. 배포가 배포되었는지 확인하려면 탐색 창에서 **배포**를 선택합니다. 배포가 되면 배포에 대한 **상태** 열의 값이 **배포 중**에서 배포 날짜 및 시간으로 변경됩니다.

------
#### [ CloudFront console ]<a name="lambda-create-functions-add-triggers-cloudfront-console-procedure"></a>

**Lambda@Edge 함수에 CloudFront 이벤트에 대한 트리거를 추가하려면**

1. 트리거를 추가하려는 Lambda 함수의 ARN을 얻습니다.

   1. AWS Management Console에 로그인하고 AWS Lambdahttps://console.aws.amazon.com/lambda/[에서 ](https://console.aws.amazon.com/lambda/) 콘솔을 엽니다.

   1. 페이지 상단의 리전 목록에서 **미국 동부(버지니아 북부)(US East (N. Virginia))**를 선택합니다.

   1. 함수 목록에서 트리거를 추가하려는 함수의 이름을 선택합니다.

   1. **함수 개요** 페이지에서 **버전** 탭을 선택하고 트리거를 추가할 번호가 매겨진 버전을 선택합니다.

   1. **ARN 복사** 버튼을 선택하여 ARN을 클립보드에 복사합니다. Lambda 함수의 ARN은 다음과 같은 형태입니다.

      `arn:aws:lambda:us-east-1:123456789012:function:TestFunction:2`

      끝부분의 숫자(이 예제에서는 **2**)가 함수의 버전 번호입니다.

1. [https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)에서 CloudFront 콘솔을 엽니다.

1. 배포 목록에서 트리거를 추가하려는 배포의 ID를 선택합니다.

1. **동작** 탭을 선택합니다.

1. 트리거를 추가하려는 캐시 동작을 선택한 다음 **편집**을 클릭합니다.

1. **함수 연결**에서, **함수 유형** 목록에서 함수를 실행할 시점, 즉 최종 사용자 요청, 최종 사용자 응답, 오리진 요청 또는 오리진 응답 중에 **Lambda@Edge**를 선택합니다.

   자세한 내용은 [함수를 트리거할 이벤트를 선택합니다.](lambda-how-to-choose-event.md) 섹션을 참조하세요.

1. **함수 ARN/이름** 텍스트 상자에 선택한 이벤트가 발생할 때 실행하려는 Lambda 함수의 ARN을 붙여넣습니다. Lambda 콘솔에서 복사한 값입니다.

1. 함수의 요청 본문에 액세스하려는 경우 **본문 포함**을 선택합니다.

   요청 본문을 바꾸기만 하려는 경우에는 이 옵션을 선택할 필요가 없습니다.

1. 더 많은 이벤트 유형에 대해 동일한 함수를 실행하려면 6 및 7단계를 반복합니다.

1. **변경 사항 저장**을 선택합니다.

1. 이 배포에 대한 다른 캐시 동작에 트리거를 추가하려면 5\$110단계를 반복합니다.

   업데이트된 CloudFront 배포가 배포되면 함수가 지정된 CloudFront 이벤트에 대한 요청을 처리하기 시작합니다. 배포가 배포되었는지 확인하려면 탐색 창에서 **배포**를 선택합니다. 배포판이 배포되면 배포에 대한 **상태** 열의 값이 **배포 중**에서 배포 시간 및 날짜로 변경됩니다.

------

# Lambda@Edge 함수 테스트 및 디버깅
<a name="lambda-edge-testing-debugging"></a>

의도한 작업을 완료하는지 확인하기 위해서는 Lambda@Edge 함수 코드를 단독으로 테스트해야 하고, 함수가 CloudFront와 함께 잘 작동하는지 확인하기 위해서는 통합 테스트를 수행해야 합니다.

통합 테스트 중 또는 함수 배포 완료 후 HTTP 5xx 오류 등과 같은 CloudFront 오류를 디버깅해야 할 수 있습니다. Lambda 함수에서 반환되는 잘못된 응답, 함수 실행 시 발생하는 실행 오류 또는 Lambda 서비스의 실행 조절로 인한 오류 등이 있을 수 있습니다. 이 주제의 단원에서는 이러한 문제가 어떤 유형의 실패인지 확인한 다음 각 문제를 해결하기 위해 수행할 수 있는 단계에 대해 설명합니다.

**참고**  
오류를 해결하기 위해 CloudWatch 로그 파일 또는 지표를 검토할 때 함수가 실행된 위치에 가장 가까운 AWS 리전에 로그 파일 또는 지표가 표시 또는 저장된다는 점에 유의하세요. 예를 들어, 영국 내 사용자가 있는 웹 사이트 또는 웹 애플리케이션이 있고 배포와 연결된 Lambda 함수가 있는 경우 런던 AWS 리전 리전에 대한 CloudWatch 지표 또는 로그 파일을 볼 수 있도록 리전을 변경해야 합니다. 자세한 내용은 [Lambda@Edge 리전 확인](#lambda-edge-testing-debugging-determine-region) 섹션을 참조하세요.

**Topics**
+ [Lambda@Edge 함수 테스트](#lambda-edge-testing-debugging-test-function)
+ [CloudFront에서 Lambda@Edge 함수 오류 식별](#lambda-edge-identifying-function-errors)
+ [잘못된 Lambda@Edge 함수 응답(검증 오류) 문제 해결](#lambda-edge-testing-debugging-troubleshooting-invalid-responses)
+ [Lambda@Edge 함수 실행 오류 문제 해결](#lambda-edge-testing-debugging-execution-errors)
+ [Lambda@Edge 리전 확인](#lambda-edge-testing-debugging-determine-region)
+ [계정이 CloudWatch로 로그를 푸시하는지 확인](#lambda-edge-testing-debugging-cloudwatch-logs-enabled)

## Lambda@Edge 함수 테스트
<a name="lambda-edge-testing-debugging-test-function"></a>

Lambda 함수 테스트에는 독립 실행형 테스트와 통합 테스트, 이렇게 두 가지 단계가 있습니다.

**독립 실행형 기능 테스트**  
CloudFront에 Lambda 함수를 추가하기 전에 Lambda 콘솔의 테스트 기능을 사용하거나 다른 방법을 사용하여 기능을 먼저 테스트해야 합니다. Lambda 콘솔 테스트에 대한 자세한 내용은 AWS Lambda 개발자 안내서**에서 [콘솔을 사용하여 Lambda 함수 호출](https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html#get-started-invoke-manually)을 참조하세요.

**CloudFront에서 함수의 작동 상태를 보기 위한 테스트**  
함수가 배포와 연결되어 있고 CloudFront 이벤트를 기반으로 실행되는 경우에는 통합 테스트를 반드시 완료해야 합니다. 함수가 올바른 이벤트 발생 시 실행되고 CloudFront에 대해 올바른 응답을 반환하는지 확인합니다. 예를 들어, 이벤트 구조가 올바른지, 유효한 헤더만 포함되어 있는지 등을 확인합니다.  
Lambda 콘솔에서 함수의 통합 테스트를 반복하는 것처럼 코드를 수정하거나 함수를 호출하는 CloudFront 트리거를 변경할 때 Lambda@Edge 자습서의 단계를 참조하세요. 예를 들어, 본 자습서의 단계([4단계: 함수를 실행할 CloudFront 트리거 추가](lambda-edge-how-it-works-tutorial.md#lambda-edge-how-it-works-tutorial-add-trigger))에서 설명하는 것처럼 다양한 버전의 함수에서 작동하는지 확인합니다.  
함수를 변경하고 배포할 때 업데이트된 함수 및 CloudFront 트리거가 모든 리전에서 복제되는 데 몇 분 정도 걸릴 수 있습니다. 일반적으로 몇 분이면 되지만 최대 15분까지 걸릴 수 있습니다.  
CloudFront 콘솔로 이동하여 해당 배포를 보고 복제가 완료되었는지 확인할 수 있습니다.  

**복제본 배포가 완료되었는지 확인하려면**

1. [https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)에서 CloudFront 콘솔을 엽니다.

1. 배포 이름을 선택합니다.

1. 배포 상태가 **진행 중**에서 다시 **배포 완료**로 바뀌었는지 확인합니다. 이것은 함수가 복제되었다는 의미입니다. 이제 다음 단원의 단계에 따라 함수가 작동하는지 확인합니다.
콘솔의 테스트는 함수의 로직만 확인하며, Lambda@Edge에 고유한 모든 서비스 할당량(이전에는 제한이라고 함)은 적용하지 않습니다.

## CloudFront에서 Lambda@Edge 함수 오류 식별
<a name="lambda-edge-identifying-function-errors"></a>

함수 로직이 올바르게 작동하는지 확인한 후 CloudFront에서 함수 실행 시 HTTP 5xx 오류가 나타날 수 있습니다. HTTP 5xx 오류는 여러 가지 이유로 반환될 수 있습니다. 여기에는 Lambda 함수 오류 또는 CloudFront의 다른 문제가 포함될 수 있습니다.
+ Lambda@Edge 함수를 사용하는 경우 CloudFront 콘솔의 그래프를 사용하여 오류의 원인을 추적하고 문제를 해결할 수 있습니다. 예를 들어, HTTP 5xx 오류가 CloudFront 또는 Lambda 함수에 의해 발생한 것인지를 확인한 다음 특정 함수에 대해 관련 로그 파일을 검토하여 문제를 조사할 수 있습니다.
+ CloudFront에서 일반적인 HTTP 오류 문제를 해결하려면 [CloudFront의 오류 응답 상태 코드 문제 해결](troubleshooting-response-errors.md) 주제의 문제 해결 단계를 참조하세요.

### CloudFront에서 Lambda@Edge 함수 오류를 일으키는 원인
<a name="lambda-edge-testing-debugging-function-errors"></a>

Lambda 함수가 HTTP 5xx 오류를 일으키는 원인에는 여러 가지가 있으며, 수행해야 할 문제 해결 단계는 오류 유형에 따라 달라집니다. 오류는 다음과 같이 분류할 수 있습니다.

**Lambda 함수 실행 오류**  
함수에 처리되지 않은 예외가 있거나 코드에 오류가 있기 때문에 CloudFront가 Lambda에서 응답을 받지 못한 경우 실행 오류가 발생합니다. 예: 코드에 콜백이 포함된 경우(오류)

**잘못된 Lambda 함수 응답이 CloudFront로 반환됨**  
함수 실행 후 CloudFront가 Lambda에서 응답을 수신했습니다. 응답의 객체 구조가 [Lambda@Edge 이벤트 구조](lambda-event-structure.md)를 따르지 않거나 응답에 잘못된 헤더 또는 기타 잘못된 필드가 포함되어 있는 경우 오류가 반환됩니다.

**Lambda 서비스 할당량(이전에는 제한이라고 함)으로 인해 CloudFront에서의 실행이 제한됨**  
Lambda 서비스는 각 리전에서 실행을 조절하는데 할당량을 초과하면 오류를 반환합니다. 자세한 내용은 [Lambda@Edge에 대한 할당량](cloudfront-limits.md#limits-lambda-at-edge) 섹션을 참조하세요.

### 실패 유형을 결정하는 방법
<a name="lambda-edge-testing-debugging-failure-type"></a>

디버깅할 때 집중할 위치를 결정하고 CloudFront에서 반환한 오류를 해결하려면 CloudFront에서 HTTP 오류가 반환된 이유를 파악하는 것이 좋습니다. 시작하려면 AWS Management Console의 CloudFront 콘솔에 있는 **모니터링** 섹션에 제공된 그래프를 사용합니다. CloudFront 콘솔의 **모니터링(Monitoring)** 섹션에서 그래프를 보는 방법에 대한 자세한 내용은 [Amazon CloudWatch를 사용한 CloudFront 지표 모니터링](monitoring-using-cloudwatch.md) 단원을 참조하세요.

다음 그래프는 오리진 또는 Lambda 함수로 오류를 반환하는지 여부를 추적하고 Lambda 함수의 오류일 경우 문제 유형의 범위를 좁히려 할 때 특히 유용합니다.

**오류 발생율 그래프**  
각 배포의 **개요** 탭에서 볼 수 있는 그래프 중에는 **오류 발생율** 그래프가 있습니다. 이 그래프는 배포에 들어오는 총 요청의 백분율로 오류 발생율을 표시합니다. 그래프는 총 오류 발생율, 총 4xx 오류, 총 5xx 오류 및 Lambda 함수의 총 5xx 오류를 보여줍니다. 오류 유형 및 볼륨에 따라 원인을 조사하고 문제를 해결할 수 있는 단계를 수행할 수 있습니다.  

![\[CloudFront 배포에 대한 오류 발생율 그래프\]](http://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/images/Distribution-error-rate-pct-full.png)

+ Lambda 오류가 표시되면 함수가 반환하는 특정 유형의 오류를 확인하여 추가 조사를 수행할 수 있습니다. **Lambda@Edge errors(Lambda@Edge 오류)** 탭에는 함수 오류를 유형별로 분류하여 특정 함수에 대한 문제를 찾아낼 수 있는 그래프가 포함되어 있습니다.
+ CloudFront 오류가 표시되면 문제를 해결하고 오리진 오류를 수정하거나 CloudFront 구성을 변경할 수 있습니다. 자세한 내용은 [CloudFront의 오류 응답 상태 코드 문제 해결](troubleshooting-response-errors.md) 단원을 참조하세요.

**실행 오류 및 잘못된 함수 응답 그래프**  
**Lambda@Edge errors(Lambda@Edge 오류)** 탭에는 특정 배포에 대한 Lambda@Edge 오류를 유형별로 분류하는 그래프가 포함되어 있습니다. 예를 들어, 한 그래프는 모든 실행 오류를 AWS 리전별로 표시합니다.  
문제를 쉽게 해결할 수 있도록 특정 함수에 대한 로그 파일을 열고 리전별로 검토하여 특정 문제를 찾을 수 있습니다.  

**리전별로 특정 함수의 로그 파일을 보려면**

1. **Lambda@Edge 오류** 탭의 **관련 Lambda@Edge 함수**에서 함수 이름을 선택한 다음 **지표 보기**를 선택합니다.

1. 그런 다음, 함수 이름이 있는 페이지의 오른쪽 상단 모서리에서 **함수 로그 보기**를 선택한 다음 리전을 선택합니다.

   예를 들어 미국 서부(오레곤) 리전에 대한 **오류** 그래프에 문제가 있는 경우 드롭다운 목록에서 해당 리전을 선택합니다. 그러면 Amazon CloudWatch 콘솔이 열립니다.

1. 해당 리전의 CloudWatch 콘솔에서 **로그 스트림** 아래의 로그 스트림을 선택하여 함수에 대한 이벤트를 확인합니다.
또한 이 장의 다음 단원에서 오류 해결 및 수정에 대한 추가 권장 사항을 읽으십시오.

**제한 그래프**  
**Lambda@Edge errors(Lambda@Edge 오류)** 탭에는 **제한** 그래프도 있습니다. 경우에 따라 리전 동시성 할당량(이전에는 제한이라고 함)에 도달하면 Lambda 서비스가 리전별로 함수 호출을 제한합니다. 제한 초과 오류가 발생한 경우는 리전에서의 실행에 대해 Lambda 서비스가 부과한 할당량에 도달한 것입니다. 이러한 할당량을 늘리기 위한 요청 방법을 비롯한 자세한 내용은 [Lambda@Edge에 대한 할당량](cloudfront-limits.md#limits-lambda-at-edge) 단원을 참조하세요.  

![\[Lambda@Edge 함수 실행에 대한 제한 그래프\]](http://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/images/Lambda-throttles-page.png)


이 정보를 사용하여 HTTP 오류 문제를 해결하는 방법에 대한 예제는 [AWS에서 콘텐츠 전송을 디버깅하기 위한 네 가지 단계](https://aws.amazon.com/blogs/networking-and-content-delivery/four-steps-for-debugging-your-content-delivery-on-aws/)를 참조하세요.

## 잘못된 Lambda@Edge 함수 응답(검증 오류) 문제 해결
<a name="lambda-edge-testing-debugging-troubleshooting-invalid-responses"></a>

Lambda 확인 오류가 문제라고 파악한 경우는 Lambda 함수가 CloudFront에 잘못된 응답을 반환하고 있다는 의미입니다. 이 단원의 지침에 따라 함수를 검토하는 단계를 수행한 다음 응답이 CloudFront 요구 사항을 따르는지 확인하세요.

CloudFront는 다음 두 가지 방법으로 Lambda 함수의 응답을 확인합니다.
+ **Lambda 응답은 필수 객체 구조를 따라야 합니다.** 잘못된 객체 구조의 예에는 구문 분석이 불가능한 JSON, 필수 필드 누락 및 응답에 포함된 잘못된 객체 등이 있습니다. 자세한 내용은 [Lambda@Edge 이벤트 구조](lambda-event-structure.md) 단원을 참조하세요.
+ **응답에는 올바른 객체만 포함되어 있어야 합니다.** 응답에 올바른 객체가 포함되어 있는데 그 값이 지원되지 않는 경우에는 오류가 발생합니다. 등록된 헤더 또는 읽기 전용 헤더를 추가 또는 업데이트하는 경우([엣지 함수에 대한 제한 사항](edge-functions-restrictions.md) 참조), 최대 본문 크기 초과(Lambda@Edge [오류](lambda-generating-http-responses.md#lambda-generating-http-responses-errors) 주제에서 *생성된 응답 크기 제한* 참조) 및 잘못된 문자 또는 값([Lambda@Edge 이벤트 구조](lambda-event-structure.md) 참조) 등을 예로 들 수 있습니다.

Lambda가 CloudFront에 대해 유효하지 않은 응답을 반환하면 CloudFront가 Lambda 함수가 실행된 리전의 CloudWatch에 푸시하는 로그 파일에 오류 메시지가 기록됩니다. 잘못된 응답이 있는 경우 CloudWatch로 로그 파일을 보내는 것은 기본 동작입니다. 그러나 Lambda 함수를 릴리스하기 전에 CloudFront와 연결한 경우에는 함수에 대해 활성화되지 않을 수 있습니다. 자세한 내용은 이 주제 뒷부분에 나오는 *계정이 CloudWatch로 로그를 푸시하는지 확인*을 참조하세요.

CloudFront는 배포와 연결된 로그 그룹 내에서 함수가 실행되는 위치에 해당하는 리전으로 로그 파일을 푸시합니다. 로그 그룹의 형식은 `/aws/cloudfront/LambdaEdge/DistributionId`인데, 여기서 *DistributionId*는 배포 ID입니다. CloudWatch 로그 파일을 찾을 수 있는 리전을 확인하려면 이 주제 뒷부분에 나오는 *Lambda@Edge 리전 확인*을 참조하세요.

오류를 재현할 수 있는 경우 오류가 발생하는 새로운 요청을 생성한 다음 실패한 CloudFront 응답(`X-Amz-Cf-Id` 헤더)에서 해당 요청 ID를 찾아 로그 파일에서 단일 실패를 확인할 수 있습니다. 로그 파일 항목에는 오류가 반환된 이유를 파악하는 데 도움이 되는 정보가 포함되어 있고 해당하는 Lambda 요청 ID가 나열되어 있기 때문에 단일 요청의 컨텍스트 내에서 근본 원인을 분석할 수 있습니다.

오류가 간헐적으로 발생하는 경우에는 CloudFront 액세스 로그를 사용하여 실패한 요청의 요청 ID를 찾은 다음 CloudWatch 로그에서 해당하는 오류 메시지를 검색할 수 있습니다. 자세한 내용은 앞의 *실패 유형 확인* 단원을 참조하십시오.

## Lambda@Edge 함수 실행 오류 문제 해결
<a name="lambda-edge-testing-debugging-execution-errors"></a>

Lambda 실행 오류가 문제인 경우에는 Lambda 함수에 대한 로깅 문을 생성해 CloudWatch 로그 파일에 CloudFront에서 함수의 실행을 모니터링하는 메시지를 작성한 다음 예상대로 작동하는지 확인하면 도움이 될 수 있습니다. 그런 다음 CloudWatch 로그 파일에서 해당 문을 검색해 함수가 작동 중인지 확인할 수 있습니다.

**참고**  
Lambda@Edge 함수를 변경하지 않은 경우에도 Lambda 함수 실행 환경에 대한 업데이트가 영향을 줄 수 있으며, 실행 오류가 반환될 수 있습니다. 테스트 및 이후 버전으로의 마이그레이션에 대한 자세한 내용은 [AWS Lambda 및 AWS Lambda@Edge 실행 환경](https://aws.amazon.com/blogs/compute/upcoming-updates-to-the-aws-lambda-execution-environment/)에 대해 예정된 업데이트를 참조하세요.

## Lambda@Edge 리전 확인
<a name="lambda-edge-testing-debugging-determine-region"></a>

Lambda@Edge 함수가 트래픽을 수신하는 리전을 보려면 에서 CloudFront 콘솔의 함수에 대한 지표를 확인하세요AWS Management Console 지표는 각 AWS 리전별로 표시됩니다. 동일한 페이지에서 리전을 선택하고 해당 리전의 로그 파일을 확인하여 문제를 조사할 수 있습니다. CloudFront에서 Lambda 함수를 실행할 때 생성된 로그 파일을 확인하려면 올바른 AWS 리전의 CloudWatch Logs 파일을 검토해야 합니다.

CloudFront 콘솔의 **모니터링(Monitoring)** 섹션에서 그래프를 보는 방법에 대한 자세한 내용은 [Amazon CloudWatch를 사용한 CloudFront 지표 모니터링](monitoring-using-cloudwatch.md) 단원을 참조하세요.

## 계정이 CloudWatch로 로그를 푸시하는지 확인
<a name="lambda-edge-testing-debugging-cloudwatch-logs-enabled"></a>

기본적으로 CloudFront는 잘못된 Lambda 함수 응답 로깅을 활성화하고 [Lambda@Edge의 서비스 연결 역할](lambda-edge-permissions.md#using-service-linked-roles-lambda-edge) 중 하나를 수행해 로그 파일을 CloudWatch로 푸시합니다. 잘못된 Lambda 함수 응답 로그 기능이 활성화되기 전에 CloudFront에 Lambda@Edge 함수를 추가한 경우에는 예를 들어, CloudFront 트리거 추가 등과 같이 다음에 Lambda@Edge 구성을 업데이트하는 경우 로깅이 활성화됩니다.

다음을 수행해 계정에 대해 CloudWatch로 로그 파일 푸시 기능이 활성화되어 있는지 확인할 수 있습니다.
+ **CloudWatch에 로그가 나타나는지 확인** - Lambda @Edge 함수가 실행된 리전에서 로그 파일을 확인해야 합니다. 자세한 내용은 [Lambda@Edge 리전 확인](#lambda-edge-testing-debugging-determine-region) 섹션을 참조하세요.
+ **IAM의 계정에 관련 서비스 연결 역할이 있는지 확인** - 계정에 IAM 역할 `AWSServiceRoleForCloudFrontLogger`가 있어야 합니다. 이에 대한 자세한 내용은 [Lambda@Edge의 서비스 연결 역할](lambda-edge-permissions.md#using-service-linked-roles-lambda-edge) 섹션을 참조하세요.

# Lambda@Edge 함수 및 복제본 삭제
<a name="lambda-edge-delete-replicas"></a>

CloudFront에서 함수의 복제본이 삭제된 경우에만 Lambda@Edge 함수를 삭제할 수 있습니다. 다음과 같은 상황에서 Lambda 함수 복제본이 자동으로 삭제됩니다.
+ 함수의 복제본은 함수에 대한 마지막 연결을 모든 CloudFront 배포에서 제거한 후에 자동으로 삭제됩니다. 둘 이상의 배포에서 함수를 사용할 경우 마지막 배포에서 함수 연결이 제거된 후에만 복제본이 삭제됩니다.
+ 함수에 연결된 마지막 배포를 삭제한 후에도 복제본이 제거됩니다.

복제본은 일반적으로 몇 시간 내에 삭제됩니다. Lambda@Edge 함수 복제본은 수동으로 삭제할 수 없습니다. 따라서 아직 사용 중인 복제본이 삭제되어 오류가 발생하는 상황을 방지할 수 있습니다.

**주의**  
CloudFront 외부에서 Lambda@Edge 함수 복제본을 사용하는 애플리케이션을 빌드하지 마세요. 이러한 복제본은 배포와의 연결이 제거되거나 배포 자체가 삭제될 때 삭제됩니다. 이 경우 외부 애플리케이션에서 사용하는 복제본이 경고도 없이 제거되어 오류가 발생할 수 있습니다.

**CloudFront 배포에서 Lambda@Edge 함수 연결을 삭제하려면**

1. AWS Management Console에 로그인한 다음 [https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)에서 CloudFront 콘솔을 엽니다.

1. 삭제할 Lambda@Edge 함수 연결이 있는 배포의 ID를 선택합니다.

1. **동작** 탭을 선택합니다.

1. 삭제할 Lambda@Edge 함수 연결이 있는 캐시 동작을 선택한 다음 **편집**을 선택합니다.

1. **함수 연결**, **함수 유형**에서, **연결 없음**을 선택하여 Lambda@Edge 함수 연결을 삭제합니다.

1. **변경 사항 저장**를 선택합니다.

CloudFront 배포에서 Lambda@Edge 함수 연결을 삭제한 후 선택적으로 에서 Lambda 함수 또는 함수 버전을 삭제할 수 있습니다AWS Lambda Lambda@Edge 함수 복제본을 정리할 수 있도록 함수 연결을 삭제한 후 몇 시간 정도 기다립니다. 그런 다음 Lambda 콘솔, AWS CLI, Lambda API 또는 AWS SDK를 사용하여 함수를 삭제할 수 있습니다.

버전에 연결된 CloudFront 배포가 없는 경우에는 특정 버전의 Lambda 함수를 삭제할 수도 있습니다.** Lambda 함수 버전에서 모든 연결을 제거한 뒤 몇 시간 정도 기다려 주세요. 그러면 해당 함수 버전을 삭제할 수 있습니다.

# Lambda@Edge 이벤트 구조
<a name="lambda-event-structure"></a>

다음 주제에서는 트리거될 때 CloudFront가 Lambda@Edge 함수에 전달하는 요청 및 응답 이벤트 객체에 대해 설명합니다.

**Topics**
+ [동적 원본 선택](#lambda-event-content-based-routing)
+ [이벤트 요청](#lambda-event-structure-request)
+ [응답 이벤트](#lambda-event-structure-response)

## 동적 원본 선택
<a name="lambda-event-content-based-routing"></a>

[캐시 동작의 경로 패턴](DownloadDistValuesCacheBehavior.md#DownloadDistValuesPathPattern)을 사용하여 `images/*.jpg`와 같이 요청된 객체의 경로와 이름을 기반으로 오리진에 요청을 라우팅할 수 있습니다. 또한 Lambda@Edge를 사용하여 요청 헤더의 값과 같은 기타 특성을 기반으로 오리진에 요청을 라우팅할 수 있습니다.

여러 경우에 이러한 동적 오리진 선택이 유용할 수 있습니다. 예를 들어, 각기 다른 지리 영역에 있는 오리진에 걸쳐 요청을 배포하여 전역 로드 밸런싱에 도움이 될 수 있습니다. 또는 봇 처리, SEO 최적화, 인증 등과 같은 특정 기능을 서비스하는 각기 다른 오리진에 요청을 선택적으로 라우팅할 수 있습니다. 이 기능을 사용하는 방법을 보여주는 코드 예제는 [콘텐츠 기반 동적 원본 선택 - 예제](lambda-examples.md#lambda-examples-content-based-routing-examples) 단원을 참조하세요.

CloudFront 오리진 요청 이벤트에서 이벤트 구조의 오리진 `origin` 객체에는 요청이 경로 패턴을 기반으로 라우팅될 오리진에 대한 정보가 들어 있습니다. `origin` 객체의 값을 업데이트하여 요청을 다른 오리진으로 라우팅할 수 있습니다. `origin` 객체를 업데이트할 때 배포에서 오리진을 정의할 필요가 없습니다. 또한 Amazon S3 오리진 객체를 사용자 지정 오리진 객체로 바꿀 수 있으며, 그 반대의 경우도 마찬가지입니다. 요청당 하나의 오리진만 지정할 수 있습니다. 즉, 사용자 지정 오리진 또는 Amazon S3 오리진 중 하나를 지정할 수 있지만 둘 모두 지정할 수는 없습니다.

## 이벤트 요청
<a name="lambda-event-structure-request"></a>

다음 주제에서는 CloudFront가 [최종 사용자 및 오리진 요청 이벤트](lambda-cloudfront-trigger-events.md)에 대한 Lambda 함수에 전달하는 객체의 구조를 보여줍니다. 이 예제에서는 본문이 없는 `GET` 요청을 보여줍니다. 다음 예제는 최종 사용자 및 오리진 요청 이벤트에서 가능한 모든 필드의 목록입니다.

**Topics**
+ [뷰어 요청 예제](#example-viewer-request)
+ [원본 요청 예제](#example-origin-request)
+ [요청 이벤트 필드](#request-event-fields)

### 뷰어 요청 예제
<a name="example-viewer-request"></a>

다음 예제에서는 최종 사용자 요청 이벤트 객체를 보여줍니다.

```
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "viewer-request",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "host": [
              {
                "key": "Host",
                "value": "d111111abcdef8.cloudfront.net"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "curl/7.66.0"
              }
            ],
            "accept": [
              {
                "key": "accept",
                "value": "*/*"
              }
            ]
          },
          "method": "GET",
          "querystring": "",
          "uri": "/"
        }
      }
    }
  ]
}
```

### 원본 요청 예제
<a name="example-origin-request"></a>

다음 예제에서는 오리진 요청 이벤트 객체를 보여줍니다.

```
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "origin-request",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "x-forwarded-for": [
              {
                "key": "X-Forwarded-For",
                "value": "203.0.113.178"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "Amazon CloudFront"
              }
            ],
            "via": [
              {
                "key": "Via",
                "value": "2.0 2afae0d44e2540f472c0635ab62c232b.cloudfront.net (CloudFront)"
              }
            ],
            "host": [
              {
                "key": "Host",
                "value": "example.org"
              }
            ],
            "cache-control": [
              {
                "key": "Cache-Control",
                "value": "no-cache"
              }
            ]
          },
          "method": "GET",
          "origin": {
            "custom": {
              "customHeaders": {},
              "domainName": "example.org",
              "keepaliveTimeout": 5,
              "path": "",
              "port": 443,
              "protocol": "https",
              "readTimeout": 30,
              "responseCompletionTimeout": 30,
              "sslProtocols": [
                "TLSv1",
                "TLSv1.1",
                "TLSv1.2"
              ]
            }
          },
          "querystring": "",
          "uri": "/"
        }
      }
    }
  ]
}
```

### 요청 이벤트 필드
<a name="request-event-fields"></a>

요청 이벤트 객체 데이터는 두 개의 하위 객체(`config`(`Records.cf.config`) 및 `request`(`Records.cf.request`))에 포함되어 있습니다. 다음 목록에서는 각 하위 객체의 필드에 대해 설명합니다.

#### 구성 객체의 필드
<a name="request-event-fields-config"></a>

다음 표에서는 `config` 객체(`Records.cf.config`)의 필드에 대해 설명합니다.

**`distributionDomainName`(읽기 전용)**  
요청과 연결된 배포의 도메인 이름입니다.

**`distributionID`(읽기 전용)**  
요청과 관련이 있는 배포의 ID입니다.

**`eventType`(읽기 전용)**  
요청(`viewer-request` 또는 `origin-request`)과 관련된 트리거의 유형입니다.

**`requestId`(읽기 전용)**  
최종 사용자 요청/CloudFront 요청을 고유하게 식별하는 암호화된 문자열입니다. 또한 `requestId` 값은 CloudFront 액세스 로그에 `x-edge-request-id`로 표시됩니다. 자세한 내용은 [액세스 로그(표준 로그)](AccessLogs.md) 및 [로그 파일 필드](standard-logs-reference.md#BasicDistributionFileFormat) 단원을 참조하세요.

#### 요청 객체의 필드
<a name="request-event-fields-request"></a>

다음 표에서는 `request` 객체(`Records.cf.request`)의 필드에 대해 설명합니다.

**`clientIp`(읽기 전용)**  
요청을 생성한 최종 사용자의 IP 주소입니다. 최종 사용자가 HTTP 프록시 또는 로드 밸런서를 사용하여 요청을 전송한 경우 이 값은 프록시 또는 로드 밸런서의 IP 주소입니다.

**headers(읽기/쓰기)**  
요청의 헤더입니다. 다음을 참조하십시오.  
+ `headers` 객체 내 키는 표준 HTTP 헤더 이름의 소문자 버전입니다. 소문자 키를 사용하여 대/소문자를 구분하지 않고 헤더 값에 액세스할 수 있습니다.
+ 각 헤더 객체(예: `headers["accept"]` 또는 `headers["host"]`)는 키–값 페어의 어레이입니다. 해당 헤더에 대해 어레이에는 요청의 각 값에 대한 하나의 키–값 페어를 포함합니다.
+ `key`에는 HTTP 요청에 표시되는 헤더의 이름(대/소문자 구분)이 포함됩니다(예: `Host`, `User-Agent`, `X-Forwarded-For`, `Cookie` 등).
+ `value`에는 HTTP 요청에 표시되는 헤더 값을 포함합니다.
+ Lambda 함수가 요청 헤더를 추가하거나 수정하고 사용자가 헤더 `key` 필드를 포함시키지 않으면, Lambda@Edge는 사용자가 입력한 헤더 이름을 사용하여 헤더 `key`를 자동으로 삽입합니다. 헤더 이름의 형식을 어떻게 지정했든 상관없이, 자동으로 삽입된 헤더 키는 각 부분의 첫 문자가 대문자로 지정되고 각 부분이 하이픈(-)으로 구분됩니다.

  예를 들어 헤더를 다음과 같이 헤더 `key` 없이 추가할 수 있습니다.

  ```
  "user-agent": [
    {
      "value": "ExampleCustomUserAgent/1.X.0"
    }
  ]
  ```

  이 예제에서는 Lambda@Edge가 자동으로 `"key": "User-Agent"`을 삽입합니다.
헤더 사용 시 제한 사항에 대한 자세한 내용은 [엣지 함수에 대한 제한 사항](edge-functions-restrictions.md) 단원을 참조하십시오.

**`method`(읽기 전용)**  
요청의 HTTP 메서드.

**`querystring`(읽기/쓰기)**  
요청의 쿼리 문자열입니다(있는 경우). 요청에 쿼리 문자열이 포함되지 않더라도 이벤트 객체에는 여전히 빈 값과 함께 `querystring`이 포함됩니다. 쿼리 문자열에 대한 자세한 내용은 [쿼리 문자열 파라미터 기반의 콘텐츠 캐싱](QueryStringParameters.md) 단원을 참조하십시오.

**`uri`(읽기/쓰기)**  
요청된 객체의 상대 경로입니다. Lambda 함수가 `uri` 값을 수정하는 경우 다음 사항에 유의하십시오.  
+ 새 `uri` 값은 슬래시(/) 로 시작해야 합니다.
+ 함수가 `uri` 값을 변경하는 경우 이로 인해 최종 사용자가 요청 중인 객체가 변경됩니다.
+ 함수가 `uri` 값을 변경하는 경우 이로 인해 요청 또는 요청이 전송되는 오리진에 대한 캐시 동작이 *변경되지 않습니다*.

**`body`(읽기/쓰기)**  
HTTP 요청의 본문입니다. `body` 구조에는 다음과 같은 필드가 포함될 수 있습니다.    
**`inputTruncated`(읽기 전용)**  
Lambda@Edge가 본문을 잘랐는지 여부를 나타내는 부울 플래그. 자세한 내용은 [본문 포함 옵션이 적용된 요청 본문에 대한 제한 사항](lambda-at-edge-function-restrictions.md#lambda-at-edge-restrictions-request-body) 단원을 참조하세요.  
**`action`(읽기/쓰기)**  
본문을 사용해 수행하고자 한 작업. `action`의 옵션은 다음과 같습니다.  
+ `read-only:` 이 값이 기본값입니다. Lambda 함수에서 응답이 반환되었는데 `action`이 read-only이면, Lambda@Edge는 `encoding` 또는 `data`에 대한 모든 변경 사항을 무시합니다.
+ `replace:` 오리진으로 전송된 본문을 바꾸려는 경우 이 옵션을 지정합니다.  
**`encoding`(읽기/쓰기)**  
본문에 대한 인코딩. Lambda@Edge가 Lambda 함수에 본문을 노출시키는 경우 먼저 본문을 base64-encoding으로 변환합니다. 본문을 바꾸기 위해 `replace`에 `action`를 선택한 경우 `base64`(기본값) 또는 `text` 인코딩을 사용하도록 선택할 수 있습니다. `encoding`을 `base64`로 지정했지만 본문이 유효한 base64가 아닌 경우 CloudFront는 오류를 반환합니다.  
**`data`(읽기/쓰기)**  
요청 본문의 내용 

**`origin`(읽기/쓰기) (오리진 이벤트만 해당)**  
요청을 전송할 오리진입니다. `origin` 구조에는 *정확히 하나의 오리진*만 포함되어야 합니다. 즉, 오리진은 사용자 지정 오리진 또는 Amazon S3 오리진일 수 있습니다.  
지정하는 오리진 유형(사용자 지정 또는 Amazon S3 오리진)에 따라 요청에 다음 필드를 지정해야 합니다.    
**`customHeaders`(읽기/쓰기)(사용자 지정 및 Amazon S3 오리진)**  
(선택 사항) 각 사용자 지정 헤더에 대한 헤더 이름 및 값 페어를 지정하여 요청을 포함한 사용자 지정 헤더를 포함할 수 있습니다. 허용되지 않는 헤더는 추가할 수 없으며 동일한 이름의 헤더가 `Records.cf.request.headers`에 존재할 수 없습니다. [요청 헤더에 대한 노트](#request-event-fields-request-headers)는 사용자 지정 헤더에도 적용됩니다. 자세한 내용은 [CloudFront에서 오리진 요청에 추가할 수 없는 사용자 지정 헤더](add-origin-custom-headers.md#add-origin-custom-headers-denylist) 및 [엣지 함수에 대한 제한 사항](edge-functions-restrictions.md) 단원을 참조하세요.  
**`domainName`(읽기/쓰기)(사용자 지정 및 Amazon S3 오리진)**  
오리진의 도메인 이름입니다. 도메인 이름은 비워둘 수 없습니다.  
+ **사용자 지정 오리진의 경우** – DNS 도메인 이름을 지정합니다(예: `www.example.com`). 도메인 이름에는 콜론(:)이 포함될 수 없으며 IP 주소가 될 수 없습니다. 도메인 이름은 최대 253자일 수 있습니다.
+ **Amazon S3 오리진의 경우** – Amazon S3 버킷의 DNS 도메인 이름을 지정합니다(예: `amzn-s3-demo-bucket.s3.eu-west-1.amazonaws.com`). 이름은 최대 128자여야 하며, 모두 소문자여야 합니다.  
**`path`(읽기/쓰기)(사용자 지정 및 Amazon S3 오리진)**  
요청이 콘텐츠를 찾아야 하는 오리진의 디렉터리 경로입니다. 경로는 슬래시(/)로 시작해야 하지만 슬래시 하나로 끝나지 않아야 합니다(예: `example-path/`와 같이 끝나면 안 됨). 사용자 지정 오리진의 경우만, 경로는 URL로 인코딩되어야 하고 최대 길이는 255자여야 합니다.  
**`keepaliveTimeout`(읽기/쓰기) (사용자 지정 오리진만 해당)**  
CloudFront가 응답의 마지막 패킷을 수신한 후 오리진 연결을 유지하기 위해 시도해야 하는 시간(초)입니다. 값은 1\$1120(경계값 포함) 사이의 숫자여야 합니다.  
**`port`(읽기/쓰기) (사용자 지정 오리진만 해당)**  
CloudFront가 사용자 지정 오리진에서 연결해야 하는 포트입니다. 포트는 80, 443 또는 1024\$165535(경계값 포함) 사이의 숫자여야 합니다.  
**`protocol`(읽기/쓰기) (사용자 지정 오리진만 해당)**  
오리진에 연결할 때 CloudFront가 사용해야 하는 연결 프로토콜입니다. `http` 또는 `https` 값을 가질 수 있습니다.  
**`readTimeout`(읽기/쓰기)(사용자 지정 및 Amazon S3 오리진)**  
요청을 오리진으로 전송한 후 CloudFront가 응답을 대기해야 하는 시간(초)입니다. 또한 다음 패킷을 수신하기 전에 응답 패킷을 수신한 후 CloudFront가 대기해야 하는 시간도 지정합니다. 값은 1\$1120(경계값 포함) 사이의 숫자여야 합니다.  
더 높은 할당량이 필요한 경우 [오리진당 응답 제한 시간](cloudfront-limits.md#limits-web-distributions)을 참조하세요.  
**`responseCompletionTimeout`(읽기/쓰기)(사용자 지정 및 Amazon S3 오리진)**  
CloudFront에서 오리진으로의 요청이 열려 있고 응답을 기다릴 수 있는 시간(초). 이 시간까지 오리진에서 완전한 응답을 받지 못하면 CloudFront는 연결을 종료합니다.  
`responseCompletionTimeout` 값은 `readTimeout` 값과 같거나 이보다 커야 합니다. 이 값을 0으로 설정하면 이전에 설정한 모든 값이 제거되고 기본값으로 돌아갑니다. 이벤트 요청에서 `responseCompletionTimeout` 필드를 삭제하여 이 작업을 수행할 수도 있습니다.  
**`sslProtocols`(읽기/쓰기) (사용자 지정 오리진만 해당)**  
오리진과의 HTTPS 연결을 설정할 때 CloudFront가 사용할 수 있는 최소 SSL/TLS 프로토콜입니다. 값은 `TLSv1.2`, `TLSv1.1`, `TLSv1` 또는 `SSLv3` 중 하나일 수 있습니다.  
**`authMethod`(읽기/쓰기)(Amazon S3 오리진만 해당)**  
[오리진 액세스 ID(OAI)](private-content-restricting-access-to-s3.md#private-content-restricting-access-to-s3-oai)를 사용하는 경우 이 필드를 `origin-access-identity`로 설정합니다. OAI를 사용하지 않는 경우 `none`으로 설정합니다. `authMethod`를 `origin-access-identity`로 설정하면 다음과 같은 몇 가지 요구 사항이 있습니다.  
+ `region`을 지정해야 합니다(다음 필드 참조).
+ 하나의 Amazon S3 오리진에서 다른 오리진으로 요청을 변경할 때 동일한 OAI를 사용해야 합니다.
+ 사용자 지정 오리진에서 Amazon S3 오리진으로 요청을 변경할 때 OAI를 사용할 수 없습니다.
이 필드는 [오리진 액세스 제어(OAC)](private-content-restricting-access-to-s3.md)를 지원하지 않습니다.  
**`region`(읽기/쓰기)(Amazon S3 오리진만 해당)**  
Amazon S3 버킷의 AWS 리전입니다. 이 작업은 `authMethod`를 `origin-access-identity`로 설정한 경우에만 필요합니다.

## 응답 이벤트
<a name="lambda-event-structure-response"></a>

다음 주제에서는 CloudFront가 [최종 사용자 및 오리진 응답 이벤트](lambda-cloudfront-trigger-events.md)에 대한 Lambda 함수에 전달하는 객체의 구조를 보여줍니다. 다음 예제는 최종 사용자 및 오리진 응답 이벤트에서 가능한 모든 필드의 목록입니다.

**Topics**
+ [원본 응답 예제](#lambda-event-structure-response-origin)
+ [뷰어 응답 예제](#lambda-event-structure-response-viewer)
+ [응답 이벤트 필드](#response-event-fields)

### 원본 응답 예제
<a name="lambda-event-structure-response-origin"></a>

다음 예제에서는 오리진 응답 이벤트 객체를 보여줍니다.

```
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "origin-response",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "x-forwarded-for": [
              {
                "key": "X-Forwarded-For",
                "value": "203.0.113.178"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "Amazon CloudFront"
              }
            ],
            "via": [
              {
                "key": "Via",
                "value": "2.0 8f22423015641505b8c857a37450d6c0.cloudfront.net (CloudFront)"
              }
            ],
            "host": [
              {
                "key": "Host",
                "value": "example.org"
              }
            ],
            "cache-control": [
              {
                "key": "Cache-Control",
                "value": "no-cache"
              }
            ]
          },
          "method": "GET",
          "origin": {
            "custom": {
              "customHeaders": {},
              "domainName": "example.org",
              "keepaliveTimeout": 5,
              "path": "",
              "port": 443,
              "protocol": "https",
              "readTimeout": 30,
              "responseCompletionTimeout": 30,
              "sslProtocols": [
                "TLSv1",
                "TLSv1.1",
                "TLSv1.2"
              ]
            }
          },
          "querystring": "",
          "uri": "/"
        },
        "response": {
          "headers": {
            "access-control-allow-credentials": [
              {
                "key": "Access-Control-Allow-Credentials",
                "value": "true"
              }
            ],
            "access-control-allow-origin": [
              {
                "key": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ],
            "date": [
              {
                "key": "Date",
                "value": "Mon, 13 Jan 2020 20:12:38 GMT"
              }
            ],
            "referrer-policy": [
              {
                "key": "Referrer-Policy",
                "value": "no-referrer-when-downgrade"
              }
            ],
            "server": [
              {
                "key": "Server",
                "value": "ExampleCustomOriginServer"
              }
            ],
            "x-content-type-options": [
              {
                "key": "X-Content-Type-Options",
                "value": "nosniff"
              }
            ],
            "x-frame-options": [
              {
                "key": "X-Frame-Options",
                "value": "DENY"
              }
            ],
            "x-xss-protection": [
              {
                "key": "X-XSS-Protection",
                "value": "1; mode=block"
              }
            ],
            "content-type": [
              {
                "key": "Content-Type",
                "value": "text/html; charset=utf-8"
              }
            ],
            "content-length": [
              {
                "key": "Content-Length",
                "value": "9593"
              }
            ]
          },
          "status": "200",
          "statusDescription": "OK"
        }
      }
    }
  ]
}
```

### 뷰어 응답 예제
<a name="lambda-event-structure-response-viewer"></a>

다음 예제에서는 최종 사용자 응답 이벤트 객체를 보여줍니다.

```
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "viewer-response",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "host": [
              {
                "key": "Host",
                "value": "d111111abcdef8.cloudfront.net"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "curl/7.66.0"
              }
            ],
            "accept": [
              {
                "key": "accept",
                "value": "*/*"
              }
            ]
          },
          "method": "GET",
          "querystring": "",
          "uri": "/"
        },
        "response": {
          "headers": {
            "access-control-allow-credentials": [
              {
                "key": "Access-Control-Allow-Credentials",
                "value": "true"
              }
            ],
            "access-control-allow-origin": [
              {
                "key": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ],
            "date": [
              {
                "key": "Date",
                "value": "Mon, 13 Jan 2020 20:14:56 GMT"
              }
            ],
            "referrer-policy": [
              {
                "key": "Referrer-Policy",
                "value": "no-referrer-when-downgrade"
              }
            ],
            "server": [
              {
                "key": "Server",
                "value": "ExampleCustomOriginServer"
              }
            ],
            "x-content-type-options": [
              {
                "key": "X-Content-Type-Options",
                "value": "nosniff"
              }
            ],
            "x-frame-options": [
              {
                "key": "X-Frame-Options",
                "value": "DENY"
              }
            ],
            "x-xss-protection": [
              {
                "key": "X-XSS-Protection",
                "value": "1; mode=block"
              }
            ],
            "age": [
              {
                "key": "Age",
                "value": "2402"
              }
            ],
            "content-type": [
              {
                "key": "Content-Type",
                "value": "text/html; charset=utf-8"
              }
            ],
            "content-length": [
              {
                "key": "Content-Length",
                "value": "9593"
              }
            ]
          },
          "status": "200",
          "statusDescription": "OK"
        }
      }
    }
  ]
}
```

### 응답 이벤트 필드
<a name="response-event-fields"></a>

응답 이벤트 객체 데이터는 세 개의 하위 객체(`config`(`Records.cf.config`), `request`(`Records.cf.request`) 및 `response`(`Records.cf.response`))에 포함되어 있습니다. 요청 객체의 필드에 대한 자세한 내용은 [요청 객체의 필드](#request-event-fields-request) 단원을 참조하세요. 다음 목록에서는 `config` 및 `response` 하위 객체의 필드에 대해 설명합니다.

#### 구성 객체의 필드
<a name="response-event-fields-config"></a>

다음 표에서는 `config` 객체(`Records.cf.config`)의 필드에 대해 설명합니다.

**`distributionDomainName`(읽기 전용)**  
응답과 연결된 배포의 도메인 이름입니다.

**`distributionID`(읽기 전용)**  
응답과 관련이 있는 배포의 ID입니다.

**`eventType`(읽기 전용)**  
응답과 관련된 트리거의 유형입니다(`origin-response` 또는 `viewer-response`).

**`requestId`(읽기 전용)**  
이 응답이 연결된 최종 사용자 요청/CloudFront 요청을 고유하게 식별하는 암호화된 문자열입니다. 또한 `requestId` 값은 CloudFront 액세스 로그에 `x-edge-request-id`로 표시됩니다. 자세한 내용은 [액세스 로그(표준 로그)](AccessLogs.md) 및 [로그 파일 필드](standard-logs-reference.md#BasicDistributionFileFormat) 단원을 참조하세요.

#### 응답 객체의 필드
<a name="response-event-fields-response"></a>

다음 표에서는 `response` 객체(`Records.cf.response`)의 필드에 대해 설명합니다. Lambda@Edge 함수를 사용하여 HTTP 응답을 생성하는 방법에 대한 자세한 내용은 [요청 트리거에서 HTTP 응답 생성](lambda-generating-http-responses.md#lambda-generating-http-responses-in-requests) 단원을 참조하세요.

**`headers`(읽기/쓰기)**  
응답의 헤더입니다. 다음을 참조하십시오.  
+ `headers` 객체 내 키는 표준 HTTP 헤더 이름의 소문자 버전입니다. 소문자 키를 사용하여 대/소문자를 구분하지 않고 헤더 값에 액세스할 수 있습니다.
+ 각 헤더 객체(예: `headers["content-type"]` 또는 `headers["content-length"]`)는 키–값 페어의 어레이입니다. 해당 헤더에 대해 어레이에는 응답의 각 값에 대한 하나의 키–값 페어를 포함합니다.
+ `key`에는 HTTP 응답에 표시되는 헤더의 대/소문자 구분 이름을 포함합니다(예: `Content-Type`, `Content-Length`, `Cookie`, 등).
+ `value`에는 HTTP 응답에 표시되는 헤더 값을 포함합니다.
+ Lambda 함수가 응답 헤더를 추가하거나 수정하고 사용자가 헤더 `key` 필드를 포함시키지 않으면, Lambda@Edge는 사용자가 입력한 헤더 이름을 사용하여 헤더 `key`를 자동으로 삽입합니다. 헤더 이름의 형식을 어떻게 지정했든 상관없이, 자동으로 삽입된 헤더 키는 각 부분의 첫 문자가 대문자로 지정되고 각 부분이 하이픈(-)으로 구분됩니다.

  예를 들어 헤더를 다음과 같이 헤더 `key` 없이 추가할 수 있습니다.

  ```
  "content-type": [
    {
      "value": "text/html;charset=UTF-8"
    }
  ]
  ```

  이 예제에서는 Lambda@Edge가 자동으로 `"key": "Content-Type"`을 삽입합니다.
헤더 사용 시 제한 사항에 대한 자세한 내용은 [엣지 함수에 대한 제한 사항](edge-functions-restrictions.md) 단원을 참조하십시오.

**`status`**  
응답의 HTTP 상태 코드입니다.

**`statusDescription`**  
응답의 HTTP 상태 설명입니다.

# 요청 및 응답 작업 수행
<a name="lambda-generating-http-responses"></a>

Lambda@Edge 요청 및 응답을 사용하려면 다음 주제를 참조하세요.

**Topics**
+ [오리진 장애 조치와 함께 Lambda@Edge 함수 사용](#lambda-and-origin-failover)
+ [요청 트리거에서 HTTP 응답 생성](#lambda-generating-http-responses-in-requests)
+ [오리진 응답 트리거에서 HTTP 응답 업데이트](#lambda-updating-http-responses)
+ [본문 포함 옵션을 선택해 요청 본문에 액세스](#lambda-include-body-access)

## 오리진 장애 조치와 함께 Lambda@Edge 함수 사용
<a name="lambda-and-origin-failover"></a>

예를 들어 고가용성 확보를 위해 구성하는 오리진 장애 조치에 대하여 오리진 그룹과 함께 설정한 CloudFront 배포와 함께 Lambda@Edge 함수를 사용할 수 있습니다. 오리진 그룹과 함께 Lambda 함수를 사용하려면, 캐시 동작을 생성할 때 오리진 그룹에 대한 오리진 요청 또는 오리진 응답 트리거에 함수를 지정합니다.

자세한 내용은 다음을 참조하세요.
+ **오리진 그룹 만들기:** [오리진 그룹 생성](high_availability_origin_failover.md#concept_origin_groups.creating)
+ **오리진 장애 조치가 Lambda@Edge와 함께 작동하는 방식:** [Lambda@Edge 함수와 함께 오리진 장애 조치 사용](high_availability_origin_failover.md#concept_origin_groups.lambda)

## 요청 트리거에서 HTTP 응답 생성
<a name="lambda-generating-http-responses-in-requests"></a>

CloudFront 요청을 수신할 때 Lambda 함수를 사용하여 CloudFront가 오리진에 응답을 전달하지 않고 최종 사용자에게 직접 반환하는 HTTP 응답을 생성할 수 있습니다. HTTP 응답을 생성하면 오리진에 대한 로드가 감소하고 일반적으로 최종 사용자에 대한 지연 시간 또한 감소합니다.

HTTP 응답 생성의 몇 가지 일반 시나리오는 다음을 포함합니다.
+ 최종 사용자에게 소규모 웹 페이지 반환
+ HTTP 301 또는 302 상태 코드를 반환하여 다른 웹 페이지로 사용자를 리디렉션
+ 사용자가 인증되지 않았을 때 최종 사용자에게 HTTP 401 상태 코드 반환

Lambda@Edge 함수는 다음 CloudFront 이벤트가 발생할 때 HTTP 응답을 생성할 수 있습니다.

**최종 사용자 요청 이벤트**  
최종 사용자 요청 이벤트에 의해 함수가 트리거될 때 CloudFront는 최종 사용자에게 응답을 반환하고 이를 캐싱하지 않습니다.

**오리진 요청 이벤트**  
오리진 요청 이벤트에 의해 함수가 트리거될 때 CloudFront는 함수에 의해 이전에 생성된 응답에 대한 엣지 캐시를 확인합니다.  
+ 응답이 캐시에 있는 경우 함수는 실행되지 않고 CloudFront는 캐싱된 응답을 최종 사용자에게 반환합니다.
+ 응답이 캐시에 없는 경우 함수는 실행되고 CloudFront는 캐싱된 응답을 최종 사용자에게 반환하며 이를 캐싱합니다.

HTTP 응답을 생성하는 몇 가지 샘플 코드를 보려면 [Lambda@Edge 예제 함수](lambda-examples.md) 단원을 참조합니다. 응답 트리거에서 HTTP 응답을 바꿀 수도 있습니다. 자세한 내용은 [오리진 응답 트리거에서 HTTP 응답 업데이트](#lambda-updating-http-responses) 단원을 참조합니다.

### 프로그래밍 모델
<a name="lambda-generating-http-responses-programming-model"></a>

이 단원에서는 Lambda@Edge를 사용하여 HTTP 응답을 생성하기 위한 프로그래밍 모델을 설명합니다.

**Topics**
+ [응답 객체](#lambda-generating-http-responses-object)
+ [오류](#lambda-generating-http-responses-errors)
+ [필수 필드](#lambda-generating-http-responses-required-fields)

#### 응답 객체
<a name="lambda-generating-http-responses-object"></a>

`result` 메서드의 `callback`파라미터로 반환한 응답은 다음 구조를 가져야 합니다(`status` 필드만이 필요함).

```
const response = {
    body: 'content',
    bodyEncoding: 'text' | 'base64',
    headers: {
        'header name in lowercase': [{
            key: 'header name in standard case',
            value: 'header value'
         }],
         ...
    },
    status: 'HTTP status code (string)',
    statusDescription: 'status description'
};
```

응답 객체에는 다음 값이 포함될 수 있습니다.

**`body`**  
CloudFront가 생성된 응답에서 반환하고자 하는 본문입니다.

**`bodyEncoding`**  
`body`에서 지정한 값에 대한 인코딩입니다. 유일하게 유효한 인코딩은 `text`과 `base64`입니다. `body` 객체에 `response`를 포함했지만 `bodyEncoding`을 누락한 경우 CloudFront는 본문을 텍스트로 처리합니다.  
`bodyEncoding`을 `base64`로 지정하지만 본문이 유효한 base64가 아닌 경우 CloudFront는 오류를 반환합니다.

**`headers`**  
CloudFront가 생성된 응답에서 반환하고자 하는 헤더입니다. 다음을 참조하십시오.  
+ `headers` 객체 내 키는 표준 HTTP 헤더 이름의 소문자 버전입니다. 소문자 키를 사용하여 대/소문자를 구분하지 않고 헤더 값에 액세스할 수 있습니다.
+ 각 헤더(예: `headers["accept"]` 또는 `headers["host"]`)는 키-값 페어의 어레이입니다. 해당 헤더에 대해 어레이에는 생성된 응답의 각 값에 대한 하나의 키-값 페어를 포함합니다.
+ `key`(선택사항)는 HTTP 요청에 표시되는 헤더의 대/소문자 구분 이름입니다(예: `accept` 또는 `host`).
+ `value`를 헤더 값으로 지정합니다.
+ 키-값 페어의 헤더 키 부분을 포함시키지 않으면 Lambda@Edge는 사용자가 입력한 헤더 이름을 사용하여 헤더 키를 자동으로 삽입합니다. 헤더 이름의 형식을 어떻게 지정했든 상관없이, 삽입된 헤더 키는 하이픈(-)으로 구분된 각 부분의 첫 문자가 자동으로 대문자로 지정됩니다.

  예를 들어 헤더를 다음과 같이 헤더 키 없이 추가할 수 있습니다. `'content-type': [{ value: 'text/html;charset=UTF-8' }]` 

  이 예제에서 Lambda@Edge는 다음과 같은 헤더 키를 생성합니다. `Content-Type` 
헤더 사용 시 제한 사항에 대한 자세한 내용은 [엣지 함수에 대한 제한 사항](edge-functions-restrictions.md) 단원을 참조하십시오.

**`status`**  
HTTP 상태 코드. 상태 코드를 문자열로 제공합니다. CloudFront는 다음에 대해 제공된 상태 코드를 사용합니다.  
+ 응답 시 반환
+ 오리진 요청 이벤트에 의해 트리거된 함수에 의해 응답이 생성되었을 때의 CloudFront 엣지 캐시에 있는 캐시
+ CloudFront [액세스 로그(표준 로그)](AccessLogs.md)에 로그인
`status` 값이 200\$1599가 아닌 경우 CloudFront는 최종 사용자에게 오류를 반환합니다.

**`statusDescription`**  
CloudFront가 응답에서 반환하고 HTTP 상태 코드를 포함하고자 하는 설명입니다. HTTP 상태 코드 200에 대해 `OK`와 같은 표준 설명을 사용할 필요는 없습니다.

#### 오류
<a name="lambda-generating-http-responses-errors"></a>

다음은 생성된 HTTP 응답에서 발생할 수 있는 오류입니다.

**본문을 포함하고 상태에 대해 204(콘텐츠 없음)을 지정한 응답**  
최종 사용자 요청 이벤트에 의해 함수가 트리거될 때 CloudFront는 다음 두 가지가 모두 true일 경우 HTTP 502 상태 코드(잘못된 게이트웨이)를 반환합니다.  
+ `status`의 기본값이 204(콘텐츠 없음)임
+ 응답에 `body`에 대한 값이 포함
이는 Lambda@Edge가 `HTTP 204` 응답에 메시지 본문이 포함될 필요가 없다고 명시한 RFC 2616의 선택적인 제한을 부과하기 때문입니다.

**생성된 응답의 크기 제한**  
Lambda 함수가 생성하는 응답의 최대 크기는 함수를 트리거한 이벤트에 따라 다릅니다.  
+ **최종 사용자 요청 이벤트** - 40KB
+ **오리진 요청 이벤트** - 1MB
응답이 허용된 크기보다 큰 경우 CloudFront는 최종 사용자에게 HTTP 502 상태 코드(잘못된 게이트웨이)를 반환합니다.

#### 필수 필드
<a name="lambda-generating-http-responses-required-fields"></a>

`status` 필드는 필수입니다.

다른 모든 필드는 선택 사항입니다.

## 오리진 응답 트리거에서 HTTP 응답 업데이트
<a name="lambda-updating-http-responses"></a>

CloudFront가 오리진 서버에서 HTTP 응답을 수신할 때 오리진 응답 트리거가 캐시 동작과 연결된 경우 HTTP 응답을 수정하여 오리진에서 반환된 것을 재정의할 수 있습니다.

HTTP 응답 업데이트의 몇 가지 일반 시나리오는 다음을 포함합니다.
+ 오리진이 오류 상태 코드(4xx 또는 5xx)를 반환할 때 상태를 변경하여 HTTP 200 상태 코드를 설정하고 정적 본문 콘텐츠를 생성하여 최종 사용자에게 반환합니다. 샘플 코드에 대한 내용은 [예시: 오리진 응답 트리거를 사용하여 오류 상태 코드를 200으로 업데이트](lambda-examples.md#lambda-examples-custom-error-static-body)를 참조하십시오.
+ 오리진이 오류 상태 코드(4xx 또는 5xx)를 반환할 때 상태를 변경하여 HTTP 301 또는 HTTP 302 상태 코드를 설정하고 사용자를 다른 웹 사이트로 리디렉션합니다. 샘플 코드에 대한 내용은 [예시: 오리진 응답 트리거를 사용하여 오류 상태 코드를 302로 업데이트](lambda-examples.md#lambda-examples-custom-error-new-site)를 참조하십시오.

**참고**  
함수는 `200`\$1`599`(경계값 포함) 사이의 상태 값을 반환해야 합니다. 그렇지 않으면 CloudFront가 최종 사용자에게 오류를 반환합니다.

최종 사용자 및 오리진 요청 이벤트에서 HTTP 응답을 바꿀 수도 있습니다. 자세한 내용은 [요청 트리거에서 HTTP 응답 생성](#lambda-generating-http-responses-in-requests) 섹션을 참조하세요.

HTTP 응답을 사용하여 작업 중일 때 Lambda@Edge는 오리진 서버에 의해 오리진 응답 트리거로 반환되는 본문을 공개하지 않습니다. 원하는 값으로 설정하여 정적 콘텐츠 본문을 생성하거나 값을 비어 있음으로 설정하여 함수 내부의 본문을 제거할 수 있습니다. 함수의 본문 필드를 업데이트하지 않은 경우 오리진 서버에서 반환된 원래 본문이 최종 사용자에게 반환됩니다.

## 본문 포함 옵션을 선택해 요청 본문에 액세스
<a name="lambda-include-body-access"></a>

Lambda 함수에서 액세스할 수 있도록 Lambda@Edge가 본문에 쓰기 가능한 HTTP 메서드(POST, PUT, DELETE 등)에 대한 요청을 노출하도록 선택할 수 있습니다. 읽기 전용 액세스 권한을 선택하거나 본문을 바꾸도록 지정할 수 있습니다.

이 옵션을 활성화하려면 함수에 대해 최종 사용자 요청 또는 오리진 요청 이벤트에 대해 실행되는 CloudFront 트리거를 생성할 때 **본문 포함**을 선택합니다. 자세한 내용은 [Lambda@Edge 함수에 대한 트리거 추가](lambda-edge-add-triggers.md) 단원을 참조하고, 함수에서 **본문 포함**을 사용하는 방법을 알아보려면 [Lambda@Edge 이벤트 구조](lambda-event-structure.md) 단원을 참조하세요.

이 기능을 사용하려는 시나리오는 다음과 같을 수 있습니다.
+ 오리진 서버로 고객 입력 데이터를 다시 보내지 않는 웹 양식(예: "문의처") 처리
+ 최종 사용자의 브라우저가 보낸 웹 비콘 데이터를 수집해 엣지에서 처리

샘플 코드에 대한 내용은 [Lambda@Edge 예제 함수](lambda-examples.md)를 참조하십시오.

**참고**  
요청 본문이 크면 Lambda@Edge가 자릅니다. 최대 크기 및 잘림에 대한 자세한 내용은 [본문 포함 옵션이 적용된 요청 본문에 대한 제한 사항](lambda-at-edge-function-restrictions.md#lambda-at-edge-restrictions-request-body) 단원을 참조하세요.

# Lambda@Edge 예제 함수
<a name="lambda-examples"></a>

Amazon CloudFront에서 Lambda 함수를 사용하려면 다음 예시를 참조하세요.

**참고**  
Lambda@Edge 함수에 대해 런타임 Node.js 18 이상을 선택하면 `index.mjs` 파일이 자동으로 생성됩니다. 다음 코드 예제를 사용하려면 `index.mjs` 파일의 이름을 `index.js`로 바꿉니다.

**Topics**
+ [일반 예제](#lambda-examples-general-examples)
+ [응답 생성 - 예시](#lambda-examples-generated-response-examples)
+ [쿼리 문자열 - 예시](#lambda-examples-query-string-examples)
+ [국가 또는 디바이스 유형 헤더별 콘텐츠 개인화 - 예제](#lambda-examples-redirecting-examples)
+ [콘텐츠 기반 동적 원본 선택 - 예제](#lambda-examples-content-based-routing-examples)
+ [오류 상태 업데이트 - 예시](#lambda-examples-update-error-status-examples)
+ [요청 본문 액세스 - 예시](#lambda-examples-access-request-body-examples)

## 일반 예제
<a name="lambda-examples-general-examples"></a>

다음 예시에서는 CloudFront에서 Lambda@Edge 를 사용하는 일반적인 방법을 보여줍니다.

**Topics**
+ [예: A/B 테스트](#lambda-examples-a-b-testing)
+ [예시: 응답 헤더 재정의](#lambda-examples-overriding-response-header)

### 예: A/B 테스트
<a name="lambda-examples-a-b-testing"></a>

다음 예제를 사용하여 리디렉션을 만들거나 URL을 변경하지 않고 두 가지 다른 버전의 이미지를 테스트할 수 있습니다. 이 예제는 최종 사용자 요청의 쿠키를 읽고 그에 따라 요청 URL을 수정합니다. 최종 사용자가 예상 값 중 하나와 함께 쿠키를 보내지 않는 경우, 이 예제에서는 최종 사용자를 URL 중 하나에 임의로 할당합니다.

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    if (request.uri !== '/experiment-pixel.jpg') {
        // do not process if this is not an A-B test request
        callback(null, request);
        return;
    }

    const cookieExperimentA = 'X-Experiment-Name=A';
    const cookieExperimentB = 'X-Experiment-Name=B';
    const pathExperimentA = '/experiment-group/control-pixel.jpg';
    const pathExperimentB = '/experiment-group/treatment-pixel.jpg';

    /*
     * Lambda at the Edge headers are array objects.
     *
     * Client may send multiple Cookie headers, i.e.:
     * > GET /viewerRes/test HTTP/1.1
     * > User-Agent: curl/7.18.1 (x86_64-unknown-linux-gnu) libcurl/7.18.1 OpenSSL/1.0.1u zlib/1.2.3
     * > Cookie: First=1; Second=2
     * > Cookie: ClientCode=abc
     * > Host: example.com
     *
     * You can access the first Cookie header at headers["cookie"][0].value
     * and the second at headers["cookie"][1].value.
     *
     * Header values are not parsed. In the example above,
     * headers["cookie"][0].value is equal to "First=1; Second=2"
     */
    let experimentUri;
    if (headers.cookie) {
        for (let i = 0; i < headers.cookie.length; i++) {
            if (headers.cookie[i].value.indexOf(cookieExperimentA) >= 0) {
                console.log('Experiment A cookie found');
                experimentUri = pathExperimentA;
                break;
            } else if (headers.cookie[i].value.indexOf(cookieExperimentB) >= 0) {
                console.log('Experiment B cookie found');
                experimentUri = pathExperimentB;
                break;
            }
        }
    }

    if (!experimentUri) {
        console.log('Experiment cookie has not been found. Throwing dice...');
        if (Math.random() < 0.75) {
            experimentUri = pathExperimentA;
        } else {
            experimentUri = pathExperimentB;
        }
    }

    request.uri = experimentUri;
    console.log(`Request uri set to "${request.uri}"`);
    callback(null, request);
};
```

------
#### [ Python ]

```
import json
import random

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    if request['uri'] != '/experiment-pixel.jpg':
        # Not an A/B Test
        return request

    cookieExperimentA, cookieExperimentB = 'X-Experiment-Name=A', 'X-Experiment-Name=B'
    pathExperimentA, pathExperimentB = '/experiment-group/control-pixel.jpg', '/experiment-group/treatment-pixel.jpg'

    '''
    Lambda at the Edge headers are array objects.

    Client may send multiple cookie headers. For example:
    > GET /viewerRes/test HTTP/1.1
    > User-Agent: curl/7.18.1 (x86_64-unknown-linux-gnu) libcurl/7.18.1 OpenSSL/1.0.1u zlib/1.2.3
    > Cookie: First=1; Second=2
    > Cookie: ClientCode=abc
    > Host: example.com

    You can access the first Cookie header at headers["cookie"][0].value
    and the second at headers["cookie"][1].value.

    Header values are not parsed. In the example above,
    headers["cookie"][0].value is equal to "First=1; Second=2"
    '''

    experimentUri = ""

    for cookie in headers.get('cookie', []):
        if cookieExperimentA in cookie['value']:
            print("Experiment A cookie found")
            experimentUri = pathExperimentA
            break
        elif cookieExperimentB in cookie['value']:
            print("Experiment B cookie found")
            experimentUri = pathExperimentB
            break

    if not experimentUri:
        print("Experiment cookie has not been found. Throwing dice...")
        if random.random() < 0.75:
            experimentUri = pathExperimentA
        else:
            experimentUri = pathExperimentB

    request['uri'] = experimentUri
    print(f"Request uri set to {experimentUri}")
    return request
```

------

### 예시: 응답 헤더 재정의
<a name="lambda-examples-overriding-response-header"></a>

다음 예제는 다른 헤더의 값을 기준으로 응답 헤더의 값을 변경하는 방법을 보여줍니다.

------
#### [ Node.js ]

```
export const handler = async (event) => {
    const response = event.Records[0].cf.response;
    const headers = response.headers;

    const headerNameSrc = 'X-Amz-Meta-Last-Modified';
    const headerNameDst = 'Last-Modified';

    if (headers[headerNameSrc.toLowerCase()]) {
        headers[headerNameDst.toLowerCase()] = [{
            key: headerNameDst,
            value: headers[headerNameSrc.toLowerCase()][0].value,
        }];
        console.log(`Response header "${headerNameDst}" was set to ` +
                    `"${headers[headerNameDst.toLowerCase()][0].value}"`);
    }

    return response;
};
```

------
#### [ Python ]

```
import json 

def lambda_handler(event, context):
    response = event['Records'][0]['cf']['response']
    headers = response['headers']
    
    header_name_src = 'X-Amz-Meta-Last-Modified'
    header_name_dst = 'Last-Modified'
    
    if headers.get(header_name_src.lower()):
        headers[header_name_dst.lower()] = [{
            'key': header_name_dst,
            'value': headers[header_name_src.lower()][0]['value']
        }]
        print(f'Response header "{header_name_dst}" was set to '
              f'"{headers[header_name_dst.lower()][0]["value"]}"')
    
    return response
```

------

## 응답 생성 - 예시
<a name="lambda-examples-generated-response-examples"></a>

다음 예시에서는 Lambda@Edge를 사용하여 응답을 생성하는 방법을 보여줍니다.

**Topics**
+ [예시: 정적 콘텐츠 제공(생성된 응답)](#lambda-examples-static-web-server)
+ [예: HTTP 리디렉션 생성(생성된 응답)](#lambda-examples-http-redirect)

### 예시: 정적 콘텐츠 제공(생성된 응답)
<a name="lambda-examples-static-web-server"></a>

다음 예제는 Lambda 함수를 사용하여 정적 웹사이트 콘텐츠를 서비스하는 방법을 보여줍니다. 이는 오리진 서버에 대한 로드를 줄이고 전체 지연 시간을 단축합니다.

**참고**  
최종 사용자 요청 및 오리진 요청 이벤트에 대해 HTTP 응답을 만들 수 있습니다. 자세한 내용은 [요청 트리거에서 HTTP 응답 생성](lambda-generating-http-responses.md#lambda-generating-http-responses-in-requests) 섹션을 참조하세요.  
원본 응답 이벤트에서 HTTP 응답의 본문을 바꾸거나 제거할 수도 있습니다. 자세한 내용은 [오리진 응답 트리거에서 HTTP 응답 업데이트](lambda-generating-http-responses.md#lambda-updating-http-responses) 섹션을 참조하세요.

------
#### [ Node.js ]

```
'use strict';

const content = `
<\!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Simple Lambda@Edge Static Content Response</title>
  </head>
  <body>
    <p>Hello from Lambda@Edge!</p>
  </body>
</html>
`;

exports.handler = (event, context, callback) => {
    /*
     * Generate HTTP OK response using 200 status code with HTML body.
     */
    const response = {
        status: '200',
        statusDescription: 'OK',
        headers: {
            'cache-control': [{
                key: 'Cache-Control',
                value: 'max-age=100'
            }],
            'content-type': [{
                key: 'Content-Type',
                value: 'text/html'
            }]
        },
        body: content,
    };
    callback(null, response);
};
```

------
#### [ Python ]

```
import json

CONTENT = """
<\!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Simple Lambda@Edge Static Content Response</title>
</head>
<body>
    <p>Hello from Lambda@Edge!</p>
</body>
</html>
"""

def lambda_handler(event, context):
    # Generate HTTP OK response using 200 status code with HTML body.
    response = {
        'status': '200',
        'statusDescription': 'OK',
        'headers': {
            'cache-control': [
                {
                    'key': 'Cache-Control',
                    'value': 'max-age=100'
                }
            ],
            "content-type": [
                {
                    'key': 'Content-Type',
                    'value': 'text/html'
                }
            ]
        },
        'body': CONTENT
    }
    return response
```

------

### 예: HTTP 리디렉션 생성(생성된 응답)
<a name="lambda-examples-http-redirect"></a>

다음 예제는 HTTP 리디렉션을 만드는 방법을 보여줍니다.

**참고**  
최종 사용자 요청 및 오리진 요청 이벤트에 대해 HTTP 응답을 만들 수 있습니다. 자세한 내용은 [요청 트리거에서 HTTP 응답 생성](lambda-generating-http-responses.md#lambda-generating-http-responses-in-requests) 섹션을 참조하세요.

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    /*
     * Generate HTTP redirect response with 302 status code and Location header.
     */
    const response = {
        status: '302',
        statusDescription: 'Found',
        headers: {
            location: [{
                key: 'Location',
                value: 'https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html',
            }],
        },
    };
    callback(null, response);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):

    # Generate HTTP redirect response with 302 status code and Location header.

    response = {
        'status': '302',
        'statusDescription': 'Found',
        'headers': {
            'location': [{
                'key': 'Location',
                'value': 'https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html'
            }]
        }
    }

    return response
```

------

## 쿼리 문자열 - 예시
<a name="lambda-examples-query-string-examples"></a>

다음 예시에서는 Lambda@Edge를 쿼리 문자열과 함께 사용할 수 있는 방법이 포함되어 있습니다.

**Topics**
+ [예시: 쿼리 문자열 파라미터를 기반으로 헤더 추가](#lambda-examples-header-based-on-query-string)
+ [예시: 쿼리 문자열 파라미터를 정규화하여 캐시 적중률 향상](#lambda-examples-normalize-query-string-parameters)
+ [예시: 인증되지 않은 사용자를 로그인 페이지로 리디렉션](#lambda-examples-redirect-to-signin-page)

### 예시: 쿼리 문자열 파라미터를 기반으로 헤더 추가
<a name="lambda-examples-header-based-on-query-string"></a>

다음 예제에서는 쿼리 문자열 파라미터의 키-값 페어를 가져오고 그 값을 토대로 헤더를 추가하는 방법을 보여줍니다.

------
#### [ Node.js ]

```
'use strict';

const querystring = require('querystring');
exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    
    /* When a request contains a query string key-value pair but the origin server
     * expects the value in a header, you can use this Lambda function to
     * convert the key-value pair to a header. Here's what the function does:
     * 1. Parses the query string and gets the key-value pair.
     * 2. Adds a header to the request using the key-value pair that the function got in step 1.
     */

    /* Parse request querystring to get javascript object */
    const params = querystring.parse(request.querystring);

    /* Move auth param from querystring to headers */
    const headerName = 'Auth-Header';
    request.headers[headerName.toLowerCase()] = [{ key: headerName, value: params.auth }];
    delete params.auth;

    /* Update request querystring */
    request.querystring = querystring.stringify(params);

    callback(null, request);
};
```

------
#### [ Python ]

```
from urllib.parse import parse_qs, urlencode

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    '''
    When a request contains a query string key-value pair but the origin server
    expects the value in a header, you can use this Lambda function to
    convert the key-value pair to a header. Here's what the function does:
        1. Parses the query string and gets the key-value pair.
        2. Adds a header to the request using the key-value pair that the function got in step 1.
    '''

    # Parse request querystring to get dictionary/json
    params = {k : v[0] for k, v in parse_qs(request['querystring']).items()}

    # Move auth param from querystring to headers
    headerName = 'Auth-Header'
    request['headers'][headerName.lower()] = [{'key': headerName, 'value': params['auth']}]
    del params['auth']

    # Update request querystring
    request['querystring'] = urlencode(params)

    return request
```

------

### 예시: 쿼리 문자열 파라미터를 정규화하여 캐시 적중률 향상
<a name="lambda-examples-normalize-query-string-parameters"></a>

다음 예제에서는 CloudFront가 요청을 오리진에 전달하기 전에 쿼리 문자열을 다음과 같이 변경하여 캐시 적중률을 향상하는 방법을 보여 줍니다.
+ 파라미터 이름을 기준으로 키-값 페어를 알파벳순으로 정렬
+ 키-값 페어의 대/소문자를 소문자로 변경

자세한 내용은 [쿼리 문자열 파라미터 기반의 콘텐츠 캐싱](QueryStringParameters.md) 섹션을 참조하세요.

------
#### [ Node.js ]

```
'use strict';

const querystring = require('querystring');

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    /* When you configure a distribution to forward query strings to the origin and
     * to cache based on an allowlist of query string parameters, we recommend
     * the following to improve the cache-hit ratio:
     * - Always list parameters in the same order.
     * - Use the same case for parameter names and values.
     *
     * This function normalizes query strings so that parameter names and values
     * are lowercase and parameter names are in alphabetical order.
     *
     * For more information, see:
     * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/QueryStringParameters.html
     */

    console.log('Query String: ', request.querystring);

    /* Parse request query string to get javascript object */
    const params = querystring.parse(request.querystring.toLowerCase());
    const sortedParams = {};

    /* Sort param keys */
    Object.keys(params).sort().forEach(key => {
        sortedParams[key] = params[key];
    });

    /* Update request querystring with normalized  */
    request.querystring = querystring.stringify(sortedParams);

    callback(null, request);
};
```

------
#### [ Python ]

```
from urllib.parse import parse_qs, urlencode

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    '''
    When you configure a distribution to forward query strings to the origin and
    to cache based on an allowlist of query string parameters, we recommend
    the following to improve the cache-hit ratio:
    Always list parameters in the same order.
    - Use the same case for parameter names and values.

    This function normalizes query strings so that parameter names and values
    are lowercase and parameter names are in alphabetical order.

    For more information, see:
    https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/QueryStringParameters.html
    '''
    print("Query string: ", request["querystring"])

    # Parse request query string to get js object
    params = {k : v[0] for k, v in parse_qs(request['querystring'].lower()).items()}

    # Sort param keys
    sortedParams = sorted(params.items(), key=lambda x: x[0])

    # Update request querystring with normalized
    request['querystring'] = urlencode(sortedParams)
    
    return request
```

------

### 예시: 인증되지 않은 사용자를 로그인 페이지로 리디렉션
<a name="lambda-examples-redirect-to-signin-page"></a>

다음 예제에서는 사용자가 자격 증명을 입력하지 않은 경우 사용자를 로그인 페이지로 리디렉션하는 방법을 보여 줍니다.

------
#### [ Node.js ]

```
'use strict';

function parseCookies(headers) {
    const parsedCookie = {};
    if (headers.cookie) {
        headers.cookie[0].value.split(';').forEach((cookie) => {
            if (cookie) {
                const parts = cookie.split('=');
                parsedCookie[parts[0].trim()] = parts[1].trim();
            }
        });
    }
    return parsedCookie;
}

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    /* Check for session-id in request cookie in viewer-request event,
     * if session-id is absent, redirect the user to sign in page with original
     * request sent as redirect_url in query params.
     */

    /* Check for session-id in cookie, if present then proceed with request */
    const parsedCookies = parseCookies(headers);
    if (parsedCookies && parsedCookies['session-id']) {
        callback(null, request);
        return;
    }

    /* URI encode the original request to be sent as redirect_url in query params */
    const encodedRedirectUrl = encodeURIComponent(`https://${headers.host[0].value}${request.uri}?${request.querystring}`);
    const response = {
        status: '302',
        statusDescription: 'Found',
        headers: {
            location: [{
                key: 'Location',
                value: `https://www.example.com/signin?redirect_url=${encodedRedirectUrl}`,
            }],
        },
    };
    callback(null, response);
};
```

------
#### [ Python ]

```
import urllib

def parseCookies(headers):
    parsedCookie = {}
    if headers.get('cookie'):
        for cookie in headers['cookie'][0]['value'].split(';'):
            if cookie:
                parts = cookie.split('=')
                parsedCookie[parts[0].strip()] = parts[1].strip()
    return parsedCookie

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    '''
    Check for session-id in request cookie in viewer-request event,
    if session-id is absent, redirect the user to sign in page with original
    request sent as redirect_url in query params.
    '''

    # Check for session-id in cookie, if present, then proceed with request
    parsedCookies = parseCookies(headers)

    if parsedCookies and parsedCookies['session-id']:
        return request

    # URI encode the original request to be sent as redirect_url in query params
    redirectUrl = "https://%s%s?%s" % (headers['host'][0]['value'], request['uri'], request['querystring'])
    encodedRedirectUrl = urllib.parse.quote_plus(redirectUrl.encode('utf-8'))

    response = {
        'status': '302',
        'statusDescription': 'Found',
        'headers': {
            'location': [{
                'key': 'Location',
                'value': 'https://www.example.com/signin?redirect_url=%s' % encodedRedirectUrl
            }]
        }
    }
    return response
```

------

## 국가 또는 디바이스 유형 헤더별 콘텐츠 개인화 - 예제
<a name="lambda-examples-redirecting-examples"></a>

다음 예시에서는 Lambda@Edge를 사용하여 뷰어가 사용하는 위치나 장치 유형에 따라 동작을 사용자 정의하는 방법을 보여줍니다.

**Topics**
+ [예시: 최종 사용자 요청을 국가별 URL로 리디렉션](#lambda-examples-redirect-based-on-country)
+ [예시: 디바이스를 기반으로 객체의 다양한 버전 제공](#lambda-examples-vary-on-device-type)

### 예시: 최종 사용자 요청을 국가별 URL로 리디렉션
<a name="lambda-examples-redirect-based-on-country"></a>

다음 예제에서는 국가별 URL이 포함된 HTTP 리디렉션 응답을 생성하고 해당 응답을 최종 사용자에게 반환하는 방법을 보여 줍니다. 이 작업은 국가별 응답을 제공하려는 경우에 유용합니다. 예:
+ 국가별 하위 도메인이 있는 경우(예: us.example.com 및 tw.example.com) 최종 사용자가 example.com을 요청할 때 리디렉션 응답을 생성할 수 있습니다.
+ 비디오를 스트리밍하지만 특정 국가에서 콘텐츠를 스트리밍할 권한이 없는 경우 비디오를 볼 수 없는 이유를 설명하는 페이지로 해당 국가의 사용자를 리디렉션할 수 있습니다.

다음을 참조하십시오.
+ `CloudFront-Viewer-Country` 헤더 값을 기반으로 캐시하도록 배포를 구성해야 합니다. 자세한 내용은 [선택한 요청 헤더 기반의 캐시](DownloadDistValuesCacheBehavior.md#DownloadDistValuesForwardHeaders) 단원을 참조하세요.
+ CloudFront는 최종 사용자 요청 이벤트 뒤에 `CloudFront-Viewer-Country` 헤더를 추가합니다. 이 예제를 사용하려면 오리진 요청 이벤트에 대한 트리거를 생성해야 합니다.

------
#### [ Node.js ]

```
'use strict';

/* This is an origin request function */
exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    /*
     * Based on the value of the CloudFront-Viewer-Country header, generate an
     * HTTP status code 302 (Redirect) response, and return a country-specific
     * URL in the Location header.
     * NOTE: 1. You must configure your distribution to cache based on the
     *          CloudFront-Viewer-Country header. For more information, see
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
     *       2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
     *          request event. To use this example, you must create a trigger for the
     *          origin request event.
     */

    let url = 'https://example.com/';
    if (headers['cloudfront-viewer-country']) {
        const countryCode = headers['cloudfront-viewer-country'][0].value;
        if (countryCode === 'TW') {
            url = 'https://tw.example.com/';
        } else if (countryCode === 'US') {
            url = 'https://us.example.com/';
        }
    }

    const response = {
        status: '302',
        statusDescription: 'Found',
        headers: {
            location: [{
                key: 'Location',
                value: url,
            }],
        },
    };
    callback(null, response);
};
```

------
#### [ Python ]

```
# This is an origin request function

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    '''
    Based on the value of the CloudFront-Viewer-Country header, generate an
    HTTP status code 302 (Redirect) response, and return a country-specific
    URL in the Location header.
    NOTE: 1. You must configure your distribution to cache based on the
            CloudFront-Viewer-Country header. For more information, see
            https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
          2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
            request event. To use this example, you must create a trigger for the
            origin request event.
    '''

    url = 'https://example.com/'
    viewerCountry = headers.get('cloudfront-viewer-country')
    if viewerCountry:
        countryCode = viewerCountry[0]['value']
        if countryCode == 'TW':
            url = 'https://tw.example.com/'
        elif countryCode == 'US':
            url = 'https://us.example.com/'

    response = {
        'status': '302',
        'statusDescription': 'Found',
        'headers': {
            'location': [{
                'key': 'Location',
                'value': url
            }]
        }
    }

    return response
```

------

### 예시: 디바이스를 기반으로 객체의 다양한 버전 제공
<a name="lambda-examples-vary-on-device-type"></a>

다음 예제에서는 사용자가 사용 중인 디바이스 유형(예: 모바일 디바이스 또는 태블릿)을 기반으로 객체의 다양한 버전을 서비스하는 방법을 보여 줍니다. 다음을 참조하십시오.
+ `CloudFront-Is-*-Viewer` 헤더 값을 기반으로 캐시하도록 배포를 구성해야 합니다. 자세한 내용은 [선택한 요청 헤더 기반의 캐시](DownloadDistValuesCacheBehavior.md#DownloadDistValuesForwardHeaders) 단원을 참조하세요.
+ CloudFront는 최종 사용자 요청 이벤트 뒤에 `CloudFront-Is-*-Viewer` 헤더를 추가합니다. 이 예제를 사용하려면 오리진 요청 이벤트에 대한 트리거를 생성해야 합니다.

------
#### [ Node.js ]

```
'use strict';

/* This is an origin request function */
exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    /*
     * Serve different versions of an object based on the device type.
     * NOTE: 1. You must configure your distribution to cache based on the
     *          CloudFront-Is-*-Viewer headers. For more information, see
     *          the following documentation:
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-device-type
     *       2. CloudFront adds the CloudFront-Is-*-Viewer headers after the viewer
     *          request event. To use this example, you must create a trigger for the
     *          origin request event.
     */

    const desktopPath = '/desktop';
    const mobilePath = '/mobile';
    const tabletPath = '/tablet';
    const smarttvPath = '/smarttv';

    if (headers['cloudfront-is-desktop-viewer']
        && headers['cloudfront-is-desktop-viewer'][0].value === 'true') {
        request.uri = desktopPath + request.uri;
    } else if (headers['cloudfront-is-mobile-viewer']
               && headers['cloudfront-is-mobile-viewer'][0].value === 'true') {
        request.uri = mobilePath + request.uri;
    } else if (headers['cloudfront-is-tablet-viewer']
               && headers['cloudfront-is-tablet-viewer'][0].value === 'true') {
        request.uri = tabletPath + request.uri;
    } else if (headers['cloudfront-is-smarttv-viewer']
               && headers['cloudfront-is-smarttv-viewer'][0].value === 'true') {
        request.uri = smarttvPath + request.uri;
    }
    console.log(`Request uri set to "${request.uri}"`);

    callback(null, request);
};
```

------
#### [ Python ]

```
# This is an origin request function
def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    '''
    Serve different versions of an object based on the device type.
    NOTE: 1. You must configure your distribution to cache based on the
            CloudFront-Is-*-Viewer headers. For more information, see
            the following documentation:
            https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
            https://docs.aws.amazon.com/console/cloudfront/cache-on-device-type
          2. CloudFront adds the CloudFront-Is-*-Viewer headers after the viewer
            request event. To use this example, you must create a trigger for the
            origin request event.
    '''

    desktopPath = '/desktop';
    mobilePath = '/mobile';
    tabletPath = '/tablet';
    smarttvPath = '/smarttv';

    if 'cloudfront-is-desktop-viewer' in headers and headers['cloudfront-is-desktop-viewer'][0]['value'] == 'true':
        request['uri'] = desktopPath + request['uri']
    elif 'cloudfront-is-mobile-viewer' in headers and headers['cloudfront-is-mobile-viewer'][0]['value'] == 'true':
        request['uri'] = mobilePath + request['uri']
    elif 'cloudfront-is-tablet-viewer' in headers and headers['cloudfront-is-tablet-viewer'][0]['value'] == 'true':
        request['uri'] = tabletPath + request['uri']
    elif 'cloudfront-is-smarttv-viewer' in headers and headers['cloudfront-is-smarttv-viewer'][0]['value'] == 'true':
        request['uri'] = smarttvPath + request['uri']

    print("Request uri set to %s" % request['uri'])

    return request
```

------

## 콘텐츠 기반 동적 원본 선택 - 예제
<a name="lambda-examples-content-based-routing-examples"></a>

다음 예시에서는 Lambda@Edge를 사용하여 요청 정보를 기반으로 다른 오리진으로 라우팅하는 방법을 보여줍니다.

**Topics**
+ [예시: 오리진 요청 트리거를 사용하여 사용자 지정 오리진에서 Amazon S3 오리진으로 변경](#lambda-examples-content-based-S3-origin-based-on-query)
+ [예시: 오리진 요청 트리거를 사용하여 Amazon S3 오리진 리전 변경](#lambda-examples-content-based-S3-origin-request-trigger)
+ [예시: 오리진 요청 트리거를 사용하여 Amazon S3 오리진에서 사용자 지정 오리진으로 변경](#lambda-examples-content-based-custom-origin-request-trigger)
+ [예시: 오리진 요청 트리거를 사용하여 하나의 Amazon S3 버킷에서 다른 버킷으로 점진적으로 트래픽 전송](#lambda-examples-content-based-gradual-traffic-transfer)
+ [예시: 오리진 요청 트리거를 사용하여 국가 헤더를 기반으로 오리진 도메인 이름 변경](#lambda-examples-content-based-geo-header)

### 예시: 오리진 요청 트리거를 사용하여 사용자 지정 오리진에서 Amazon S3 오리진으로 변경
<a name="lambda-examples-content-based-S3-origin-based-on-query"></a>

이 함수는 오리진 요청 트리거를 사용하여 사용자 지정 오리진에서 콘텐츠를 요청 속성을 기반으로 가져오는 Amazon S3 오리진으로 변경하는 방법을 보여줍니다.

------
#### [ Node.js ]

```
'use strict';

 const querystring = require('querystring');
 
 exports.handler = (event, context, callback) => {
     const request = event.Records[0].cf.request;
 
     /**
      * Reads query string to check if S3 origin should be used, and
      * if true, sets S3 origin properties.
      */
 
     const params = querystring.parse(request.querystring);
 
     if (params['useS3Origin']) {
         if (params['useS3Origin'] === 'true') {
             const s3DomainName = 'amzn-s3-demo-bucket.s3.amazonaws.com';
 
             /* Set S3 origin fields */
             request.origin = {
                 s3: {
                     domainName: s3DomainName,
                     region: '',
                     authMethod: 'origin-access-identity',
                     path: '',
                     customHeaders: {}
                 }
             };
             request.headers['host'] = [{ key: 'host', value: s3DomainName}];
         }
     }
     
    callback(null, request);
};
```

------
#### [ Python ]

```
from urllib.parse import parse_qs

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    '''
    Reads query string to check if S3 origin should be used, and
    if true, sets S3 origin properties
    '''
    params = {k: v[0] for k, v in parse_qs(request['querystring']).items()}
    if params.get('useS3Origin') == 'true':
        s3DomainName = 'amzn-s3-demo-bucket.s3.amazonaws.com'

        # Set S3 origin fields
        request['origin'] = {
            's3': {
                'domainName': s3DomainName,
                'region': '',
                'authMethod': 'origin-access-identity',
                'path': '',
                'customHeaders': {}
            }
        }
        request['headers']['host'] = [{'key': 'host', 'value': s3DomainName}]
    return request
```

------

### 예시: 오리진 요청 트리거를 사용하여 Amazon S3 오리진 리전 변경
<a name="lambda-examples-content-based-S3-origin-request-trigger"></a>

이 함수는 오리진 요청 트리거를 사용하여 콘텐츠를 요청 속성을 기반으로 가져오는 Amazon S3 오리진을 변경하는 방법을 보여줍니다.

이 예제에서는 `CloudFront-Viewer-Country` 헤더 값을 사용하여 뷰어에게 더 가까운 리전의 버킷으로 S3 버킷 도메인 이름을 업데이트합니다. 이는 여러 방법에서 유용할 수 있습니다.
+ 지정된 리전이 최종 사용자의 국가와 더욱 근접할 때 지연 시간을 감소합니다.
+ 데이터가 요청이 온 곳과 동일한 국가에 있는 리전에서 서비스되는지 확인함으로써 데이터 주권을 제공합니다.

이 예제를 활용하려면 다음과 같이 해야 합니다.
+ `CloudFront-Viewer-Country` 헤더를 기반으로 캐시하도록 배포를 구성합니다. 자세한 내용은 [선택한 요청 헤더 기반의 캐시](DownloadDistValuesCacheBehavior.md#DownloadDistValuesForwardHeaders) 섹션을 참조하세요.
+ 오리진 요청 이벤트에서 이 함수에 대한 트리거를 생성합니다. CloudFront는 뷰어 요청 이벤트 뒤에 `CloudFront-Viewer-Country` 헤더를 추가하므로 이 예제를 사용하려면 함수가 원본 요청에 대해 실행되는지 확인해야 합니다.

**참고**  
다음 예제 코드는 오리진에 사용 중인 모든 S3 버킷에 대해 동일한 오리진 액세스 ID(OAI)를 사용합니다. 자세한 내용은 [오리진 액세스 ID](private-content-restricting-access-to-s3.md#private-content-restricting-access-to-s3-oai) 섹션을 참조하세요.

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;

    /**
     * This blueprint demonstrates how an origin-request trigger can be used to
     * change the origin from which the content is fetched, based on request properties.
     * In this example, we use the value of the CloudFront-Viewer-Country header
     * to update the S3 bucket domain name to a bucket in a Region that is closer to
     * the viewer.
     * 
     * This can be useful in several ways:
     *      1) Reduces latencies when the Region specified is nearer to the viewer's
     *         country.
     *      2) Provides data sovereignty by making sure that data is served from an
     *         origin that's in the same country that the request came from.
     * 
     * NOTE: 1. You must configure your distribution to cache based on the
     *          CloudFront-Viewer-Country header. For more information, see
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
     *       2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
     *          request event. To use this example, you must create a trigger for the
     *          origin request event.
     */

    const countryToRegion = {
        'DE': 'eu-central-1',
        'IE': 'eu-west-1',
        'GB': 'eu-west-2',
        'FR': 'eu-west-3',
        'JP': 'ap-northeast-1',
        'IN': 'ap-south-1'
    };

    if (request.headers['cloudfront-viewer-country']) {
        const countryCode = request.headers['cloudfront-viewer-country'][0].value;
        const region = countryToRegion[countryCode];
        
        /**
         * If the viewer's country is not in the list you specify, the request
         * goes to the default S3 bucket you've configured.
         */  
        if (region) {
            /**
             * If you've set up OAI, the bucket policy in the destination bucket
             * should allow the OAI GetObject operation, as configured by default
             * for an S3 origin with OAI. Another requirement with OAI is to provide
             * the Region so it can be used for the SIGV4 signature. Otherwise, the
             * Region is not required.
             */
            request.origin.s3.region = region;
            const domainName = `amzn-s3-demo-bucket-in-${region}.s3.${region}.amazonaws.com`;
            request.origin.s3.domainName = domainName;
            request.headers['host'] = [{ key: 'host', value: domainName }];
        }
    }

    callback(null, request);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    '''
    This blueprint demonstrates how an origin-request trigger can be used to
    change the origin from which the content is fetched, based on request properties.
    In this example, we use the value of the CloudFront-Viewer-Country header
    to update the S3 bucket domain name to a bucket in a Region that is closer to
    the viewer.
    
    This can be useful in several ways:
        1) Reduces latencies when the Region specified is nearer to the viewer's
            country.
        2) Provides data sovereignty by making sure that data is served from an
            origin that's in the same country that the request came from.
    
    NOTE: 1. You must configure your distribution to cache based on the
            CloudFront-Viewer-Country header. For more information, see
            https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
          2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
            request event. To use this example, you must create a trigger for the
            origin request event.
    '''

    countryToRegion = {
        'DE': 'eu-central-1',
        'IE': 'eu-west-1',
        'GB': 'eu-west-2',
        'FR': 'eu-west-3',
        'JP': 'ap-northeast-1',
        'IN': 'ap-south-1'
    }

    viewerCountry = request['headers'].get('cloudfront-viewer-country')
    if viewerCountry:
        countryCode = viewerCountry[0]['value']
        region = countryToRegion.get(countryCode)

        # If the viewer's country in not in the list you specify, the request
        # goes to the default S3 bucket you've configured
        if region:
            '''
            If you've set up OAI, the bucket policy in the destination bucket
            should allow the OAI GetObject operation, as configured by default
            for an S3 origin with OAI. Another requirement with OAI is to provide
            the Region so it can be used for the SIGV4 signature. Otherwise, the
            Region is not required.
            '''
            request['origin']['s3']['region'] = region
            domainName = 'amzn-s3-demo-bucket-in-{0}.s3.{0}.amazonaws.com'.format(region)
            request['origin']['s3']['domainName'] = domainName
            request['headers']['host'] = [{'key': 'host', 'value': domainName}]

    return request
```

------

### 예시: 오리진 요청 트리거를 사용하여 Amazon S3 오리진에서 사용자 지정 오리진으로 변경
<a name="lambda-examples-content-based-custom-origin-request-trigger"></a>

이 함수는 오리진 요청 트리거를 사용하여 콘텐츠를 요청 속성을 기반으로 가져오는 사용자 지정 오리진을 변경하는 방법을 보여줍니다.

------
#### [ Node.js ]

```
'use strict';

const querystring = require('querystring');
 
 exports.handler = (event, context, callback) => {
     const request = event.Records[0].cf.request;
 
     /**
      * Reads query string to check if custom origin should be used, and
      * if true, sets custom origin properties.
      */
 
     const params = querystring.parse(request.querystring);
 
     if (params['useCustomOrigin']) {
         if (params['useCustomOrigin'] === 'true') {
 
             /* Set custom origin fields*/
             request.origin = {
                 custom: {
                     domainName: 'www.example.com',
                     port: 443,
                     protocol: 'https',
                     path: '',
                     sslProtocols: ['TLSv1', 'TLSv1.1'],
                     readTimeout: 5,
                     keepaliveTimeout: 5,
                     customHeaders: {}
                 }
             };
             request.headers['host'] = [{ key: 'host', value: 'www.example.com'}];
         }
     }
    callback(null, request);
};
```

------
#### [ Python ]

```
from urllib.parse import parse_qs

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    # Reads query string to check if custom origin should be used, and
    # if true, sets custom origin properties

    params = {k: v[0] for k, v in parse_qs(request['querystring']).items()}

    if params.get('useCustomOrigin') == 'true':
            # Set custom origin fields
            request['origin'] = {
                'custom': {
                    'domainName': 'www.example.com',
                    'port': 443,
                    'protocol': 'https',
                    'path': '',
                    'sslProtocols': ['TLSv1', 'TLSv1.1'],
                    'readTimeout': 5,
                    'keepaliveTimeout': 5,
                    'customHeaders': {}
                }
            }
            request['headers']['host'] = [{'key': 'host', 'value': 'www.example.com'}]

    return request
```

------

### 예시: 오리진 요청 트리거를 사용하여 하나의 Amazon S3 버킷에서 다른 버킷으로 점진적으로 트래픽 전송
<a name="lambda-examples-content-based-gradual-traffic-transfer"></a>

이 함수는 통제된 방법을 통해 하나의 Amazon S3 버킷에서 다른 버킷으로 점진적으로 트래픽을 전송하는 방법을 보여 줍니다.

------
#### [ Node.js ]

```
'use strict';

    function getRandomInt(min, max) {
        /* Random number is inclusive of min and max*/
        return Math.floor(Math.random() * (max - min + 1)) + min;
 }

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const BLUE_TRAFFIC_PERCENTAGE = 80;

    /**
      * This Lambda function demonstrates how to gradually transfer traffic from
      * one S3 bucket to another in a controlled way.
      * We define a variable BLUE_TRAFFIC_PERCENTAGE which can take values from
      * 1 to 100. If the generated randomNumber less than or equal to BLUE_TRAFFIC_PERCENTAGE, traffic
      * is re-directed to blue-bucket. If not, the default bucket that we've configured
      * is used.
      */

    const randomNumber = getRandomInt(1, 100);

if (randomNumber <= BLUE_TRAFFIC_PERCENTAGE) {
         const domainName = 'blue-bucket.s3.amazonaws.com';
         request.origin.s3.domainName = domainName;
         request.headers['host'] = [{ key: 'host', value: domainName}];
     }
    callback(null, request);
};
```

------
#### [ Python ]

```
import math
import random

def getRandomInt(min, max):
    # Random number is inclusive of min and max
    return math.floor(random.random() * (max - min + 1)) + min

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    BLUE_TRAFFIC_PERCENTAGE = 80

    '''
    This Lambda function demonstrates how to gradually transfer traffic from
    one S3 bucket to another in a controlled way.
    We define a variable BLUE_TRAFFIC_PERCENTAGE which can take values from
    1 to 100. If the generated randomNumber less than or equal to BLUE_TRAFFIC_PERCENTAGE, traffic
    is re-directed to blue-bucket. If not, the default bucket that we've configured
    is used.
    '''

    randomNumber = getRandomInt(1, 100)

    if randomNumber <= BLUE_TRAFFIC_PERCENTAGE:
        domainName = 'blue-bucket.s3.amazonaws.com'
        request['origin']['s3']['domainName'] = domainName
        request['headers']['host'] = [{'key': 'host', 'value': domainName}]

    return request
```

------

### 예시: 오리진 요청 트리거를 사용하여 국가 헤더를 기반으로 오리진 도메인 이름 변경
<a name="lambda-examples-content-based-geo-header"></a>

이 함수는 `CloudFront-Viewer-Country` 헤더를 기반으로 원본 도메인 이름을 변경하여 뷰어의 국가에 더 가까운 원본에서 콘텐츠를 제공하는 방법을 보여줍니다.

배포에서 이 기능을 구현하면 다음과 같은 이점을 누릴 수 있습니다.
+ 지정된 리전이 최종 사용자의 국가와 더욱 근접할 때 지연 시간이 감소
+ 데이터가 요청이 온 곳과 동일한 국가에 있는 오리진에서 서비스되는지 확인함으로써 데이터 주권을 제공

이 함수를 사용하려면 `CloudFront-Viewer-Country` 헤더를 기반으로 캐시하도록 배포를 구성해야 합니다. 자세한 내용은 [선택한 요청 헤더 기반의 캐시](DownloadDistValuesCacheBehavior.md#DownloadDistValuesForwardHeaders) 섹션을 참조하세요.

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
     const request = event.Records[0].cf.request;
     
  if (request.headers['cloudfront-viewer-country']) {
         const countryCode = request.headers['cloudfront-viewer-country'][0].value;
         if (countryCode === 'GB' || countryCode === 'DE' || countryCode === 'IE' ) {
             const domainName = 'eu.example.com';
             request.origin.custom.domainName = domainName;
             request.headers['host'] = [{key: 'host', value: domainName}];
         } 
     }
     
    callback(null, request);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    viewerCountry = request['headers'].get('cloudfront-viewer-country')
    if viewerCountry:
        countryCode = viewerCountry[0]['value']
        if countryCode == 'GB' or countryCode == 'DE' or countryCode == 'IE':
            domainName = 'eu.example.com'
            request['origin']['custom']['domainName'] = domainName
            request['headers']['host'] = [{'key': 'host', 'value': domainName}]
    return request
```

------

## 오류 상태 업데이트 - 예시
<a name="lambda-examples-update-error-status-examples"></a>

다음 예시에서는 Lambda@Edge를 사용하여 사용자에게 반환되는 오류의 상태를 변경하는 방법에 대한 지침을 제공합니다.

**Topics**
+ [예시: 오리진 응답 트리거를 사용하여 오류 상태 코드를 200으로 업데이트](#lambda-examples-custom-error-static-body)
+ [예시: 오리진 응답 트리거를 사용하여 오류 상태 코드를 302로 업데이트](#lambda-examples-custom-error-new-site)

### 예시: 오리진 응답 트리거를 사용하여 오류 상태 코드를 200으로 업데이트
<a name="lambda-examples-custom-error-static-body"></a>

이 함수는 응답 상태를 200으로 업데이트하고 정적 본문 콘텐츠를 생성하여 다음 시나리오에서 최종 사용자에게 반환하는 방법을 보여줍니다.
+ 함수가 오리진 응답에서 트리거됩니다.
+ 오리진 서버의 응답 상태는 오류 상태 코드(4xx 또는 5xx)입니다.

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    const response = event.Records[0].cf.response;

    /**
     * This function updates the response status to 200 and generates static
     * body content to return to the viewer in the following scenario:
     * 1. The function is triggered in an origin response
     * 2. The response status from the origin server is an error status code (4xx or 5xx)
     */

    if (response.status >= 400 && response.status <= 599) {
        response.status = 200;
        response.statusDescription = 'OK';
        response.body = 'Body generation example';
    }

    callback(null, response);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):
    response = event['Records'][0]['cf']['response']

    '''
    This function updates the response status to 200 and generates static
    body content to return to the viewer in the following scenario:
    1. The function is triggered in an origin response
    2. The response status from the origin server is an error status code (4xx or 5xx)
    '''

    if int(response['status']) >= 400 and int(response['status']) <= 599:
        response['status'] = 200
        response['statusDescription'] = 'OK'
        response['body'] = 'Body generation example'
    return response
```

------

### 예시: 오리진 응답 트리거를 사용하여 오류 상태 코드를 302로 업데이트
<a name="lambda-examples-custom-error-new-site"></a>

이 함수는 HTTP 상태 코드를 302로 업데이트하여 다른 오리진이 구성된 다른 경로(캐시 동작)로 리디렉션하는 방법을 보여줍니다. 다음을 참조하십시오.
+ 함수가 오리진 응답에서 트리거됩니다.
+ 오리진 서버의 응답 상태는 오류 상태 코드(4xx 또는 5xx)입니다.

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    const response = event.Records[0].cf.response;
    const request = event.Records[0].cf.request;

    /**
     * This function updates the HTTP status code in the response to 302, to redirect to another
     * path (cache behavior) that has a different origin configured. Note the following:
     * 1. The function is triggered in an origin response
     * 2. The response status from the origin server is an error status code (4xx or 5xx)
     */

    if (response.status >= 400 && response.status <= 599) {
        const redirect_path = `/plan-b/path?${request.querystring}`;

        response.status = 302;
        response.statusDescription = 'Found';

        /* Drop the body, as it is not required for redirects */
        response.body = '';
        response.headers['location'] = [{ key: 'Location', value: redirect_path }];
    }

    callback(null, response);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):
    response = event['Records'][0]['cf']['response']
    request = event['Records'][0]['cf']['request']

    '''
    This function updates the HTTP status code in the response to 302, to redirect to another
    path (cache behavior) that has a different origin configured. Note the following:
    1. The function is triggered in an origin response
    2. The response status from the origin server is an error status code (4xx or 5xx)
    '''

    if int(response['status']) >= 400 and int(response['status']) <= 599:
        redirect_path = '/plan-b/path?%s' % request['querystring']

        response['status'] = 302
        response['statusDescription'] = 'Found'

        # Drop the body as it is not required for redirects
        response['body'] = ''
        response['headers']['location'] = [{'key': 'Location', 'value': redirect_path}]

    return response
```

------

## 요청 본문 액세스 - 예시
<a name="lambda-examples-access-request-body-examples"></a>

다음 예시에서는 Lambda@Edge를 사용하여 POST 요청을 처리하는 방법을 보여줍니다.

**참고**  
이러한 예제를 사용하려면 배포의 Lambda 함수 연결에서 *본문 포함* 옵션을 활성화해야 합니다. 기본적으로 활성화되어 있지 않습니다.  
CloudFront 콘솔에서 이 설정을 활성화하려면 **Lambda 함수 연결(Lambda Function Association)**에서 **본문 포함(Include Body)** 확인란을 선택하세요.
CloudFront API 또는 CloudFormation에서 이 설정을 활성화하려면 `LambdaFunctionAssociation`에서 `IncludeBody` 필드를 `true`로 설정합니다.

**Topics**
+ [예시: 요청 트리거를 사용하여 HTML 양식 읽기](#lambda-examples-access-request-body-examples-read)
+ [예시: 요청 트리거를 사용하여 HTML 양식 수정](#lambda-examples-access-request-body-examples-replace)

### 예시: 요청 트리거를 사용하여 HTML 양식 읽기
<a name="lambda-examples-access-request-body-examples-read"></a>

이 기능은 "문의처" 양식 등과 같은 HTML 양식(웹 양식)으로 인해 생성된 POST 요청의 본문을 처리할 수 있는 방법을 보여줍니다. 예를 들어, 다음과 같은 HTML 양식이 있을 수 있습니다.

```
<html>
  <form action="https://example.com" method="post">
    Param 1: <input type="text" name="name1"><br>
    Param 2: <input type="text" name="name2"><br>
    input type="submit" value="Submit">
  </form>
</html>
```

예를 들어, 다음 함수는 CloudFront 최종 사용자 요청 또는 오리진 요청 시 실행되어야 합니다.

------
#### [ Node.js ]

```
'use strict';

const querystring = require('querystring');

/**
 * This function demonstrates how you can read the body of a POST request 
 * generated by an HTML form (web form). The function is triggered in a
 * CloudFront viewer request or origin request event type.
 */

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;

    if (request.method === 'POST') {
        /* HTTP body is always passed as base64-encoded string. Decode it. */
        const body = Buffer.from(request.body.data, 'base64').toString();
 
        /* HTML forms send the data in query string format. Parse it. */
        const params = querystring.parse(body);
 
        /* For demonstration purposes, we only log the form fields here.
         * You can put your custom logic here. For example, you can store the 
         * fields in a database, such as Amazon DynamoDB, and generate a response
         * right from your Lambda@Edge function.
         */
        for (let param in params) {
            console.log(`For "${param}" user submitted "${params[param]}".\n`);
        }
    }
    return callback(null, request);
};
```

------
#### [ Python ]

```
import base64
from urllib.parse import parse_qs

'''
Say there is a POST request body generated by an HTML such as:

<html>
<form action="https://example.com" method="post">
    Param 1: <input type="text" name="name1"><br>
    Param 2: <input type="text" name="name2"><br>
    input type="submit" value="Submit">
</form>
</html>

'''

'''
This function demonstrates how you can read the body of a POST request 
generated by an HTML form (web form). The function is triggered in a
CloudFront viewer request or origin request event type.
'''

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    if request['method'] == 'POST':
        # HTTP body is always passed as base64-encoded string. Decode it
        body = base64.b64decode(request['body']['data'])

        # HTML forms send the data in query string format. Parse it
        params = {k: v[0] for k, v in parse_qs(body).items()}

        '''
        For demonstration purposes, we only log the form fields here.
        You can put your custom logic here. For example, you can store the
        fields in a database, such as Amazon DynamoDB, and generate a response
        right from your Lambda@Edge function.
        '''
        for key, value in params.items():
            print("For %s use submitted %s" % (key, value))
            
    return request
```

------

### 예시: 요청 트리거를 사용하여 HTML 양식 수정
<a name="lambda-examples-access-request-body-examples-replace"></a>

이 기능은 "문의처" 양식 등과 같은 HTML 양식(웹 양식)으로 인해 생성된 POST 요청의 본문을 수정할 수 있는 방법을 보여줍니다. 이 함수는 CloudFront 최종 사용자 요청 또는 오리진 요청 시 실행됩니다.

------
#### [ Node.js ]

```
'use strict';
				
const querystring = require('querystring');

exports.handler = (event, context, callback) => {
    var request = event.Records[0].cf.request;
    if (request.method === 'POST') {
        /* Request body is being replaced. To do this, update the following
        /* three fields:
         *    1) body.action to 'replace'
         *    2) body.encoding to the encoding of the new data.
         *
         *       Set to one of the following values:
         *
         *           text - denotes that the generated body is in text format.
         *               Lambda@Edge will propagate this as is.
         *           base64 - denotes that the generated body is base64 encoded.
         *               Lambda@Edge will base64 decode the data before sending
         *               it to the origin.
         *    3) body.data to the new body.
         */
        request.body.action = 'replace';
        request.body.encoding = 'text';
        request.body.data = getUpdatedBody(request);
    }
    callback(null, request);
};

function getUpdatedBody(request) {
    /* HTTP body is always passed as base64-encoded string. Decode it. */
    const body = Buffer.from(request.body.data, 'base64').toString();

    /* HTML forms send data in query string format. Parse it. */
    const params = querystring.parse(body);

    /* For demonstration purposes, we're adding one more param.
     *
     * You can put your custom logic here. For example, you can truncate long
     * bodies from malicious requests.
     */
    params['new-param-name'] = 'new-param-value';
    return querystring.stringify(params);
}
```

------
#### [ Python ]

```
import base64
from urllib.parse import parse_qs, urlencode

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    if request['method'] == 'POST':
        '''
        Request body is being replaced. To do this, update the following
        three fields:
            1) body.action to 'replace'
            2) body.encoding to the encoding of the new data.
        
            Set to one of the following values:
        
                text - denotes that the generated body is in text format.
                    Lambda@Edge will propagate this as is.
                base64 - denotes that the generated body is base64 encoded.
                    Lambda@Edge will base64 decode the data before sending
                    it to the origin.
            3) body.data to the new body.
        '''
        request['body']['action'] = 'replace'
        request['body']['encoding'] = 'text'
        request['body']['data'] = getUpdatedBody(request)
    return request

def getUpdatedBody(request):
    # HTTP body is always passed as base64-encoded string. Decode it
    body = base64.b64decode(request['body']['data'])

    # HTML forms send data in query string format. Parse it
    params = {k: v[0] for k, v in parse_qs(body).items()}

    # For demonstration purposes, we're adding one more param

    # You can put your custom logic here. For example, you can truncate long
    # bodies from malicious requests
    params['new-param-name'] = 'new-param-value'
    return urlencode(params)
```

------