
Prisma๋ ํ๋์ ์ธ TypeScript ๊ธฐ๋ฐ ORM(Object-Relational Mapping) ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ํด์ด์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, ๊ฐ์ฒด์งํฅ ์ฝ๋๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฝ๊ฒ ๋ค๋ฃฐ ์ ์๋๋ก ๋์์ค๋๋ค.
SQL ์์ฑ ์์ด๋ ๋ฐ์ดํฐ์ ์ ๊ทผํ๊ณ ์กฐ์ํ ์ ์์ผ๋ฉฐ, ํ์
์์ ์ฑ๊ณผ ์๋์์ฑ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
| ๊ตฌ๋ถ | ๋ผ์ด๋ธ๋ฌ๋ฆฌ | ํด (Tool, CLI ๋๊ตฌ) |
|---|---|---|
| ๋ชฉ์ | ์ฝ๋ ๋ด๋ถ์์ ๊ธฐ๋ฅ์ ๋ถ๋ฌ์ ์ฌ์ฉ | ์ธ๋ถ์์ ์์ ์ ์ํํ๊ฑฐ๋ ์๋ํ |
| ์์ | @prisma/client, lodash, axios | npx prisma migrate, git, webpack |
| ์ฌ์ฉ ์์น | ์ฝ๋ ์ (importํด์ ์ฌ์ฉ) | ํฐ๋ฏธ๋์ด๋ ๋ช ๋ น์ด ํ๊ฒฝ์์ ์ฌ์ฉ |
| ๊ธฐ๋ฅ | DB ์์ฒญ, ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๋ฑ ๋ฐํ์ ๋ก์ง | DB ๋ง์ด๊ทธ๋ ์ด์ , ์ฝ๋ ์์ฑ, ์ด๊ธฐํ ์์ ๋ฑ |
๐ก Extendsion ์ค์น ๊ถ์ฅ
- ์ค์น ํ์ง ์์ ์ shema.prisma - code๋ค์ ์์ด ํฐ์์ผ๋ก ๋์ต๋๋ค.
โ Prisma์ ์ฃผ์ ์ฅ์
| ํญ๋ชฉ | ์ค๋ช |
|---|---|
| ํ์ ์์ ์ฑ | DB ๋ชจ๋ธ ๊ธฐ๋ฐ์ผ๋ก ์๋ ํ์ ์์ฑ๋์ด ์ฝ๋ ์์ฑ ์ค ์ค๋ฅ ์๋ฐฉ |
| ๊ฐ๊ฒฐํ ๋ฌธ๋ฒ | ๋ฉ์๋ ์ฒด์ด๋ ๋ฐฉ์์ผ๋ก ์ง๊ด์ ์ธ ์ฟผ๋ฆฌ ์์ฑ ๊ฐ๋ฅ |
| ์๋ ๋ง์ด๊ทธ๋ ์ด์ | Prisma CLI๋ก ์คํค๋ง ๋ณ๊ฒฝ โ ๋ง์ด๊ทธ๋ ์ด์ ์๋ ์์ฑ |
| ํ๋ก ํธ์๋ ์นํ์ | Next.js ๋ฑ TypeScript ๊ธฐ๋ฐ ํ๋ก์ ํธ์ ๊ถํฉ์ด ๋งค์ฐ ์ข์ |
| ๋น ๋ฅธ ์์ฐ์ฑ | Prisma Studio๋ฅผ ํตํด GUI๋ก ๋ฐ์ดํฐ ํ์ธ ๋ฐ ์์ ๊ฐ๋ฅ |
โ ๏ธ Prisma์ ๋จ์
| ํญ๋ชฉ | ์ค๋ช |
|---|---|
| ๋ณต์กํ ์ฟผ๋ฆฌ์ ํ๊ณ | ๋ณต์กํ join/subquery๋ native SQL๋ก ์ฒ๋ฆฌํด์ผ ํจ |
| ๊ด๊ณ ๋งคํ ํํ์ ์ ์ฝ | ์ผ๋ถ ๊ณ ๊ธ ๊ด๊ณ ์ค์ ์ ํํ์ด ๋ค์ ์ ํ์ ์ผ ์ ์์ |
| ๋ฐํ์ ์ฑ๋ฅ ์ค๋ฒํค๋ | ๋ด๋ถ ์ถ์ํ(abstraction) ๊ตฌ์กฐ๋ก ์ฑ๋ฅ ์ด์ ๋ฐ์ ๊ฐ๋ฅ (๊ทน๋จ์ ๊ฒฝ์ฐ) |
npm install prisma tsx --save-dev
npx prisma init
npm install @prisma/client
prisma init ์คํ ์ prisma/schema.prisma ํ์ผ๊ณผ .env ํ์ผ์ด ์์ฑ๋จ.
.env ํ์ผ์์ DB ์ฐ๊ฒฐ ๋ฌธ์์ด์ ์ค์ ํฉ๋๋ค.
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
โ DB ๋ฌธ์์ด(DATABASE_URL ์ฐพ๋ ๋ฐฉ๋ฒ) supabase ๊ธฐ์ค
- https://supabase.com/ ์ ์
- ํ์๊ฐ์ ๋ฐ ๋ก๊ทธ์ธ
- project ์์ฑ
- ์๋จ ์ฐ์ธก Connect ํด๋ฆญ
- ORMs ํด๋ฆญ
- ๋ณต์ฌํ .env์ ๋ณต์ฌ
(2025.06.10 ๊ธฐ์ค โconntetion string์ผ๋ก ํ๋ฉด ์๋ฌโ)
๋ชจ๋ธ ์ ์์ ์์ธํ ๋ฐฉ๋ฒ์ ์ฌ๊ธฐ์ ์์๋ณด๊ฒ ์ต๋๋ค.
model User {
id Int @id @default(autoincrement())
name String
email String @unique
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
author User @relation(fields: [authorId], references: [id])
authorId Int
}
npx prisma migrate dev --name init
DB์ ํ
์ด๋ธ ์์ฑ๋จ
๋ง์ด๊ทธ๋ ์ด์
ํ์ผ ์๋ ์์ฑ๋จ
PrismaClient: Prisma๊ฐ ์๋์ผ๋ก ์์ฑํ DB์ ๊ทผ ๋๊ตฌ, SQL์์ด DB๋ฅผ ์กฐ์ํ ์ ์๊ฒ ๋์์ฃผ๋ ํต์ฌ ๊ฐ์ฒด
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
// create: instert ์ฟผ๋ฆฌ ์ญํ , ํ์ฌ post์ ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ๋ฃ๋ ๋ฉ์๋
const user = await prisma.user.create({
data: {
name: 'ํ๊ธธ๋',
email: 'hong@example.com',
posts: {
create: {
title: '์ฒซ ๋ฒ์งธ ๊ฒ์๋ฌผ',
},
},
},
});
// findMany: select ์ฟผ๋ฆฌ ์ญํ , ๋ฐ์ดํฐ ์ฌ๋ฌ ๊ฐ ์กฐํ, ์กฐ๊ฑด์ ๋ฃ์ผ๋ฉด ํํฐ๋ง๋ ๊ฐ๋ฅ
const allUsers = await prisma.user.findMany({
// include: join์ ์ญํ , ํ์ฌ usser๋ฅผ ์ฐพ์๋ ์ฐ๊ด๋ posts๋ ํจ๊ป ๊ฐ์ ธ์์ค ๋ผ๋ ์๋ฏธ
include: {
posts: true,
},
});
console.log(allUsers);
}
// ์ค์ ๋ก ์คํ
main();
๐ฅ๏ธ ์ฃผ์ ํฐ๋ฏธ๋ ๋ช ๋ น์ด
| ๋ช ๋ น์ด | ์ค๋ช |
|---|---|
npx prisma init | ์ด๊ธฐ ์ค์ ํ์ผ ์์ฑ |
npx prisma generate | Prisma Client ์ฌ์์ฑ (๋ชจ๋ธ ๋ณ๊ฒฝ ์ ํ์) |
npx prisma migrate dev (--name [์ค๋ช
]) | ๊ฐ๋ฐ์ฉ ๋ง์ด๊ทธ๋ ์ด์ ์ํ- prisma ์คํค๋ง ๋ณ๊ฒฝ-> DB์ ๋ฐ์ |
npx prisma studio | ์น ๊ธฐ๋ฐ GUI(DB ๋ธ๋ผ์ฐ์ ) ์คํ |
npx prisma format | schema.prisma ํ์ผ ํฌ๋งท ์ ๋ฆฌ(ํฌ๋งทํ ): ๊ฐ๋ ์ฑ ์ฆ๊ฐ |
npx prisma db pull | ๊ธฐ์กด DB์์ ์คํค๋ง ๋ถ๋ฌ์ค๊ธฐ- DB ๋ณ๊ฒฝ-> Prisma ์คํค๋ง ๋ฐ์ (๋ฐ๋๋ก ๊ฐ์ ธ์ค๊ธฐ) |
npx prisma migrate reset | ๐จ ๊ธฐ์กด DB ์ด๊ธฐํ(Shema ํฌํจ ๋ฐ์ดํฐ ์ ์ค, ์ฃผ์ํด์ ์ฌ์ฉ) |
๐ SQL vs Prisma ๋ฌธ๋ฒ ๋น๊ต
| ์์ | SQL | Prisma |
|---|---|---|
| โ ๋ฐ์ดํฐ ์์ฑ (INSERT) | INSERT INTO users (name, email) VALUES ('ํ๊ธธ๋', 'hong@example.com'); | await prisma.user.create({ data: { name: 'ํ๊ธธ๋', email: 'hong@example.com' } }); |
| ๐ ์ ์ฒด ์กฐํ (SELECT all) | SELECT * FROM users; | await prisma.user.findMany(); |
| ๐ ์กฐ๊ฑด ์กฐํ (SELECT with WHERE) | SELECT * FROM users WHERE email = 'hong@example.com'; | await prisma.user.findMany({ where: { email: 'hong@example.com' } }); |
| โ๏ธ ์์ (UPDATE) | UPDATE users SET name = '๊น์ฒ ์' WHERE id = 1; | await prisma.user.update({ where: { id: 1 }, data: { name: '๊น์ฒ ์' } }); |
| โ ์ญ์ (DELETE) | DELETE FROM users WHERE id = 1; | await prisma.user.delete({ where: { id: 1 } }); |
| ๐ ์กฐ์ธ ํฌํจ ์กฐํ (JOIN) | SELECT * FROM users u JOIN posts p ON u.id = p.user_id; | await prisma.user.findMany({ include: { posts: true } }); |
| ๐งต ๊ด๊ณ ํฌํจ ์์ฑ (user์ post ํจ๊ป ์์ฑ) | INSERT INTO users ...; INSERT INTO posts (user_id, title) VALUES (...); | await prisma.user.create({ data: { name: '', posts: { create: { title: '' } } } }); |
| ๐ Upsert (์์ผ๋ฉด ์์ , ์์ผ๋ฉด ์์ฑ) | (SQL์ ๋ณต์กํ ์กฐ๊ฑด๋ฌธ ํ์) | await prisma.user.upsert({ where: { email }, update: { name }, create: { email, name } }); |
๐ฆ Prisma ์ฃผ์ ๋ฉ์๋ ์ ๋ฆฌ
๐น Create (๋ฐ์ดํฐ ์์ฑ)
| ๋ฉ์๋ | ์ค๋ช |
|---|---|
create() | ๋จ์ผ ๋ ์ฝ๋ ์์ฑ |
createMany() | ์ฌ๋ฌ ๋ ์ฝ๋ ํ ๋ฒ์ ์์ฑ |
๐ Read (๋ฐ์ดํฐ ์กฐํ)
| ๋ฉ์๋ | ์ค๋ช |
|---|---|
findUnique() | ๊ณ ์ ์กฐ๊ฑด์ผ๋ก ๋จ์ผ ๋ ์ฝ๋ ์กฐํ (e.g. id) |
findFirst() | ์กฐ๊ฑด์ ๋ง๋ ์ฒซ ๋ฒ์งธ ๋ ์ฝ๋ ์กฐํ |
findMany() | ์ฌ๋ฌ ๋ ์ฝ๋ ์กฐํ |
count() | ์กฐ๊ฑด์ ๋ง๋ ๋ ์ฝ๋ ์ ๋ฐํ |
aggregate() | sum, avg, max, min, count ๋ฑ ์ง๊ณ ํจ์ |
groupBy() | ํ๋ ๊ธฐ์ค์ผ๋ก ๊ทธ๋ฃนํํ์ฌ ํต๊ณ ๊ณ์ฐ |
โ๏ธ Update (๋ฐ์ดํฐ ์์ )
| ๋ฉ์๋ | ์ค๋ช |
|---|---|
update() | ๋จ์ผ ๋ ์ฝ๋ ์์ |
updateMany() | ์กฐ๊ฑด์ ๋ง๋ ์ฌ๋ฌ ๋ ์ฝ๋ ์์ |
๐ Upsert (์กฐ๊ฑด๋ถ ์ฝ์ or ์์ )
| ๋ฉ์๋ | ์ค๋ช |
|---|---|
upsert() | ์กด์ฌํ๋ฉด update, ์์ผ๋ฉด create ์ํ |
๐ก ๊ธฐํ ์ ์ฉํ ๊ธฐ๋ฅ
| ๋ฉ์๋ | ์ค๋ช |
|---|---|
include | ๊ด๊ณ ํ ์ด๋ธ ๊ฐ์ด ์กฐํํ ๋ ์ฌ์ฉ |
select | ํ์ํ ํ๋๋ง ์ ํํด์ ์กฐํํ ๋ ์ฌ์ฉ |
orderBy | ์ ๋ ฌ |
where | ์กฐ๊ฑด ์ง์ |
distinct | ์ค๋ณต ์ ๊ฑฐ |