Svelte 5 Runes 완전 정복: React 개발자를 위한 전환 가이드 - 코드픽 블로그
Svelte 5 Runes 완전 정복: React 개발자를 위한 전환 가이드
기술 가이드

Svelte 5 Runes 완전 정복: React 개발자를 위한 전환 가이드

2026년 2월 28일 57 views by 코드벤터

Svelte 5 Runes 완전 정복: React 개발자를 위한 전환 가이드

React를 쓰다 보면 한 번쯤 이런 생각이 든다. "왜 의존성 배열을 일일이 관리해야 하지?" "useMemo를 언제 써야 하는 거지?" Svelte 5는 이 불편함을 근본적으로 해결한다. 바로 **Runes(룬)**라는 새로운 반응성 시스템을 통해서다.

이 글에서는 React 개발자의 시각으로 Svelte 5 Runes를 분석하고, 실제 코드를 비교하면서 전환 방법을 단계별로 안내한다.


Runes란 무엇인가?

Runes는 Svelte 5에서 도입된 컴파일러 기반 반응성 프리미티브다. $ 접두사가 붙은 특별한 함수 형태로 제공되며, Svelte 컴파일러가 이를 분석해 최적화된 바닐라 JavaScript를 생성한다.

핵심 Runes는 다음 네 가지다:

  • $state: 반응형 상태 선언
  • $derived: 다른 상태에서 파생된 계산값
  • $effect: 상태 변화에 따른 부작용 처리
  • $props: 컴포넌트 props 선언

가장 큰 장점은 컴포넌트 외부에서도 사용 가능하다는 것이다. .ts 혹은 .js 파일에서도 Runes를 쓸 수 있어, React 훅의 "반드시 컴포넌트 안에서만" 규칙에서 벗어날 수 있다.


React vs Svelte 5 Runes: 핵심 비교

기능React (Hooks)Svelte 5 (Runes)
상태 선언useState(0)$state(0)
계산된 값useMemo(() => ...)$derived(...)
부작용useEffect(() => ..., [deps])$effect(() => ...)
Props함수 인자$props()
의존성 관리수동 (배열)자동 (컴파일러 추적)
사용 위치컴포넌트 내부만어디서든 가능
가상 DOM사용미사용 (직접 DOM 조작)
번들 크기상대적으로 큼매우 작음

1. 상태 관리: useState → $state

React에서 가장 기본적인 상태 관리 방식인 useState는 Svelte 5에서 $state로 대체된다.

React 방식:

jsx
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <button onClick={() => setCount(count + 1)}>
      클릭 횟수: {count}
    </button>
  );
}

Svelte 5 방식:

svelte
<script>
  let count = $state(0);
</script>

<button onclick={() => count++}>
  클릭 횟수: {count}
</button>

차이가 보이는가? Svelte 5는 직접 변수에 할당하면 반응성이 트리거된다. setter 함수가 필요 없다. 또한 $state는 객체나 배열에도 깊은 반응성(Deep Reactivity)을 제공한다.

svelte
<script>
  let user = $state({ name: '김개발', age: 28 });

  function birthday() {
    user.age++;
  }
</script>

<p>{user.name}님, {user.age}세</p>
<button onclick={birthday}>생일 축하!</button>

2. 계산된 값: useMemo → $derived

React에서 성능 최적화를 위해 useMemo를 쓰는 패턴은 Svelte 5에서 $derived로 훨씬 간결해진다.

React 방식:

jsx
import { useState, useMemo } from 'react';

function Cart({ items }) {
  const total = useMemo(
    () => items.reduce((sum, item) => sum + item.price * item.qty, 0),
    [items]
  );

  return <p>합계: {total}원</p>;
}

Svelte 5 방식:

svelte
<script>
  let { items } = $props();
  let total = $derived(
    items.reduce((sum, item) => sum + item.price * item.qty, 0)
  );
</script>

<p>합계: {total}원</p>

의존성 배열이 사라졌다. Svelte 컴파일러가 $derived 내부에서 참조하는 반응형 값을 자동으로 감지해 추적한다. 복잡한 로직이라면 $derived.by를 사용하면 된다.

svelte
<script>
  let numbers = $state([1, 2, 3, 4, 5]);

  let stats = $derived.by(() => {
    const sum = numbers.reduce((a, b) => a + b, 0);
    const avg = sum / numbers.length;
    return { sum, avg };
  });
