AWS 기술 블로그

프로덕션 트래픽을 사용하여 Amazon CloudFront의 CloudFront 함수 테스트하기

이 글은 AWS Networking & Content Delivery 블로그의 Writing and testing CloudFront Functions with production traffic by Julian Ju and Yujin Jeong을 한국어 번역 및 편집하였습니다.

웹 애플리케이션을 유지 관리하는 동안 짧은 지연 시간으로 실행되어야 하는 간단한 로직을 구축해야 할 때가 있습니다. 예를 들어 조건에 따라 웹사이트 리디렉션을 설정하거나 들어오는 요청 헤더를 빠르게 확인해야 할 수 있습니다.

CloudFront 함수는 JavaScript로 경량 함수를 작성하여 응답 헤더를 추가, 수정할 수 있습니다. 그리고 요청을 신규 URL로 리디렉션하거나 사용자 지정 응답을 생성할 수도 있습니다. 그러므로 위에 언급한 사례에 이상적으로 사용될 수 있습니다.

CloudFront 함수에서 제공하는 스크립트 기반 접근 방식은 높은 유연성을 제공하지만, 효과적인 코드를 작성하고 올바르게 테스트해야 하는 책임도 따릅니다. 코드를 개발할 때 모범 사례를 따르면 엣지 컴퓨팅 기능을 가장 효율적으로 사용하고 런타임 오류의 위험을 최소화할 수 있습니다.

이 게시글에서는 CloudFront 함수 코드를 작성하고 테스트할 때 고려해야 할 몇 가지 사항을 공유합니다. 그리고 실제 HTTP 트래픽을 기반으로 함수를 테스트하는 방법을 알아보겠습니다.

CloudFront 함수 특징

CloudFront 함수는 안전하고 빠르며 비용 효율적인 엣지 컴퓨팅으로 간단한 HTTP 요청이나 응답을 조작하는 데 적합합니다. CloudFront 함수 실행은 Amazon CloudFront가 뷰어로부터 요청을 수신한 후(뷰어 요청), 뷰어에게 응답을 전달하기 전에(뷰어 응답) 트리거 됩니다.

아래 하나의 예제 코드를 통해 CloudFront 함수가 어떤 식으로 구성되는지 살펴보도록 하겠습니다. (GitHub 리포지토리에서 더 많은 예제를 확인할 수 있습니다.)

function handler(event) {
    var request = event.request;
    var headers = request.headers;
    var host = request.headers.host.value;
    var country = 'DE' // Choose a country code
    var newurl = `https://${host}/de/index.html` // Change the redirect URL to your choice 
  
    if (headers['cloudfront-viewer-country']) {
        var countryCode = headers['cloudfront-viewer-country'].value;
        if (countryCode === country) {
            var response = {
                statusCode: 302,
                statusDescription: 'Found',
                headers:
                    { "location": { "value": newurl } }
                }

            return response;
        }
    }
    return request;
}

위 예시를 보면 알 수 있듯이 CloudFront 함수는 handler(event) 함수를 갖는 JavaScript 코드입니다. CloudFront 함수가 CloudFront 배포와 연결되면 CloudFront는 진입점으로 handler 함수를 실행합니다. event 객체에는 이벤트 트리거에 따라 HTTP 요청 메타데이터 또는 응답 메타데이터가 포함됩니다. handler 함수는 반드시 HTTP 요청 또는 응답 객체(HTTP 뷰어 요청의 경우 요청/응답 모두를 반환할 수 있지만 뷰어 응답 이벤트의 경우 HTTP 응답만 허용)를 반환해야 합니다. CloudFront가 handler 함수로부터 반환된 객체를 기반으로 요청에 대한 처리를 지속할 수 있어야 하기 때문입니다. 참고로 CloudFront 함수는 응답 객체의 헤더에만 접근할 수 있지만, Lambda@Edge는 응답 본문까지 접근할 수 있습니다.

그림 1: CloudFront 함수는 반드시 HTTP 요청 또는 응답 객체를 반환해야 한다.

그림 1: CloudFront 함수는 반드시 HTTP 요청 또는 응답 객체를 반환해야 한다.

