핵심 요약
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 5 | Svelte 6 | 변화 |
|---|---|---|---|
| 프로덕션 빌드 | 62s | 33s | -47% |
| HMR 갱신 | 180ms | 72ms | -60% |
| 클라이언트 번들 | 284KB | 221KB | -22% |
| 1만 리스트 렌더 | 148ms | 96ms | -35% |
| 리스트 1행 업데이트 | 2.1ms | 0.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.82s | 1.41s |
| TBT(p75) | 180ms | 96ms |
| JS 전송 크기 | 312KB | 248KB |
8. 마이그레이션 추천
- 지금 가야 함: Svelte 5 이미 쓰는 팀
- 준비 후: Svelte 4 코드베이스. 5 거쳐서 6로(점진적)
- 유보: 컴포넌트 1500개+의 대규모
참고
- svelte.dev/docs
- kit.svelte.dev

댓글 0