🧡 Svelte 치트시트
빌드 시점에 최적화되는 현대적인 웹 프레임워크
기본 컴포넌트
컴포넌트 구조
<script>
let count = 0;
let name = 'Svelte';
function increment() {
count += 1;
}
function handleInput(event) {
name = event.target.value;
}
</script>
<div class="container">
<h1>Hello {name}!</h1>
<p>Count: {count}</p>
<input type="text" bind:value={name} placeholder="Enter name">
<button on:click={increment}>Increment</button>
</div>
<style>
.container {
padding: 20px;
text-align: center;
}
button {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
</style>
반응성
<script>
let items = ['apple', 'banana', 'cherry'];
let newItem = '';
function addItem() {
if (newItem.trim()) {
items = [...items, newItem];
newItem = '';
}
}
function removeItem(index) {
items = items.filter((_, i) => i !== index);
}
// 반응성 문
$: itemCount = items.length;
$: hasItems = items.length > 0;
</script>
<div>
<h2>Todo List ({itemCount} items)</h2>
<div>
<input bind:value={newItem} placeholder="Add new item">
<button on:click={addItem}>Add</button>
</div>
{#if hasItems}
<ul>
{#each items as item, index}
<li>
{item}
<button on:click={() => removeItem(index)}>Remove</button>
</li>
{/each}
</ul>
{:else}
<p>No items yet!</p>
{/if}
</div>
Props와 이벤트
<script>
export let title = 'Default Title';
export let count = 0;
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function handleClick() {
count += 1;
dispatch('countChange', { count });
}
</script>
<div class="card">
<h3>{title}</h3>
<p>Count: {count}</p>
<button on:click={handleClick}>Increment</button>
</div>
<style>
.card {
border: 1px solid #ccc;
border-radius: 8px;
padding: 16px;
margin: 8px;
}
</style>
<!-- 사용법 -->
<Card title="My Card" bind:count on:countChange={handleCountChange} />
조건부 렌더링
<script>
let user = { name: 'John', age: 25, isActive: true };
let showDetails = false;
</script>
<div>
<h2>User Profile</h2>
<!-- 조건부 렌더링 -->
{#if user.isActive}
<p class="active">User is active</p>
{:else}
<p class="inactive">User is inactive</p>
{/if}
<!-- 중첩 조건 -->
{#if user.age >= 18}
{#if user.age >= 65}
<p>Senior user</p>
{:else}
<p>Adult user</p>
{/if}
{:else}
<p>Minor user</p>
{/if}
<!-- 토글 버튼 -->
<button on:click={() => showDetails = !showDetails}>
{showDetails ? 'Hide' : 'Show'} Details
</button>
{#if showDetails}
<div class="details">
<p>Name: {user.name}</p>
<p>Age: {user.age}</p>
</div>
{/if}
</div>
고급 기능
스토어
// stores.js
import { writable, readable, derived } from 'svelte/store';
// 쓰기 가능한 스토어
export const count = writable(0);
// 읽기 전용 스토어
export const time = readable(new Date(), function start(set) {
const interval = setInterval(() => {
set(new Date());
}, 1000);
return function stop() {
clearInterval(interval);
};
});
// 파생 스토어
export const elapsed = derived(
time,
$time => Math.round(($time - start) / 1000)
);
// 컴포넌트에서 사용
<script>
import { count, time, elapsed } from './stores.js';
function increment() {
count.update(n => n + 1);
}
function reset() {
count.set(0);
}
</script>
<div>
<p>Count: {$count}</p>
<p>Time: {$time}</p>
<p>Elapsed: {$elapsed}s</p>
<button on:click={increment}>Increment</button>
<button on:click={reset}>Reset</button>
</div>
애니메이션
<script>
import { fade, fly, slide } from 'svelte/transition';
import { tweened } from 'svelte/motion';
let visible = true;
let progress = tweened(0);
function toggle() {
visible = !visible;
}
function animateProgress() {
progress.set(100);
}
</script>
<div>
<button on:click={toggle}>Toggle</button>
<button on:click={animateProgress}>Animate Progress</button>
{#if visible}
<div transition:fade>
Fade transition
</div>
{/if}
{#if visible}
<div transition:fly={{ x: 100, duration: 500 }}>
Fly transition
</div>
{/if}
{#if visible}
<div transition:slide>
Slide transition
</div>
{/if}
<div class="progress-bar">
<div class="progress" style="width: {$progress}%"></div>
</div>
</div>
<style>
.progress-bar {
width: 100%;
height: 20px;
background-color: #f0f0f0;
border-radius: 10px;
overflow: hidden;
}
.progress {
height: 100%;
background-color: #4CAF50;
transition: width 0.3s ease;
}
</style>
액션
<script>
import { fly } from 'svelte/transition';
function longpress(node, duration) {
let timer;
const handleMousedown = () => {
timer = setTimeout(() => {
node.dispatchEvent(new CustomEvent('longpress'));
}, duration);
};
const handleMouseup = () => {
clearTimeout(timer);
};
node.addEventListener('mousedown', handleMousedown);
node.addEventListener('mouseup', handleMouseup);
return {
destroy() {
node.removeEventListener('mousedown', handleMousedown);
node.removeEventListener('mouseup', handleMouseup);
}
};
}
</script>
<button
use:longpress={500}
on:longpress={() => alert('Long press detected!')}
transition:fly={{ y: 20, duration: 200 }}
>
Hold me for 500ms
</button>
커스텀 스토어
// customStore.js
import { writable } from 'svelte/store';
function createCount() {
const { subscribe, set, update } = writable(0);
return {
subscribe,
increment: () => update(n => n + 1),
decrement: () => update(n => n - 1),
reset: () => set(0)
};
}
export const count = createCount();
// 컴포넌트에서 사용
<script>
import { count } from './customStore.js';
</script>
<div>
<p>Count: {$count}</p>
<button on:click={count.increment}>+</button>
<button on:click={count.decrement}>-</button>
<button on:click={count.reset}>Reset</button>
</div>