웹사이트나 웹 앱을 개발할 때 속도 최적화에만 몇 시간씩 소모하고 싶은 개발자는 없습니다. 하지만 빠른 웹사이트는 그만큼 중요하며, 성능은 프로젝트의 성패를 좌우하는 결정적 요소가 되곤 합니다.
Remix Run과 같은 웹 프레임워크는 설정을 최소화("제로 컨피그")하면서도 웹 성능을 극대화한다는 목표로 탄생했습니다. 요즘은 높은 속도를 실현하는 데 도움을 주는 프레임워크가 수십 가지나 출시되어 있습니다.
그러나 안타깝게도 앱의 데이터베이스가 그만큼 빠르지 않다면, 아무리 짧은 웹사이트 로딩 시간도 큰 의미가 없습니다.
이번 글에서는 훌륭한 웹사이트 로딩 속도를 달성하는 방법과, 이를 Upstash Redis 글로벌 데이터베이스의 최상급 데이터베이스 성능과 결합하는 방법을 살펴보겠습니다.
왜 글로벌 데이터베이스일까요?
대부분의 사용자는 웹사이트를 온라인에 배포할 때 CDN(콘텐츠 전송 네트워크)을 활용합니다. CDN은 전 세계에 분산된 서버 네트워크로, 웹사이트 파일을 클라이언트에서 지리적으로 가장 가까운 위치에 캐싱해 제공할 수 있기 때문에 단일 서버 시스템보다 훨씬 낮은 지연 시간을 보장합니다. 덕분에 웹사이트가 전 세계 어디서든 빠르게 로딩됩니다.
Upstash의 글로벌 데이터베이스는 바로 이 CDN 개념을 Redis 데이터베이스에 적용한 것입니다. 글로벌 데이터베이스를 사용하면 데이터베이스 복제본이 전 세계 여러 리전에 분산 배치되고, 클라이언트는 가장 가까운 리전으로 자동 라우팅되어 10ms 미만의 지연 시간을 경험하게 됩니다.
시작하기
많은 것을 약속드렸으니, 이제 실제로 어떻게 작동하는지 단계별로 살펴보겠습니다.
만들어볼 프로젝트
Remix Run으로 웹 앱을 만들어 Upstash Redis 글로벌 데이터베이스를 연동하고, Cloudflare Workers에 배포해 보겠습니다.
Cloudflare Workers는 CDN처럼 전 세계에 분산 배포된다는 강력한 장점이 있습니다. 즉, Remix 앱의 서버 사이드 렌더링(SSR)이 클라이언트와 최대한 가까운 곳에서 실행되어 지연 시간이 최소화됩니다. 여기에 Upstash 글로벌 데이터베이스를 더하면 성능 면에서 이상적인 구성이 완성됩니다.
Remix 프로젝트 설정
원하는 폴더로 이동한 뒤 다음 명령어를 실행합니다.
npx create-remix@latest
Remix Run 앱 설정을 안내하는 대화형 마법사가 나타납니다.
배포 대상으로는 반드시 Cloudflare Workers를 선택하세요. 이 가이드에서는 JavaScript 앱으로 진행하지만, TypeScript를 선택해도 무방합니다. 설정 화면은 대략 다음과 같습니다.
R E M I X - v1.2.3
💿 Welcome to Remix! Let's get you set up with a new project.
? Where would you like to create your app? upstash-remix
? Where do you want to deploy? Choose Remix if you're unsure, it's easy to change deployment targets. Cloudflare Workers
? TypeScript or JavaScript? JavaScript
? Do you want me to run `npm install`? Yes
설치가 완료되면 새로 생성된 폴더를 코드 에디터에서 엽니다.
Upstash Redis 글로벌 데이터베이스 생성
https://upstash.com/ 에 접속해 로그인하거나 계정을 생성합니다. 콘솔에서 "Create Database"를 클릭하고 이름을 입력한 뒤, 데이터베이스 유형으로 "Global"을 선택합니다. 마지막으로 "Create"를 누르고 데이터베이스 생성이 완료될 때까지 기다립니다. 이후 UPSTASH_REDIS_REST_URL과 UPSTASH_REDIS_REST_TOKEN 값을 복사해 둡니다.
프로젝트 루트에 .env 파일을 생성하고 두 변수를 다음과 같이 추가합니다.
UPSTASH_REDIS_REST_TOKEN=<INSERT YOUR UPSTASH_REDIS_REST_TOKEN HERE>
UPSTASH_REDIS_REST_URL=<INSERT YOUR UPSTASH_REDIS_REST_URL HERE>
데이터베이스와 상호작용하기 위해 npm install @upstash/redis 명령어로 @upstash/redis 패키지를 설치합니다.
Remix Run 애플리케이션 작성
데이터베이스 설정이 끝났으면 app/routes/index.jsx 파일을 열고 보일러플레이트 코드를 삭제한 후 다음 코드를 입력합니다.
import { json, redirect, useLoaderData } from "remix";
import redis from "../utils/redis.server";
export const loader = async () => {
const start = new Date();
const count = await redis.get("counter");
return json({ count, loadingTime: new Date() - start });
};
export const action = async () => {
await redis.incr("counter");
return redirect("/");
};
export default function Index() {
const { count, loadingTime } = useLoaderData();
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
<h1>Record speeds with Remix on Cloudflare and Upstash Redis</h1>
<p>The button below was clicked {count} times already.</p>
<form method="post" action="/?index">
<button type="submit">Click me!</button>
</form>
<p>
It took <b>{loadingTime} ms</b> to read the number of button clicks from{" "}
<a href="https://upstash.com/redis">Upstash Redis</a>{" "}
<a href="https://docs.upstash.com/redis/features/globaldatabase">
Global Database
</a>
.
</p>
</div>
);
}
추가로 app/utils/redis.server.js 파일을 생성하고 다음 내용을 작성합니다.
// app/utils/redis.server.js
import { Redis } from "@upstash/redis/cloudflare";
export default Redis.fromEnv();
이 코드가 하는 역할은 두 가지입니다. 하나씩 자세히 살펴보겠습니다.
1. useLoaderData로 서버 사이드 데이터 렌더링
가장 아래 return 문에는 간단한 UI가 있으며, 다음 두 변수를 화면에 표시합니다.
count라는 이름의 카운터 변수loadingTime이라는 이름의 로딩 시간 변수
코드를 조금 더 올려다보면 이 두 변수가 useLoaderData() 훅에서 비롯된다는 것을 알 수 있습니다. 이 Remix Run 고유의 훅은 파일 상단의 loader 함수와 함께 사용됩니다. loader 함수에서는 데이터베이스에서 counter 키를 읽고, 소요 시간을 측정한 뒤 두 값을 모두 useLoaderData()로 반환합니다.
loader 함수는 Remix Run 앱이 로딩될 때마다 실행됩니다. 즉, 누군가 웹사이트를 요청할 때마다 현재 counter 값을 읽어 서버 사이드 렌더링을 수행하고 그 결과를 클라이언트에 전달하는 방식입니다.
2. "Click me!" 버튼 클릭 시 카운터 증가
Remix Run은 폼을 매우 손쉽게 만들 수 있게 해줍니다. JSX 안에는 제출 버튼을 클릭하면 POST 요청을 /?index로 전송하는 <form/> 태그가 있습니다. Remix Run의 놀라운 점은 파일에 action 함수만 추가하면 API를 명시적으로 만들 필요 없이 프론트엔드-백엔드 상호작용이 완성된다는 것입니다.
action 함수에서는 단순히 카운터를 증가시키고, 변경 사항을 반영하도록 클라이언트에게 페이지를 다시 로드하라고 지시합니다.
🥳 이제 준비가 완료되었습니다!
로컬에서 실행하기
이제 방금 만든 앱을 직접 확인해 보고 싶으실 겁니다. npm run dev를 실행한 뒤 브라우저에서 앱을 열어보세요.
- "Click me!" 버튼을 클릭하고 카운트가 증가하는지 확인합니다.
- 측정된 로딩 시간을 살펴봅니다. 필자의 경우 독일에서 보통 20ms 미만의 지연 시간이 측정되었는데, Cloudflare에 배포하면 그 절반까지 줄어들 것입니다.
Cloudflare에 배포하기
이제 Cloudflare의 힘을 빌려 한 자릿수 밀리초 지연 시간을 달성해 보겠습니다!
먼저, 아직 설치하지 않았다면 공식 문서(https://developers.cloudflare.com/workers/cli-wrangler/install-update/)를 참고해 Cloudflare Workers CLI인 Wrangler를 설치하고 CLI 인증까지 완료하세요.
계정이 아직 없다면 Cloudflare 계정을 생성한 뒤, 이메일 인증을 마치고 대시보드로 이동해 무료 Cloudflare Workers 커스텀 서브도메인을 설정합니다.
.env에 저장한 시크릿 값들을 Cloudflare에도 등록하는 것을 잊지 마세요.
wrangler secret put UPSTASH_REDIS_REST_TOKEN
...
wrangler secret put UPSTASH_REDIS_REST_URL
...
여기까지 완료했다면 이제 앱을 배포할 수 있습니다.
npm run deploy
배포가 완료되면 Wrangler가 앱 링크를 제공합니다. 링크를 열고 여러분의 앱이 얼마나 빨라졌는지 직접 확인해 보세요.
참고 링크
실제 배포 예시는 https://remix-cloudflare-workers.soenkep.workers.dev/ 에서 확인할 수 있습니다.
전체 코드(환경 변수 제외)는 https://github.com/zunkelty/upstash-remix-run 에서 확인할 수 있습니다.