prisma를 뜯어보자 (CRUD, Join)

janghoosa·2023년 3월 6일
2

Node.js

목록 보기
4/4
post-custom-banner

prisma를 Node.js에서 왜 사용하지?

Node.js에서 ORM을 사용하면 나은가?

Object Relational Mapping은 우리가 DB에서 다루는 객체를 추상화시켜 OOP적 사고방식을 적용시키기 위해 사용한다.
DB를 사용하는 유저들 중에서 SQL을 모르는 유저는 없을 것이지만 필요한 DB 작업을 위해 SQL Query문을 짜는 것은 많은 시간을 필요로 하여 비용이 크다. 또한 쿼리문을 통해 받은 데이터는 추상화가 전혀 되어 있지 않은 평문 데이터이기 때문에 이 데이터에 어떤 정보가 있을지 알 수 없다. 또한 Entity에 변경이 필요할 경우 모든 Query를 수정해야하는 대참사가 일어날수도 있다. 이를 해결하기 위해 ORM의 사용은 필요하다고 생각한다.

장점

  • 생산성 향상
    객체지향적인 코드를 작성해 더 직관적이고 로직에 집중할 수 있다.
    또한 SQL문을 사용하지 않아도 구현이 가능하다.
  • 유지 보수 비용 감소 및 재사용 증가
    변경에 따른 확장이 쉽고 재활용하기 쉽다.
  • DBMS에 대한 종속성 감소
    프로그램을 DBMS에 종속적이지 않게 설계할 수 있다.

단점

  • 성능 저하
    직접 만드는 Raw Query문에 비해 성능이 떨어진다.
  • 튜닝의 어려움
    미세한 수정 및 디버그가 상대적으로 어렵다.

prisma란?

prisma의 공식 홈페이지를 보면 Next-generation Node.js and TypeScript ORM이라고 적혀있다.
기존에 node.js에서 사용하던 SequelizeTypeORM은 node.js치고는 많은 추상화 수준을 갖추게 해줬지만 그에 맞게 성능은 많이 떨어지는 모습을 보여줬다.

하지만 prismaClientMigrate, Studio로 나누어 구성되어 있어 더 높은 생산성을 얻을 수 있고 동시에 높은 제어성을 갖게 되었다.

  • Client
    client는 prisma의 schema 정보를 입력받아 미리 object들을 구성하고 사용할 수 있게 해줍니다. 그래서 schema를 변경하면 다시 반영해주는 명령어를 실행해야 합니다.

  • Migrate
    DB에 직접 적용시키는 migration을 담당하는 부분입니다.

  • Studio
    데이터베이스에 접근하여 보고 편집할 수 있는 GUI입니다.

prisma의 쿼리는 client를 통하여 접근하고 이를 Query Engine을 통해 원하는 데이터를 받아와 더 나은 성능과 한단계 높은 추상화를 제공해줍니다.

prisma를 사용해보자

prisma 설치법

우선 공식 사이트에 나와 있는 QuickStart를 따라해보자.

# 디렉토리 생성
mkdir hello-prisma 
cd hello-prisma
# 프로젝트 생성 및 typescript, node 설치
npm init -y 
npm install typescript ts-node @types/node --save-dev
# typescript 초기 설정
npx tsc --init
# prisma 설치
npm install prisma --save-dev
# prisma 초기 설정
npx prisma init --datasource-provider `DB Engine 이름`# ex) sqlite, mysql, postgresql...

다음과 같이 초기설정을 해준 후에 생성된 prisma/schema.prisma 파일을 수정하여 Prisma Schema 설정을 해주면 된다.

prisma Schema 작성법

generator client {
  provider = "prisma-client-js"
}

// env 파일로 DATABASE_URL을 넣어주면 된다.
// mysql의 경우는 다음과 같은 주소이다.
// mysql://mysql유저:비밀번호@서버 주소:포트번호/DB이름
datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

