Computer >> 컴퓨터 >  >> 프로그래밍 >> Redis

Cloudflare Workers와 Redis로 구현하는 엣지 컴퓨팅: 엣지에서의 웹 분석

엣지 컴퓨팅(Edge Computing)은 최근 몇 년간 가장 주목받는 기술 중 하나입니다. CDN이 파일을 사용자 가까이에 저장해 준다면, 엣지 컴퓨팅은 애플리케이션 자체를 사용자 근처에서 실행할 수 있게 해줍니다. 덕분에 개발자는 전 세계에 분산된 고성능 애플리케이션을 구축할 수 있습니다.

Cloudflare Workers란?

Cloudflare Workers는 현재 이 분야의 선두주자입니다. 콜드 스타트(cold start) 없는 서버리스 처리 환경을 제공하며, Cloudflare의 글로벌 네트워크를 활용해 애플리케이션의 지연 시간을 최소화할 수 있습니다. JavaScript, Rust, C, C++ 등 다양한 언어로 함수를 작성할 수 있다는 점도 큰 장점입니다.

그런데 AWS Lambda 같은 서버리스 함수와 마찬가지로 Cloudflare Workers도 상태를 저장하지 않는(stateless) 방식으로 동작합니다. Cloudflare의 설문조사에서 확인할 수 있듯이, 많은 개발자들이 엣지 함수에서 데이터베이스에 연결하는 방법을 찾고 있습니다. 문제는 대부분의 데이터베이스가 서버리스 환경을 고려해 설계되지 않았고 영구적인 연결을 요구한다는 것입니다. 이에 우리는 Redis 위에 REST API를 개발하여 서버리스 엣지 함수가 가장 간단하고 빠른 방식으로 Upstash에 접근할 수 있도록 했습니다.

Cloudflare Workers KV와 비교

Cloudflare는 엣지 함수의 상태를 저장할 수 있는 기본적인 키-값(Key-Value) 저장소를 제공합니다. 하지만 Upstash Redis는 여러 면에서 Cloudflare KV보다 우위에 있습니다:

  • 풍부한 데이터 구조: Cloudflare KV는 get/set/delete 수준의 기본 기능만 제공합니다. 반면 Upstash는 해시(Hash), 리스트(List), 정렬된 집합(Sorted Set), 범위 조회, 증감 연산 등 모든 Redis 데이터 구조를 지원하므로 훨씬 정교한 기능을 구현할 수 있습니다.
  • 자유로운 접근성: Cloudflare KV는 Cloudflare 생태계 안에서만 접근하도록 설계되었지만, Upstash Redis는 Redis 프로토콜과 REST API를 모두 지원하기 때문에 어디서든 접근하고 활용할 수 있습니다. 엣지에서 Redis로 데이터를 옮긴 뒤 어떤 Redis 클라이언트로든 처리할 수 있습니다.
  • 쓰기 성능: Cloudflare KV는 읽기 위주의 애플리케이션에 최적화되어 있어, 쓰기 작업이 다른 위치로 복제되기까지 최대 60초가 걸릴 수 있습니다. 반면 Upstash의 쓰기 지연 시간은 밀리초 단위입니다.

이제 Cloudflare와 Redis 조합의 강력함을 보여줄 간단한 예제를 만들어 보겠습니다.

엣지에서의 웹 분석(Analytics)

웹 분석은 모든 웹사이트 운영자에게 필수적인 요구 사항입니다. Google Analytics는 강력하지만, 많은 개발자들이 트래픽 데이터를 Google과 공유하는 것과 사용자 브라우저에 쿠키를 삽입하는 것을 꺼립니다. 웹사이트 트래픽이 CDN을 통해 들어온다면 엣지 레이어에서 손쉽게 트래픽을 추적할 수 있습니다. 여기서는 Cloudflare Workers로 사용자 트래픽을 추적하는 아주 간단한 예제를 구현해 보겠습니다. Cloudflare Workers에서 트래픽을 가로채 사용자 요청을 Upstash Redis에 저장한 후, 이를 분석해 선택한 날짜의 다음 정보를 보여주는 독립형 애플리케이션을 작성할 것입니다:

  • 페이지뷰(Page Views)
  • 순수 방문자(Unique Visitors)
  • 방문자 수가 가장 많은 상위 국가
  • 가장 많이 방문된 페이지

설정

아직 데이터베이스가 없다면 관련 가이드를 참고해 생성하세요. 데이터베이스 페이지에서 REST API 버튼을 클릭하고 REST URL을 복사합니다. Cloudflare 계정이 있다면 Workers 함수를 바로 생성할 수 있고, 계정이 없어도 플레이그라운드를 통해 테스트할 수 있습니다.

참고: Upstash 데이터베이스를 생성할 때는 반드시 Global Database를 선택하세요. 글로벌 데이터베이스는 데이터를 여러 리전에 복제하여 엣지 함수의 지연 시간을 크게 줄여 줍니다.

Cloudflare Workers 코드

Cloudflare Workers 함수는 request를 매개변수로 받습니다. Upstash의 REST API를 사용해 요청을 Redis 리스트에 기록하며, 현재 날짜를 리스트의 키로 사용하므로 요청이 날짜별로 별도의 리스트에 저장됩니다.

Upstash REST API를 호출하려면 엔드포인트와 토큰이 필요합니다. 데이터베이스를 생성한 후 콘솔에서 REST API 버튼을 클릭하면 엔드포인트와 토큰을 복사할 수 있습니다. 아래 코드에서 엔드포인트와 토큰 값을 교체해 Cloudflare Workers 함수를 업데이트하세요:

