SvelteKit을 사용하여 URL 단축기를 만드는 방법.
자원
소개
이것은 프로젝트 가이드입니다. 스스로 단계별로 따르도록 제안합니다. 이 기사에서는 Svelte, SvelteKit, Redis 및 TailwindCSS를 사용하여 URL 단축기를 만드는 방법을 배웁니다.
초기화
우리는 sveltekit 스타터 프로젝트부터 시작할 것입니다. 그런 다음 TailwindCSS를 설정합니다(my 를 읽는 방법을 모르는 경우).
1. redis
2. dotenv
.env
파일을 만듭니다. 이 프로젝트에서 우리는 Redis를 사용할 것이고 저는 DB용 PaaS 공급자인 Railway을 사용하고 있습니다.// .env
REDIS_URL=redis://localhost:6379 // Use your own it's for example.
src
디렉토리에 hooks.ts
라는 이름으로 새 파일을 만듭니다.// hooks.ts
import dotenv from 'dotenv';
dotenv.config();
이것은 svelteKit에서 환경 변수를 로드합니다.
Redis에 서버 연결
Make sure you have installed it in your project if you haven't then run this command in your terminal
npm i redis
lib
디렉토리에 src
폴더를 만들고 lib
폴더에 redisConnection.ts
파일을 만듭니다. 이 파일에서 Redis
연결을 처리하고 set
또는 get
값과 Redis
간의 기본 기능을 추가할 것입니다.// redisConnection.ts
import { createClient } from 'redis';
import log from './log';
const client = createClient({ url: process.env.REDIS_URL as string });
let connectPromise: Promise<void> | undefined;
let errorOnce = true;
async function autoConnect(): Promise<void> {
if (!connectPromise) {
errorOnce = true;
connectPromise = new Promise((resolve, reject) => {
client.once('error', (err) => reject(new Error(`Redis: ${err.message}`)));
client.connect().then(resolve, reject);
});
}
await connectPromise;
}
client.on('error', (err) => {
if (errorOnce) {
log.error('Redis:', err);
errorOnce = false;
}
});
client.on('connect', () => {
log('Redis up');
});
client.on('disconnect', () => {
connectPromise = undefined;
log('Redis down');
});
async function get<T>(key: string): Promise<T | undefined>;
async function get<T>(key: string, fallback: T): Promise<T>;
async function get<T>(key: string, fallback?: T): Promise<T | undefined> {
await autoConnect();
const value = await client.get(key);
if (value === null) {
return fallback;
}
return JSON.parse(value);
}
async function set(
key: string,
value: unknown,
options?: { ttl: number } // TTL in seconds
): Promise<void> {
const data = JSON.stringify(value);
const config = options ? { EX: options.ttl } : {};
await autoConnect();
await client.set(key, data, config);
await client.publish(key, data);
}
const storage = {
get,
set
};
export default storage;
Here I'm using logger too, so skip logger for now. If you wanna learn how i did it check out Multiplayer Dice Game by bfanger on github. He is using
socket.io
,redis
and many other things, you gonna learn alot from this.
--
redis
에서 node
에 대한 클라이언트를 만듭니다. createClient
에서 redis
를 사용합니다. REDIS_URL
에 추가한 .env
가 필요합니다.const client = createClient({ url: process.env.REDIS_URL as string });
-- 우리는 redis에서
Redis
또는 set
값을 사용할 때 get
에 연결하는 autoConnect 기능을 추가할 것입니다.let connectPromise: Promise<void> | undefined;
let errorOnce = true;
async function autoConnect(): Promise<void> {
if (!connectPromise) {
errorOnce = true;
connectPromise = new Promise((resolve, reject) => {
client.once('error', (err) => reject(new Error(`Redis: ${err.message}`)));
client.connect().then(resolve, reject);
});
}
await connectPromise;
}
-- 이제 우리는 redis와의 연결을 확인하고 이에 따라 응답하는 3개의 개시자를 추가했습니다.
error
, connected
및 disconnected
.client.on('error', (err) => {
if (errorOnce) {
log.error('Redis:', err);
errorOnce = false;
}
});
client.on('connect', () => {
log('Redis up');
});
client.on('disconnect', () => {
connectPromise = undefined;
log('Redis down');
});
-- 이제 redis에서 데이터를 가져오는 기능 및 필요 기능을 추가할 것입니다.
async function get<T>(key: string): Promise<T | undefined>;
async function get<T>(key: string, fallback: T): Promise<T>;
async function get<T>(key: string, fallback?: T): Promise<T | undefined> {
await autoConnect();
const value = await client.get(key);
if (value === null) {
return fallback;
}
return JSON.parse(value);
}
Here we first defined the types of function. Function takes one parameter
key
which is required to find value inredis
. First we going to connect withredis
using our auto connect function then going to get the value using our providedkey
and then we going to return parsed JSON.
-- 이제
redis
에 값을 추가하는 데 도움이 되는 set 함수를 추가하겠습니다. 설정 함수는 key
및 value
두 개의 매개변수를 사용합니다. redis
에서 항목을 가져오는 데 도움이 되는 고유한 키가 필요합니다.async function set(
key: string,
value: unknown,
options?: { ttl: number } // TTL in seconds
): Promise<void> {
const data = JSON.stringify(value);
const config = options ? { EX: options.ttl } : {};
await autoConnect();
await client.set(key, data, config);
await client.publish(key, data);
}
-- 마지막으로 프로젝트의 어느 곳에서나 사용할 수 있도록 내보낼 것입니다.
const storage = {
get,
set
};
export default storage;
이것이 우리 프로젝트에서 작업하기 위해 redis에 추가해야 하는 전부입니다.
프런트엔드 및 섀도우 엔드포인트 추가
여기서 우리는 입력 상자와 버튼을 만들기 위해 html과 tailwindcss를 추가할 것입니다.
// index.svelte
<script lang="ts">
import { page } from '$app/stores';
import Clipboard from '$lib/Clipboard.svelte';
let url: string;
let isURLGenerated: boolean = false;
let isInvalidURL: boolean = false;
function isValidHttpUrl(string) {
let url;
try {
url = new URL(string);
} catch (_) {
return false;
}
return url.protocol === 'http:' || url.protocol === 'https:';
}
async function getURL() {
if (isValidHttpUrl(url)) {
const r = (Math.random() + 1).toString(36).substring(7);
const redirectURL = `${$page.url}${r}`;
isInvalidURL = false;
const data = { key: r, url: url };
await fetch('/', {
method: 'POST',
headers: {
accept: 'application/json'
},
body: JSON.stringify(data)
});
url = redirectURL;
isURLGenerated = true;
} else isInvalidURL = true;
}
</script>
<svelte:head>
<title>Shrink | Home</title>
</svelte:head>
<div class="bg-white w-screen h-screen flex flex-col justify-center text-center">
<h1 class="text-6xl p-4 text-fuchsia-500 font-bold">Shrink Me, Web.</h1>
<div class="flex justify-center w-full p-10">
<div class="mb-3 xl:w-2/4">
{#if isInvalidURL}
<div
id="alert-border-2"
class="flex p-4 mb-4 bg-red-100 border-t-4 border-red-500 dark:bg-red-200"
role="alert"
>
<svg
class="flex-shrink-0 w-5 h-5 text-red-700"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
><path
fill-rule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
clip-rule="evenodd"
/></svg
>
<div class="ml-3 text-sm font-medium text-red-700">You have typed wrong URL.</div>
</div>
{/if}
<div class="input-group relative flex items-stretch w-full mb-4">
<input
type="text"
class="form-control relative flex-auto block w-full border-b-2 px-3 py-1.5 text-base font-normal text-gray-700 bg-white bg-clip-padding transition ease-in-out m-0 focus:outline-none duration-300 focus:text-gray-700 focus:bg-white {isURLGenerated
? 'border-emerald-600 focus:border-emerald-600'
: 'border-fuchsia-600 focus:border-fuchsia-600'}"
placeholder="Paste or type your URL"
aria-label="url"
aria-describedby="url"
bind:value={url}
/>
<Clipboard
text={url}
let:copy
on:copy={() => {
console.log('Has Copied');
}}
>
{#if isURLGenerated}
<button
on:click={copy}
class="inline-block px-6 py-2 border-2 border-emerald-600 bg-emerald-600 text-white font-medium text-xs leading-tight uppercase hover:bg-white hover:text-emerald-600 transition duration-300 ease-in-out"
type="button"
id="button-copy">Copy</button
>
<button
on:click={() => {
url = '';
isURLGenerated = false;
}}
class="inline-block px-6 py-2 border-2 border-rose-600 bg-rose-600 text-white font-medium text-xs leading-tight uppercase hover:bg-white hover:text-rose-600 transition duration-300 ease-in-out"
type="button"
id="button-reset">Reset</button
>
{:else}
<button
on:click={getURL}
class="inline-block px-6 py-2 border-2 border-fuchsia-600 bg-fuchsia-600 text-white font-medium text-xs leading-tight uppercase hover:bg-white hover:text-fuchsia-600 transition duration-300 ease-in-out"
type="button"
id="button-addon3">Shrink</button
>
{/if}
</Clipboard>
</div>
<div class="flex justify-center items-center">
<div class="spinner-grow inline-block w-8 h-8 bg-fuchsia-600 rounded-full opacity-0" />
</div>
</div>
</div>
</div>
설명
여기서는 Shadow Endpoint에 대한 기본 기능 및 요청 작성에 대해 설명하겠습니다. 이전 게시물의 html 및 tailwind 부분을 이해하십시오.
-- 먼저 pur
script
태그에 초점을 맞출 것입니다.<script lang="ts">
import { page } from '$app/stores';
import Clipboard from '$lib/Clipboard.svelte';
let url: string;
let isURLGenerated: boolean = false;
let isInvalidURL: boolean = false;
function isValidHttpUrl(string) {
let url;
try {
url = new URL(string);
} catch (_) {
return false;
}
return url.protocol === 'http:' || url.protocol === 'https:';
}
async function getURL() {
if (isValidHttpUrl(url)) {
const r = (Math.random() + 1).toString(36).substring(7);
const redirectURL = `${$page.url}${r}`;
isInvalidURL = false;
const data = { key: r, url: url };
await fetch('/', {
method: 'POST',
headers: {
accept: 'application/json'
},
body: JSON.stringify(data)
});
url = redirectURL;
isURLGenerated = true;
} else isInvalidURL = true;
}
</script>
Here, I have to define are parameters. Those are URL : which is provided by the user, isURLGenerated : checking if url generated or not (we need it to change are buttons from generate to copy), isInvalidURL : is defined for html to activate alert if url is not valid.
isValidHttpUrl
: This function help to verify our URL which is provided by user is a valid URL or not.
getURL
: This function generates are short url. First, I have added check forisValidHttpUrl
if this is valid we gonna proceed otherwise we gonna returnisInvalidURL = true
. If URL is valid then going to generate a random string and after that we going to make a request to our shadow endpoint/api to save ourkey
generated string andurl
provided by the user.
섀도우 엔드포인트 또는 API
// index.ts
import storage from '$lib/redisConnection';
import type { RequestHandler } from '@sveltejs/kit';
export const POST: RequestHandler = async ({ request }) => {
const data = await request.json();
await storage.set(data.key, data.url);
return {
body: {
status: 200,
error: null
}
};
};
-- 여기에
POST
에서 요청하는 동안 제공하는 데이터를 가져오는 요청 매개변수를 사용하는 index.svelte
메서드 처리를 추가했습니다. 여기에서 set 함수를 사용하여 해당 데이터를 redis에 저장합니다.이것이 유효한 짧은 URL을 생성하고 redis에 저장하는 데 필요한 전부입니다. 이제
how we redirect user to URL when visit using short URL generated by you
.원래 URL 가져오기 및 URL로 리디렉션
이 섹션에서는 URL에서 매개 변수를 가져오고 redis에서 데이터를 가져온 다음 원래 URL로 리디렉션하는 방법을 배웁니다.
--
routes
디렉토리[slug].ts
에 새 파일을 추가하고 다음 코드 줄을 추가합니다.// [slug].ts
import storage from '$lib/redisConnection';
import type { RequestHandler } from '@sveltejs/kit';
export const GET: RequestHandler = async ({ params }) => {
if (params.slug.length > 3) {
return {
headers: {
Location: await storage.get(params.slug)
},
status: 302
};
} else
return {
headers: {
Location: '/'
},
status: 302,
error: new Error('Short URL doesn't exist')
};
};
Here we have added a
GET
method which will be called when we gonna visit any short url example. We are using {params} which is an inbuilt dictionary which contains our slug of the page.I have added condition to check length of the slug(generated string in
index.svelte
) and using that we gonna query our redis using get function and going to get the value which is our saved original URL. I have addedLocation
in headers which helps us to redirect to that URL and added a status or redirect. If condition fails its going to redirected to home of our URL Shortner.
원래 URL을 가져오고 리디렉션하는 방법입니다.
그것이 기사의 끝입니다. 이 게시물을 훨씬 더 잘 이해하는 데 도움이 되는 언급된 모든 리소스와 링크를 확인해야 합니다.
이것은 내가 당신을 위해 쓰는 것입니다. 묻고 싶은 것이나 제안하고 싶은 것이 있다면 댓글에 적어주세요.
Reference
이 문제에 관하여(SvelteKit을 사용하여 URL 단축기를 만드는 방법.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/theether0/how-to-make-a-url-shortner-using-sveltekit-55kc텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)