이 기사에서는 AI 기반 여행 계획 스타트업인 PlanTripAI 개발에서 Upstash의 중추적인 역할을 살펴봅니다. Upstash Redis를 활용하는 것은 사용자 액세스를 위한 주요 라이선스 저장, 여행 콘텐츠의 효율적인 저장 및 캐싱과 같은 필수 측면을 관리하는 데 매우 중요했습니다. 또한 Upstash의 속도 제한 기능은 시스템을 보호하는 데 중요한 역할을 합니다. 요청 빈도를 효과적으로 관리하여 잠재적인 보안 위협에 대한 강력한 보호 기능을 제공하고 사용자에게 원활하고 중단 없는 서비스를 보장합니다.
plantripai.com은 목적지, 체류 기간, 여행 스타일, 예산 등 사용자 선호도를 기반으로 개인화된 여행 일정을 빠르게 생성하는 AI 기반 여행 플래너입니다. 무제한 여행 일정 생성과 다양한 다운로드 형식을 제공합니다.
- @upstash/redis
- @upstash/속도 제한
- next.js
작동 방식
AI 회사는 일반적으로 사용자가 제품을 사용해 볼 수 있는 다양한 방법을 제공합니다. PlanTripAI에서는 부분 유료 플랜과 유료 플랜을 모두 제공합니다.
가장 인기 있는 프리미엄 플랜:사용자가 유효한 라이선스 없이 여행을 생성할 때마다 새로운 무작위 키가 생성되어 Redis 해시 (redis.hset(trip:${key}, data))에 저장됩니다. , 여행 콘텐츠와 함께. 이 키는 여행 세부정보 페이지에 대한 임시 액세스 권한을 부여합니다. 이 액세스 권한이 만료되면 사용자는 여행에 대한 영구적인 액세스 권한을 유지할지 여부를 결정할 수 있습니다.
또한 프리미엄(Freemium) 사용자에게는 비율 제한(fixedWindow)이 적용됩니다. ) 10초당 요청 3개로, 애플리케이션에 대한 잠재적인 공격을 방지하는 데 도움이 됩니다.

유료 요금제의 경우 사용자는 결제 게이트웨이를 통해 제품을 구매할 수 있습니다. 거래가 성공하면 결제 게이트웨이의 웹후크가 성공 이벤트를 내보내고 키는 Redis에 유효한 키 (redis.set(${licenseKey}, true))로 저장됩니다. .
유효한 라이선스가 있는 사용자가 여행을 생성하면 여전히 임의의 키가 생성되어 Redis에 저장됩니다. 또한 새로운 여행은 사용자 개체에 저장되고 라이센스 키와 연결됩니다.
유료 사용자는 프리미엄(Freemium) 사용자보다 더 관대한 요금 제한을 누리게 됩니다. 이 향상된 기능은 Redis에서 유효한 키를 확인하여 동적으로 제공됩니다.