const endpoint = "REPLACE_UPSTASH_REST_ENDPOINT";
const token = "REPLACE_UPSTASH_REST_TOKEN";

async function recordRequest(request) {
  let d = new Date();
  let datestr = d.getFullYear() + "-" + (d.getMonth() + 1) + "-" + d.getDate();
  let data = [["url", request.url], ...request.headers];
  let url = endpoint + "/lpush/" + datestr;
  const init = {
    body: JSON.stringify(data),
    method: "POST",
    headers: {
      Authorization: "Bearer " + token,
    },
  };
  return await fetch(url, init);
}

async function handleRequest(request) {
  recordRequest(request);
  return new Response("My Awesome Website");
}

addEventListener("fetch", (event) => {
  event.respondWith(
    handleRequest(event.request).catch(
      (err) => new Response(err.stack, { status: 500 })
    )
  );
});

분석 도구 코드

이제 날짜를 인자로 받아 분석 데이터를 반환하는 간단한 명령줄 애플리케이션을 작성해 보겠습니다. 폴더를 생성하고 npm init을 실행한 뒤, npm install ioredis로 Redis 클라이언트를 설치합니다. .env.example 파일을 .env로 복사하고 Redis URL(ioredis)을 설정한 후, index.js를 아래 내용으로 업데이트하세요:

const Redis = require("ioredis");
require("dotenv").config();
console.log("EDGE analytics with CloudFlare Workers and Upstash Redis.");
let redis = new Redis(process.env.REDIS_URL);
let dateArg = process.argv[2];
let datestr;
if (dateArg) {
  datestr = dateArg;
} else {
  let d = new Date();
  datestr = d.getFullYear() + "-" + (d.getMonth() + 1) + "-" + d.getDate();
}

redis.lrange(datestr, "0", "-1", function (err, result) {
  let pageview = 0;
  let users = new Set();
  let countries = new Map();
  let paths = new Map();
  if (err) {
    console.log("Could not connect to Redis.");
    console.error(err);
  } else {
    console.log(
      "Upstash Redis Connection Successful. Analyzing the access logs..."
    );
    if (!result || result.length === 0) {
      console.log("Could not find any access logs for the date:" + datestr);
    } else {
      for (const elem of result) {
        let entries = JSON.parse(elem);
        for (let entry of entries) {
          if (entry[0] === "accept") {
            if (entry[1].startsWith("text")) pageview++;
          }
          if (entry[0] === "cf-ipcountry") {
            let temp = countries.get(entry[1]);
            if (!temp) {
              temp = 0;
            }
            countries.set(entry[1], temp + 1);
          }
          if (entry[0] === "url") {
            let temp = paths.get(entry[1]);
            if (!temp) {
              temp = 0;
            }
            paths.set(entry[1], temp + 1);
          }
          if (entry[0] === "x-real-ip") {
            users.add(entry[1]);
          }
        }
      }
      countries = new Map([...countries.entries()].sort((a, b) => b[1] - a[1]));
      paths = new Map([...paths.entries()].sort((a, b) => b[1] - a[1]));
      console.log("\nDATE: " + datestr);
      console.log("\nPAGE VIEWS: " + pageview);
      console.log("\nUNIQUE VISITORS: " + users.size);
      logMap("TOP COUNTRIES", countries);
      logMap("TOP PAGES", paths);
    }
  }
});

function logMap(title, data) {
  console.log("\n" + title);
  console.log("----------------");
  console.log(mapEntriesToString(data));
}

function mapEntriesToString(entries) {
  return Array.from(entries, ([k, v]) => `${k} : ${v}\n`).join("");
}

이제 node index 2021-6-16 또는 단순히 node index로 애플리케이션을 실행할 수 있습니다. 후자는 오늘 날짜를 기준으로 조회합니다.

Cloudflare Workers와 Redis로 구현하는 엣지 컴퓨팅: 엣지에서의 웹 분석

이 예제는 어디까지나 시작점입니다. 분석 목적에 맞게 차트와 테이블을 갖춘 풍부한 웹 애플리케이션으로 발전시킬 수 있으며, 더 강력한 분석을 위해 다른 Redis 데이터 구조를 활용할 수도 있습니다.

Upstash 엣지 로드맵

REST API는 우리 엣지 스토리의 첫 번째 단계입니다. 올해는 두 가지 중요한 개발을 계획하고 있습니다.

  • 엣지 캐싱(Edge Caching): (업데이트: 이미 출시되었습니다) 현재 모든 REST 요청은 데이터베이스가 위치한 리전으로 전달됩니다. 엣지 캐싱이 지원되면 REST 요청이 전 세계 모든 엣지 위치에서 캐시되어, Upstash Redis도 Cloudflare KV처럼 전 세계적으로 낮은 지연 시간을 제공할 수 있습니다.
  • 글로벌 복제 데이터베이스(Global Replicated Databases): (업데이트: 이미 출시되었습니다) 글로벌(멀티 리전) 복제는 데이터를 여러 리전에 복제합니다. 따라서 모든 요청(Redis 및 REST API 모두)이 가장 가까운 위치로 전달되어, 일관성을 최소한으로 희생하면서 전 세계적으로 낮은 지연 시간을 확보할 수 있습니다.

엣지 로드맵에 대한 전용 블로그 포스트도 준비할 계획입니다. 많은 기대 부탁드리며, 최신 소식은 트위터에서 확인하실 수 있습니다.