🌐 웹개발

Svelte

Svelte Framework

컴파일 타임에 JavaScript로 변환되는 UI 프레임워크. Virtual DOM 없이 직접 DOM 조작.

📖 상세 설명

Svelte는 Rich Harris가 개발한 컴파일러 기반 UI 프레임워크입니다. React, Vue와 달리 런타임 프레임워크 없이 빌드 시 순수 JavaScript로 컴파일됩니다. Virtual DOM diffing 없이 직접 DOM을 조작하여 작은 번들 크기와 뛰어난 런타임 성능을 제공합니다.

Svelte의 핵심 철학은 "Less code, more done"입니다. 리액티비티가 언어 수준에서 지원되어 $ 기호만으로 반응형 선언이 가능합니다. let count = 0; 만으로 반응형 변수가 되고, $: doubled = count * 2; 로 파생 값이 자동 계산됩니다. useState, useEffect 같은 훅이 필요 없습니다.

Svelte 5(2024)에서는 Runes라는 새로운 리액티비티 시스템이 도입되었습니다. $state(), $derived(), $effect() 등의 rune으로 더 명시적인 반응성 제어가 가능합니다. 기존 $ 문법과 공존하면서 컴포넌트 외부에서도 반응형 로직을 쉽게 재사용할 수 있게 되었습니다.

SvelteKit은 Next.js에 대응되는 Svelte의 풀스택 프레임워크입니다. 파일 기반 라우팅, SSR/SSG/ISR, API Routes, 레이아웃 시스템을 제공합니다. Vercel이 Svelte를 만든 Rich Harris를 채용하여 적극 지원하고 있습니다.

💻 코드 예제

<!-- Svelte 5 Runes 예제 -->
<script lang="ts">
  // $state - 반응형 상태
  let count = $state(0);
  let name = $state('');
  let items = $state<string[]>([]);

  // $derived - 파생 값 (자동 계산)
  const doubled = $derived(count * 2);
  const isValid = $derived(name.length >= 3);

  // $effect - 사이드 이펙트 (useEffect 대체)
  $effect(() => {
    console.log(`Count changed: ${count}`);
    // 클린업 함수 반환 가능
    return () => console.log('Cleanup');
  });

  // 이벤트 핸들러
  function increment() {
    count++;  // 직접 변경 가능
  }

  function addItem() {
    if (name) {
      items.push(name);  // 배열 직접 수정 가능
      name = '';
    }
  }

  // Props 정의 (Svelte 5)
  interface Props {
    title: string;
    onSave?: (data: string) => void;
  }
  let { title, onSave }: Props = $props();
</script>

<!-- 템플릿 - HTML과 거의 동일 -->
<div class="container">
  <h1>{title}</h1>

  <button onclick={increment}>
    Count: {count} (doubled: {doubled})
  </button>

  <input
    bind:value={name}
    placeholder="이름 입력"
    class:invalid={!isValid}
  />

  {#if isValid}
    <button onclick={addItem}>추가</button>
  {/if}

  <ul>
    {#each items as item, index (item)}
      <li>
        {index + 1}. {item}
        <button onclick={() => items.splice(index, 1)}>삭제</button>
      </li>
    {:else}
      <li>항목이 없습니다</li>
    {/each}
  </ul>
</div>

<style>
  /* 컴포넌트 스코프 CSS (자동 스코핑) */
  .container {
    padding: 1rem;
  }

  .invalid {
    border-color: red;
  }

  button {
    background: #ff3e00;
    color: white;
    border: none;
    padding: 0.5rem 1rem;
    border-radius: 4px;
    cursor: pointer;
  }
</style>

<!-- 재사용 가능한 반응형 로직 (Svelte 5) -->
<!-- lib/counter.svelte.ts -->
<script context="module" lang="ts">
export function createCounter(initial = 0) {
  let count = $state(initial);

  return {
    get count() { return count; },
    increment: () => count++,
    decrement: () => count--,
    reset: () => count = initial
  };
}
</script>

🗣️ 실무 대화 예시

프레임워크 선택에서:

"번들 크기가 중요한 임베드 위젯이에요. React는 기본 30KB 이상인데, Svelte는 컴포넌트만 번들되어서 훨씬 작아요. 복잡한 상태 관리도 필요 없고 간결한 코드가 장점이에요."

기술 면접에서:

"Svelte가 React보다 빠른 이유는?" - "Virtual DOM diffing이 없어요. Svelte는 컴파일 시점에 어떤 변수가 변경될 때 어떤 DOM을 업데이트할지 결정합니다. 런타임에 비교 연산이 없어서 빠르고, 번들에 프레임워크 코드가 포함되지 않아요."

팀 기술 스택 논의에서:

"Svelte 도입하면 채용이 어렵지 않나요?" - "생태계는 작지만 배우기 쉬워요. React 경험자면 며칠이면 적응합니다. 문법이 직관적이라 오히려 온보딩이 빨라요. 다만 복잡한 라이브러리가 필요하면 React 생태계가 유리합니다."

⚠️ 주의사항

🔗 관련 용어

SvelteKit React Vue Virtual DOM Signals

📚 더 배우기

📄 Svelte 공식 문서 📄 Svelte 인터랙티브 튜토리얼 📄 SvelteKit 문서