반응형
6장: 실전 프로젝트 - URL 단축기 만들기 (on Cloudflare Workers)
Hono의 가장 큰 장점 중 하나인 엣지(Edge) 환경 배포를 경험해봅니다. Cloudflare Workers 플랫폼에 간단한 URL 단축기(URL Shortener) API를 Hono로 만들어 배포하는 전 과정을 실습합니다.
1. 프로젝트 목표 및 환경
기능 요구사항:
- 긴 URL을 받아 짧은 단축 URL 코드를 생성 (POST /shorten)
- 단축 URL 코드로 접속 시 원래의 긴 URL로 리다이렉트 (GET /:code)
기술 스택:
- 프레임워크: Hono
- 배포 환경: Cloudflare Workers
- 데이터베이스: Cloudflare KV (간단한 Key-Value 저장소)
Cloudflare Workers란?: 전 세계에 분산된 Cloudflare의 엣지 네트워크에서 코드를 직접 실행하는 서버리스 플랫폼입니다. 사용자와 가장 가까운 위치에서 코드가 실행되므로 응답 속도가 매우 빠릅니다.
2. 개발 환경 설정
Cloudflare 계정 생성: Cloudflare 웹사이트에서 무료 계정을 생성합니다.
Wrangler CLI 설치: Cloudflare Workers 프로젝트를 관리하기 위한 공식 CLI 도구입니다.
npm install -g wranglerWrangler 로그인:
wrangler login- 브라우저가 열리며 Cloudflare 계정으로 로그인하고 권한을 부여하라는 메시지가 나타납니다.
Hono 프로젝트 생성 (Cloudflare Workers 템플릿 사용):
npm create hono@latest my-url-shortener- 템플릿 선택 화면에서
cloudflare-workers를 선택합니다.
- 템플릿 선택 화면에서
프로젝트 구조:
my-url-shortener/ ├── src/index.ts # Hono 애플리케이션 코드 ├── package.json └── wrangler.toml # Cloudflare Worker 설정 파일
3. 구현 단계
1단계: KV 네임스페이스 생성
Cloudflare KV는 엣지에서 사용할 수 있는 글로벌 Key-Value 데이터 저장소입니다. 단축 코드(Key)와 원본 URL(Value)을 저장하는 데 사용합니다.
wrangler.toml파일 수정: KV 네임스페이스를 바인딩(binding) 설정을 추가합니다.# wrangler.toml name = "my-url-shortener" main = "src/index.ts" compatibility_date = "2023-10-30" # 아래 내용 추가 [[kv_namespaces]] binding = "URLS_KV" # 코드에서 이 이름으로 KV에 접근 id = "" # wrangler dev 실행 시 자동으로 채워짐 preview_id = "" # wrangler dev 실행 시 자동으로 채워짐로컬 개발 서버 실행:
wrangler dev- 로컬에서 Cloudflare 환경을 시뮬레이션하여 실행합니다. KV도 메모리 내에서 에뮬레이션됩니다.
2단계: Hono 애플리케이션 코드 작성 (src/index.ts)
// src/index.ts
import { Hono } from 'hono';
// 1. 바인딩 타입 정의 (wrangler.toml의 binding 이름과 일치)
type Bindings = {
URLS_KV: KVNamespace;
};
// 2. Hono 앱 생성 시 제네릭으로 타입 전달
const app = new Hono<{ Bindings: Bindings }>();
// POST /shorten: URL 단축
app.post('/shorten', async (c) => {
const { url } = await c.req.json<{ url: string }>();
if (!url || !url.startsWith('http')) {
return c.json({ error: '유효한 URL을 제공해야 합니다.' }, 400);
}
// 3. 컨텍스트(c.env)를 통해 KV 네임스페이스에 접근
const kv = c.env.URLS_KV;
// 간단한 랜덤 코드를 생성 (실제로는 더 정교한 방법 사용)
const code = Math.random().toString(36).substring(2, 8);
// 4. KV에 저장 (Key: code, Value: url)
await kv.put(code, url);
const shortUrl = `${new URL(c.req.url).origin}/${code}`;
return c.json({ code, shortUrl });
});
// GET /:code : 원본 URL로 리다이렉트
app.get('/:code', async (c) => {
const code = c.req.param('code');
const kv = c.env.URLS_KV;
// 5. KV에서 코드로 원본 URL 조회
const originalUrl = await kv.get(code);
if (!originalUrl) {
return c.text('URL을 찾을 수 없습니다.', 404);
}
// 6. 원본 URL로 리다이렉트
return c.redirect(originalUrl, 301);
});
export default app;
3단계: 배포
KV 네임스페이스 생성 (프로덕션용):
wrangler kv:namespace create "URLS_KV"- 위 명령을 실행하면
id와preview_id가 출력됩니다. 이 값을wrangler.toml파일의 해당 필드에 복사하여 붙여넣습니다.
- 위 명령을 실행하면
배포 실행:
wrangler deploy- 몇 초 후 배포가 완료되고,
my-url-shortener.your-worker-name.workers.dev와 같은 공개 URL이 발급됩니다.
- 몇 초 후 배포가 완료되고,
테스트: Postman 등을 사용하여 배포된 URL로 API를 테스트합니다.
POST https://.../shorten(Body:{ "url": "https://www.google.com" })- 응답으로 받은 단축 URL(예:
https://.../abcdef)로 브라우저에서 접속하여 Google로 리다이렉트되는지 확인합니다.
4. 연습 문제 및 개선 과제
문제 1: 커스텀 단축 코드 기능
- 요구사항: 사용자가 URL을 단축할 때 원하는 단축 코드(custom code)를 직접 지정할 수 있는 기능을 추가하세요.
- 세부사항:
POST /shorten요청의 본문에customCode필드를 선택적으로 받을 수 있도록 합니다.customCode가 전달된 경우, 해당 코드가 이미 KV에 존재하는지 확인합니다.- 존재한다면 "이미 사용 중인 코드입니다." 라는 오류(409 Conflict)를 응답합니다.
- 존재하지 않는다면, 랜덤 코드 대신 사용자가 지정한 코드를 사용하여 URL을 저장합니다.
문제 1 힌트
// POST /shorten 핸들러 내부
const { url, customCode } = await c.req.json<{ url: string, customCode?: string }>();
let code = customCode;
if (code) {
const existing = await c.env.URLS_KV.get(code);
if (existing) {
return c.json({ error: '이미 사용 중인 코드입니다.' }, 409);
}
} else {
code = Math.random().toString(36).substring(2, 8);
}
// ... 이후 로직은 동일
문제 2: 방문 횟수 추적 기능
- 요구사항: 각 단축 URL이 몇 번이나 리다이렉트되었는지 방문 횟수를 추적하는 기능을 추가하세요.
- 세부사항:
- KV에 URL을 저장할 때,
{ "url": originalUrl, "count": 0 }와 같이 객체 형태로 저장하도록 변경합니다.JSON.stringify()를 사용해야 합니다. GET /:code핸들러에서 KV 값을 가져온 후,JSON.parse()로 객체를 복원합니다.count를 1 증가시킨 후, 다시JSON.stringify()하여 KV에 업데이트합니다.- (심화) 방문 횟수를 보여주는
GET /:code/stats라는 새로운 엔드포인트를 만들어보세요.
- KV에 URL을 저장할 때,
문제 2 힌트
// POST /shorten
const data = { url, count: 0 };
await kv.put(code, JSON.stringify(data));
// GET /:code
const value = await kv.get(code);
if (!value) { /* ... */ }
const data = JSON.parse(value);
data.count++;
// KV 업데이트 (백그라운드에서 실행)
c.executionCtx.waitUntil(kv.put(code, JSON.stringify(data)));
return c.redirect(data.url, 301);
반응형
'백엔드 > 호노' 카테고리의 다른 글
| [Hono] 5장: 고급 기능 - 유효성 검사와 RPC 모드 (0) | 2025.09.23 |
|---|---|
| [Hono] 4장: 요청과 응답 심화 - 데이터 흐름 다루기 (0) | 2025.09.23 |
| [Hono] 3장: 미들웨어 - 요청 처리 흐름 제어하기 (0) | 2025.09.23 |
| [Hono] 2장: 라우팅과 컨텍스트 - 요청을 이해하고 응답하기 (2) | 2025.09.22 |
| [Hono] 1장: Hono 소개 - 작고, 간단하고, 초고속! (0) | 2025.09.22 |