AWS 기술 블로그

전자상거래 웹 사이트에서 CloudFront 및 CloudFront Functions를 활용한 방문자 우선 순위 지정하기

이 글은 Networking & Content Delivery Blog에 게시된 Visitor Prioritization on e-Commerce Websites with CloudFront and CloudFront Functions by Gabin Lee, Akira Mori, and Yoshihisa Nakatani을 한국어로 번역 및 편집하였습니다.

5년 전 이전 게시물(전자상거래 웹 사이트에서 CloudFront 및 Lambda@Edge를 활용 방문자 우선 순위 지정하기)이 작성되었을 때 방문자 우선 순위 지정은 비교적 새로운 개념이었습니다. 그 이후로 우리는 특히 게임 및 미디어 산업에서 트래픽 쉐이핑, 스로틀링 및 요청 우선 순위 지정에 대한 엄청난 필요성을 확인했습니다. 물론 전자상거래 사이트는 여전히 일일 판매 및 캠페인을 위해 이 기능을 필요로 합니다.

2021년 5월에 AWS에서 CloudFront Functions를 발표한 후, 고객은 다음과 같은 여러 요청과 질문을 했습니다.

  • 이 사용 사례에서 CloudFront Functions를 사용할 수 있습니까?
  • 만약 사용할 수 있다면, 장단점은 무엇입니까?
  • 가격 구조, 전제 조건 등은 무엇입니까?

첫번째 질문에 대한 대답은 ‘예’이며, CloudFront Functions는 이 영역에서 도움이 됩니다. 이 새로운 기능은 단순한 헤더 조작 뿐만 아니라 엣지 컴퓨팅을 위한 것이기 때문입니다. 따라서 방문자 우선순위 지정 사례에도 적합합니다.

AWS는 또한 2021년 4월에 AWS WAF Bot Control을 출시했습니다. 이 기능은 악성 봇으로부터 생성되는 원치 않는 트래픽과 액세스로부터 고객의 사이트를 보호합니다. 고객의 질문 중에는 CloudFront Functions과 함께 AWS WAF Bot Control을 활용하여 어떻게 더 나은 사용자 경험을 제공할 수 있는지 에 관한 것도 포함되어 있었습니다. 따라서 우리는 바로 지금이 CloudFront Functions을 활용한 방문자 우선 순위 지정 기능을 다시 한번 소개하는 동시에 어떻게 AWS WAF Bot Control과 통합될 수 있는지를 보여줄 좋은 기회라는 것을 깨달았습니다.

Lambda@Edge/CloudFront Functions의 호출 지점

CloudFront Functions는 뷰어 단계에 대한 요청/응답 이벤트에만 작동하는 반면 Lambda@Edge는 뷰어 및 오리진 단계의 이벤트에 모두 작동합니다. 따라서 CloudFront Functions은 뷰어 요청 단계에서 방문자 우선순위 지정 기능을 구현해야 합니다. 해당 기능을 통해 오리진 애플리케이션을 보호할 수 있습니다.

CloudFront Function의 가격은 Lambda@Edge보다 훨씬 저렴하며 동시성 제약 조건은 Lambda@Edge보다 높습니다(할당량 문서 참조). 그러나 Lambda@Edge는 여전히 오리진 단계의 요청/응답 조작 기능을 제공합니다. 또한 CloudFront Functions와 비교할 때 더 유연한 컴퓨팅 기능을 제공합니다(CloudFront Functions 게시물 참조). 따라서 특히 복잡한 사용 사례의 경우 CloudFront Functions 외에 Lambda@Edge를 사용해야 할 수도 있습니다.

그림 1. Lambda@Edge 및 CloudFront Functions의 이벤트 호출 지점

방문자 우선 순위 지정 워크플로우 개요(적법한 또는 일반적인 사용 사례)

다음 다이어그램은 일반적인 방문자 우선 순위 지정 워크플로우을 나타냅니다. 각 요청에 대해 CloudFront Functions는 요청이 적법한지 여부를 확인한 다음 오리진으로 이동할 수 있는지 여부를 결정합니다.

그림 2. 적법한 사용자를 위한 방문자 우선 순위 지정 워크플로우

  1. 클라이언트는 캠페인 중에 전자상거래 사이트 액세스를 요청합니다.
  2. CloudFront Function은 방문자 우선 순위 지정 기능을 실행합니다.
  3. 적법한 요청은 오리진(이 다이어그램에서는 Amazon EC2 + Amazon Aurora)으로 이동하여 요청을 처리합니다.

