svelte:window로 윈도우 이벤트를, svelte:head로 메타 태그를 — 컴포넌트 안에서 전역 요소를 다루는 Svelte만의 방법입니다.

개념 정의

Special Elements 는 svelte: 접두사로 시작하는 Svelte 전용 요소입니다. 일반 HTML로는 표현할 수 없는 동적 컴포넌트 렌더링, 윈도우/바디 이벤트 바인딩, HEAD 조작 등을 가능하게 합니다.

svelte:component — 동적 컴포넌트

SVELTE
<script>
  import Home from './Home.svelte';
  import About from './About.svelte';
  import Contact from './Contact.svelte';

  const routes = { home: Home, about: About, contact: Contact };
  let currentRoute = $state('home');
</script>

<nav>
  <button onclick={() => currentRoute = 'home'}>홈</button>
  <button onclick={() => currentRoute = 'about'}>소개</button>
  <button onclick={() => currentRoute = 'contact'}>연락</button>
</nav>

<!-- this에 컴포넌트를 동적으로 전달 -->
<svelte:component this={routes[currentRoute]} />

svelte:element — 동적 HTML 태그

SVELTE
<script>
  let { level = 1, children } = $props();

  // h1~h6 중 동적으로 선택
  let tag = $derived(`h${Math.min(Math.max(level, 1), 6)}`);
</script>

<!-- 태그명을 동적으로 결정 -->
<svelte:element this={tag}>
  {@render children()}
</svelte:element>
SVELTE
<!-- 사용 예시 -->
<Heading level={1}>제목 1</Heading>
<Heading level={2}>제목 2</Heading>
<Heading level={3}>제목 3</Heading>

svelte:window — 윈도우 이벤트

SVELTE
<script>
  let innerWidth = $state(0);
  let innerHeight = $state(0);
  let scrollY = $state(0);
  let online = $state(true);

  function handleKeydown(e) {
    if (e.key === 'Escape') {
      console.log('ESC 키 눌림');
    }
  }
</script>

<!-- 윈도우 이벤트 리스너 (자동 정리됨) -->
<svelte:window
  onkeydown={handleKeydown}
  onscroll={() => {}}
  bind:innerWidth
  bind:innerHeight
  bind:scrollY
  bind:online
/>

<p>화면 크기: {innerWidth} x {innerHeight}</p>
<p>스크롤: {scrollY}px</p>
<p>네트워크: {online ? '온라인' : '오프라인'}</p>

<!-- 스크롤에 따른 헤더 숨기기 -->
<header class:hidden={scrollY > 100}>
  네비게이션 바
</header>

<style>
  header { position: fixed; top: 0; width: 100%; transition: transform 0.3s; }
  .hidden { transform: translateY(-100%); }
</style>

svelte:document — 문서 이벤트

SVELTE
<script>
  function handleVisibilityChange() {
    if (document.hidden) {
      console.log('탭 비활성화');
    } else {
      console.log('탭 활성화');
    }
  }

  function handleSelectionChange() {
    const selection = document.getSelection()?.toString();
    if (selection) {
      console.log('선택된 텍스트:', selection);
    }
  }
</script>

<svelte:document
  onvisibilitychange={handleVisibilityChange}
  onselectionchange={handleSelectionChange}
/>

svelte:body — 바디 이벤트

SVELTE
<script>
  let isDragging = $state(false);
</script>

<svelte:body
  ondragenter={() => isDragging = true}
  ondragleave={() => isDragging = false}
  ondrop={(e) => {
    e.preventDefault();
    isDragging = false;
    console.log('파일 드롭됨');
  }}
  ondragover={(e) => e.preventDefault()}
/>

<div class:drag-active={isDragging}>
  {isDragging ? '여기에 놓으세요!' : '파일을 드래그하세요'}
</div>

<style>
  .drag-active { border: 2px dashed #ff3e00; background: #fff5f5; }
</style>

svelte:head — HEAD 태그 조작

SVELTE
<script>
  let { title, description } = $props();
</script>

<svelte:head>
  <title>{title} | 내 블로그</title>
  <meta name="description" content={description} />
  <meta property="og:title" content={title} />
  <link rel="canonical" href="https://example.com" />
</svelte:head>

<h1>{title}</h1>

svelte:options — 컴파일러 옵션

SVELTE
<!-- 이 컴포넌트를 immutable로 처리 (최적화) -->
<svelte:options immutable />

<!-- 커스텀 엘리먼트로 컴파일 -->
<svelte:options customElement="my-component" />

svelte:boundary — 에러 바운더리 (Svelte 5)

SVELTE
<script>
  import RiskyComponent from './RiskyComponent.svelte';
</script>

<svelte:boundary>
  <RiskyComponent />

  {#snippet failed(error, reset)}
    <div class="error">
      <p>오류 발생: {error.message}</p>
      <button onclick={reset}>다시 시도</button>
    </div>
  {/snippet}
</svelte:boundary>

<style>
  .error { background: #fee; padding: 1rem; border-radius: 4px; border: 1px solid #fcc; }
</style>

실전 예시 — 반응형 레이아웃

SVELTE
<script>
  let innerWidth = $state(0);

  let breakpoint = $derived(
    innerWidth >= 1200 ? 'xl' :
    innerWidth >= 992 ? 'lg' :
    innerWidth >= 768 ? 'md' :
    innerWidth >= 576 ? 'sm' : 'xs'
  );

  let isMobile = $derived(innerWidth < 768);
</script>

<svelte:window bind:innerWidth />

<div class="container breakpoint-{breakpoint}">
  {#if isMobile}
    <MobileNav />
  {:else}
    <DesktopNav />
  {/if}

  <main>
    <p>현재 브레이크포인트: {breakpoint} ({innerWidth}px)</p>
  </main>
</div>

면접 포인트

  • "svelte:window가 addEventListener보다 나은 점은?": 컴포넌트가 소멸할 때 자동으로 이벤트 리스너가 제거됩니다. 수동으로 cleanup할 필요가 없어 메모리 누수 위험이 줄어듭니다.
  • "svelte:boundary와 React ErrorBoundary의 차이는?": React ErrorBoundary는 클래스 컴포넌트에서만 사용 가능하고, svelte:boundary는 마크업 어디서든 사용할 수 있으며 reset 기능도 기본 제공됩니다.

정리

Special Elements는 Svelte가 HTML의 한계를 확장하는 방법입니다. 컴포넌트 안에서 window, document, head를 안전하게 다루고, 동적 컴포넌트와 에러 바운더리까지 — 이 도구들을 알면 Svelte의 활용 범위가 크게 넓어집니다.

댓글 로딩 중...