Svelte 완벽 가이드 | 컴파일러 기반 프레임워크·Reactivity·SvelteKit·실전 활용
이 글의 핵심
Svelte로 고성능 웹 앱을 개발하는 완벽 가이드입니다. Reactivity, Components, Stores, SvelteKit, TypeScript까지 실전 예제로 정리했습니다.
실무 경험 공유: React에서 Svelte로 전환하면서, 번들 크기가 70% 감소하고 성능이 크게 향상된 경험을 공유합니다.
들어가며: “Virtual DOM이 느려요”
실무 문제 시나리오
시나리오 1: 번들 크기가 커요
React는 런타임이 필요합니다. Svelte는 컴파일러라 작습니다.
시나리오 2: 보일러플레이트가 많아요
useState, useEffect가 번거롭습니다. Svelte는 간단합니다.
시나리오 3: 성능이 중요해요
Virtual DOM 오버헤드가 있습니다. Svelte는 컴파일 타임에 최적화합니다.
1. Svelte란?
핵심 특징
Svelte는 컴파일러 기반 프론트엔드 프레임워크입니다.
주요 장점:
- 컴파일러: 런타임 없음
- 작은 번들: 3KB
- 빠른 성능: Virtual DOM 없음
- 간단한 문법: 보일러플레이트 최소
- Reactivity: 자동 반응형
2. 설치 및 프로젝트 생성
SvelteKit
npm create svelte@latest my-app
cd my-app
npm install
npm run dev
Vite + Svelte
npm create vite@latest my-app -- --template svelte-ts
3. 기본 컴포넌트
<script lang="ts">
let count = 0;
function increment() {
count += 1;
}
</script>
<div>
<h1>Count: {count}</h1>
<button on:click={increment}>Increment</button>
</div>
<style>
div {
padding: 2rem;
text-align: center;
}
h1 {
font-size: 2rem;
color: #333;
}
button {
padding: 0.5rem 1rem;
background: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
opacity: 0.8;
}
</style>
4. Reactivity
기본 Reactivity
<script lang="ts">
let count = 0;
$: doubled = count * 2;
$: console.log(`Count is ${count}`);
</script>
<p>Count: {count}</p>
<p>Doubled: {doubled}</p>
<button on:click={() => count++}>Increment</button>
배열/객체 Reactivity
<script lang="ts">
let items = [1, 2, 3];
function addItem() {
items = [...items, items.length + 1];
}
</script>
<ul>
{#each items as item}
<li>{item}</li>
{/each}
</ul>
<button on:click={addItem}>Add Item</button>
5. Props
<!-- Button.svelte -->
<script lang="ts">
export let label: string;
export let variant: 'primary' | 'secondary' = 'primary';
</script>
<button class={variant}>
{label}
</button>
<style>
.primary {
background: #3498db;
color: white;
}
.secondary {
background: #2ecc71;
color: white;
}
</style>
<!-- App.svelte -->
<script lang="ts">
import Button from './Button.svelte';
</script>
<Button label="Primary" variant="primary" />
<Button label="Secondary" variant="secondary" />
6. Stores
// stores.ts
import { writable, derived } from 'svelte/store';
export const count = writable(0);
export const doubled = derived(count, ($count) => $count * 2);
// Component.svelte
<script lang="ts">
import { count, doubled } from './stores';
</script>
<p>Count: {$count}</p>
<p>Doubled: {$doubled}</p>
<button on:click={() => $count++}>Increment</button>
7. SvelteKit
파일 기반 라우팅
src/routes/
├── +page.svelte # /
├── about/
│ └── +page.svelte # /about
└── users/
└── [id]/
└── +page.svelte # /users/:id
+page.svelte
<script lang="ts">
export let data;
</script>
<h1>User: {data.user.name}</h1>
+page.ts (Loader)
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ params, fetch }) => {
const response = await fetch(`/api/users/${params.id}`);
const user = await response.json();
return { user };
};
8. Form Actions
<!-- +page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
</script>
<form method="POST" use:enhance>
<input name="email" type="email" required />
<input name="password" type="password" required />
<button type="submit">Login</button>
</form>
// +page.server.ts
import type { Actions } from './$types';
export const actions: Actions = {
default: async ({ request }) => {
const data = await request.formData();
const email = data.get('email');
const password = data.get('password');
// 로그인 로직
return { success: true };
},
};
9. 실전 예제: Todo 앱
<script lang="ts">
interface Todo {
id: number;
text: string;
done: boolean;
}
let todos: Todo[] = [
{ id: 1, text: 'Learn Svelte', done: false },
{ id: 2, text: 'Build an app', done: false },
];
let newTodo = '';
function addTodo() {
if (!newTodo.trim()) return;
todos = [...todos, { id: Date.now(), text: newTodo, done: false }];
newTodo = '';
}
function toggleTodo(id: number) {
todos = todos.map((todo) =>
todo.id === id ? { ...todo, done: !todo.done } : todo
);
}
function deleteTodo(id: number) {
todos = todos.filter((todo) => todo.id !== id);
}
$: remaining = todos.filter((t) => !t.done).length;
</script>
<div class="container">
<h1>Todo App</h1>
<div class="input-group">
<input
bind:value={newTodo}
on:keydown={(e) => e.key === 'Enter' && addTodo()}
placeholder="Add todo"
/>
<button on:click={addTodo}>Add</button>
</div>
<p>{remaining} remaining</p>
<ul>
{#each todos as todo (todo.id)}
<li class:done={todo.done}>
<input type="checkbox" checked={todo.done} on:change={() => toggleTodo(todo.id)} />
<span>{todo.text}</span>
<button on:click={() => deleteTodo(todo.id)}>Delete</button>
</li>
{/each}
</ul>
</div>
<style>
.container {
max-width: 600px;
margin: 0 auto;
padding: 2rem;
}
.input-group {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
}
input[type='text'] {
flex: 1;
padding: 0.5rem;
border: 1px solid #ddd;
border-radius: 4px;
}
ul {
list-style: none;
padding: 0;
}
li {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
border-bottom: 1px solid #eee;
}
li.done span {
text-decoration: line-through;
opacity: 0.5;
}
</style>
정리 및 체크리스트
핵심 요약
- Svelte: 컴파일러 기반 프레임워크
- 작은 번들: 3KB
- 빠른 성능: Virtual DOM 없음
- 간단한 문법: 보일러플레이트 최소
- Reactivity: 자동 반응형
- SvelteKit: 풀스택 프레임워크
구현 체크리스트
- Svelte 설치
- 기본 컴포넌트 작성
- Reactivity 활용
- Props 전달
- Stores 사용
- SvelteKit 구현
- Form Actions
- 배포
같이 보면 좋은 글
- React 완벽 가이드
- Vue 3 Composition API 가이드
- SvelteKit 완벽 가이드
이 글에서 다루는 키워드
Svelte, SvelteKit, Compiler, Reactivity, Frontend, TypeScript, Performance
자주 묻는 질문 (FAQ)
Q. React와 비교하면 어떤가요?
A. Svelte가 더 간단하고 빠릅니다. React는 생태계가 더 큽니다.
Q. 학습 곡선은 어떤가요?
A. React보다 낮습니다. 문법이 간단합니다.
Q. 프로덕션에서 사용해도 되나요?
A. 네, 많은 기업에서 안정적으로 사용하고 있습니다.
Q. TypeScript를 지원하나요?
A. 네, 완벽하게 지원합니다.