본문 바로가기
Prisma

[Prisma] 3. Prisma query 사용 (CRUD, query options)

by spare8433 2025. 1. 14.

앞으로 등장할 코드에서 prisma 변수는 const prisma = new PrismaClient() 를 의미합니다.
mysql 사용 기준으로 작성되어 몇몇 기능 및 지원에 대한 내용이 없을 수 있습니다.





Generated Type

prisma generate 명령어 실행 시 기본적으로 각 model type 이 생성되고 query 의 옵션, 필터, 입력값, 결과값 등등 여러가지 관련 type 또한 생성되어 필요한 경우 import 하여 사용할 수 있습니다.



example

// ex) UserCreateInput
type UserCreateInput = {
  name: string;
  email: string;
};



// generated type 사용 예시
import { Prisma } from  "@prisma/client";

let user: Prisma.UserCreateInput







레코드 생성 (Create)



create() : 단일 레코드 생성 후 해당 레코드 데이터 반환

const prisma = new PrismaClient()

const user = await prisma.user.create({
  // 생성 시 필요한 데이터
  data: {
    email: 'elsa@prisma.io',
    name: 'Elsa Prisma',
  },
})





createMany() : 여러 레코드 생성 후 해당 레코드 개수 반환

// createMany 의 값은 { count: 3 }
const createMany = await prisma.user.createMany({
  data: [
    { name: 'Bob', email: 'bob@prisma.io' },
    { name: 'Bobo', email: 'bob@prisma.io' }, // 이메일이 unique key 라면 bob@prisma.io' 는 중복된 unique key
    { name: 'Yewande', email: 'yewande@prisma.io' },
  ],
  skipDuplicates: true, // Skip 'Bobo'
})





createManyAndReturn() : 여러 레코드 생성 후 해당 레코드 데이터 목록 반환

const users = await prisma.user.createManyAndReturn({
  data: [
    { name: 'Alice', email: 'alice@prisma.io' },
    { name: 'Bob', email: 'bob@prisma.io' },
  ],
})

// users results:
[
    {  
        id:  22,  
        name:  'Alice',  
        email:  'alice@prisma.io',  
    },  {  
        id:  23,  
        name:  'Bob',  
        email:  'bob@prisma.io',  
    }
]







레코드 읽기 (Read)



findUnique() : ID 또는 unique identifier 로 특정된 레코드 반환

// By unique identifier
const user = await prisma.user.findUnique({
  where: { email: 'elsa@prisma.io' },
})

// By ID
const user = await prisma.user.findUnique({
  where: { id: 99 },
})





findMany() : 특정 기준에 해당되는 레코드 목록 반환

// 전체 레코드 반환
const users = await prisma.user.findMany()

// 필터링된 레코드 목록 반환
const users = await prisma.user.findMany({
  where: {
    email: { endsWith: 'prisma.io' },
  },
})





findFirst() : 특정 기준과 일치하는 첫 번째 레코드 반환

// 좋아요가 100개 이상인 게시물이 하나 이상 있는 ID 기준 내림차순 첫 번째 사용자를 반환
const findUser = await prisma.user.findFirst({
  where: {
    posts: {
      some: {
        likes: { gt: 100 },
      },
    },
  },
  orderBy: { id: 'desc' },
})







레코드 변경 (Update)



update() : 단일 레코드 변경 후 해당 레코드 데이터 반환

const updateUser = await prisma.user.update({
  where: { email: 'viola@prisma.io' },
  data: { name: 'Viola the Magnificent' },
})





updateMany() : 여러 레코드 변경 후 해당 레코드 개수 반환

// email 에 prisma.io 를 포함하는 모든 사용자 레코드를 업데이트
// updateUsersCount 의 형식은 { count: number }
const updateUsersCount = await prisma.user.updateMany({
  where: {
    email: { contains: 'prisma.io' },
  },
  data: { role: 'ADMIN' },
})





updateManyAndReturn() : 여러 레코드 변경 후 해당 레코드 데이터 반환

// email 에 prisma.io 를 포함하는 모든 사용자 레코드를 업데이트
const users = await prisma.user.updateManyAndReturn({
  where: {
    email: { contains: 'prisma.io' }
  },
  data: { role: 'ADMIN' }
})





upsert() : 레코드를 변경하거나 해당 레코드가 없는 경우 해당 레코드를 만듭니다.

const upsertUser = await prisma.user.upsert({
  where: { email: 'viola@prisma.io' },
  update: { name: 'Viola the Magnificent' },
  create: {
    email: 'viola@prisma.io',
    name: 'Viola the Magnificent',
  },
})







레코드 삭제 (Delete)



delete() : 단일 레코드 삭제 후 해당 레코드 데이터 반환

const deleteUser = await prisma.user.delete({
  where: { email: 'bert@prisma.io' },
})





