본문으로 건너뛰기

11. SvelteKit 소개

모던 웹 애플리케이션은 단순한 클라이언트 사이드 렌더링을 넘어 서버 사이드 렌더링, 정적 사이트 생성, API 라우팅 등 다양한 기능을 요구합니다. SvelteKit은 Svelte를 기반으로 한 풀스택 웹 프레임워크로, 프로덕션 레벨의 웹 애플리케이션을 구축하는 데 필요한 모든 도구와 기능을 제공합니다. 이 장에서는 SvelteKit의 핵심 개념과 프로젝트 구조를 이해하고, 본격적인 SvelteKit 개발을 시작하기 위한 기초를 완전히 마스터해보겠습니다.


11.1 SvelteKit이란?

Svelte vs SvelteKit

Svelte는 UI 컴포넌트를 구축하기 위한 컴파일러이고, SvelteKit은 Svelte를 사용하여 완전한 웹 애플리케이션을 구축하기 위한 메타 프레임워크입니다. React와 Next.js, Vue와 Nuxt.js의 관계와 유사하지만, SvelteKit은 처음부터 Svelte와 긴밀하게 통합되어 설계되었습니다. SvelteKit은 라우팅, 서버 사이드 렌더링, API 엔드포인트, 빌드 최적화 등 프로덕션 애플리케이션에 필요한 모든 기능을 제공합니다.

Svelte와 SvelteKit 비교

특징SvelteSvelteKit
목적UI 컴포넌트 라이브러리풀스택 웹 프레임워크
라우팅별도 라이브러리 필요파일 시스템 기반 라우팅 내장
렌더링클라이언트 사이드만SSR, SSG, CSR 모두 지원
API별도 백엔드 필요API 라우트 내장
빌드Vite/Rollup 직접 설정사전 구성된 빌드 시스템
배포정적 호스팅만다양한 플랫폼 어댑터 지원
svelte-only-app.js
// Svelte만 사용하는 경우 - 수동 설정 필요
import App from './App.svelte';
import { Router, Link, Route } from 'svelte-routing';

// 라우팅 직접 구현
const app = new App({
target: document.body,
props: {
// 라우터 설정 필요
},
});

// API 호출은 별도 백엔드로
fetch('https://api.example.com/data')
.then(res => res.json())
.then(data => console.log(data));
sveltekit-app/+page.js
// SvelteKit - 모든 것이 내장됨
// 파일 이름이 곧 라우트 (/about은 routes/about/+page.svelte)

// 서버/클라이언트 모두에서 실행되는 데이터 로딩
export async function load({ fetch }) {
// 내장 API 라우트 호출
const res = await fetch('/api/data');
const data = await res.json();

return {
posts: data,
};
}

Next.js, Nuxt.js와의 비교

SvelteKit은 다른 메타 프레임워크들과 비슷한 기능을 제공하지만, Svelte의 컴파일 타임 최적화 덕분에 더 작은 번들 크기와 빠른 성능을 제공합니다. 또한 복잡한 설정 없이도 즉시 사용 가능한 개발 경험을 제공하며, 타입스크립트 지원이 기본으로 포함되어 있습니다. Vite를 기반으로 하여 빠른 HMR과 개발 서버를 제공하고, 다양한 배포 플랫폼을 지원합니다.

주요 메타 프레임워크 비교

기능SvelteKitNext.jsNuxt.js
기반 프레임워크SvelteReactVue
번들 크기가장 작음 (~10KB)중간 (~90KB)큰 편 (~60KB)
빌드 도구ViteWebpack/TurbopackVite/Webpack
타입스크립트기본 지원기본 지원기본 지원
API 라우트+server.js/api 디렉토리/server/api
데이터 페칭load 함수getServerSidePropsasyncData/fetch
정적 생성어댑터 설정next exportnuxt generate
pages-comparison.svelte
<!-- SvelteKit: routes/blog/[slug]/+page.svelte -->
<script>
export let data;
// load 함수에서 전달된 데이터
</script>

<article>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
</article>

<!-- Next.js: pages/blog/[slug].jsx -->
<!-- export async function getServerSideProps({ params }) { ... } -->

<!-- Nuxt.js: pages/blog/_slug.vue -->
<!-- async asyncData({ params }) { ... } -->

메타 프레임워크의 이점

메타 프레임워크를 사용하면 개발자가 비즈니스 로직에 집중할 수 있도록 많은 인프라 작업을 자동화합니다. 라우팅, 코드 스플리팅, 프리페칭, 이미지 최적화 등이 자동으로 처리되며, SEO와 성능 최적화가 기본으로 적용됩니다. 또한 개발에서 프로덕션까지의 워크플로우가 표준화되어 팀 협업이 쉬워지고, 유지보수가 간편해집니다.