model User {
  // domain         Type      Attributes
  user_id 			Int    	  @id @default(autoincrement()) 
  name    			String 	  @unique
  info    			String
  created_date      DateTime  @default(now())
  updated_date      DateTime  @updatedAt
  posts   			Post[]
}

model Post {
  post_id   Int     @id @default(autoincrement())
  title     String
  // Not Null이 아니면 ?로 표시한다.
  content   String?
  published Boolean @default(false)
  author    User    @relation(fields: [authorId], references: [user_id])
  authorId  Int
}

prisma의 포맷을 지원하는 익스텐션은 VSCode에만 있으므로 다른 IDE를 쓸때에는 prisma format명령어를 사용하여 이쁘게 맞춰줄 수 있다.

사용가능한 Type은 정해져있어 사용하는 DB 엔진에 맞게 알아서 변환을 해준다.

사용가능한 Type

  • String
  • Boolean
  • Int
  • BigInt
  • Float
  • DECIMAL
  • DATETIME
  • JSON
  • Bytes

schema 관련 참고 - Type과 Attributes

prisma 사용

schema를 적용시킨 DB가 있을 수 있지만 만든 schema를 서버에 적용시키려면 다음 명령어를 통해 migration 가능하다.

npx prisma migrate dev --name init

그리고 스키마를 client에 반영시켜줘야한다. 다음 명령어를 실행해주자.

npx prisma generate

CRUD 예제

서버에서 사용하려면 client에서 Type을 받아와 사용가능하다.
User에 대한 CRUD service는 다음과 같이 구현하면 된다.

// User.servite.ts
import { PrismaClient, User } from '@prisma/client'

const prisma = new PrismaClient()
const CountPerPage = 20

class UserService {
  public getAllUsers = async (pageNo: number): Promise<User[]> => {
    const skipNo = pageNo * CountPerPage;
    const result = await prisma.user.findMany({
      // pagination은 다음과 같이 할 수 있다.
      skip: skipNo,
      take: CountPerPage,
    });
    prisma.$disconnect()
    return result
  };

  public getUserById = async (userId: number): Promise<User> => {
    const result = await prisma.user.findUniqueOrThrow({
      where: { user_id: userId },
    });
    prisma.$disconnect();
    return result;
  };

  public createUser = async (user: UserDto): Promise<User> => {
    const result = await prisma.user.create({
      data: user,
    });
    prisma.$disconnect();
    return result;
  };

  public updateUser = async (userId: number, user: UserDto): Promise<User> => {
    const result = await prisma.user.update({
      where: { user_id: userId },
      data: user,
    });
    prisma.$disconnect();
    return result;
  };

  public deleteUser = async (userId: number): Promise<User> => {
    const result = await prisma.user.delete({
      where: { user_id: userId },
    });
    prisma.$disconnect();
    return result;
  };
}

export default UserService;

join 예제

join은 선언한 정보만 받아오는 select와 모든 정보를 받아오는 include를 통하여 가능하다.

// User와 Post의 모든 정보를 받아오는 함수 
public getAllUsersAndPosts = async (): Promise<any> => {
  const result = await prisma.user.findMany({
    include: {
      Post: {
        orderBy: {
          module_id:"asc",
        },
      },
    },
    orderBy:{
      module_id:"asc",
    }
  });
  prisma.$disconnect()
  return result
};

// User와 Post의 ID만 받아오는 함수
public getUsersIdAndPostsId = async (): Promise<any> => {
  const result = await prisma.user.findMany({
    select: {
      user_id: true,
      Post: {
      	select: {
      	  post_id: true,
    	},
      },
    },
  });
  prisma.$disconnect()
  return result
};

상당히 직관적으로 join을 통하여 정보를 받아와 사용할 수 있어 쉽게 사용가능하다.

prisma studio 사용하기

npx prisma studio

이 명령어를 통하여 웹 페이지를 통해 쉽게 DB에 구조를 확인하고 변경 할 수 있다.

profile
백엔드 개발자 지망생입니다 :)
post-custom-banner

0개의 댓글