CloudFront 함수는 지연 시간을 최소화하고 대규모로 운영할 수 있도록 제한된 CPU 시간 내에서 실행되도록 설계되어 있습니다. 각 함수 실행에는 컴퓨팅 자원이 제한되어 있고 이는 컴퓨팅 사용률을 통해 확인할 수 있습니다. 컴퓨팅 사용률은 함수가 실행되는 데 걸린 시간의 최대 허용 시간의 백분율입니다. 함수가 허용된 양을 초과하여 컴퓨팅 자원을 사용하는 경우 CloudFront 함수가 실패할 수 있습니다. 이는 CloudFront가 뷰어에게 오류를 반환하는 결과를 초래합니다. CloudFront 콘솔과 TestFunction API는 테스트 결과의 일부로 컴퓨팅 사용률을 제공하여 테스트 실행 중에 허용된 최대 컴퓨팅 자원의 몇 퍼센트가 사용되었는지 보여줍니다. CloudFront 함수 코드는 컴퓨팅을 100% 사용해서는 안 됩니다. 컴퓨팅 사용률은 변동될 수 있으므로 컴퓨팅 사용률에 여유를 두는 것이 좋습니다. 예를 들어 80% 미만의 컴퓨팅 사용률을 확인하여 20%의 여유분을 가져가는 구조라고 생각할 수 있습니다.

그림 2: CloudFront 콘솔 상에서 확인할 수 있는 CloudFront 함수의 컴퓨팅 사용률

그림 2: CloudFront 콘솔 상에서 확인할 수 있는 CloudFront 함수의 컴퓨팅 사용률

CloudFront 함수 컴퓨팅 사용률은 Amazon CloudWatch의 지표로 확인할 수 있습니다. CloudWatch 지표를 모니터링하여 함수의 컴퓨팅 사용률을 거의 실시간으로 확인할 수 있습니다.

그림 3: CloudFront 함수의 컴퓨팅 사용률을 보여주는 CloudWatch 지표 예시

그림 3: CloudFront 함수의 컴퓨팅 사용률을 보여주는 CloudWatch 지표 예시

CloudFront 함수 테스트

코드 작성을 마치면 몇 가지 테스트 케이스로 코드를 테스트하여 함수가 의도한 대로 작동하는지 확인해야 합니다. 이러한 종류의 테스트를 일반적으로 단위 테스트라고 합니다. 테스트 케이스 중 최소한 하나는 함수가 예상되는 입력값에 대해 올바르게 작동하는지를 검증해야 합니다. 그리고 다른 테스트 케이스에서는 함수가 코너 케이스에서 중단되지 않는지를 확인합니다. CloudFront 함수의 경우 해당 함수가 허용된 컴퓨팅 사용률 범위 내에 있는지도 확인해야 합니다.

콘솔 또는 API로 코드를 테스트한 다음 HTTP 요청의 컨텍스트 데이터(뷰어 응답 이벤트용 함수인 경우 HTTP 응답)를 준비합니다. CloudFront 콘솔은 입력 데이터에 대한 테스트 템플릿을 제공합니다. 그러므로 사용자가 테스트 템플릿에 입력한 값을 기반으로 함수를 빠르게 테스트할 수 있습니다.

그림 4: CloudFront 콘솔에서의 함수 테스트

그림 4: CloudFront 콘솔에서의 함수 테스트

API 호출을 사용할 때는 다음과 같이 컨텍스트 데이터를 JSON 객체로 제공해야 합니다.

{
    "version": "1.0",
    "context": {
        "eventType": "viewer-request"
    },
    "viewer": {
        "ip": "198.51.1.1"
    },
    "request": {
        "method": "GET",
        "uri": "/example.png",
        "headers": {
            "host": {"value": "example.org"}
        }
    }
}

테스트는 테스트를 충족하는 올바른 입력을 만들어 내는 것이라고 볼 수 있습니다. 이를 위해서는 헤더, 쿠키, 쿼리 문자열과 입력값을 넣었을 때의 테스트 결과를 확인해야 합니다. 테스트를 효과적으로 수행하는 방법에는 몇 가지가 있습니다.

가능한 모든 케이스를 테스트하고 테스트 자동화하기

CloudFront 함수 코드는 경량이기 때문에 대부분의 경우 모든 코드 실행 경로에 대해 테스트 케이스를 작성할 수 있어야 합니다. 테스트 케이스가 두 개 이상 있을 수 있습니다. 그러므로 테스트 프로세스를 자동화하여 각 테스트에서 사용할 컨텍스트 데이터를 로드한 다음, API를 호출하는 것이 필요할 수 있습니다. 다음은 테스트에 필요한 여러 개의 컨텍스트 데이터인 JSON 파일들을 로드해서 파일 개수만큼 테스트를 실행하는 예제입니다.

for f in *.json; do aws cloudfront test-function \
  --name ExampleFunction \
  --if-match ETVABCEXAMPLE \
  --event-object fileb://$f \
  --stage DEVELOPMENT; \

done

실제 트래픽으로 테스트하고 컴퓨팅 사용률 모니터링하기

