๐Ÿ“ข prisma

๋ฐ•์ƒ์€ยท2022๋…„ 4์›” 22์ผ
0

๐Ÿ‘‰ ๋ฏธ๋ž˜์˜ ๋ณธ์ธ์„ ์œ„ํ•ด ์ •๋ฆฌํ•˜๋Š” prisma ๐Ÿ‘ˆ

๐Ÿ“ข prisma๋ž€

sequelize์™€ ๊ฐ™์€ ORM์ž…๋‹ˆ๋‹ค.

  • ORM: Object Relational Mapping์œผ๋กœ JavaScript์˜ ๊ฐ์ฒด๋ฅผ Database์˜ ํ…Œ์ด๋ธ” ํ˜น์€ ์Šคํ‚ค๋งˆ์™€ ์—ฐ๊ฒฐํ•ด์„œ SQL๋ฌธ ๋ณด๋‹ค ์‰ฝ๊ณ  ํŽธํ•˜๊ฒŒ DB๋ฅผ ์ปจํŠธ๋กคํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

JS์˜ class๋‚˜ TS์˜ interface๋กœ ํ˜•ํƒœ์™€ ์†์„ฑ๋“ค์„ ์ •์˜ํ•ด์ฃผ๋ฉด ๊ทธ๊ฒƒ์— ๋งž๊ฒŒ ์ž๋™์ ์œผ๋กœ SQL๋ฌธ์„ ์ƒ์„ฑํ•ด์„œ Database๋ฅผ ๊ด€๋ฆฌํ•ด์ฃผ๋Š” ๊ฒƒ์œผ๋กœ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

ORM์˜ ์žฅ์ ์€ ๊ตณ์ด ๋ณต์žกํ•œ SQL์— ๋Œ€ํ•ด ์ž์„ธํ•˜๊ฒŒ ์ดํ•ดํ•˜์ง€ ์•Š์•„๋„ ๋˜๋ฉฐ(๋ฌผ๋ก  ์–ด๋Š์ •๋„๋Š” ์•Œ๊ณ  ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•จ), CRUD ๊ทธ๋ฆฌ๊ณ  JOIN๊ฐ™์€ ๋ณต์žกํ•œ ์—ฐ์‚ฐ์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ•ด๊ฒฐํ•ด์ฃผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๋ฏธ๋ฆฌ ๊ตฌํ˜„ํ•ด๋†จ๊ธฐ ๋•Œ๋ฌธ์— ์‰ฝ๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿช„ ์„ค์น˜

npm i -D prisma
npm i @prisma/client

โš™๏ธ ์ดˆ๊ธฐ ์„ธํŒ…

npx prisma init

/prisma/schema.prisma์™€ .env๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

  • ์ด์™ธ์˜ ๋‹ค๋ฅธ DB๋“ค์€ ๊ณต์‹ํŽ˜์ด์ง€์—์„œ ์ฐพ์•„๋ณด๊ณ  ๊ทธ๋Œ€๋กœ ์ ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

mysql ๊ธฐ์ค€ ์„ธํŒ… ๋ฐฉ๋ฒ•

  • /prisma/schema
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
}
  • .env
DATABASE_URL="mysql://testUser:test@localhost:3306/testDB"
DATABASE_URL="mysql://<์œ ์ €๋ช…>:<๋น„๋ฐ€๋ฒˆํ˜ธ>@<ํ˜ธ์ŠคํŠธ>:<port>/<DB๋ช…>

๐Ÿ”Ž schema.prisma

prisma์˜ ์„ธํŒ…์„ ์ž…๋ ฅํ•˜๋Š” ํŒŒ์ผ์ž…๋‹ˆ๋‹ค.

# prisma์™€ Database๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.

datasource db {
  provider             = "mysql"
  url                  = env("DATABASE_URL")
  referentialIntegrity = "prisma"
}
# 

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["referentialIntegrity"]
}
# Database์˜ ํ…Œ์ด๋ธ”์˜ ํ˜•ํƒœ๋ฅผ ๋ช…์‹œ
# user์™€ post ๊ด€๊ณ„ ( 1 : N )

model User {
  id        Int      @id @default(autoincrement())
  name      String
  phone     Int?     @unique
  email     String?  @unique
  avatar    String?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  
  posts     Post[]
}

model Post {
  id        Int      @id @default(autoincrement())
  content   String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  
  user      User @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId    Int
}

๐Ÿค ๊ด€๊ณ„

  • @relation์„ ์ •์˜ํ•˜๋Š” ๋ชจ๋ธ์— foreign key๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

