이 게시물에서는 Upstash, Astro, GitHub 및 Edgio를 사용하여 itsmy.fyi(LinkTree의 오픈 소스 대안)를 구축하는 방법에 대해 설명합니다. Upstash는 모든 사용자의 (CRUD) 데이터를 관리하는 데 도움이 되었고, CRUD 작업을 위해 GitHub API에 비해 훨씬 관대한 속도 제한을 제공하고, 세분화된 속도 제한을 구현했습니다.

우리가 사용할 것
- Astro(프런트엔드 및 백엔드)
- Upstash(속도 제한 및 CRUD 작업)
- GitHub 문제 및 웹후크(사용자 프로필을 관리하기 위한 공개 CMS)
- Tailwind CSS(스타일링)
- Edgio(배포)
필요한 것
- GitHub 계정
- 데이터베이스 생성을 위한 Upstash 계정
Upstash Redis 설정
Upstash 계정을 생성하고 로그인하면 Redis 탭으로 이동하여 데이터베이스를 생성하게 됩니다.


데이터베이스를 생성한 후 세부정보 탭으로 이동합니다. Connect your database을 찾을 때까지 아래로 스크롤합니다. 섹션. 콘텐츠를 복사하여 안전한 곳에 저장하세요.

또한 REST API 섹션을 찾을 때까지 아래로 스크롤하고 .env 버튼을 선택합니다. 콘텐츠를 복사하여 안전한 곳에 저장하세요.

프로젝트 설정
설정하려면 앱 저장소를 복제하고 이 튜토리얼을 따라 그 안에 있는 모든 내용을 알아보세요. 프로젝트를 포크하려면 다음을 실행하세요:
git clone https://github.com/rishi-raj-jain/itsmy.fyi
cd itsmy.fyi
yarn install 저장소를 복제한 후에는 .env 파일을 생성하게 됩니다. 위 섹션에서 저장한 항목을 추가하게 됩니다.
다음과 같아야 합니다:
.env# Obtained from your GitHub repo
GITHUB_API_TOKEN="to_create_and_update_github_comments"
GITHUB_WEBHOOK_SECRET="if_you_are_matching_github_webhooks_sha256"
# Obtained from the steps as above
UPSTASH_DB="your_upstash_redis_from_above"
UPSTASH_REDIS_REST_URL="your_upstash_redis_rest__url_from_above"
UPSTASH_REDIS_REST_TOKEN="your_upstash_redis_rest__token_from_above" 이 단계 후에는 다음 명령을 사용하여 로컬 환경을 시작할 수 있습니다:
yarn run edgio:dev 저장소 구조
이는 프로젝트의 기본 폴더 구조입니다. CRUD 작업, 속도 제한 및 참조되는 파일을 다루는 이 게시물에서 추가로 논의될 파일에 빨간색 원을 표시했습니다.

대규모 데이터 흐름
이는 데이터 흐름 방식을 개략적으로 보여주는 다이어그램입니다.
- 사용자가 itsmy.fyi/me/slug를 방문하고 이 페이지에 대한 응답이 캐시되지 않거나 재검증되는 경우, Upstash DB에서 사용자 json을 검색하는 getUserInfo 함수를 호출합니다.
- 사용자가 GitHub 문제를 생성, 업데이트 또는 삭제하면 GitHub는 엔드포인트에 POST를 보내는 Webhook을 트리거합니다. 해당 엔드포인트에서는 먼저 Upstash Rate Limiting을 사용하여 요청된 변경이 가능한지 평가한 다음 Upstash를 사용하여 사용자 json을 생성, 업데이트 또는 삭제합니다.

