지난 몇 년간 메타 프레임워크 사이를 전전하며 시간을 보냈다면, SvelteKit 2는 안도감과 의구심이 뒤섞인 묘한 기분으로 다가올 것입니다. 안도감은 이 프레임워크가 복잡성을 더하는 대신 실제로 제거하기 때문이며, 의구심은 뭔가 잘못될 것만 같은 불안감이 계속 들기 때문입니다. 하지만 그런 일은 결코 일어나지 않습니다. Svelte 5와 결합된 이 스택은 2026년에 풀스택 애플리케이션을 배포할 수 있는 가장 생산적인 방법 중 하나이며, 수치로도 그 개발자 경험(DX)이 증명됩니다. 번들 크기는 Svelte 4가 생성했던 것보다 약 35% 더 작게 나옵니다. 라우팅, 서버 함수, 인증 패턴은 모두 임시방편으로 짜 맞춘 플러그인이 아니라 일급 시민(first-class citizens)으로 취급됩니다.

runes를 통한 명시적 반응성

가장 큰 사고방식의 전환은 Svelte 5의 runes에서 옵니다. 이전 버전의 Svelte는 의존성을 추적하기 위해 $: 라벨과 많은 컴파일러 마법을 사용했습니다. 잘 작동하긴 했지만, 무언가 문제가 생기면 보이지 않는 배선을 디버깅해야 했습니다. runes는 이러한 마법을 명시적인 함수로 대체합니다. 컴파일러에게 무엇을 추적해야 하는지 정확히 알려주면, 컴파일러가 이를 경청합니다.

반드시 알아야 할 내용은 다음과 같습니다.

  • $state는 반응형 변수를 처리합니다. 어떤 값이든 $state()로 감싸면 컴파일러가 이를 감시합니다.
  • $derived는 다른 상태로부터 값을 계산합니다. 필터링된 리스트나 포맷팅된 합계가 필요한가요? $derived를 사용하세요. $effect와의 핵심 차이점은 $derived는 액션이 아닌 '값'을 위한 것이라는 점입니다.
  • $effect는 사이드 이펙트를 실행합니다. 문서 제목 업데이트, 수동 DOM 측정, 또는 클린업이 필요한 타이머 등을 생각하면 됩니다. 이는 라이프사이클 훅과 유사하지만 특정 반응형 의존성에 결합되어 DOM이 커밋된 후에 실행됩니다.
  • $props는 컴포넌트에서 데이터를 받기 위한 기존의 export let 패턴을 대체합니다. 더 명확하며 TypeScript와 더 잘 상호작용합니다.

이 모델은 실무에서 빛을 발합니다. 컴파일러가 사용자가 표시한 것만 추적하기 때문에, 데드 코드(dead code)는 그대로 유지됩니다. 왜 특정 변수가 업데이트를 트리거했는지 고민하는 대신, 사용자가 내린 명시적인 지시를 신뢰하기 시작하게 됩니다.

설정이 아닌 폴더 기반의 라우팅

SvelteKit은 라우팅을 위해 파일 시스템을 사용합니다. 별도로 유지 관리해야 할 라우터 파일이 없습니다. 디렉토리에 +page.svelte 파일을 넣기만 하면, 해당 디렉토리가 즉시 활성화된 라우트가 됩니다.

공유 UI는 +layout.svelte를 통해 해당 라우트들을 감쌉니다. 루트에 배치하면 모든 하위 라우트가 이를 상속받습니다. 트리 구조의 더 깊은 곳에 배치하면 해당 섹션에만 래퍼가 적용됩니다.

서버 측 로직은 +page.server.ts에 위치합니다. 이 코드는 페이지가 렌더링되기 전에 실행되므로, 데이터베이스를 쿼리하거나, 쿠키를 검증하거나, 인증되지 않은 사용자를 거부하는 작업을 수행하기에 적합합니다. 타입은 load 함수에서 페이지 컴포넌트로 자동으로 흐르므로, 수동으로 인터페이스를 작성하지 않아도 데이터에 타입이 지정됩니다.