1. ๊ด€๊ณ„ ์ •์˜ ์˜ˆ์‹œ

  • 1 : 1
# 1 : 1 ๊ด€๊ณ„์ด๋ฏ€๋กœ User or Profile์ค‘์— foreign key๋ฅผ ์ƒ์„ฑํ•  ์œ„์น˜๋Š” ์„ ํƒํ•  ์ˆ˜ ์žˆ์Œ ( @relation ์œ„์น˜ )
model User {
  id      Int      @id @default(autoincrement())
  profile Profile?
}

model Profile {
  id     Int    @id @default(autoincrement())
  name   String
  user   User   @relation(fields: [userId], references: [id])
  userId Int    @unique
}
  • 1 : N
model User {
  id    Int     @id @default(autoincrement())
  email String
  name  String?

  # vscode์˜ prismaํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์น˜ํ–ˆ์„ ๊ฒฝ์šฐ์—
  # ์•„๋ž˜ ์ฝ”๋“œ๋ถ€ํ„ฐ ์ž…๋ ฅํ•˜๋ฉด ์ž๋™์œผ๋กœ Post์— ๊ด€๊ณ„์„ค์ • ์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ•ด์ค€๋‹ค.
  posts Post[]
}

model Post {
  id       Int    @id @default(autoincrement())
  contents String

  User   User? @relation(fields: [userId], references: [id])
  userId Int?
}
  • N : M
# ๋ฌต์‹œ์  N : M
# @relation("MyTable")์€ ์ค‘๊ฐ„ ํ…Œ์ด๋ธ” ์ด๋ฆ„ ์ง€์ • ( _MyTable )
# ์ƒ๋žต์‹œ _PostToCategory์™€ ๊ฐ™์€ ๋ช…์นญ์˜ ์ค‘๊ฐ„ ํ…Œ์ด๋ธ”์ด ์ƒ์„ฑ๋จ
model Post {
  id        Int    @id @default(autoincrement())
  contents  String

  categories Category[] @relation("MyTable")
}
model Category {
  id       Int    @id @default(autoincrement())
  category String

  posts Post[] @relation("MyTable")
}


# ๋ช…์‹œ์  N : M
model Post {
  id    Int    @id @default(autoincrement())
  title String

  categories PostCategory[]
}
model Category {
  id       Int    @id @default(autoincrement())
  category String

  posts PostCategory[]
}
model PostCategory {
  Post       Post     @relation(fields: [postId], references: [id])
  postId     Int
  Category   Category @relation(fields: [categoryId], references: [id])
  categoryId Int

  @@id([postId, categoryId])
}
  • ํ•˜๋‚˜์˜ ํ…Œ์ด๋ธ”์ด ๋™์‹œ์— ๋‘ ๊ฐ€์ง€ ์ด์ƒ์˜ 1 : N๊ด€๊ณ„๋ฅผ ๊ฐ€์งˆ ๋•Œ
# user์™€ message๊ฐ€ ๋‘ ๋ฒˆ 1 : N๊ด€๊ณ„๋ฅผ ๊ฐ€์ง€๋Š” ๊ฒฝ์šฐ, "name"์œผ๋กœ ๊ตฌ๋ถ„
model User {
  id    Int     @id @default(autoincrement())
  email String
  name  String?

  sentMessage     Message[] @relation("sent")
  receivedMessage Message[] @relation("received")
}

model Message {
  id      Int    @id @default(autoincrement())
  message String

  sent       User? @relation(name: "sent", fields: [sentId], references: [id])
  sentId     Int?
  received   User? @relation(name: "received", fields: [receivedId], references: [id])
  receivedId Int?
}

2. ๊ด€๊ณ„ ์‚ฌ์šฉ

  1. create: ์—ฐ๊ด€๋œ ํ…Œ์ด๋ธ”์— ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
await prisma.user.create({
  data: {
    name: "admin",
    email: "admin@naver.com",
    posts: {
      create: {
        contents: "์ฒซ ๋ฒˆ์งธ ๊ฒŒ์‹œ๊ธ€",
      },
    },
  },
});
  1. connect: ์ด๋ฏธ ์ƒ์„ฑ๋œ ๋ฐ์ดํ„ฐ ์—ฐ๊ฒฐ
// async ํ•จ์ˆ˜ ๋‚ด๋ถ€์˜ ์ฝ”๋“œ๋ผ ๊ฐ€์ •
const createdPost = await prisma.post.create({
  data: {
    contents: "์ฒซ ๋ฒˆ์งธ ๊ฒŒ์‹œ๊ธ€",
  },
});