Upstash Redis를 통한 사용자 프로필 CRUD 작업
이 섹션에서는 사용자 프로필에 대한 데이터 가져오기, 업데이트 및 삭제가 수행되는 방법을 자세히 살펴보겠습니다. 우리는 Upstash를 지속적으로 사용합니다(ioredis를 통해). ) 데이터를 가져오고 표시합니다.
CRUD 작업을 위해 GitHub에서 Upstash로 전환한 이유는 무엇입니까?
데이터 관리의 소스로 GitHub를 시작하는 동안 Github Issues 데이터 양식에서 GitHub Webhooks, 저장소 내의 CRUD 사용자 json에 이르기까지 GitHub REST API의 제한 사항은 다음과 같습니다. 1,000 requests per hour per repository 플랫폼의 의도된 사용을 제한하고 오히려 퇴행시키는 것처럼 보였습니다.
Upstash는 처음에는 무료 플랜으로 매일 10,000개의 명령을 제공하고 그 이후에는 사용량이 늘어남에 따라 매우 최소한의 명령을 제공했기 때문에 훨씬 더 눈에 띄었습니다. 이러한 접근 방식을 통해 거의 무료로 더 많은 사용자를 확보하고 데이터베이스 확장 및 관리 비용에 대한 걱정 없이 더 빠르게 반복할 수 있었습니다.
getUserInfo:사용자 프로필 기능 가져오기
getUserInfo 함수는 ioredis의 hget을 사용합니다. 고유한 slug로 식별되는 관련 사용자 프로필 페이지에 대해 Upstash에 API 요청을 하기 위한 키로 슬러그를 사용합니다. .해당 사용자 프로필이 없거나 오류가 있는 경우 함수는 { code: 0 }가 포함된 개체를 반환하도록 설정됩니다. 그러면 사용자는 Astro의 동적 경로를 통해 자동으로 404로 리디렉션될 수 있습니다.
// File: lib/Upstash/users/get.js
// Read User Profile Code
import redis from "../setup";
export async function getUserInfo(slug) {
try {
const userData = await redis.hget("profiles", slug);
const parsedData = JSON.parse(userData);
if (parsedData.slug === slug) {
return { ...parsedData, code: 1 };
}
return {
code: 0,
error: `slug doesn't match for the user.`,
};
} catch (e) {
const error = e.message || e.toString();
console.log(error);
return {
code: 0,
error,
};
}
} 마찬가지로 나머지 CRUD 작업은 다음과 같습니다:
import redis from "../setup";
// File: @/lib/Upstash/users/delete.js
// Delete User Profile Code
export async function deleteUserInfo(slug) {
try {
await redis.hdel("profiles", slug);
return { code: 1 };
} catch (e) {
console.log(e.message || e.toString());
return {
code: 0,
};
}
}
// File: @/lib/Upstash/users/post.js
// Create/Update User Profile Code
export async function postUserInfo(data) {
try {
await redis.hset("profiles", data.slug, JSON.stringify(data));
return { code: 1 };
} catch (e) {
const error = e.message || e.toString();
console.log(error);
return {
code: 0,
error,
};
}
} 속도 제한
Edgio를 사용하여 서버리스 환경에서 속도 제한을 구현하기 위해 Upstash Redis 데이터베이스 클라이언트와 @upstash/ratelimit라는 속도 제한기 라이브러리를 사용합니다. .
// Reference Function to ratelimiting
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
import { getENV } from "@/lib/env";
const url = getENV("UPSTASH_REDIS_REST_URL");
const token = getENV("UPSTASH_REDIS_REST_TOKEN");
export const ratelimit = (number, time) => {
if (url && token) {
return new Ratelimit({
redis: new Redis({
url,
token,
}),
limiter: Ratelimit.fixedWindow(number, time),
});
}
return;
}; Rate Limiting을 사용하여 다음을 달성할 수 있었습니다:
아. 서비스 이용 - 완전 무료 및 무제한
Rate Limiting을 사용하면 프로필 생성 API를 공개적으로 공개할 수 있습니다! 이를 통해 GUI를 통해 프로필을 쉽게 설정할 수 있다는 시스템의 이점을 보여줄 수 있었습니다. 말 그대로 누구나 웹사이트(itsmy.fyi) 자체를 통해 일주일에 3개의 프로필을 만들 수 있으며, 프로필 편집, 무제한 프로필 생성과 같은 기능에 무제한으로 액세스하려면 사용자는 프로필 생성 방식인 GitHub로 전환해야 합니다. IP 주소를 기반으로 일주일에 3개의 프로필에 대한 속도 제한을 시행할 수 있습니다. 키.
// Rate limit 3 profiles in a week via the web for a user
const ratelimitUser = ratelimit(3, 7 * 24 * 60 * 60 + " s");
if (rateLimiter) {
// Look at the x-0-client-ip set by Edgio in serverless
const result = await rateLimiter.limit("x-0-client-ip");
limit = result.limit;
remaining = result.remaining;
if (!result.success) {
// Return a message
}
} 베. 사용자의 편집 횟수에 대한 세분화된 조정 구현
또한 속도 제한을 통해 GitHub 사용자 이름을 기반으로 사용자가 1분 안에 편집한 횟수를 조절할 수 있습니다. 현재로서는 1분 내에 최대 3개까지 변경할 수 있습니다. 이는 보이지 않는 스팸을 줄이는 데 도움이 됩니다.
@/pages/github/hook/issue.jsconst rateLimiter = ratelimit(3, "60 s");
if (rateLimiter) {
const result = await rateLimiter.limit(context.sender.login);
limit = result.limit;
remaining = result.remaining;
if (!result.success) {
return {
headers: {
"X-RateLimit-Limit": limit,
"X-RateLimit-Remaining": remaining,
},
body: JSON.stringify({
message:
"Too many updates in 1 minute. Please try again in a few minutes.",
}),
};
}
} 모든 사용자 프로필에 대해 에지에서 오래된 유효성 재검사 구현
다음 코드는 재검증하는 동안 오래된 개념을 사용하여 캐시 적중률을 높이는 방법을 설명합니다. 코드(routes.js)에서 ), router.match 기능은 모든 사용자 프로필(/me/으로 시작)을 일치시키는 데 사용됩니다. ). 캐시 방법 내에서는 브라우저의 페이지 캐싱을 방지하고 엣지 캐싱만 활성화하여 사용자에게 항상 최신 콘텐츠를 빠르게 제공합니다. 엣지 옵션은 maxAgeSeconds: 1로 설정됩니다. 데이터가 1초 동안만 캐시되도록 합니다. staleWhileRevalidateSeconds 캐시가 새로 고쳐지는 동안 캐시에서 직접 데이터를 제공할 수 있도록 옵션이 1년으로 설정되어 있습니다.
// User path(s)
router.match("/me/:path", ({ cache, removeUpstreamResponseHeader }) => {
// Remove the cache-control header from Astro's standalone server
removeUpstreamResponseHeader("cache-control");
// Disable in browser caching, and use Edgio's edge to use SWR
cache({
edge: {
maxAgeSeconds: 1,
staleWhileRevalidateSeconds: 60 * 60 * 24 * 365,
},
browser: false,
});
}); 재검증하는 동안 오래된 기능을 사용하면 서버의 로드를 줄이고 사용자에게 더 빠른 응답을 제공하여 앱 성능을 향상시키는 데 도움이 될 수 있습니다.
즉시 동적 사용자 프로필 만들기
Astro를 사용하면 동적 경로를 매우 쉽게 설정할 수 있습니다. 앱에서 src/pages/me/[slug].astro을 찾을 수 있습니다. , /me/로 시작하는 페이지를 매핑합니다. .예에는 /me/rishi-raj-jain이 포함됩니다. 및 /me/some-other-user .
사용자 프로필을 가져오는 중
slug을 사용하여 현재 사용자에 대한 데이터를 가져옵니다. Astro 매개변수에서 추출된 쿼리 매개변수를 사용하고 getUserInfo 함수(위에 설명된 대로)를 호출하여 관련된 모든 사용자 데이터를 가져옵니다. 찾을 수 없거나 오류가 있는 경우 방문자를 404로 리디렉션합니다.
import { getUserInfo } from "@/lib/Upstash/users";
// Extract slug query
const { slug } = Astro.params;
// Get data from Upstash using the getUserInfo function
const {
name: userName,
image: userImage,
links = [],
socials = [],
about = "",
og = {},
background = {},
code = 1,
} = await getUserInfo(userSlug);
// In case the code: 0 is recevied, redirect to a 404
if (code === 0) {
return Astro.redirect("/404");
} CLI에서 배포
다음을 사용하여 앱의 프로덕션 빌드를 수행하고 로컬에서 테스트할 수 있습니다.
yarn run edgio:build && yarn run edgio:production 배포하려면 Edgio에 계정이 필요합니다. 여기에서 무료로 가입하세요. 계정이 있으면 프로젝트의 루트 폴더에서 다음 명령을 실행하여 Edgio에 배포할 수 있습니다.
yarn run edgio:deploy 이제 배포가 완료되었습니다! 네, 그게 전부였습니다.
결론
결론적으로, 이 프로젝트는 세분화된 속도 제한 구현, 서버리스에서의 CRUD 데이터 작업, GitHub 문제를 CMS로 사용, 필요에 따라 확장되는 서비스(예:Upstash)를 사용하여 MVP 제공에 대한 더 나은 결정을 내리는 데 귀중한 경험을 제공했습니다.