방문자 우선순위 지정 워크플로우 개요(대기실)

방문자 우선 순위 지정을 켜야 하는 경우 CloudFront Functions는 요청이 오리진으로 갈 수 있는지 또는 대기실로 라우팅되어야 하는지 확인합니다. 위에서 소개했듯이 CloudFront Functions는 뷰어 단계에서만 작동하며 현재 오리진을 Amazon Simple Storage Service(Amazon S3)와 같은 다른 오리진으로 변경할 수 없습니다. 대기실로 라우팅하기 위한 URL 경로만 변경할 수 있습니다. 예를 들어, 요청된 경로를 데이터베이스 또는 백엔드 리소스에 연결된 원래 워크로드가 아닌 정적 파일로 구성된 “waitingroom.html”로 수정할 수 있습니다. 이 접근 방식은 EC2 및 Aurora 데이터베이스에서 생성되는 부하를 줄이는 데 많은 도움이 될 수 있지만 오리진 워크로드 오프로딩을 100% 제공하지는 않습니다.

그림 3. 대기실 트리거 시 방문자 우선순위 지정 워크플로우

따라서 이와 같은 경우에는 HTTP 리디렉션을 사용하여 오리진 부하를 오프로드할 수 있습니다. CloudFront는 경로 조건(동작)에 따라 오리진을 변경할 수 있습니다. 또한 일부 지정된 경로에 대해 Amazon S3 오리진을 설정하면 모든 대기실 트래픽을 Amazon S3로 라우팅할 수 있습니다. 또한 이 리디렉션된 경로에 다른 TTL을 설정하여 대기실 콘텐츠의 TTL을 다르게 제어할 수 있습니다.

그림 4. 대기실 트래픽이 S3로 분리될 때 방문자 우선 순위 지정 워크플로우

페이지 기반이 아닌 또 다른 리디렉션 기술은 도메인 기반입니다. 여기에 다른 도메인 기법을 도입한 것이 이상하게 보일 수 있습니다. 그러나 긴급한 이유로 오리진을 폐쇄하려는 경우 완전히 독립된 대기실을 갖는 것이 많은 도움이 될 수 있습니다. 따라서 시나리오에 따라 대기실 전환을 위해 두 가지를 모두 고려 및 구현하는 것이 좋습니다. 좋은 소식은 CloudFront Function이 이러한 요구 사항을 지원한다는 것입니다. 우리는 이미 다음의 샘플코드에서 기본 동작을 구현해 놓았으며, 추가 요구 사항을 구현하기 위하여 해당 코드를 커스터마이징할 수 있습니다.

그림 5. 대기실 트래픽이 다른 도메인으로 분리될 때 방문자 우선 순위 지정 워크플로우

AWS WAF Bot Control과 통합

AWS WAF Bot Control은 악성 봇 트래픽으로부터 사이트를 보호합니다. 방문자 우선순위 지정 솔루션을 사용하는 동시에 이러한 유형의 봇 트래픽을 최소화하려는 경우에 특히 AWS WAF 및 Bot Control을 사용하는 것이 좋습니다. Bot Control 이전에도 AWS WAF는 아래와 같은 IP 목록 기반 Bot 완화 기능을 제공했습니다.

이러한 규칙을 사용하여 악의적인 요청을 차단할 수 있습니다. 또한 이러한 IP 규칙 외에도 자체 IP 목록 또는 파트너 제공 규칙 세트를 사용할 수 있습니다.
AWS WAF Bot Control은 이러한 IP 기반 규칙과 함께 작동할 수 있으며, 봇 트래픽에 대해 보다 세분화된 제어를 제공할 수 있습니다. 일반적으로 검색 엔진 액세스는 차단하지 않습니다. 그러나 방문자 우선 순위 지정을 사용하려는 경우 합법적인 검색 엔진 트래픽도 차단할 수 있습니다. 물론 항상 대부분의 봇 기반 트래픽을 차단하고 싶을 것입니다. 방문자 우선 순위 지정 솔루션과 함께 AWS WAF Bot Control을 사용하면 봇 기반 트래픽을 제어할 수 있습니다. 그러면 높은 트래픽이 발생하는 상황에서도 훨씬 더 나은 사용자 경험을 제공할 수 있습니다.

