React에서는 useEffect 하나로 모든 라이프사이클을 처리하는데, Svelte는 용도별로 나눠져 있어서 의도가 더 명확합니다.

개념 정의

라이프사이클(Lifecycle) 은 컴포넌트가 생성되고, 화면에 마운트되고, 업데이트되고, 제거되는 일련의 과정입니다. Svelte 5에서는 $effect가 주된 라이프사이클 도구이며, onMountonDestroy도 여전히 사용됩니다.

라이프사이클 단계

PLAINTEXT
생성 (Initialization)

마운트 (Mount) — DOM에 삽입

업데이트 (Update) — 상태 변경 시 반복

소멸 (Destroy) — DOM에서 제거

onMount — 마운트 시점

SVELTE
<script>
  import { onMount } from 'svelte';

  let data = $state(null);
  let canvas = $state(null);

  // DOM이 마운트된 직후 실행
  onMount(() => {
    console.log('컴포넌트가 마운트되었습니다');

    // API 호출 — 가장 흔한 사용 사례
    fetch('/api/data')
      .then(r => r.json())
      .then(d => data = d);

    // 정리 함수 반환 가능 (onDestroy 대체)
    return () => {
      console.log('컴포넌트가 제거됩니다');
    };
  });

  // Canvas 초기화 — DOM 요소가 필요한 작업
  onMount(() => {
    if (canvas) {
      const ctx = canvas.getContext('2d');
      ctx.fillStyle = '#ff3e00';
      ctx.fillRect(0, 0, 100, 100);
    }
  });
</script>

<canvas bind:this={canvas} width="200" height="200"></canvas>

**핵심 **: onMount는 ** 서버 사이드 렌더링(SSR) 시에는 실행되지 않습니다 **. 브라우저 전용 API(DOM, window, localStorage 등)를 안전하게 사용할 수 있는 지점입니다.

onDestroy — 소멸 시점

SVELTE
<script>
  import { onDestroy } from 'svelte';

  let count = $state(0);

  // 타이머 설정
  const interval = setInterval(() => {
    count++;
  }, 1000);

  // 컴포넌트가 제거될 때 타이머 정리
  onDestroy(() => {
    clearInterval(interval);
    console.log('타이머 정리 완료');
  });
</script>

<p>경과 시간: {count}초</p>

$effect로 라이프사이클 관리

Svelte 5에서는 $effect가 대부분의 라이프사이클 시나리오를 처리합니다.

SVELTE
<script>
  let count = $state(0);
  let query = $state('');

  // 마운트 시 + 의존성 변경 시 실행
  $effect(() => {
    console.log('count가 변경됨:', count);
  });

  // 정리 함수로 언마운트 처리
  $effect(() => {
    const handler = (e) => console.log('키 입력:', e.key);
    window.addEventListener('keydown', handler);

    // 정리 — 다음 실행 전 또는 언마운트 시 호출
    return () => {
      window.removeEventListener('keydown', handler);
    };
  });

  // 디바운스 패턴
  $effect(() => {
    if (!query) return;

    const timeout = setTimeout(async () => {
      const res = await fetch(`/api/search?q=${query}`);
      console.log(await res.json());
    }, 300);

    return () => clearTimeout(timeout);
  });
</script>

$effect.pre — DOM 업데이트 전

SVELTE
<script>
  let messages = $state([]);
  let chatContainer = $state(null);

  // DOM이 업데이트되기 전에 실행
  $effect.pre(() => {
    // 새 메시지 추가 전 스크롤 위치 기록
    if (chatContainer) {
      const isAtBottom =
        chatContainer.scrollHeight - chatContainer.scrollTop ===
        chatContainer.clientHeight;

      // DOM 업데이트 후 스크롤 유지를 위한 준비
      if (isAtBottom) {
        // tick() 후에 스크롤 이동하는 로직
      }
    }
  });
</script>

tick() — 다음 DOM 업데이트 대기

SVELTE
<script>
  import { tick } from 'svelte';

  let text = $state('');
  let textArea = $state(null);

  async function handleInput() {
    text = text.toUpperCase();

    // DOM 업데이트가 완료될 때까지 대기
    await tick();

    // DOM이 업데이트된 후의 작업
    textArea?.setSelectionRange(text.length, text.length);
  }
</script>

<textarea bind:this={textArea} bind:value={text} oninput={handleInput}></textarea>

onMount vs $effect

특성onMount$effect
실행 시점마운트 시 1회마운트 시 + 의존성 변경 시
SSR 실행XX (브라우저에서만)
의존성 추적없음자동
정리 함수반환 가능반환 가능
주 용도초기화, API 호출반응형 부수 효과
SVELTE
<script>
  import { onMount } from 'svelte';

  let userId = $state(1);
  let userData = $state(null);

  // onMount — 최초 1회만 실행
  onMount(() => {
    console.log('컴포넌트 마운트됨');
  });

  // $effect — userId가 바뀔 때마다 다시 실행
  $effect(() => {
    fetch(`/api/users/${userId}`)
      .then(r => r.json())
      .then(d => userData = d);
  });
</script>

실전 패턴 — 인터섹션 옵저버

SVELTE
<script>
  import { onMount } from 'svelte';

  let element = $state(null);
  let isVisible = $state(false);

  onMount(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        isVisible = entry.isIntersecting;
      },
      { threshold: 0.5 }
    );

    if (element) observer.observe(element);

    return () => observer.disconnect();
  });
</script>

<div bind:this={element} class:visible={isVisible}>
  {isVisible ? '화면에 보임!' : '화면 밖'}
</div>

<style>
  div {
    opacity: 0;
    transform: translateY(20px);
    transition: all 0.5s;
  }
  .visible {
    opacity: 1;
    transform: translateY(0);
  }
</style>

면접 포인트

  • "onMount와 $effect의 차이는?": onMount는 마운트 시 1회만 실행되고, $effect는 의존성이 바뀔 때마다 재실행됩니다. 초기 설정은 onMount, 반응형 로직은 $effect를 사용합니다.
  • "SSR에서 onMount가 실행되지 않는 이유는?": onMount는 브라우저 DOM이 준비된 후 실행되도록 설계되었습니다. SSR 환경에는 DOM이 없으므로 건너뜁니다. 서버에서 데이터가 필요하면 SvelteKit의 load 함수를 사용해야 합니다.

정리

Svelte의 라이프사이클은 심플합니다. onMount로 초기화하고, $effect로 반응형 부수 효과를 관리하고, 정리 함수로 리소스를 해제하면 됩니다. React의 useEffect처럼 하나의 함수에 모든 것을 우겨 넣을 필요 없이, 의도에 맞는 도구를 골라 쓸 수 있습니다.

댓글 로딩 중...