sveltekit-benefits.js
// 1. 자동 코드 스플리팅
// routes/products/+page.svelte는 자동으로 별도 청크로 분리

// 2. 자동 프리페칭
// <a href="/about">는 마우스 호버 시 자동으로 프리페치

// 3. 이미지 최적화 (enhanced:img)
// <enhanced:img src="./photo.jpg" alt=""> 자동 최적화

// 4. 타입 안전성
// load 함수의 반환 타입이 자동으로 컴포넌트에 추론됨

// 5. 환경 변수 관리
import { PUBLIC_API_URL } from '$env/static/public';
import { SECRET_API_KEY } from '$env/static/private';

// 6. 빌드 최적화
// 사용하지 않는 코드 자동 제거, CSS 자동 퍼징

11.2 프로젝트 구조

디렉토리 구조 이해

SvelteKit 프로젝트는 명확한 규칙을 가진 디렉토리 구조를 따릅니다. 각 디렉토리와 파일은 특정한 목적을 가지며, 파일 이름 규칙이 기능을 결정합니다. 이러한 구조는 대규모 애플리케이션에서도 일관성을 유지하고 개발자 경험을 향상시킵니다.

project-structure
my-sveltekit-app/
├── src/
│ ├── routes/ # 라우트와 페이지
│ │ ├── +layout.svelte # 루트 레이아웃
│ │ ├── +page.svelte # 홈페이지 (/)
│ │ ├── about/
│ │ │ └── +page.svelte # /about 페이지
│ │ └── api/
│ │ └── posts/
│ │ └── +server.js # API 엔드포인트
│ ├── lib/ # 재사용 가능한 컴포넌트/유틸
│ │ ├── components/
│ │ ├── stores/
│ │ └── utils/
│ ├── params/ # 라우트 매개변수 검증
│ ├── hooks.client.js # 클라이언트 훅
│ ├── hooks.server.js # 서버 훅
│ └── app.html # HTML 템플릿
├── static/ # 정적 파일
├── tests/ # 테스트 파일
├── package.json
├── svelte.config.js # SvelteKit 설정
├── vite.config.js # Vite 설정
└── tsconfig.json # TypeScript 설정

핵심 디렉토리 설명

directory-purposes.js
// src/routes/ - 애플리케이션의 라우트 정의
// 파일 이름이 기능을 결정 (+page, +layout, +server 등)

// src/lib/ - $lib 별칭으로 접근 가능한 모듈
import Button from '$lib/components/Button.svelte';
import { userStore } from '$lib/stores/user.js';

// src/params/ - 동적 라우트 매개변수 검증
// params/integer.js
export function match(param) {
return /^\d+$/.test(param);
}

// static/ - 변경되지 않는 정적 자산
// favicon.ico, robots.txt, 이미지 등

설정 파일들

SvelteKit 프로젝트는 여러 설정 파일을 통해 동작을 커스터마이징할 수 있습니다. 각 설정 파일은 특정 영역을 담당하며, 대부분의 경우 기본 설정으로도 충분합니다. 필요에 따라 점진적으로 설정을 추가하여 프로젝트를 최적화할 수 있습니다.

svelte.config.js

svelte.config.js
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

const config = {
// 전처리기 설정 (TypeScript, SCSS 등)
preprocess: vitePreprocess(),

kit: {
// 어댑터 설정 (배포 플랫폼)
adapter: adapter(),

// 경로 별칭
alias: {
'@components': './src/lib/components',
'@stores': './src/lib/stores',
},

// CSP 설정
csp: {
directives: {
'script-src': ['self'],
},
},

// 프리렌더링 설정
prerender: {
handleHttpError: 'warn',
entries: ['*'],
},
},
};

export default config;

vite.config.js

vite.config.js
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';

export default defineConfig({
plugins: [sveltekit()],

// 서버 설정
server: {
port: 3000,
host: true,
},

// 빌드 최적화
build: {
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['svelte', '@sveltejs/kit'],
},
},
},
},

// 환경 변수 접두사
envPrefix: 'PUBLIC_',
});

app.html

app.html
<!DOCTYPE html>
<html lang="%lang%">
<head>
<meta charset="utf-8" />
<link
rel="icon"
href="%sveltekit.assets%/favicon.png"
/>
<meta
name="viewport"
content="width=device-width, initial-scale=1"
/>

