prisma.schema 구조 알아보기

hyub·2024년 6월 25일

discord

목록 보기
1/3

개인프로젝트를 하다가 prisma를 사용해봤는데,
원래 타입설정하는것이 불편한데, prisma 스키마로 만들어져있는 타입을 그냥 가져와서 쓴다는게 너무 편리했다.

초기 문턱은 높지만, 조금 익숙해지면 너무 편하게 사용할 수 있을것 같아서 정리를 해본다. 어렵지만 뭔가 재밌는 느낌.

1. 설치

npm i -D prisma

2. 설정파일 생성

npm prisma init

prisma 폴더 생성, 설정파일이 생긴다.

3. DB생성

schema.prisma 파일에 모델 및 코드 작성.

4. PUSH

schema.prisma파일이 수정될때마다 generate 명령어는 실행해줘야하고

npx prisma generate

스키마 파일을 기반으로 최신 클라이언트를 생성.

npx prisma db push

스키마 파일의 내용을 데이터베이스에 저장

npx prisma studio 로 웹에서 확인도 가능하다.


5. 전역 설정

prisma 를 전역으로 사용할 수 잇도록 db.ts파일을 설정해줘야하는데, 아래에서 코드로 살펴보자

.env에서 db설정을 해줘야하고
db설정은 무료플랜들이 없어서 supabase로 설정을 해줬다.




전체코드를 하나씩 뜯어보자


/prisma/schema.prisma

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

datasource db {
  provider     = "postgresql"
  url          = env("DATABASE_URL")
  relationMode = "prisma"
}

model Profile {
  id       String @id @default(uuid())
  userId   String @unique
  name     String
  imageUrl String @db.Text
  email    String @db.Text

  servers  Server[]
  members  Member[]
  channels Channel[]

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model Server {
  id         String @id @default(uuid())
  name       String
  imageUrl   String @db.Text
  inviteCode String @db.Text

  profileId String
  profile   Profile @relation(fields: [profileId], references: [id], onDelete: Cascade)

  members  Member[]
  channels Channel[]

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([profileId])
}

enum MembershipRole {
  ADMIN
  MODERATOR
  GUEST
}

model Member {
  id   String         @id @default(uuid())
  role MembershipRole @default(GUEST)

  profileId String
  profile   Profile @relation(fields: [profileId], references: [id], onDelete: Cascade)

  serverId String
  server   Server @relation(fields: [serverId], references: [id], onDelete: Cascade)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([profileId])
  @@index([serverId])
}

db.ts

전역에서 사용할 수 있도록

import { PrismaClient } from "@prisma/client";

declare global {
  var prisma: PrismaClient | undefined;
}
// 전역 변수에 PrismaClient 인스턴스가 이미 존재하면 그것을 사용하고,
// 그렇지 않으면 새로운 PrismaClient 인스턴스를 생성합니다.
export const db = global.prisma || new PrismaClient();

// 개발 환경에서는 전역 변수에 PrismaClient 인스턴스를 할당합니다.
// 이를 통해 개발 중 핫 리로딩이 발생해도 기존 인스턴스를 재사용할 수 있습니다.

if (process.env.NODE_ENV !== "production") {
  global.prisma = db;
}

Generator

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

Prisma Client를 생성하는 부분. prisma-client-js는 Prisma가 JavaScript/TypeScript 클라이언트를 생성하도록 지정.

Data Source

datasource db {
  provider     = "postgresql"
  url          = env("DATABASE_URL")
  relationMode = "prisma"
}

데이터베이스 연결 설정입니다.

  • provider: 데이터베이스 유형을 지정, 여기서는 PostgreSQL을 사용. supabase에 mysql이 없어서
  • url: 데이터베이스 URL을 환경 변수 DATABASE_URL에서 가져온다.
  • relationMode: Prisma의 관계 모드를 사용하며 이는 Prisma가 외래 키 관계를 어떻게 처리할지를 지정.

Model

model Profile {
  id       String @id @default(uuid())
  userId   String @unique
  name     String
  imageUrl String @db.Text
  email    String @db.Text

  servers  Server[]
  members  Member[]
  channels Channel[]

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

Mysql만 주구장창 쓰다가 비슷한듯 달라서 좀 생소하긴한데, 익숙해지면 개발속도도 빨라질 것 같고, 궁금해지는 기술이다.

  • UUID로 기본 키 의 값 설정
  • @unique 의 형태로 유니크 키 설정
  • name에서 Text와 같은 타입설정이 없는 이유는, 기본문자열타입 String으로 충분하기때문에.
    • 비교적 길이가 짧기때문에 충분
  • servers Server[] 이부분은 일대다 관계, Join문처럼 사용하기 위한 관계설정이다.

Join

model Profile {
...

  servers  Server[]
...
}

model Server {
...
  profileId String
  profile   Profile @relation(fields: [profileId], references: [id], onDelete: Cascade)

  members  Member[]
  channels Channel[]
...

  @@index([profileId])
}

model Profile와 Server이 존재하는데

@relation 어노테이션은 모델간의 관계를 정의할 때 사용된다.

profileId String
profile   Profile @relation(fields: [profileId], references: [id], onDelete: Cascade)
  • Profile 모델과 관계를 맺을거다.
  • 현재 모델의 profield 의 값을 외래키로 사용하고, Profile모델의 id 필드를 참조한다.
    • profileId는 profile모델의 id필드를 가르킨다.
  • onDelete: Cascade : Profile모델의 인스턴스가 삭제될 때, 참조하고있는 현재 모델의 관련 인스턴스로 함께 삭제한다.
  • @@index
    - 인덱스 설정이 필수는 아닌데, 외래키나 자주 검색되는 필드에 인덱스를 추가하면 쿼리 성능이 향상된다.
    일대다 관계설정과 @relation은 함께 해야 한다는 것이다.

설정뒤에는 migrate해주면된다.
익숙하지는 않지만, 확실히 뭔가 간결함이 느껴진다.


추가사용

Mysql where문 처럼 데이터 가져오기

const profile = await db.profile.findUnique({
	where: { userId: user.id },
});

Create로 데이터 생성하기

const newProfile = await db.profile.create({
  data: {
    userId: user.id,
    name: `${user.firstName} ${user.lastName}`,
    imageUrl: user.imageUrl,
    email: user.emailAddresses[0].emailAddress,
  },
});

데이터 수정

방을 만든사람은 방을 떠날수없고, 방을 삭제시켜야한다는 조건.

  const server = await db.server.update({
      where: { // 조건
        id: params.serverId,
        profileId: { 
          not: profile.id, // 서버장과 아이디가 달라야한다. 
        },
        members: { // 외래키 members참조
          some: {
            profileId: profile.id, // 서버장이 아닌 유저인가? 
          },
        },
      },
      data: { // 위의 조건에 맞는 서버데이터를 찾았다면, 그 서버의 member중에 현재 유저를 제거한다. 
        members: {
          deleteMany: {
            profileId: profile.id,
          },
        },
      },
    });
profile
시작하면 결과를 볼 때 까지

0개의 댓글