이 글에서는 SvelteKit과 서버리스 Redis(Upstash)를 활용하여 간단한 TODO 애플리케이션을 만들어 보겠습니다.
SvelteKit은 아름다운 개발 경험과 유연한 파일 시스템 기반 라우팅을 갖춘, 모든 규모의 웹 애플리케이션을 구축하기 위한 프레임워크입니다.
프로젝트 생성
먼저 아래 명령어를 실행하여 프로젝트를 생성합니다:
npm init svelte@next todo-app-with-redis
프로젝트가 준비되었다면, 이제 의존성 패키지를 설치하고 개발 서버를 실행해 보겠습니다:
npm install
npm run dev
사용자 인터페이스(UI) 만들기
TODO 항목을 담을 간단한 폼과 리스트를 생성합니다. 우선은 하드코딩된 데이터로 화면을 구성하고, 이후 데이터베이스와 연결하게 됩니다.
// src/routes/index.svelte
<script lang="ts">
import type {Todo} from "../lib/types";
import TodoItem from "../components/Todo.svelte";
export let todos: Todo[] = [
{
id: '1',
text: 'Create database',
status: true
},
{
id: '2',
text: 'Copy database url',
status: false
}
];
const uncheckedTodos = todos.filter((todo) => !todo.status);
const checkedTodos = todos.filter((todo) => todo.status);
</script>
<main class="container">
<form class="new" action="/" method="post">
<input
type="text"
name="text"
autofocus
aria-label="Add todo"
class="input"
placeholder="What needs to be done?"
/>
</form>
{#if uncheckedTodos.length}
<div class="todos">
{#each uncheckedTodos as todo (todo.id)}
<TodoItem {todo}/>
{/each}
</div>
{/if}
{#if checkedTodos.length}
<div class="todos todos-done">
{#each checkedTodos as todo (todo.id)}
<TodoItem {todo}/>
{/each}
</div>
{/if}
</main>
다음은 개별 TODO 항목을 렌더링하는 컴포넌트입니다:
// src/components/Todo.svelte
<script lang="ts">
import type {Todo} from "../lib/types";
export let todo: Todo;
</script>
<div class="todo">
<form action="/?_method=PATCH" method="post">
<input type="hidden" name="todo" value={JSON.stringify(todo)}/>
<button
class="checkbox"
aria-label="Mark todo as {todo.status ? 'not done' : 'done'}"
>
{todo.status ? "✓" : ""}
</button>
</form>
<span class="text">{todo.text}</span>
<form action="/?_method=DELETE" method="post">
<input type="hidden" name="id" value={todo.id}/>
<button class="delete" aria-label="Delete todo">✕</button>
</form>
</div>
여기까지 완료하면 다음과 같은 화면을 볼 수 있습니다:
데이터베이스 준비하기
데이터는 Upstash Redis에 저장할 예정입니다. 먼저 Upstash 콘솔에서 새로운 데이터베이스를 생성하세요. Upstash는 HTTP 기반 클라이언트를 제공하며, 이는 서버리스(Serverless) 환경에서 특히 잘 동작합니다. 필요한 패키지를 설치합니다:
npm install @upstash/redis
Upstash 콘솔에서 Redis 데이터베이스를 생성한 후, UPSTASH_REDIS_REST_URL과 UPSTASH_REDIS_REST_TOKEN 값을 복사하여 .env 파일에 붙여넣습니다.
UPSTASH_REDIS_REST_URL=https://global-renewing-gecko-31543.upstash.io
UPSTASH_REDIS_REST_TOKEN=AXs3ACsjfg684jJBFQgN34je7RFJ58wYjg4NjMt=
환경 변수를 사용하려면 dotenv 패키지도 설치가 필요합니다.
npm install dotenv
이제 lib/redis.ts 파일을 생성하고, 아래와 같이 Redis 인스턴스를 초기화합니다:
// src/lib/redis.ts
import "dotenv/config";
import { Redis } from "@upstash/redis";
export const databaseName =
process.env.NODE_ENV === "development"
? "redis-with-svelte-kit-dev"
: "redis-with-svelte-kit";
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL,
token: process.env.UPSTASH_REDIS_REST_TOKEN,
});
export default redis;
API 만들기
SvelteKit의 라우트 폴더에서 js/ts 파일은 API 백엔드 역할을 합니다. 즉, index.svelte가 프론트엔드라면 index.ts는 백엔드입니다.
index.ts 파일을 생성하고 필요한 API 메서드들을 추가해 보겠습니다.
// src/routes/index.ts
import redis, { databaseName } from "../lib/redis";
import type { RequestHandler } from "@sveltejs/kit";
const redirect = {
status: 303,
headers: {
location: "/",
},
};
export const get: RequestHandler = async () => {
// 페이지가 로드될 때마다 실행됨
return { body: { todos: [] } };
};
export const post: RequestHandler = async ({ request }) => {
// 폼: TODO 생성
return redirect;
};
export const patch: RequestHandler = async ({ request }) => {
// 폼: TODO 수정
return redirect;
};
export const del: RequestHandler = async ({ request }) => {
// 폼: TODO 삭제
return redirect;
};
POST — TODO 항목 생성
TODO 항목 생성 기능을 구현해 보겠습니다:
// src/routes/index.ts
export const post: RequestHandler = async ({ request }) => {
const form = await request.formData();
const text = form.get("text");
const id = Date.now().toString();
const todo = JSON.stringify({ text, status: false });
await redis.hset(databaseName, id, todo);
return redirect;
};
GET — TODO 목록 불러오기
이제 저장된 TODO 항목들을 조회하여 목록으로 표시합니다:
// src/routes/index.ts
export const get: RequestHandler = async () => {
let todos = [];
const data = await redis.hgetall(databaseName);
if (!data) return { body: { todos } };
// 데이터 정규화
todos = Object.keys(data)
.map((key) => ({
id: key,
text: data[key]["text"],
status: data[key]["status"],
}))
// id = 타임스탬프이므로 내림차순 정렬
.sort((a, b) => parseInt(b.id) - parseInt(a.id));
return { body: { todos } };
};
본문의 간결함을 위해
할 일 삭제와완료 처리코드는 여기에 포함하지 않았지만, GitHub 저장소에서 전체 코드를 확인하실 수 있습니다.
마무리
이제 모든 준비가 끝났습니다! 위 과정을 거치면 SvelteKit과 Upstash Redis가 연동된 TODO 앱이 완성됩니다.
전체 소스 코드는 GitHub에서 확인할 수 있으며, 데모 앱도 함께 제공됩니다.
필자는 동일한 TODO 애플리케이션을 Next.js로도 구현할 계획입니다. 이후 두 프레임워크에서의 개발 경험을 비교하여 공유할 예정이니 많은 관심 부탁드립니다.
최신 소식을 받아보시려면 Twitter와 Discord에서 저희를 팔로우해 주세요!