<!-- SvelteKit이 자동으로 채우는 부분 -->
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">
<!-- 앱이 렌더링되는 위치 -->
%sveltekit.body%
</div>
</body>
</html>

빌드 시스템 (Vite)

SvelteKit은 Vite를 빌드 도구로 사용하여 빠른 개발 경험과 최적화된 프로덕션 빌드를 제공합니다. Vite의 HMR(Hot Module Replacement)은 밀리초 단위로 변경사항을 반영하며, ES 모듈을 활용한 빠른 콜드 스타트를 제공합니다. 프로덕션 빌드 시에는 Rollup을 사용하여 최적화된 번들을 생성합니다.

vite-features.js
// 1. 빠른 HMR
// 파일 변경 시 전체 페이지 새로고침 없이 즉시 반영

// 2. 환경 변수
// .env 파일 자동 로드
// PUBLIC_ 접두사로 클라이언트 노출 제어

// 3. 자동 의존성 프리번들링
// node_modules의 패키지를 ESM으로 변환

// 4. CSS 모듈과 전처리기
// SCSS, PostCSS 등 자동 처리

// 5. 최적화된 빌드
// 코드 스플리팅, 트리 쉐이킹, 압축
custom-vite-plugin.js
// 커스텀 Vite 플러그인 예제
import { defineConfig } from 'vite';
import { sveltekit } from '@sveltejs/kit/vite';

function myCustomPlugin() {
return {
name: 'my-custom-plugin',

transform(code, id) {
if (id.endsWith('.md')) {
// 마크다운 파일 처리
return {
code: `export default ${JSON.stringify(code)}`,
};
}
},

configureServer(server) {
server.middlewares.use((req, res, next) => {
// 커스텀 미들웨어
console.log(req.url);
next();
});
},
};
}

export default defineConfig({
plugins: [sveltekit(), myCustomPlugin()],
});

11.3 첫 SvelteKit 프로젝트

프로젝트 생성과 설정

SvelteKit 프로젝트를 시작하는 가장 쉬운 방법은 공식 CLI를 사용하는 것입니다. 대화형 프롬프트를 통해 TypeScript, ESLint, Prettier 등의 옵션을 선택할 수 있습니다. 프로젝트 생성 후 즉시 개발을 시작할 수 있는 완전한 개발 환경이 구성됩니다.

create-project.sh
# SvelteKit 프로젝트 생성
npm create svelte@latest my-app

# 프롬프트 옵션:
# - Skeleton project (최소 구성)
# - SvelteKit demo app (예제 포함)
# - Library project (패키지 개발용)

cd my-app
npm install
npm run dev
package.json
{
"name": "my-sveltekit-app",
"version": "0.0.1",
"scripts": {
"dev": "vite dev", // 개발 서버 시작
"build": "vite build", // 프로덕션 빌드
"preview": "vite preview", // 빌드 결과 미리보기
"check": "svelte-kit sync && svelte-check",
"lint": "eslint .",
"format": "prettier --write ."
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"svelte": "^5.0.0",
"vite": "^5.0.0"
},
"type": "module"
}

기본 라우팅 구현

SvelteKit의 파일 시스템 기반 라우팅은 직관적이고 강력합니다. 디렉토리 구조가 URL 구조를 그대로 반영하며, 특별한 파일 이름으로 다양한 기능을 구현합니다. 동적 라우팅, 레이아웃, API 엔드포인트 등을 쉽게 만들 수 있습니다.

routes/+page.svelte
<!-- src/routes/+page.svelte (홈페이지) -->
<script>
let count = $state(0);
</script>

<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="/about">About page</a></p>

<button onclick="{()" ="">
count++}> Clicked {count} times
</button>

로컬 실행하기: SvelteKit은 REPL에서 실행할 수 없습니다. npm create svelte@latest로 프로젝트를 생성하여 로컬에서 실행해보세요!

routes/about/+page.svelte
<!-- src/routes/about/+page.svelte (/about 페이지) -->
<script>
import { page } from '$app/stores';
</script>

<h1>About Page</h1>
<p>Current URL: {$page.url.pathname}</p>
<a href="/">Back to Home</a>

로컬 실행하기: SvelteKit은 REPL에서 실행할 수 없습니다. npm create svelte@latest로 프로젝트를 생성하여 로컬에서 실행해보세요!

routes/blog/[slug]/+page.svelte
<!-- src/routes/blog/[slug]/+page.svelte (동적 라우트) -->
<script>
export let data;
</script>

<article>
<h1>{data.post.title}</h1>
<p>Published: {data.post.date}</p>
<div>{@html data.post.content}</div>
</article>