await prisma.user.create({
  data: {
    name: "admin",
    email: "admin@naver.com",
    posts: {
      connect: {
        id: createdPost.id,
      },
    },
  },
});
  1. include: ํฌํ•จํ•ด์„œ ๊ฐ€์ ธ์˜ค๊ธฐ
// ๊ธฐ๋ณธ ํ˜•ํƒœ ( post์˜ ๋ชจ๋“  row๋“ค์„ ๊ฐ€์ ธ์˜ด )
const usersWithPosts = await prisma.user.findMany({
  include: {
    posts: true,
  },
});

// select์ด์™ธ์˜ ๋‹ค๋ฅธ ์กฐ๊ฑด๋“ค๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ ( post์˜ id, name๋งŒ ๊ฐ€์ ธ์˜ด )
const usersWithPosts = await prisma.user.findMany({
  include: {
    posts: {
      select: {
        id: true,
        name: true,
      },
    },
  },
});

๐ŸŽฐ ๋ฐ์ดํ„ฐ ํƒ€์ž…

npx prisma db push๋กœ ํ…Œ์ด๋ธ” ์ƒ์„ฑ ์‹œ ์ž๋™์ ์œผ๋กœ ํƒ€์ž…์„ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.
๋งŒ๋“ค์ž๋งˆ์ž importํ•˜๋ ค๋ฉด VSCode์—์„œ ์ธ์‹์„ ๋ชปํ•˜๋Š”๋ฐ ๊ทธ๋•Œ๋Š” ์ง์ ‘ ํŒŒ์ผ์— ๋“ค์–ด๊ฐ”๋‹ค๊ฐ€ ๋‚˜์˜ค๋ฉด VSCode์—์„œ ํƒ€์ž…์„ ์ธ์‹ํ•ฉ๋‹ˆ๋‹ค.

  • ๋ชจ๋ธ
model User {
  id    Int    @id @default(autoincrement())
  name  String @db.VarChar(20)
  email String

  posts Post[]
}
model Post {
  id Int @id @default(autoincrement())

  User   User? @relation(fields: [userId], references: [id])
  userId Int?
}
  • ์ƒ์„ฑ๋œ ํƒ€์ž…
// node_modules/.prisma/client/index.d.ts
export type User = {
  id: number
  name: string
  email: string | null
}
export type Post = {
  id: number
  userId: number
}

// import { Product } from "@prisma/client";
  • <DB๋ช…>CreateInput ์‚ฌ์šฉ ์˜ˆ์‹œ
let user: Prisma.UserCreateInput = {
  name: "",
  email: "aa",
};

const exUser = await prisma.user.create({
  data: user,
});

๐Ÿ› ๏ธ ๋ฐ์ดํ„ฐ CRUD

๋ชจ๋‘ user์™€ post๋ผ๋Š” ํ…Œ์ด๋ธ”์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ  ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

1. Create

  • prisma.user.create()
const createdUser = await prisma.user.create({
  data: {
    name: "john",
    email: "j@naver.com"
  },
});
  • prisma.user.createMany()
const count = await prisma.user.createMany({
  data: [
    { name: "apple", email: "a@naver.com" },
    { name: "blue", email: "b@naver.com" },
    { name: "color", email: "c@naver.com" },
    // ...
  ],
});

console.log(count.count);	// 3

2. Read

  • prisma.user.findUnique()
const exUser = await prisma.user.findUnique({
  // ์œ ๋‹ˆํฌํ•œ ๊ฐ’๋งŒ ๊ฒ€์ƒ‰์กฐ๊ฑด์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ( type์ด ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์ ธ ์žˆ์–ด์„œ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋Š” row๋ฅผ ๋ณด์—ฌ์คŒ )
  where: {
    id: 1,
  },
});

// ๋งŒ์•ฝ @@unique([email, name])๊ฐ™์€ ํ˜•์‹์œผ๋กœ ๋งŒ๋“ค์—ˆ๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
// ํ˜น์‹œ @@unique([email, name], name: "yourName")์ฒ˜๋Ÿผ ๋งŒ๋“ค์—ˆ๋‹ค๋ฉด "email_name"๋Œ€์‹  "yourName"์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
const exUser = await prisma.user.findUnique({
  where: {
    email_name: {
      email: "a@naver.com",
      name: "a",
    },
  },
});
  • prisma.user.findMany()
// ๋ชจ๋“  ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
const allUser = await prisma.user.findMany();