여행 데이터 저장
PlanTripAI는 Redis를 활용하여 사용자가 만든 여행을 저장합니다. Redis는 빠른 키-값 저장소로서의 효율성 때문에 우리에게 이상적인 선택입니다. 일단 생성된 여행은 변경할 수 없으며 해시를 사용하면 주요 데이터와 함께 메타데이터를 저장할 수 있습니다.
여행 데이터는 다음과 같이 Redis 해시로 구성됩니다:
{
"itinerary": [
{ "day": "Day 1", "data": [...] }
],
"info": "This itinerary is designed for a city explorer visiting Paris, France for 2 days.",
"inputs": {
"city": "Paris, France",
"days": 2,
"accommodation": "Paris France Hotel",
"kind": "city explorer",
"currency": "USD",
"budget": 3000,
"transportation": "bus"
},
"createdAt": ...,
"shareable": false
} HTTP를 통해 Upstash Redis API를 사용하면 데이터가 무료 Redis 데이터베이스에 저장됩니다. 이 데이터베이스는 하루에 10,000개의 요청을 무료로 제공하며 이는 우리의 요구 사항을 충족하기에 충분합니다.
import { Redis } from "@upstash/redis";
const redis = new Redis({
url: "..." // UPSTASH_REDIS_REST_URL
token: "..." // UPSTASH_REDIS_REST_TOKEN
});
redis.hset(`trip:${id}`, data); 유효한 라이센스 키 저장
앞서 언급했듯이 PlanTripAI의 모든 기능에 액세스하는 한 가지 방법은 유료 구독을 통하는 것입니다. 여기서 각 유료 사용자에게는 Redis에 저장된 유효한 라이센스 키가 할당됩니다. 이 설정은 키-값 데이터베이스로서 Redis의 효율성을 고려할 때 빠른 유효성 검사에 이상적입니다.
사용자를 유효한 것으로 표시하기 위해 다음 명령을 실행합니다:
import { Redis } from "@upstash/redis";
const redis = new Redis({
url: "..." // UPSTASH_REDIS_REST_URL
token: "..." // UPSTASH_REDIS_REST_TOKEN
});
await redis.set(licenseKey, true); 사용자의 유효성을 확인하기 위해 다음 명령을 사용합니다:
import { Redis } from "@upstash/redis";
const redis = new Redis({
url: "..." // UPSTASH_REDIS_REST_URL
token: "..." // UPSTASH_REDIS_REST_TOKEN
});
const redisLicenseKey = await redis.get(licenseKey);
const valid = Boolean(redisLicenseKey);
if (!valid) {
return new Response(
JSON.stringify({ message: "The license key is invalid!" }),
{
status: 401,
}
);
}
// successful code here...
비율 제한 구현
애플리케이션을 보호하는 주요 기능은 Upstash Rate Limit에서 지원하는 속도 제한 논리입니다. 자세한 내용은 여기에서 확인할 수 있습니다. 이 서비스는 전체 애플리케이션에 영향을 주지 않고 악의적인 사용자의 공격을 성공적으로 막아 무료 사용자와 유료 사용자 모두에게 원활한 환경을 유지하는 데 중요합니다.
다음은 next.js를 미들웨어로 사용하는 코드 조각입니다. 이 코드는 사용자 IP 확인을 관리하고 후속 요청을 진행할지 여부를 결정하는 데 도움이 됩니다.
import {
NextResponse,
type NextFetchEvent,
type NextRequest,
} from "next/server";
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
const cache = new Map();
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.fixedWindow(3, "10s"),
ephemeralCache: cache,
analytics: true,
});
export default async function middleware(
request: NextRequest,
event: NextFetchEvent
): Promise<Response | undefined> {
const id = request.ip ?? "anonymous";
// optional hard coded IPs
const blockeds = [];
if (blockeds.includes(id.trim())) {
new NextResponse(
JSON.stringify({
message: "Blocked :)",
}),
{ status: 429, headers: { "Content-Type": "application/json" } }
);
}
const { success, pending, limit, reset, remaining } = await ratelimit.limit(
id
);
event.waitUntil(pending);
request.headers.set("X-RateLimit-Limit", limit.toString());
request.headers.set("X-RateLimit-Remaining", remaining.toString());
request.headers.set("X-RateLimit-Reset", reset.toString());
return success
? NextResponse.next()
: new NextResponse(
JSON.stringify({
message:
"Request cannot be processed! You sent too many requests in a given amount of time.",
}),
{ status: 429, headers: { "Content-Type": "application/json" } }
);
}
export const config = {
matcher: ["/api/generate-trip", "/api/get-license/(.*)"],
}; 마지막 단어
시간을 내어 읽어주셔서 감사합니다! PlanTripAI는 SaaS 프레임워크에서 Upstash Redis와 Next.js를 통합하는 것에 대한 호기심에서 탄생한 사이드 프로젝트로 시작되었습니다.
이 내용이 유익하고 통찰력이 있기를 진심으로 바랍니다. 귀하의 질문이나 피드백을 높이 평가합니다. 질문이나 의견이 있으시면 언제든지 Twitter를 통해 저에게 연락해 주세요.