deep-dive2025-05-01·8·100/348

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-token

Problem

빠른 데이터 저장과 조회가 필요한 캐싱 시스템이 필요했습니다:

# 기존 데이터베이스 한계
- 높은 레이턴시 (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

  1. 캐싱 전략: 자주 접근하는 데이터는 Vercel KV로 캐싱하세요
  2. TTL 설정: 데이터에 적절한 만료 시간을 설정하세요
  3. 레이트 리밋링: API 보호를 위해 레이트 리밋을 적용하세요
  4. 세션 관리: 사용자 세션을 Vercel KV로 관리하여 확장성을 확보하세요
  5. 비용 최적화: 불필요한 데이터를 주기적으로 정리하세요

This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.