Snippet은 "컴포넌트를 만들기엔 과한데, 마크업을 반복하기엔 아까운" 그 중간 지대를 채웁니다.

개념 정의

Snippet 은 Svelte 5에서 도입된 재사용 가능한 마크업 블록입니다. {#snippet name()}...{/snippet}으로 선언하고, {@render name()}으로 렌더링합니다. 별도 컴포넌트 파일을 만들지 않고도 마크업을 재사용할 수 있습니다.

기본 사용법

SVELTE
<script>
  let users = $state([
    { name: '홍길동', role: 'admin', active: true },
    { name: '김철수', role: 'user', active: false },
    { name: '이영희', role: 'editor', active: true },
  ]);
</script>

<!-- Snippet 선언 -->
{#snippet badge(text, variant)}
  <span class="badge badge-{variant}">{text}</span>
{/snippet}

<!-- Snippet 사용 -->
{#each users as user}
  <div class="user-card">
    <h3>{user.name}</h3>
    {@render badge(user.role, user.role === 'admin' ? 'red' : 'blue')}
    {@render badge(user.active ? '활성' : '비활성', user.active ? 'green' : 'gray')}
  </div>
{/each}

<style>
  .badge { padding: 2px 8px; border-radius: 4px; font-size: 0.8rem; color: white; }
  .badge-red { background: #e74c3c; }
  .badge-blue { background: #3498db; }
  .badge-green { background: #27ae60; }
  .badge-gray { background: #95a5a6; }
</style>

컴포넌트 Props로 Snippet 전달

Snippet은 props로 전달하여 컴포넌트의 렌더링을 커스터마이징할 수 있습니다.

SVELTE
<!-- Table.svelte -->
<script>
  let { data, columns, row, header } = $props();
</script>

<table>
  <thead>
    <tr>
      {#if header}
        {@render header()}
      {:else}
        {#each columns as col}
          <th>{col.label}</th>
        {/each}
      {/if}
    </tr>
  </thead>
  <tbody>
    {#each data as item}
      <tr>
        {#if row}
          {@render row(item)}
        {:else}
          {#each columns as col}
            <td>{item[col.key]}</td>
          {/each}
        {/if}
      </tr>
    {/each}
  </tbody>
</table>
SVELTE
<!-- App.svelte -->
<script>
  import Table from './Table.svelte';

  const users = [
    { id: 1, name: '홍길동', email: 'hong@test.com', role: 'admin' },
    { id: 2, name: '김철수', email: 'kim@test.com', role: 'user' },
  ];

  const columns = [
    { key: 'name', label: '이름' },
    { key: 'email', label: '이메일' },
    { key: 'role', label: '역할' },
  ];
</script>

<Table {data} {columns}>
  {#snippet row(user)}
    <td><strong>{user.name}</strong></td>
    <td><a href="mailto:{user.email}">{user.email}</a></td>
    <td>{user.role === 'admin' ? '관리자' : '사용자'}</td>
  {/snippet}
</Table>

Snippet vs 컴포넌트

특성Snippet컴포넌트
별도 파일불필요필요
자체 상태부모 스코프 공유독립적
재사용 범위같은 파일 또는 props어디서든 import
스타일 스코프부모 컴포넌트독립적
용도반복 마크업 조각독립적 UI 단위

재귀 Snippet

SVELTE
<script>
  let tree = $state({
    name: 'root',
    children: [
      { name: 'src', children: [
        { name: 'routes', children: [
          { name: '+page.svelte', children: [] }
        ]},
        { name: 'lib', children: [] }
      ]},
      { name: 'static', children: [] }
    ]
  });
</script>

{#snippet treeNode(node, depth)}
  <div style:padding-left="{depth * 20}px">
    {node.children.length > 0 ? '📁' : '📄'} {node.name}
  </div>
  {#each node.children as child}
    {@render treeNode(child, depth + 1)}
  {/each}
{/snippet}

{@render treeNode(tree, 0)}

면접 포인트

  • "Snippet과 slot의 차이는?": Snippet은 Svelte 5에서 slot을 대체합니다. Snippet은 일반 함수처럼 매개변수를 받을 수 있어 더 유연하고, TypeScript 타입 지원도 우수합니다. slot의 let: 디렉티브보다 직관적입니다.
  • "Snippet을 언제 쓰고 컴포넌트를 언제 쓰나요?": 자체 상태나 독립적인 스타일이 필요하면 컴포넌트, 같은 파일 내에서 마크업 반복을 줄이거나 컴포넌트의 렌더링을 커스터마이징할 때는 Snippet이 적합합니다.

정리

Snippet은 컴포넌트와 인라인 마크업 사이의 빈 공간을 채우는 기능입니다. "이건 컴포넌트로 분리하기엔 작은데, 반복되니까 추출하고 싶다" — 그런 순간에 Snippet이 빛납니다.

댓글 로딩 중...