Claude Code Svelte / SvelteKit: Claude Code 활용 가이드
claude code svelte / sveltekit: Claude Code 활용. 실용적인 팁과 코드 예시를 포함합니다.
Svelte개발でClaude Code를 사용하는メリット
Svelteはコンパイラベースの프레임워크で、少ない보일러플레이트で高性能なUIを구축할 수 있습니다。Claude Code는 リアクティブ宣言やSvelteKit固有の라우팅・데이터ロードのパターンを理解し、効率的なコードを생성할 수 있습니다。
Svelte컴포넌트の생성
> Svelteで모달付きの이미지ギャラリー컴포넌트를 생성해줘。
> 그리드표시、클릭で拡大、キーボード내비게이션대응で。
<!-- ImageGallery.svelte -->
<script lang="ts">
interface Image {
id: string;
src: string;
alt: string;
thumbnail: string;
}
export let images: Image[] = [];
let selectedIndex = -1;
let isModalOpen = false;
$: selectedImage = selectedIndex >= 0 ? images[selectedIndex] : null;
function openModal(index: number) {
selectedIndex = index;
isModalOpen = true;
}
function closeModal() {
isModalOpen = false;
selectedIndex = -1;
}
function navigate(direction: number) {
selectedIndex = (selectedIndex + direction + images.length) % images.length;
}
function handleKeydown(e: KeyboardEvent) {
if (!isModalOpen) return;
switch (e.key) {
case 'Escape': closeModal(); break;
case 'ArrowLeft': navigate(-1); break;
case 'ArrowRight': navigate(1); break;
}
}
</script>
<svelte:window on:keydown={handleKeydown} />
<div class="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4">
{#each images as image, i}
<button
class="overflow-hidden rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
on:click={() => openModal(i)}
>
<img
src={image.thumbnail}
alt={image.alt}
class="h-48 w-full object-cover transition-transform hover:scale-105"
loading="lazy"
/>
</button>
{/each}
</div>
{#if isModalOpen && selectedImage}
<div
class="fixed inset-0 z-50 flex items-center justify-center bg-black/80"
on:click={closeModal}
role="dialog"
aria-modal="true"
aria-label="画像拡大表示"
>
<div class="relative max-h-[90vh] max-w-[90vw]" on:click|stopPropagation>
<img
src={selectedImage.src}
alt={selectedImage.alt}
class="max-h-[90vh] max-w-[90vw] object-contain"
/>
<button
class="absolute -left-12 top-1/2 -translate-y-1/2 rounded-full bg-white/20 p-2 text-white hover:bg-white/40"
on:click={() => navigate(-1)}
aria-label="前の画像"
>
←
</button>
<button
class="absolute -right-12 top-1/2 -translate-y-1/2 rounded-full bg-white/20 p-2 text-white hover:bg-white/40"
on:click={() => navigate(1)}
aria-label="次の画像"
>
→
</button>
<button
class="absolute -top-10 right-0 text-white hover:text-gray-300"
on:click={closeModal}
aria-label="Close"
>
Close
</button>
</div>
<p class="absolute bottom-4 text-center text-white">
{selectedIndex + 1} / {images.length}
</p>
</div>
{/if}
Svelteストアの설계
> Svelteのストアで테마と사용자설정を관리して。
> localStorage永続化付きで。
// stores/settings.ts
import { writable, derived } from 'svelte/store';
import { browser } from '$app/environment';
interface UserSettings {
theme: 'light' | 'dark' | 'system';
language: 'ja' | 'en';
fontSize: 'sm' | 'md' | 'lg';
sidebarCollapsed: boolean;
}
const defaultSettings: UserSettings = {
theme: 'system',
language: 'ja',
fontSize: 'md',
sidebarCollapsed: false,
};
function createSettingsStore() {
const initial = browser
? JSON.parse(localStorage.getItem('settings') || 'null') || defaultSettings
: defaultSettings;
const { subscribe, set, update } = writable<UserSettings>(initial);
return {
subscribe,
update(partial: Partial<UserSettings>) {
update(current => {
const next = { ...current, ...partial };
if (browser) localStorage.setItem('settings', JSON.stringify(next));
return next;
});
},
reset() {
set(defaultSettings);
if (browser) localStorage.removeItem('settings');
},
};
}
export const settings = createSettingsStore();
// 派生ストア:실제로適用される테마
export const effectiveTheme = derived(settings, ($settings) => {
if ($settings.theme !== 'system') return $settings.theme;
if (!browser) return 'light';
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
});
SvelteKitの서버サイド機能
> SvelteKitでブ로그の데이터ロードと폼アクション를 구현해줘。
// src/routes/blog/[slug]/+page.server.ts
import type { PageServerLoad, Actions } from './$types';
import { error, fail } from '@sveltejs/kit';
import { prisma } from '$lib/server/db';
export const load: PageServerLoad = async ({ params }) => {
const post = await prisma.post.findUnique({
where: { slug: params.slug },
include: {
author: { select: { name: true, avatar: true } },
comments: {
include: { author: { select: { name: true } } },
orderBy: { createdAt: 'desc' },
},
},
});
if (!post) throw error(404, 'Post not found');
return { post };
};
export const actions: Actions = {
comment: async ({ request, params, locals }) => {
const session = await locals.getSession();
if (!session) return fail(401, { message: 'ログインが必要です' });
const formData = await request.formData();
const body = formData.get('body')?.toString();
if (!body || body.length < 2) {
return fail(400, { message: 'コメントは2文字以上入力してください', body });
}
await prisma.comment.create({
data: {
body,
postSlug: params.slug,
authorId: session.user.id,
},
});
return { success: true };
},
};
<!-- src/routes/blog/[slug]/+page.svelte -->
<script lang="ts">
import type { PageData, ActionData } from './$types';
import { enhance } from '$app/forms';
export let data: PageData;
export let form: ActionData;
</script>
<article class="prose mx-auto max-w-3xl">
<h1>{data.post.title}</h1>
<p class="text-gray-500">
{data.post.author.name} - {new Date(data.post.createdAt).toLocaleDateString('en-US')}
</p>
{@html data.post.content}
</article>
<section class="mx-auto mt-12 max-w-3xl">
<h2 class="text-xl font-bold">コメント ({data.post.comments.length})</h2>
<form method="POST" action="?/comment" use:enhance>
<textarea
name="body"
rows="3"
class="mt-4 w-full rounded border p-3"
placeholder="コメントを入力..."
value={form?.body ?? ''}
/>
{#if form?.message}
<p class="text-sm text-red-600">{form.message}</p>
{/if}
<button type="submit" class="mt-2 rounded bg-blue-600 px-4 py-2 text-white">
送信
</button>
</form>
{#each data.post.comments as comment}
<div class="mt-4 rounded border p-4">
<p class="font-medium">{comment.author.name}</p>
<p class="mt-1 text-gray-700">{comment.body}</p>
</div>
{/each}
</section>
테스트
import { render, fireEvent } from '@testing-library/svelte';
import { describe, it, expect } from 'vitest';
import ImageGallery from './ImageGallery.svelte';
describe('ImageGallery', () => {
const images = [
{ id: '1', src: '/img1.jpg', alt: 'Image 1', thumbnail: '/thumb1.jpg' },
{ id: '2', src: '/img2.jpg', alt: 'Image 2', thumbnail: '/thumb2.jpg' },
];
it('should render all images', () => {
const { getAllByRole } = render(ImageGallery, { props: { images } });
expect(getAllByRole('button')).toHaveLength(2);
});
it('should open modal on click', async () => {
const { getAllByRole, getByRole } = render(ImageGallery, { props: { images } });
await fireEvent.click(getAllByRole('button')[0]);
expect(getByRole('dialog')).toBeTruthy();
});
});
정리
Claude Code를 활용하면 Svelte / SvelteKitの컴포넌트、ストア、서버サイドロジックを효율적으로개발할 수 있습니다。Svelteのリアクティブ宣言やSvelteKit固有のload함수・폼アクションもClaude Code는 正確に扱えます。プロンプトの工夫で品質を高めるコツはプロンプトテクニック完全가이드를 참고하세요.프레임워크比較に興味がある方はClaude Code vs GitHub Copilot도 확인하세요.
Claude Code의 상세 정보는Anthropic공식 문서를 확인하세요.SvelteKitの公式가이드はSvelteKit공식 사이트를 참고하세요.
#Claude Code
#Svelte
#SvelteKit
#frontend
#TypeScript
Related Posts
Use Cases
Use Cases
Claude Code로 리팩토링을 자동화하는 방법
Claude Code를 활용해 코드 리팩토링을 효율적으로 자동화하는 방법을 알아봅니다. 실전 프롬프트와 구체적인 리팩토링 패턴을 소개합니다.
Use Cases
Use Cases
Claude Code로 사이드 프로젝트 개발 속도를 극대화하는 방법 [예제 포함]
Claude Code를 활용해 개인 프로젝트 개발 속도를 획기적으로 높이는 방법을 알아봅니다. 실전 예제와 아이디어부터 배포까지의 워크플로를 포함합니다.
Use Cases
Use Cases
Complete CORS Configuration Guide: Claude Code 활용 가이드
complete cors configuration guide: Claude Code 활용. 실용적인 팁과 코드 예시를 포함합니다.