본문 바로가기
Frontend2026년 5월 16일4분 읽기

SvelteKit 3 + Svelte 6 — 새 컴파일러 마이그레이션 후기

YS
김영삼
조회 862
SvelteKit 3 + Svelte 6 — 새 컴파일러 마이그레이션 후기

핵심 요약

Svelte 6은 runes 안정화와 새 컴파일러(SSA 기반)를 가져왔다. SvelteKit 3은 라우터 데이터 모델을 type-safe로 다시 짰다. 중규모 SaaS(컴포넌트 약 800개) 마이그레이션 결과 빌드 47% 단축, 클라이언트 번들 22% 감소. 다만 reactive 구문 자동 변환 한계로 수동 수정 200여 곳.

1. runes — 무엇이 바뀌나

<!-- Svelte 4 -->
<script>
  let count = 0;
  $: doubled = count * 2;
  export let label;
</script>

<!-- Svelte 6 -->
<script>
  let count = $state(0);
  let doubled = $derived(count * 2);
  let { label } = $props();
</script>

명시적인 것이 핵심. 어떤 변수가 반응형인지 한눈에 보인다. 대신 모든 곳에 적어야 한다.

2. snippet — slot을 대체

<!-- 정의 -->
{#snippet row(item)}
  <tr><td>{item.name}</td><td>{item.price}</td></tr>
{/snippet}

<!-- 사용 -->
<Table items={products} {row} />

인자를 받는 fragment. slot보다 표현력이 강하고 타입이 통한다. slot은 deprecated, 경고 뜬다.

3. 새 컴파일러 — 무엇이 빨라졌나

지표Svelte 5Svelte 6변화
프로덕션 빌드62s33s-47%
HMR 갱신180ms72ms-60%
클라이언트 번들284KB221KB-22%
1만 리스트 렌더148ms96ms-35%
리스트 1행 업데이트2.1ms0.6ms-71%

4. 마이그레이션 — 자동 변환의 한계

npx sv migrate svelte-6

800개 컴포넌트 중 자동 변환 성공 78%. 수동 수정이 필요한 경우 4가지.

  • 비순수 reactive 블록: $: { fetch(...) } 같이 부수효과 있는 것
  • store auto-subscription 우회: $store를 변수에 담아 쓰던 패턴
  • slot fallback + named slot 조합: snippet 매핑 모호
  • bind:this 후 메서드 호출: runes 환경에서 시점 변화

5. SvelteKit 3 — 라우트 데이터 타입화

// +page.ts
import type { PageLoad } from "./$types"

export const load: PageLoad = async ({ params, fetch }) => {
  const res = await fetch("/api/posts/" + params.slug)
  return { post: await res.json() as Post }
}

6. 함정

  • $effect 무한 루프: $effect 안에서 $state를 write하면 다시 트리거
  • $state.raw vs $state: 큰 객체는 raw로 안 하면 proxy 오버헤드
  • $bindable() 명시: 양방향 바인딩은 자식이 명시해야 함
  • SSR 직렬화: $state 안에 클래스 인스턴스 넣으면 hydration 깨짐

7. 실측 — 실제 앱 효과

지표마이그 전마이그 후
LCP(p75)1.82s1.41s
TBT(p75)180ms96ms
JS 전송 크기312KB248KB

8. 마이그레이션 추천

  • 지금 가야 함: Svelte 5 이미 쓰는 팀
  • 준비 후: Svelte 4 코드베이스. 5 거쳐서 6로(점진적)
  • 유보: 컴포넌트 1500개+의 대규모

참고

  • svelte.dev/docs
  • kit.svelte.dev

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록