로컬 실행하기: SvelteKit은 REPL에서 실행할 수 없습니다. npm create svelte@latest로 프로젝트를 생성하여 로컬에서 실행해보세요!

routes/blog/[slug]/+page.js
// src/routes/blog/[slug]/+page.js
export async function load({ params }) {
// params.slug로 URL 매개변수 접근
const post = await fetchPost(params.slug);

return {
post,
};
}

async function fetchPost(slug) {
// 실제로는 DB나 API에서 가져옴
return {
title: `Post: ${slug}`,
date: new Date().toISOString(),
content: '<p>Post content here...</p>',
};
}

서버 사이드 렌더링 기초

SvelteKit은 기본적으로 서버 사이드 렌더링을 지원하여 SEO와 초기 로딩 성능을 개선합니다. +page.server.js 파일을 통해 서버에서만 실행되는 코드를 작성할 수 있으며, 데이터베이스 접근이나 비밀 키 사용이 안전합니다. 클라이언트로 전송되는 JavaScript 양을 줄이고, 초기 HTML을 완전히 렌더링된 상태로 제공합니다.

routes/products/+page.server.js
// src/routes/products/+page.server.js
import { SECRET_API_KEY } from '$env/static/private';

export async function load({ fetch, url }) {
// 서버에서만 실행 - SECRET_API_KEY 안전
const category = url.searchParams.get('category');

const response = await fetch(
'https://api.example.com/products',
{
headers: {
Authorization: `Bearer ${SECRET_API_KEY}`,
},
}
);

const products = await response.json();

return {
products: category
? products.filter(p => p.category === category)
: products,
generatedAt: new Date().toISOString(),
};
}
routes/products/+page.svelte
<!-- src/routes/products/+page.svelte -->
<script>
export let data;

// 클라이언트 사이드 상태
let searchTerm = $state('');

let filteredProducts = $derived(
data.products.filter(p =>
p.name
.toLowerCase()
.includes(searchTerm.toLowerCase())
)
);
</script>

<h1>Products</h1>
<p>Generated at: {data.generatedAt}</p>

<input
type="search"
bind:value="{searchTerm}"
placeholder="Search products..."
/>

<div class="products">
{#each filteredProducts as product}
<div class="product">
<h2>{product.name}</h2>
<p>${product.price}</p>
</div>
{/each}
</div>

<style>
.products {
display: grid;
grid-template-columns: repeat(
auto-fill,
minmax(200px, 1fr)
);
gap: 1rem;
}
</style>

로컬 실행하기: SvelteKit은 REPL에서 실행할 수 없습니다. npm create svelte@latest로 프로젝트를 생성하여 로컬에서 실행해보세요!

hooks.server.js
// src/hooks.server.js - 모든 서버 요청 인터셉트
export async function handle({ event, resolve }) {
// 요청 시작 시간 기록
const start = Date.now();

// 요청 처리
const response = await resolve(event);

// 응답 헤더에 처리 시간 추가
const duration = Date.now() - start;
response.headers.set('X-Response-Time', `${duration}ms`);

return response;
}

export async function handleFetch({ request, fetch }) {
// 외부 API 요청 수정
if (request.url.startsWith('https://api.example.com')) {
request.headers.set('X-Custom-Header', 'value');
}

return fetch(request);
}

정리

SvelteKit의 기초를 완전히 마스터했습니다! 이제 다음과 같은 핵심 개념들을 이해했습니다:

핵심 요약

  • SvelteKit이란: Svelte 기반의 풀스택 메타 프레임워크로, 라우팅, SSR, API 등 모든 기능 내장
  • 프로젝트 구조: 파일 시스템 기반 라우팅과 명확한 디렉토리 구조로 대규모 앱 개발 지원
  • 개발 시작: CLI를 통한 빠른 시작, Vite 기반의 빠른 개발 경험, 서버 사이드 렌더링 기본 지원

실무 활용 팁

  • 작은 프로젝트는 Svelte만으로도 충분하지만, 프로덕션 앱은 SvelteKit 사용 권장
  • +page.server.js는 민감한 데이터 처리에, +page.js는 공개 가능한 로직에 사용
  • 파일 이름 규칙(+page, +layout, +server)을 정확히 따라야 기능이 작동

다음 단계: 12장 "라우팅 시스템"에서는 SvelteKit의 강력한 파일 시스템 기반 라우팅을 심도 있게 알아보겠습니다. 동적 라우팅, 레이아웃, 라우트 그룹 등 고급 라우팅 기법을 마스터해보세요!