🧡 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>
더 많은 Svelte 학습 자료

체계적인 학습을 위해 다음 자료들도 확인해보세요