그림 6. AWS WAF Bot Control과 방문자 우선 순위 지정 워크플로우 결합

  1. 악성 봇이 전자상거래 사이트 액세스를 요청합니다.
  2. AWS WAF는 요청을 확인한 다음, 이 요청이 악성 봇에서 오는지 감지합니다. 그 후에 AWS WAF Bot Control 설정(예: 403 오류 상태 코드)에 따른 응답을 제공합니다.
  3. CloudFront는 Bot Control의 설정에 따라 응답을 반환합니다.

AWS WAF는 사용자 지정 응답을 지원합니다. 이 설정에 따라 AWS WAF는 403뿐만 아니라, 200을 포함한 다른 적절한 응답 코드를 반환합니다.

솔루션 배포 전제 조건

다음 전제 조건이 필요합니다.

  • 새 AWS 계정을 생성하거나 기존 계정을 사용합니다.
  • 새 CloudFront 배포를 생성하거나 기존 배포를 사용합니다.
  • 경로 수정 동작을 확인하려면 오리진에 두 개 이상의 HTML 파일을 설정하십시오.
    • 이 예에서는 “/index.html”을 기본 페이지로 사용하고 “/waitingroom.html”을 대기실 페이지로 사용합니다.

새로운 CloudFront Function 생성

1단계: Function 코드 사용자 정의

  • 방문자 우선 순위 지정 CloudFront Function은 다음 JavaScript 코드로 제공됩니다.
/*
 * A flag indicating whether the origin is ready to accept traffic.
 * Unlike Lambda@Edge, CloudFront Functions doesn't support network call.
 * So if you want to change this value, you need to modify then re-deploy
 * this function.
 */
var originAcceptingTraffic = true;

/*
 * The origin hit rate (a value between 0 and 1) specifies a percentage of
 * users that go directly to the origin, while the rest go to
 * a "waiting room." Premium users always go to the origin.  if you want to
 * change this value, you need to modify then re-deploy this function.
 */
var originHitRate = 0.3;

/*
 * Waiting Room Redirect URL
 */

var FullClose = `https://FullCLOSE SITE` // Change the redirect URL to your choice

function handler(event) {
    var request = event.request;
//    var response = event.response;
    var uri = event.request.uri;
    var cookies = event.request.cookies;
    var premiumUserCookieValue = 'some-secret-cookie-value';


    if(!originAcceptingTraffic) {
        console.log("Origin is not accepting any traffic. " +
                    "All requests go to the Full close waiting room.");
        var response = {
                 statusCode: 302,
                 statusDescription: 'Found',
                 headers:
                         { "location": { "value": FullClose } }
                     }
        return response;
    }

    // Check Whether Cookie is available or not.
    // in this sample it checks premium-user-cookie. This name is case
    // sensitive, so if you use upper charactor, please modify name parameter.
    if(cookies.hasOwnProperty("premium-user-cookie") && cookies["premium-user-cookie"].value === premiumUserCookieValue){
        console.log(`Verified Permium user cookie, this request goes to Origin cause it has Cookie with a valid secret value of "${premiumUserCookieValue}".`);
        return request;
      }

    // Lotterly to check go to origin
    if (Math.random() >= originHitRate) {
        console.log("An unlucky user goes to the waiting room.");
        request.uri = '/waitingroom.html';
        return request;
    }
    console.log("A lucky user goes to the origin.");
    return request;
};
  • 텍스트 편집기에서 파일을 연 다음, 요구 사항에 맞게 코드의 다음 필드를 업데이트합니다.
    • originAcceptingTraffic: 기본값은 true입니다. 오리진이 트래픽을 허용할 준비가 되지 않은 경우 이를 false로 설정하십시오.
    • originHitRate: 0과 1 사이의 숫자입니다. 이 값은 웹 사이트로 이동할 수 있는 일반 고객 요청의 백분율을 나타냅니다.
    • FullClose: 오리진을 사용할 수 없을 때(‘originAcceptingTraffic = false’) 사용할 리디렉션 URL.

2단계: CloudFront Function 생성

  • CloudFront 콘솔에서 CloudFront Function을 생성하고 1단계에서 편집한 Function 코드를 복사합니다. 그런 다음 콘솔의 코드 편집기에 붙여넣어 편집기의 기본 코드를 바꿉니다.
  • Save changes 버튼을 선택하여 개발 단계에 배포합니다.

Function 테스트

개발 단계에서 Function이 배포되면 해당 Function 코드가 잘 작동하는지 확인할 수 있습니다. CloudFront Function 테스트 콘솔을 기반으로 검증이 필요한 다양한 테스트 시나리오를 시뮬레이션할 수 있습니다.