기존 CloudFront 배포에 새로운 CloudFront 함수를 작성하여 연결하는 경우 프로덕션으로 배포하기 전에 실제 트래픽 데이터로 테스트하는 것이 유용합니다. 단위 테스트를 만들 때 예상하지 못했던 코너 케이스를 발견할 수 있기 때문입니다. 예를 들어 일부 클라이언트의 User-Agent 헤더 값이 매우 다르거나 헤더가 누락된 것을 발견할 수 있습니다. 또한 실제 트래픽을 대상으로 함수의 컴퓨팅 사용률을 테스트하는 데 이용할 수도 있습니다. 실제로 코드의 효율성을 높이려면 코드를 작성하기 전에 실제 트래픽 데이터를 검토해야 합니다. 아직 배포되지 않은 새로운 CloudFront 배포를 위한 함수를 작성하는 경우에도 새로 배포할 CloudFront와 유사한 기존 배포에서 테스트할 수 있는 데이터를 확보하는 것이 좋습니다.

프로덕션 트래픽을 기반으로 함수 테스트 생성하기

그렇다면 프로덕션 트래픽을 기반으로 테스트 데이터를 생성하려면 어떻게 해야 할까요? 액세스 로그를 수집하여 테스트를 위한 컨텍스트 데이터를 만들어야 합니다. 액세스 로그를 처리하는 방법에는 여러 가지가 있지만 이 게시글에서는 Amazon Athena를 예로 들어 설명하겠습니다. 뷰어 요청으로 들어오는 referer 헤더와 URL 경로를 읽는 CloudFront 함수를 작성한다고 가정해 보겠습니다. 다음 쿼리를 사용하면 가장 빈번한 조합을 추출할 수 있습니다.

SELECT
count(*) cnt, uri, referrer
-- 실제 사용하는 테이블 이름으로 변경 필요
FROM combined
-- 하루동안(24시간) 발생한 데이터에 대한 필터링
WHERE concat(year, month, day, hour) >= DATE_FORMAT
GROUP BY uri, referrer 
ORDER BY cnt desc
-- 상위 10개 데이터만 추출
limit 10

한 단계 더 나아가 다음과 같이 CloudFront 함수 코드에 대한 자동화된 테스트를 만들 수도 있습니다. 자세한 내용은 샘플 코드를 참조하세요.

> python3 testingCFF.py --function CORS-Preflight
input(url, referer) --> output(status(Err or OK), ComputeUtilization%)
input(/images/sample.jpg, https://example.com/) --> output(OK, 11%)
input(/images/sample2.jpg, https://example.com/) --> output(OK, 15%)
input(/images/sample2.jpg, -) --> output(OK, 14%)
……………

고려사항

이 접근 방식을 사용할 때는 비용을 고려해야 합니다. Athena는 스캔한 데이터의 용량에 비례하여 비용이 발생합니다. 따라서 모든 기존 로그에 대해 쿼리를 실행하는 것은 권장하지 않습니다. 대신 로그를 파티션에 로드하고 로그의 일부만 스캔하도록 쿼리를 제한할 수 있습니다. 만약 CloudFront 배포가 대량의 HTTP 요청과 응답을 전송한다면 낮은 샘플링 비율을 적용한 CloudFront 실시간 로그를 활용할 수도 있습니다.

더불어 CloudFront 표준 액세스 로그에는 제한된 수의 헤더만 있기 때문에, 표준 액세스 로그에 없는 헤더를 사용하려면 실시간 액세스 로그가 필요할 수 있습니다.

코드상에서 AWS의 다른 자원들과 CloudFront 배포를 관리하는 경우 코드 파이프라인에 CloudFront 함수 테스트를 통합하는 것이 좋습니다.

결론

이 게시글에서 CloudFront 함수의 컴퓨팅 사용률 테스트가 필요한 이유를 알아보았습니다. 또한, 실제 트래픽 데이터로 사전 테스트를 하여 과도한 컴퓨팅 사용률로 인한 오작동을 회피하는 방법에 대해 알아보았습니다.

자세한 내용은 CloudFront 함수에 대한 설명서를 참조하거나 GitHub 예제 코드를 확인하기를 바랍니다.

AWS CloudFront 콘솔에서 코드 작성을 시작하십시오.

Julian Ju

Julian Ju

Julian is an Edge services specialist Solutions Architect at AWS in ASEAN region. He’s been working over 25 years in the IT industry as a developer, tech project manager, consultant, and architect. At AWS, he helps customers to adopt AWS Edge services for safer and faster internet experiences.

Yujin Jeong

Yujin Jeong

Yujin Jeong is a Solutions Architect at AWS based in Korea. She has a passion for helping enterprise customers navigate their cloud journey and finding the right solutions to meet their unique business needs. With her technical expertise and experience, she has helped customers achieve their cloud goals and drive business outcomes through innovative and scalable cloud solutions.