순수 API 엔드포인트는 +server.ts 파일에 작성합니다. 이 파일들은 GET, POST, PUT, DELETE와 같은 표준 HTTP 핸들러를 내보내므로, 페이지와 함께 REST 백엔드를 구축하는 것이 매우 자연스럽습니다.

과소평가된 기능 중 하나는 라우트 그룹화입니다. 폴더 이름을 (auth)와 같이 괄호로 감싸면, URL 세그먼트를 추가하지 않고도 공유 레이아웃을 만들 수 있습니다. 이는 /auth/login이 아닌 /login/signup 경로를 사용하면서도 동일한 최소한의 UI 구성을 공유해야 하는 로그인 및 회원가입 페이지에 완벽합니다.

데이터, 보안, 그리고 점진적 향상

현대적인 프레임워크들은 풀스택을 강조하지만, 많은 프레임워크가 인증 체크나 폼 로직을 어디에 두어야 할지 모호하게 만듭니다. SvelteKit은 명확한 훅(hook)을 제공합니다.

데이터 페칭에는 +page.server.ts를 사용하세요. 이곳의 load 함수는 서버에서만 독점적으로 실행되므로, 데이터베이스 자격 증명이 브라우저로 유출될 염려가 없습니다. SvelteKit은 load 함수의 반환 값으로부터 타입을 생성하므로 프론트엔드의 타입 안정성이 유지됩니다.

애플리케이션 전체의 관문 역할을 하려면 hooks.server.ts를 사용하세요. 이는 모든 요청에서 실행되므로 세션을 검증하거나, JWT 만료를 확인하거나, 들어오는 이벤트에 사용자 컨텍스트를 첨부하기에 가장 적합한 장소입니다.

데이터 변경(mutation)에는 폼 액션(form actions)을 사용하세요. 별도의 API 엔드포인트를 연결하고 JSON을 처리하는 대신, +page.server.ts 내부에 액션을 정의합니다. 여기서의 묘미는 점진적 향상(progressive enhancement)입니다. JavaScript 로딩에 실패하거나 사용자가 JavaScript를 비활성화했더라도, 폼은 여전히 서버 액션으로 제출되며 페이지는 결과와 함께 다시 렌더링됩니다. JavaScript가 활성화되어 있다면, SvelteKit은 전체 페이지 새로고침 없이 경험을 향상시킵니다. 동일한 코드로 회복 탄력성과 완성도를 모두 얻을 수 있습니다.

한 가지 명심해야 할 규칙이 있습니다. 계산은 $effect가 아닌 $derived에서 수행하세요. 값을 계산하기 위해 $effect를 사용하면 추적하기 어려운 업데이트 루프가 발생할 수 있습니다. $effect는 진정한 사이드 이펙트를 위해 남겨두고, 계산된 상태는 $derived가 담당하게 하세요.

SvelteKit versus Next.js

두 프레임워크 모두 프로덕션 애플리케이션을 배포할 수 있지만, 그에 따른 트레이드오프는 확실합니다.

번들 크기 측면에서는 SvelteKit이 유리합니다. Svelte는 컴포넌트를 vanilla JavaScript로 컴파일하고 Virtual DOM을 완전히 생략하기 때문에 런타임 풋프린트가 작게 유지됩니다. 반면 Next.js는 React의 reconciliation 엔진을 함께 포함해야 합니다.

반응성 또한 다릅니다. SvelteKit은 컴파일 타임에 runes를 처리합니다. 따라서 브라우저는 단순한 업데이트만 전달받습니다. 반면 Next.js는 React의 런타임 훅과 reconciliation에 의존하므로, 클라이언트 측에서 수행해야 할 작업이 더 많습니다.

SvelteKit은 온보딩 과정이 더 수월합니다. 학습해야 할 멘탈 모델이 더 단순하기 때문입니다. 리렌더링을 방지하기 위해 useEffect 의존성 배열을 관리하거나 메모이제이션 퍼즐을 풀 필요가 없습니다. TypeScript 통합성 또한 언급할 만한 장점입니다. 두 프레임워크 모두