테스트 사례 1: 프리미엄 쿠키를 사용한 요청

  • 이전 단계에서 생성한 Function 페이지에서 Test 탭을 선택합니다.
  • 뷰어 요청 단계에서 Function이 트리거되어야 하므로 Event Type 필드에서 Viewer Request을 선택합니다.
  • 코드는 현재 개발 단계에 배포되어 있으므로 Stage 필드에서 Development를 선택합니다.
  • 다음과 같이 요청 매개변수를 입력합니다.
    • HTTP Method: GET
    • URL Path: 기본 페이지 경로를 설정합니다(예: /index.html).
    • IP address: 비어 있는 상태로 둡니다.
  • 다음과 같이 쿠키를 추가합니다.
    • Name: premium-user-cookie
    • Value: some-secret-cookie-value
    • Attributes: 비어 있는 상태로 둡니다.
  • Test function” 버튼을 선택합니다.
  • Output 섹션의 uri 값이 항상 기본 페이지 경로임을 알 수 있습니다.

그림 7. 프리미엄 쿠키로 요청할 때 CloudFront Functions 응답

테스트 사례 2: 프리미엄 쿠키 없이 요청

  • 테스트 케이스 1에서 쿠키 설정만 제거한 후 다시 테스트를 진행합니다.
  • 출력 섹션의 uri 값이 기본 페이지 경로 또는 /waitingroom.html로 임의로 변경된 것을 볼 수 있습니다. 코드에서 originHitRate 값을 조정하여 각 페이지의 응답 비율을 제어할 수 있습니다.

그림 8. 프리미엄 쿠키 없이 요청 시 CloudFront Function의 응답

코드 내 다양한 변수의 값을 조정하여 Function이 의도한 대로 작동하는지 확인할 수도 있습니다. 그러나 코드를 업데이트하면 개발 단계에만 영향을 미친다는 점에 유의해야 합니다. 라이브 단계에서 코드를 업데이트하려면 Function를 게시해야 합니다.

Function를 CloudFront 배포와 연결

다양한 시나리오를 테스트한 후 Function를 사용할 준비가 되면 Function를 게시할 차례입니다.

1단계: 라이브 단계에 Function 게시

  • Function 페이지에서 Publish 탭을 선택합니다.
  • Publish function 버튼을 선택하여 개발 단계에서 테스트한 코드를 라이브 단계에 게시합니다.

Function를 게시한 후 Function를 CloudFront 배포에서 하나 이상의 캐시 동작과 연결할 수 있습니다.

2단계: Function 연결

  • Add association 버튼을 선택합니다.
    • Distribution: CloudFront 배포 ID 선택
    • Event type: Viewer Request 선택
    • Cache behavior: Default(*)(또는 CloudFront Function를 적용하려는 다른 동작)를 선택합니다.
    • Add association 버튼을 선택합니다.
  • Function이 CloudFront에 완전히 배포될 때까지 기다립니다.

이제 CloudFront Function이 CloudFront 배포에 대해 구성한 동작과 연결되고 업데이트가 전 세계적으로 AWS 위치에 게시되었습니다.

배포 테스트

배포가 완료되면 라이브 단계에서 CloudFront Function이 제대로 작동하는지 확인할 수 있습니다.
먼저 “index.html” 및 “waitingroom.html”에 대한 ETag 헤더를 확인해야 합니다.

$ curl -i https://[your origin endpoint]/index.html
HTTP/1.1 200 OK
x-amz-id-2: N4+xzqRSqQY+ZuAJkmRL6xAAesBVjsg20TVGYlzzeMmbJ4pdejuGh/pVItKvIshcpSOcthC2zMc=
x-amz-request-id: X6TM23EJY639X5R5
Date: Fri, 13 Aug 2021 06:25:26 GMT
Last-Modified: Tue, 20 Jul 2021 08:16:11 GMT
ETag: "d85834344bc3cb3267806005e1f9bf79"
Accept-Ranges: bytes
Content-Type: text/html
Server: AmazonS3
Content-Length: 10

$ curl -i https://[your origin endpoint]/waitingroom.html
HTTP/1.1 200 OK
x-amz-id-2: c8J1YAkV4n79eVxA0OImCec+KSnUDFHnjTCBE9hZukYRDwKnpRePeNHlsw/jfHDFd2upiItANrU=
x-amz-request-id: 5QKH9WDW0R453FH5
Date: Fri, 13 Aug 2021 06:26:18 GMT
Last-Modified: Tue, 20 Jul 2021 08:16:12 GMT
ETag: "87a8e81f406d0e3252d1e045d6c247f9"
Accept-Ranges: bytes
Content-Type: text/html
Server: AmazonS3
Content-Length: 12

