Prisma + SvelteKit — 풀스택 데이터베이스 연동
SvelteKit + Prisma 조합이면 프론트엔드 개발자도 풀스택 앱을 뚝딱 만들 수 있습니다.
개념 정의
Prisma 는 TypeScript/JavaScript를 위한 ORM입니다. 스키마 파일로 데이터 모델을 정의하고, 자동 생성된 타입 안전한 클라이언트로 데이터베이스를 조작합니다.
설정
npm install prisma @prisma/client
npx prisma init
// prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String @id @default(cuid())
email String @unique
name String
posts Post[]
createdAt DateTime @default(now())
}
model Post {
id String @id @default(cuid())
title String
content String
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
npx prisma migrate dev --name init
npx prisma generate
Prisma 클라이언트 싱글턴
// src/lib/server/database.js
import { PrismaClient } from '@prisma/client';
// 개발 환경에서 핫 리로드 시 연결 누수 방지
const globalForPrisma = globalThis;
export const db = globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = db;
}
CRUD — 목록 조회
// src/routes/posts/+page.server.js
import { db } from '$lib/server/database';
export async function load({ url }) {
const page = Number(url.searchParams.get('page') ?? '1');
const limit = 10;
const [posts, total] = await Promise.all([
db.post.findMany({
where: { published: true },
include: { author: { select: { name: true } } },
orderBy: { createdAt: 'desc' },
skip: (page - 1) * limit,
take: limit,
}),
db.post.count({ where: { published: true } }),
]);
return {
posts,
pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
};
}
CRUD — 생성
// src/routes/posts/new/+page.server.js
import { fail, redirect } from '@sveltejs/kit';
import { db } from '$lib/server/database';
export const actions = {
default: async ({ request, locals }) => {
if (!locals.user) redirect(302, '/login');
const formData = await request.formData();
const title = formData.get('title')?.toString();
const content = formData.get('content')?.toString();
if (!title || title.length < 2) {
return fail(400, { title, content, error: '제목은 2자 이상이어야 합니다' });
}
const post = await db.post.create({
data: {
title,
content: content ?? '',
authorId: locals.user.id,
}
});
redirect(303, `/posts/${post.id}`);
}
};
CRUD — 수정/삭제
// src/routes/posts/[id]/edit/+page.server.js
import { error, fail, redirect } from '@sveltejs/kit';
import { db } from '$lib/server/database';
export async function load({ params, locals }) {
const post = await db.post.findUnique({ where: { id: params.id } });
if (!post) error(404, '포스트를 찾을 수 없습니다');
if (post.authorId !== locals.user?.id) error(403, '권한이 없습니다');
return { post };
}
export const actions = {
update: async ({ request, params }) => {
const formData = await request.formData();
await db.post.update({
where: { id: params.id },
data: {
title: formData.get('title')?.toString(),
content: formData.get('content')?.toString(),
}
});
redirect(303, `/posts/${params.id}`);
},
delete: async ({ params }) => {
await db.post.delete({ where: { id: params.id } });
redirect(303, '/posts');
},
};
트랜잭션
// 여러 작업을 하나의 트랜잭션으로
async function transferPost(postId, newAuthorId) {
await db.$transaction([
db.post.update({
where: { id: postId },
data: { authorId: newAuthorId },
}),
db.auditLog.create({
data: {
action: 'TRANSFER_POST',
postId,
newAuthorId,
},
}),
]);
}
면접 포인트
- "ORM을 사용하는 이유는?": 타입 안전한 쿼리, 마이그레이션 관리, SQL 인젝션 방지를 자동으로 제공합니다. Prisma는 스키마에서 TypeScript 타입을 자동 생성하여 개발 경험이 우수합니다.
- "Prisma 클라이언트 싱글턴이 왜 필요한가요?": 개발 모드에서 핫 리로드 시마다 새 PrismaClient가 생성되면 데이터베이스 연결이 고갈됩니다. 전역 변수에 캐시하여 연결 재사용을 보장합니다.
정리
SvelteKit + Prisma는 풀스택 개발의 가장 생산적인 조합 중 하나입니다. Prisma의 타입 안전한 쿼리와 SvelteKit의 load/actions 패턴이 자연스럽게 결합되어, 보안적이고 타입 안전한 CRUD 앱을 빠르게 구축할 수 있습니다.
댓글 로딩 중...