</script>

<p>합계: {stats.sum}, 평균: {stats.avg}</p>

3. 부작용: useEffect → $effect

React의 useEffect는 의존성 배열을 빠뜨리거나 잘못 지정하면 버그가 생기는 대표적인 함정이다. Svelte 5의 $effect는 이 문제를 해결한다.

React 방식:

jsx
import { useState, useEffect } from 'react';

function TitleSync() {
  const [title, setTitle] = useState('홈');

  useEffect(() => {
    document.title = title + ' - 코드픽';
  }, [title]);

  return <input value={title} onChange={e => setTitle(e.target.value)} />;
}

Svelte 5 방식:

svelte
<script>
  let title = $state('홈');

  $effect(() => {
    document.title = title + ' - 코드픽';
  });
</script>

<input bind:value={title} />

클린업 함수도 React와 동일하게 반환값으로 처리한다.

svelte
<script>
  $effect(() => {
    const id = setInterval(() => {
      console.log('틱');
    }, 1000);

    return () => clearInterval(id);
  });
</script>

4. Props 선언: $props

React 함수형 컴포넌트에서 props를 구조 분해하는 방식과 유사하지만, Svelte 5는 $props()를 통해 명시적으로 선언한다.

React 방식:

jsx
function UserCard({ name, role = '사용자' }) {
  return <div>{name} ({role})</div>;
}

Svelte 5 방식:

svelte
<script>
  let { name, role = '사용자' } = $props();
</script>

<div>{name} ({role})</div>

타입스크립트와 함께 사용할 때는 다음과 같이 쓴다.

typescript
// UserCard.svelte
<script lang="ts">
  interface Props {
    name: string;
    role?: string;
  }

  let { name, role = '사용자' }: Props = $props();
</script>

5. 컴포넌트 외부에서 상태 공유

React에서 전역 상태를 공유하려면 Context API나 Redux 같은 외부 라이브러리가 필요하다. Svelte 5 Runes는 .svelte.ts 파일에서도 사용 가능해, 별도 라이브러리 없이 반응형 전역 상태를 만들 수 있다.

typescript
// store/auth.svelte.ts
let user = $state(null);

export function getUser() { return user; }
export function login(name, token) {
  user = { name, token };
}
export function logout() {
  user = null;
}
svelte
<!-- App.svelte -->
<script>
  import { getUser, logout } from './store/auth.svelte.ts';
  let user = $derived(getUser());
</script>

{#if user}
  <p>환영합니다, {user.name}님!</p>
  <button onclick={logout}>로그아웃</button>
{/if}

마이그레이션 팁

  1. 점진적 전환 가능: Svelte 5는 Svelte 4 문법과 공존한다. 한 번에 전환하지 않아도 된다.
  2. 자동 마이그레이션 도구 활용: npx sv migrate svelte-5 명령으로 기본적인 코드 변환이 가능하다.
  3. $effect 남용 주의: React의 useEffect 습관으로 모든 것을 $effect로 처리하지 말 것. 가능하면 $derived를 우선 검토하라.
  4. 깊은 반응성 주의: $state로 선언한 객체는 내부 속성까지 모두 반응형이다. 성능에 민감하다면 $state.raw를 고려하라.

코드벤터는 Svelte 5와 같은 최신 프론트엔드 기술을 활용해 효율적인 웹 서비스를 개발합니다. React에서 Svelte로의 전환은 단순히 문법 교체가 아니라, 더 직관적이고 성능 좋은 코드를 향한 패러다임 전환입니다. 코드벤터의 개발 철학처럼, 복잡한 것을 단순하게 - Svelte 5 Runes가 그 방향을 제시합니다.

개발 의뢰 상담

AI 서비스나 플랫폼 개발을
고민 중이신가요?

CodePick에서는
기획 → 개발 → 운영까지 함께합니다.
아이디어만 있어도 상담 가능합니다.

CodeVenter 개발팀이 직접 담당 · 1~2 영업일 내 회신

✓ 스타트업 MVP 개발✓ AI 서비스 개발✓ 웹 플랫폼 개발✓ 기업 시스템 구축✓ 모바일 앱 개발

AI Development Studio

코드픽 by 코드벤터

  • 대표: 윤승환 · 사업자등록번호: 121-57-64983
  • 대구광역시 중구 국채보상로 586, 16층 · info@codeventer.com

© 2025 코드벤터. All rights reserved.