// ํŠน์ • ๋ฐ์ดํ„ฐ ๋ชจ๋‘ ๊ฐ€์ ธ์˜ค๊ธฐ
const allUser = await prisma.user.findMany({
  where: {
    // ...
  },
});
  • prisma.user.findFirst()
// ์กฐ๊ฑด์— ๋งŒ์กฑํ•˜๋Š” ์ฒซ ๋ฒˆ์งธ ๋ฐ์ดํ„ฐ๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐ
const allUser = await prisma.user.findFirst({
  where: {
    // ...
  },
});

3. Update

  • prisma.user.updateMany()
const updateUsers = await prisma.user.updateMany({
  where: {
    // ...
  },
  data: {
    // ...
  },
})
  • prisma.user.upsert()
// ์—†์œผ๋ฉด ์ƒ์„ฑ, ์žˆ์œผ๋ฉด ์—…๋ฐ์ดํŠธ
const upsertUser = await prisma.user.upsert({
  where: {
	// ...
  },
  update: {
    // ...
  },
  create: {
    // ...
  },
})

4. Delete

  • prisma.user.delete()
const deleteUser = await prisma.user.delete({
  where: {
    email: 'bert@prisma.io',
  },
});
  • prisma.user.delete()
// ์กฐ๊ฑด์— ๋งŒ์กฑํ•˜๋Š” ์ผ๋ถ€๋ถ„ ์‚ญ์ œ
const deleteUsers = await prisma.user.deleteMany({
  where: {
    email: {
      contains: 'prisma.io',
    },
  },
});

// ์ „์ฒด ์‚ญ์ œ
const deleteUsers = await prisma.user.deleteMany({});

์ดํ›„์— ์ •๋ฆฌ

๐Ÿ•Š๏ธ

  • select: ํŠน์ • ํ•„๋“œ ์„ ํƒ or ๊ด€๊ณ„๋œ ํ…Œ์ด๋ธ” ๋ฐ ํ…Œ์ด๋ธ”์˜ ํŠน์ • ํ•„๋“œ ์„ ํƒ
  • include: ๊ด€๊ณ„๋œ ํ…Œ์ด๋ธ” ์„ ํƒ
  • orderBy: ์ •๋ ฌ
  • contains: ํฌํ•จ๋œ๊ฒƒ ์ฐพ๊ธฐ
  • disconnect: ํŠน์ • ๋ ˆ์ฝ”๋“œ ๊ด€๊ณ„ ๋Š๊ธฐ
  • set: ๋ชจ๋“  ๋ ˆ์ฝ”๋“œ ๊ด€๊ณ„ ๋Š๊ธฐ
  • deleteMany: ๋ชจ๋“  or ํŠน์ • ๋ ˆ์ฝ”๋“œ ์‚ญ์ œ
  • update: ํŠน์ • ๋ ˆ์ฝ”๋“œ ์ˆ˜์ •
  • upsert: ํŠน์ • ๋ ˆ์ฝ”๋“œ ์ƒ์„ฑ or ์ˆ˜์ •

๐Ÿฒ

  • @@map("posts"): ์‹ค์ œ ํ…Œ์ด๋ธ”๋ช…์€ ๋ฐ”๋€Œ์ง€๋งŒ prisma์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ช…์นญ์„ ๋ฐ”๋€Œ์ง€ ์•Š์Œ
  • @id, @default(), @default(autoincrement()), @unique
  • @@id([id, email]): id์™€ email์„ ํ•ฉ์ณ์„œ primary key๋กœ ์ง€์ •
  • @@unique([email, name]): email๊ณผ name ํ•ฉ์ณ์„œ unique key๋กœ ์„ค์ •
  • @@index([email, name]): email๊ณผ name ํ•ฉ์ณ์„œ index๋กœ ์„ค์ •

@@id, @@unique์˜ ๋ฐฐ์—ด ๋‚ด๋ถ€์— ํ•œ๋ฒˆ์— ์ ์„ ๊ฒฝ์šฐ ๊ฐ๊ฐ์ด ์•„๋‹ˆ๋ผ ๋‘ ๊ฐ€์ง€๋ฅผ ํ•ฉ์ณ์„œ ์ธ๋ฑ์Šค๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

  • now(): ํ˜„์žฌ ์‹œ๊ฐ„
  • uuid(): UUID ์‚ฌ์–‘๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ณ ์œ ํ•œ ์‹๋ณ„์ž ์ƒ์„ฑ
  • autoincrement()
  • @relation()
  • fields: []
  • references: []
  • onDelete: Cascade
  • onUpdate: Cascade

0๊ฐœ์˜ ๋Œ“๊ธ€