SvelteKit Form Actions — 서버에서 폼 처리하기
JavaScript가 꺼져 있어도 폼이 동작합니다 — SvelteKit Form Actions의 프로그레시브 인핸스먼트입니다.
개념 정의
Form Actions 는 SvelteKit에서 <form> 제출을 서버에서 처리하는 메커니즘입니다. +page.server.js에 actions를 정의하면, HTML의 기본 폼 제출 방식으로 동작하면서도 JavaScript가 활성화되면 향상된 경험을 제공합니다.
기본 사용법
// src/routes/login/+page.server.js
import { fail, redirect } from '@sveltejs/kit';
export const actions = {
// 기본 action (method="POST"로 제출 시)
default: async ({ request, cookies }) => {
const data = await request.formData();
const email = data.get('email');
const password = data.get('password');
// 유효성 검사
if (!email) {
return fail(400, { email, missing: true });
}
// 인증 로직
const user = await authenticate(email, password);
if (!user) {
return fail(401, { email, incorrect: true });
}
cookies.set('session', user.sessionId, { path: '/' });
redirect(303, '/dashboard');
}
};
<!-- src/routes/login/+page.svelte -->
<script>
import { enhance } from '$app/forms';
let { form } = $props();
</script>
<h1>로그인</h1>
<!-- use:enhance로 프로그레시브 인핸스먼트 -->
<form method="POST" use:enhance>
<label>
이메일
<input name="email" type="email" value={form?.email ?? ''} />
</label>
{#if form?.missing}
<p class="error">이메일을 입력해주세요.</p>
{/if}
<label>
비밀번호
<input name="password" type="password" />
</label>
{#if form?.incorrect}
<p class="error">이메일 또는 비밀번호가 올바르지 않습니다.</p>
{/if}
<button type="submit">로그인</button>
</form>
Named Actions
여러 액션을 구분할 때 사용합니다.
// src/routes/todos/+page.server.js
export const actions = {
create: async ({ request }) => {
const data = await request.formData();
const text = data.get('text');
await db.todo.create({ data: { text } });
},
delete: async ({ request }) => {
const data = await request.formData();
const id = data.get('id');
await db.todo.delete({ where: { id } });
},
toggle: async ({ request }) => {
const data = await request.formData();
const id = data.get('id');
const todo = await db.todo.findUnique({ where: { id } });
await db.todo.update({
where: { id },
data: { done: !todo.done }
});
}
};
<!-- action 속성으로 어떤 action을 호출할지 지정 -->
<form method="POST" action="?/create" use:enhance>
<input name="text" placeholder="할 일 입력" />
<button>추가</button>
</form>
{#each data.todos as todo}
<div>
<form method="POST" action="?/toggle" use:enhance>
<input type="hidden" name="id" value={todo.id} />
<button>{todo.done ? '완료' : '미완료'}: {todo.text}</button>
</form>
<form method="POST" action="?/delete" use:enhance>
<input type="hidden" name="id" value={todo.id} />
<button>삭제</button>
</form>
</div>
{/each}
use:enhance 커스터마이징
<script>
import { enhance } from '$app/forms';
let loading = $state(false);
</script>
<form
method="POST"
use:enhance={() => {
loading = true;
return async ({ result, update }) => {
loading = false;
if (result.type === 'success') {
// 성공 시 커스텀 로직
alert('저장 완료!');
}
// 기본 동작 실행 (폼 초기화, 데이터 갱신 등)
await update();
};
}}
>
<input name="title" />
<button disabled={loading}>
{loading ? '저장 중...' : '저장'}
</button>
</form>
파일 업로드
// +page.server.js
export const actions = {
upload: async ({ request }) => {
const data = await request.formData();
const file = data.get('file');
if (!(file instanceof File) || file.size === 0) {
return fail(400, { message: '파일을 선택해주세요' });
}
// 파일 크기 제한 (5MB)
if (file.size > 5 * 1024 * 1024) {
return fail(400, { message: '파일 크기는 5MB 이하여야 합니다' });
}
const buffer = await file.arrayBuffer();
// 파일 저장 로직...
return { success: true, filename: file.name };
}
};
<form method="POST" action="?/upload" enctype="multipart/form-data" use:enhance>
<input type="file" name="file" accept="image/*" />
<button>업로드</button>
</form>
면접 포인트
- "프로그레시브 인핸스먼트가 뭔가요?": JavaScript 없이도 기본 HTML 폼으로 동작하고, JavaScript가 활성화되면
use:enhance가 AJAX로 업그레이드하여 전체 페이지 새로고침 없이 동작합니다. - "Form Actions vs API Routes?": 폼 제출 처리에는 Form Actions가 적합합니다. 프로그레시브 인핸스먼트, CSRF 보호, 자동 데이터 재로딩을 기본 제공하기 때문입니다. API Routes는 외부 클라이언트(모바일 앱 등)가 호출할 때 적합합니다.
정리
Form Actions는 "서버에서 폼을 처리하는 가장 Svelte다운 방법"입니다. HTML 표준 폼 동작을 유지하면서 use:enhance로 UX를 향상시키고, fail()로 에러를 깔끔하게 처리할 수 있습니다.
댓글 로딩 중...