그런 다음 CloudFront 테스트 콘솔에서 수행한 테스트 시나리오를 다시 한 번 시도할 수 있습니다.

테스트 사례 1: 프리미엄 쿠키를 사용한 요청(30회 시도)

$ for i in `seq 1 30`; do echo $i; curl -i --cookie "premium-user-cookie=some-secret-cookie-value" https://[CloudFront endpoint]/index.html 2>&1 | grep etag >> curlResultWithCookie.log; done; cat curlResultWithCookie.log |sort |uniq -c;

30 etag: "d85834344bc3cb3267806005e1f9bf79"  <<< index.html's etag

쿠키가 포함된 경우 대기실로 리디렉션되지 않고 “index.html”이 모든 요청에 응답하는 것을 볼 수 있습니다.

테스트 사례 2: 프리미엄 쿠키 없이 요청(30회 시도)

$ for i in `seq 1 30`; do echo $i; curl -i https://[CloudFront endpoint]/index.html 2>&1 | grep etag >> curlResultWithoutCookie.log; done; cat curlResultWithoutCookie.log |sort |uniq -c

  21 etag: "87a8e81f406d0e3252d1e045d6c247f9"  <<< waitingroom.html's etag
   9 etag: "d85834344bc3cb3267806005e1f9bf79"  <<< index.html's etag

반대로 쿠키가 포함되지 않은 경우 응답에서 index.html과 waitroom.html이 혼합된 것을 볼 수 있습니다. 대기실로의 리디렉션 비율은 CloudFront Function 코드에 설정된 “originHitRate” 변수를 통해 제어할 수 있습니다.

AWS CloudFormation 템플릿 및 AWS Cloud Development Kit

즉시 테스트하려는 경우 이 템플릿을 사용하여 AWS CloudFormation 스택을 생성할 수 있습니다.
이 템플릿은 아래의 자원들을 생성합니다.

  • Origin Access Identity가 있는 S3 버킷
  • 위의 Amazon S3 오리진을 사용하는 CloudFront 배포
  • CloudFront Functions

AWS Cloud Development Kit(AWS CDK)에 익숙하다면 추가 구현이 포함되어 있는 Github 리포지토리의 AWS CDK 샘플을 활용할 수도 있습니다.
이 샘플은 Amazon CloudWatch 지표(예: 분당 요청 수)에 따라 대기실로 보낼지 여부를 자동으로 켜고 끄는 방법을 보여줍니다.

그림 9. 방문자 우선순위 전환기 솔루션에 대한 아키텍처

결론

CloudFront Functions는 Lambda@Edge와 유사하거나 더 나은 방문자 우선순위 솔루션 기능을 제공합니다. CloudFront Functions는 낮은 대기 시간과 비용으로 엄청난 양의 트래픽을 처리할 수 있기 때문입니다. 반면에 Lambda@Edge는 외부 네트워크 통합, 라이브러리 지원 및 훨씬 더 긴 컴퓨팅 기능과 같은 더 복잡한 사용 사례를 지원할 수 있습니다. 또한 경우에 따라서 CloudFront Functions와 Lambda@Edge를 모두 사용할 수도 있습니다.

AWS WAF 및 Bot Control은 원치 않는 트래픽 방지를 포함하여 추가적인 봇 완화 기능을 제공합니다. 또한 이러한 서비스를 사용하면 방문자 우선 순위 지정 워크로드가 아니더라도 사이트에 도움이 됩니다. 보안 및 가시성은 항상 사이트에 도움이 되며 지속적인 모니터링은 사이트의 가용성에 도움이 됩니다. 이 블로그의 지침이 사이트의 가용성, 확장성 및 성능을 개선하는 데 도움이 되기를 바랍니다.

Gabin Lee

Gabin Lee

이갑인 솔루션즈 아키텍트는 다양한 AWS 엣지 서비스를 활용하여 확장 가능하고 탄력적이며 안전한 아키텍처를 구축하는 것에 열정적이며, 특히 CloudFront, Global Accelerator, WAF, Shield 등과 같은 AWS 엣지 서비스를 활용하고 있는 한국 고객들을 집중적으로 지원하고 있습니다.