Vercel KV 데이터베이스 활용 가이드
Vercel KV를 사용하여 Redis 기반 데이터베이스를 효과적으로 활용하는 방법을 다룹니다.
Introduction
Vercel KV는 Redis 기반의 키-값 데이터베이스로, 빠른 데이터 저장과 조회를 제공합니다. 이번 포스트에서는 Vercel KV를 활용하는 방법을 살펴보겠습니다.
Environment
# Vercel KV 패키지
npm list @vercel/kv
# @vercel/kv@1.0.0
# 환경 변수
KV_REST_API_URL=https://your-kv-url
KV_REST_API_TOKEN=your-tokenProblem
빠른 데이터 저장과 조회가 필요한 캐싱 시스템이 필요했습니다:
# 기존 데이터베이스 한계
- 높은 레이턴시 (100ms+)
- 높은 비용
- 스케일링 어려움
# 요구사항
- 빠른 읽기/쓰기 (<10ms)
- 낮은 비용
- 자동 스케일링Solution
1. Vercel KV 설치 및 설정
npm install @vercel/kv// lib/kv.js
import { kv } from '@vercel/kv';
export default kv;2. 기본 사용법
// 쓰기
await kv.set('user:123', { name: 'Joel', email: 'joel@example.com' });
// 읽기
const user = await kv.get('user:123');
// 만료 시간 설정 (초)
await kv.set('session:abc', { userId: 123 }, { ex: 3600 });
// 삭제
await kv.del('user:123');
// 키 존재 확인
const exists = await kv.exists('user:123');3. 캐싱 구현
// lib/cache.js
import kv from './kv';
export async function getCachedData(key, fetchFn, ttl = 3600) {
// 캐시에서 데이터 가져오기
const cached = await kv.get(key);
if (cached) {
return cached;
}
// 데이터베이스에서 데이터 가져오기
const data = await fetchFn();
// 캐시에 저장
await kv.set(key, data, { ex: ttl });
return data;
}
// 사용 예시
const user = await getCachedData(
'user:123',
() => db.users.findById(123),
3600 // 1시간
);4. 세션 관리
// lib/session.js
import kv from './kv';
export async function createSession(userId) {
const sessionId = crypto.randomUUID();
await kv.set(`session:${sessionId}`, {
userId,
createdAt: Date.now(),
expiresAt: Date.now() + 24 * 60 * 60 * 1000,
}, { ex: 86400 }); // 24시간
return sessionId;
}
export async function getSession(sessionId) {
const session = await kv.get(`session:${sessionId}`);
if (!session || session.expiresAt < Date.now()) {
return null;
}
return session;
}
export async function deleteSession(sessionId) {
await kv.del(`session:${sessionId}`);
}5. 레이트 리밋링
// lib/rateLimit.js
import kv from './kv';
export async function checkRateLimit(key, limit, windowMs) {
const now = Date.now();
const windowStart = now - windowMs;
// 현재 윈도우의 요청 수 가져오기
const requests = await kv.get(`ratelimit:${key}`) || [];
// 윈도우 외의 요청 제거
const validRequests = requests.filter(time => time > windowStart);
if (validRequests.length >= limit) {
return { allowed: false, remaining: 0 };
}
// 새 요청 추가
validRequests.push(now);
await kv.set(`ratelimit:${key}`, validRequests, { ex: windowMs / 1000 });
return {
allowed: true,
remaining: limit - validRequests.length,
};
}
// API 미들웨어
export function rateLimitMiddleware(limit = 100, windowMs = 60000) {
return async (req, res, next) => {
const key = req.ip;
const result = await checkRateLimit(key, limit, windowMs);
if (!result.allowed) {
return res.status(429).json({ error: 'Too many requests' });
}
res.setHeader('X-RateLimit-Remaining', result.remaining);
next();
};
}6. 실시간 데이터
// 카운터 증가
await kv.incr('pageviews:homepage');
// 해시 데이터
await kv.hset('user:123:profile', {
name: 'Joel',
bio: 'Developer',
});
const profile = await kv.hgetall('user:123:profile');
// 리스트
await kv.lpush('notifications:123', { message: 'New alert' });
const notifications = await kv.lrange('notifications:123', 0, -1);Lessons Learned
- 캐싱 전략: 자주 접근하는 데이터는 Vercel KV로 캐싱하세요
- TTL 설정: 데이터에 적절한 만료 시간을 설정하세요
- 레이트 리밋링: API 보호를 위해 레이트 리밋을 적용하세요
- 세션 관리: 사용자 세션을 Vercel KV로 관리하여 확장성을 확보하세요
- 비용 최적화: 불필요한 데이터를 주기적으로 정리하세요
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.