출시·고도화 중
SvelteKit 안내서 · 4/6
SvelteKit에서 페이지 데이터는 load 함수로 불러오고, 데이터 변경은 폼 액션으로 처리합니다. 이 장에서는 두 가지와 함께 API 엔드포인트, 서버 전용 모듈, 환경 변수, 컴포넌트 사이의 공유 상태를 다룹니다.
+page.ts의 load는 유니버설 load로, 첫 요청 때는 서버에서, 이후 클라이언트 이동 때는 브라우저에서 실행됩니다. +page.server.ts의 load는 서버 load로 항상 서버에서만 실행되므로 데이터베이스나 비밀 키를 다룰 수 있습니다. 어느 쪽이든 돌려준 객체가 페이지의 data 속성이 됩니다.
// src/routes/blog/[slug]/+page.server.ts
import { error } from '@sveltejs/kit';
import { db } from '#lib/server/db.js';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params }) => {
const post = await db.post.findBySlug(params.slug);
if (!post) error(404, '글을 찾을 수 없습니다');
return { post };
};<!-- src/routes/blog/[slug]/+page.svelte -->
<script lang="ts">
import type { PageProps } from './$types';
let { data }: PageProps = $props();
</script>
<h1>{data.post.title}</h1>
<p>{data.post.summary}</p>./$types는 SvelteKit이 경로마다 만들어 주는 타입이므로 params.slug와 data.post의 타입이 자동으로 맞춰집니다. error()와 redirect()는 호출하면 바로 처리를 멈추므로 throw를 붙이지 않습니다. 유니버설 load에서는 인자로 받은 fetch를 쓰면 서버 렌더링 중에도 쿠키와 상대 주소가 올바르게 처리되고, 응답이 HTML에 담겨 브라우저에서 같은 요청을 다시 보내지 않습니다. 레이아웃의 load가 돌려준 값은 하위 페이지의 data에 합쳐지고, 하위 load에서는 await parent()로 읽을 수 있습니다.
+server.ts는 HTTP 메서드 이름의 함수를 내보내 표준 Request를 받고 Response를 돌려줍니다. JSON은 표준 Response.json()으로 만듭니다.
// src/routes/api/search/+server.ts
import type { RequestHandler } from './$types';
import { searchPosts } from '#lib/server/search.js';
export const GET: RequestHandler = async ({ url }) => {
const q = url.searchParams.get('q') ?? '';
const results = await searchPosts(q);
return Response.json({ q, results });
};데이터를 바꾸는 작업은 +page.server.ts의 actions로 처리합니다. 일반 <form method="POST">로 동작하므로 자바스크립트가 없어도 작동하고, use:enhance를 붙이면 페이지를 새로 고치지 않고 결과만 갱신합니다. 검증에 실패하면 fail()로 상태 코드와 데이터를 돌려줍니다.
// src/routes/subscribe/+page.server.ts
import { fail, redirect } from '@sveltejs/kit';
import { db } from '#lib/server/db.js';
import type { Actions } from './$types';
export const actions = {
default: async ({ request }) => {
const form = await request.formData();
const email = String(form.get('email') ?? '');
if (!email.includes('@')) {
return fail(400, { email, invalid: true });
}
await db.subscriber.create({ email });
redirect(303, '/subscribe/thanks');
}
} satisfies Actions;<script lang="ts">
import { enhance } from '$app/forms';
import type { PageProps } from './$types';
let { form }: PageProps = $props();
</script>
<form method="POST" use:enhance>
<input name="email" type="email" value={form?.email ?? ''} />
{#if form?.invalid}<p class="error">이메일 주소를 확인하세요.</p>{/if}
<button>구독</button>
</form>액션이 여러 개라면 actions에 이름을 붙여 정의하고 action="?/register"처럼 지정합니다.
파일 이름에 server 조각이 있거나(db.server.ts) server 폴더 안에 있는 모듈(src/lib/server/)은 브라우저 코드에서 가져올 수 없으며, 실수로 가져오면 빌드가 오류를 냅니다. 환경 변수는 src/env.ts에 선언하고 $app/env/private 또는 $app/env/public에서 가져옵니다. 기본은 비공개이고, public: true인 변수만 브라우저에서 쓸 수 있습니다.
// src/env.ts
import { defineEnvVars } from '@sveltejs/kit/env';
export const variables = defineEnvVars({
DATABASE_URL: {},
SITE_NAME: { public: true }
});
// src/lib/server/db.ts 에서:
// import { DATABASE_URL } from '$app/env/private';예전 자료에 나오는 $env/static/private 같은 $env/* 모듈은 현재 판에서 사용이 권장되지 않습니다. 요청마다 달라지는 값(로그인 사용자 등)은 src/hooks.server.ts의 handle에서 event.locals에 넣고 서버 load와 액션에서 읽습니다.
여러 컴포넌트가 같은 상태를 써야 하면 .svelte.ts 파일에 $state를 두고 내보냅니다. 이 확장자의 파일에서는 룬을 쓸 수 있습니다.
// src/lib/cart.svelte.ts
export const cart = $state({ items: [] as { id: string; qty: number }[] });
export function addToCart(id: string) {
const found = cart.items.find((item) => item.id === id);
if (found) found.qty += 1;
else cart.items.push({ id, qty: 1 });
}단, 서버는 여러 사용자가 함께 쓰는 하나의 프로세스이므로 이런 모듈 수준 상태에 사용자별 데이터를 담으면 다른 사용자에게 새어 나갈 수 있습니다. 사용자별 상태는 load가 돌려준 data로 내려 주거나, Svelte의 setContext와 getContext로 요청마다 따로 만들어지는 컴포넌트 트리에 묶습니다. 필터와 정렬처럼 새로 고침 뒤에도 남아야 하는 상태는 ?sort=price처럼 URL 검색 매개변수에 두는 것이 좋습니다. svelte/store의 writable 같은 저장소도 계속 쓸 수 있지만, 새 코드는 룬을 기본으로 합니다.
+page.ts(유니버설) 또는 +page.server.ts(서버)의 load로 불러옵니다.use:enhance, 외부용 API는 +server.ts로 만듭니다.server 모듈과 $app/env/private로 서버에만 둡니다..svelte.ts의 $state로 만들되, 사용자별 데이터는 서버 모듈 상태에 두지 않습니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.