deleteMany() : 여러 레코드 삭제 후 해당 레코드 개수 반환

const deleteUsersCount = await prisma.user.deleteMany({
  where: {
    email: { contains: 'prisma.io' },
  },
})

// 전체 레코드 삭제
const deleteAllUsersCount =  await prisma.user.deleteMany({})







Query Option



select : 지정한 필드 하위 집합을 반환

const user = await prisma.user.findUnique({
  where: {
    email: 'emma@prisma.io',
  },
  // 이메일 및 이름 필드만 반환
  select: {
    email: true,
    name: true,
  },
})


// 중첩 select
const usersWithPostTitles = await prisma.user.findFirst({
  select: {
    name: true,
    posts: {
      select: { title: true },
    },
  },
})

// usersWithPostTitles results:
{
  "name":"Sabelle",
  "posts":[
    { "title":"Getting started with Azure Functions" },
    { "title":"All about databases" }
  ]
}





omit: 일부 필드 하위 집합을 제외하고 반환

const users = await prisma.user.findFirst({
  // 전체 데이터 중 password 만 제외하고 반환
  omit: { password: true } 
})





include : 관계된 필드 내용을 포함해서 반환

const user = await prisma.user.findFirst({
  include: { posts: true },
})

// user results example:
{
  id: 19,
  name: null,
  email: 'emma@prisma.io',
  posts: [
    {
      id: 20,
      ...
    },
  ]
}


// include 중첩 사용
const user = await prisma.user.findFirst({
  include: {
    posts: {
      include: {
        categories: true,
      },
    },
  },
})

// user results example:
{
  id: 19,
  name: null,
  email: 'emma@prisma.io',
  posts: [
    {
      id: 20, 
      categories: [
        {
          id: 3,
          name: "Easy cooking"
        },
      ]
      ...
    },
  ]
}





(주의 사항)



1. include 내부에 select 사용 가능하지만, select 내부에 include 는 불가능

기본적으로 select는 특정 컬럼(필드)만 선택하는 역할을 하며, 이는 데이터베이스에서 SELECT 쿼리로 특정 데이터만 가져오는 작업입니다. 반면, include는 관계 데이터를 함께 가져오는 역할을 하며, 이는 SQL에서 JOIN 을 사용하여 두 개 이상의 테이블을 연결하고 데이터를 조합하는 방식과 유사합니다.



이 두 기능을 종합적으로 고려했을 때, include(JOIN) 로 확장된 데이터는 다시 select를 통해 필터링하거나 선택할 수 있습니다. 그러나 select(SELECT)로 선택된 특정 필드를 다시 **include를 통해 확장하는 것은 부적절한 방식입니다. 왜냐하면 **select 는 이미 필요한 필드를 제한하는 역할을 하고, 관계를 확장하는 include와는 본질적으로 다른 작업이기 때문입니다.




2. include 와 select 는 같은 레벨에서 사용 불가

일반적으로 db 에서는 JOIN 과 SELECT 같이 사용 가능하지만 prisma 는 select와 include 두 작업이 서로 충돌할 수 있기에 같은 레벨에서 함께 사용하는 것을 허용하지 않습니다.



※ prisma 상에서 include(JOIN) 하여 통합된 일부 데이터만 select(SELECT)싶다면 차라리 쿼리를 분리하는 방식을 고려해볼 수 있겠다.



// ❌ prisma 에서 작동하지 않는 잘못된 코드
const user = await prisma.user.findUnique({
  where: { id: 1 },
  select: {
    email: true, // 이메일만 선택
    posts: {
      select: { title: true }, // 포스트 제목만 선택
    },
  },
  include: { posts: true }, // posts 관계 데이터를 포함하려는 시도
});

// ✅ 쿼리를 분리한 다른 패턴 코드 
const [user, posts] = await Promise.all([
  prisma.user.findUnique({
    where: { id: 1 },
    select: { email: true }, // 이메일만 선택
  }),
  prisma.post.findMany({
    where: { userId: 1 },
    select: { title: true }, // 포스트 제목만 선택
  }),
]);

const result = {
  ...user,
  posts,
}



다음 포스트에서 이어집니다.






참고

https://www.prisma.io/docs/orm/prisma-client/queries/crud
https://www.prisma.io/docs/orm/prisma-client/queries/select-fields
https://www.prisma.io/docs/orm/prisma-client/queries/relation-queries

'Prisma' 카테고리의 다른 글

[Prisma] 6. Pagination & Aggregation  (0) 2025.01.18
[Prisma] 5. Nested Queries  (0) 2025.01.17
[Prisma] 4. query filtering & sorting  (0) 2025.01.17
[Prisma] 2. Prisma Model 디테일  (0) 2025.01.12
[Prisma] 1. Prisma 란 무엇인가?  (0) 2025.01.11