Prisma & PlanetScale DB 세팅

summereuna🐥·2023년 10월 18일

🌟 Prisma


  • Prisma는 Node.js와 Typescript의 ORM(Object Relational Mapping)이다.
    자바스크립트 혹은 타입스크립트 코드와 데이터베이스 사이에 다리를 놓아준다고 생각하면 된다.
  • SQL 같은 데이터베이스 언어를 작성하지 않고, 오류를 미리 알려주는 타입스크립트 코드만 작성해도 된다. 프리즈마를 사용하면 좀 더 편리하게 데이터베이스를 사용할 수 있다.
  • PostgreSQL, MySQL, SQL Server, SQLite, MongoDB에 사용 가능하다.

  • 프리즈마를 사용하려면 먼저 프리즈마에게 schema.prisma 파일을 통해 데이터베이스가 어떻게 생겼는지 설명해줘야 한다.
  • 프리즈마에게 내 데이터베이스에 대해 설명하면, 프리즈마는 데이터베이스의 타입에 대해 알게 된다.

  • 프리즈마가 타입에 관한 정보를 알고 있으면, client를 생성한다. Prisma Client는 내 스키마에 맞춰진 쿼리 빌더이다.
  • client를 이용하면, 데이터베이스 언어인 MySQL 등의 언어를 사용하지 않고도 타입스크립트 언어만으로 DB와 직접 상호작용 할 수 있다.
  • 그리고 Prisma Schema에 User model이 있으면 자동 완성 기능도 사용할 수 있다.

👉 MySQL과 호환되는 PlanetScale 데이터베이스를 사용하고, Prisma로 데이터베이스를 연결해보자.

⚙️ Prisma 셋업 (Typescript + MySQL)


1. VSC extention에서 Prisma 인스톨하기

syntax highlight 기능이 있어서 프리즈마 파일을 가독성 좋게 만들어 준다.

💡 스키마 파일이 자동정렬이 안된다면 아래 처럼 세팅해주자.

  • vscode setting 파일에 아래 설정값 추가
"[prisma]": {
"editor.defaultFormatter": "Prisma.prisma"
}

2. 프리즈마 설치하기

  • npm install prisma -D
    Developer Dependency로 프리즈마를 설치한다.

3. 프리즈마 불러오기

프리즈마를 사용하려면 항상 npx prisma라는 명령어를 사용하면 된다.

처음 세팅이기 때문에 init으로 초기화 해주자.

  • npx prisma init
    이 명령을 실행하면 프로젝트 루트에 .env 파일을 생성하고, /prisma 라는 새 디렉토리를 생성하여 그 안에 schema.prisma 파일을 생성한다.
    • schema.prisma 파일은 데이터베이스 연결Prisma Client 생성기가 있는 Prisma 스키마를 포함한다.
    • .env는 환경 변수를 정의하기 위한 dotenv 파일로 데이터베이스 연결에 사용된다.

3-1. DATABASE_URL 설정하기

.env 파일에 DATABASE_URL을 설정해야 한다.

  • 프리즈마는 번역기 역할일 뿐이므로, 실제 데이터베이스를 연결하기 위해 .env 파일에서 내 DB를 연결해줘야 한다.

👉 PlanetScale에서 데이터베이스를 생성한 후 URL을 수정해 주면 된다.

# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

# 나중에 PlanetScale 데이터베이스 url 적으면 됨
DATABASE_URL="어쩌구저쩌구"

3-2. 프로바이더 설정하기

schema.prisma파일에서 datasource의 provider를 설정해야 한다.

  • provider는 내가 사용할 데이터베이스라고 생각하면 된다.
  • prisma가 provider로 사용할 수 있는 데이터베이스는 postgresql, mysql, sqlite, sqlserver, mongodb이다.

👉 난 PlanetScale을 사용할 거기 때문에 mysql로 설정하면 된다.

📍 schema.prisma

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

//provider는 내가 사용할 데이터베이스
datasource db {
  provider             = "mysql"
  url                  = env("DATABASE_URL")
}

3-3. 데이터베이스에 사용할 model 만들기

schema.prisma 파일에 데이터베이스에서 사용할 model을 만들면 된다.

📍 schema.prisma

//데이터베이스에서 사용할 모델 만들기
model User {
  id      Int      @id @default(autoincrement())
  name    String
  phone   Int?     @unique
  email   String?  @unique
  avatar  String?
  created DateTime @default(now())
  updated DateTime @updatedAt
}
  • id Int @id @default(autoincrement())

    • 타입 Int는 정수를 의미한다.
    • @id 이 파라미터는 이게 모델의 id라는 것을 설명하는 역할을 한다.
    • @default(autoincrement()) 기본적으로 자동으로 증가하는 필드로 설정했다.
  • phone Int? @unique, email String? @unique

    • 내가 만든 당근마켓의 로그인 시스템은 phone 혹은 email로 로그인이 가능하므로 phone과 email은 required가 아니다. 필수 사항이 아닌 선택적 옵션으로 만들고 싶으면 타입 뒤에 물음표?를 붙이면 된다.
    • 유저에게 phone이나 email이 있으면 그 값은 유일해야 하므로 @unique 파라미터를 추가한다.
  • created DateTime @default(now())

    • created의 기본값으로는 새 유저가 만들어지는 그 시점의 날짜로 설정한다.
  • updated DateTime @updatedAt

    • 유저가 업데이트될 때마다 @updatedAt으로 이 필드가 변할거라고 프리즈마에게 알린다.

이렇게 프리즈마에게 유저가 어떻게 생겼는지 스키마를 작성하여 알릴 수 있다.
뭔가 엄청 깔꼼해서 이해하기가 편한듯..
프리즈마는 이 파일을 읽고 데이터베이스에 변경 사항을 배포할 수 있다.

🌟 이 처럼 schema.prisma 파일은 데이터베이스에 대한 모든 설명을 담은 파일이라서 아주 중요하다. 변경 사항을 데이터베이스에 push 할 때도 사용하고 client를 만들 때 사용한다.

❗️ 여기서 Planet Scale 파트 마치고 3-4. 3-5.를 보세용


🌟 PlanetScale


PlanetScale은MySQL과 호환(compatible)되는 Serverless(서버를 내가 유지관리 및 유지보수할 필요가 없음) 데이터베이스 플랫폼이다.


1. Serverless


AWS의 RDS(관계형 데이터베이스 서비스) 같은 것과 다르다. 보통 데이터베이스를 만들면 서버를 만들어 적당한 크기를 설정하는 등 모든 걸 직접해야 한다. 예를 들면 백만명이 데이터베이스에 연결되면 직접 스케일링 해야 한다.

하지만 이런 서버리스 플랫폼을 사용하면 그런 작업을 대신해 주기 때문에 개발자가 편하다.


2. MySQL-Compatible(호환)


PlanetScale은 MySQL을 쓰는게 아니라 MySQL과 호환되는 Vitess를 데이터베이스로 사용한다.

  • MySQL과 호환되기 때문에 prisma에서 provider를 mysql로 설정하여 사용할 수 있다.

  • 하지만 prisma는 mysql과 호환되는 Vitess 기반이기 때문에 schema.prisma 파일을 약간 바꿔줘야 한다.

  • sql 이나 postresql 등의 관계형 DB에서는 foreign key로 DB를 연결한다.
    • 예를 들면, Users DB에 {id:1, name:euna}인 유저 데이터가 있는데, 이 유저가 코멘트를 작성한다고 하자. 이 유저가 "hi"라는 코멘트를 작성하면 Comments DB에 {id:1, text:"hi", user:(Users DB: 1)} 라는 데이터가 생성된다.
    • user 객체의 주소를 알려주는 (Users DB: 1) 이런 방식을 foreign key라고 한다. DB에 user의 id만 저장해도 DB가 자동으로 이 정보가 Users DB의 정보인 것을 알기 때문에 댓글을 찾을 때 user도 같이 찾을 수 있다.
    • 만약 Users DB에 없는 user가 댓글을 작성하려고 한다고 해보자. (ex. 로그인하지 않은 유저)
      예를 들어 Comments DB에 {id:1, text:"hi", user:(Users DB: 100)} 이런식으로 댓글을 저장하려고 한다면 100에 해당하는 User가 없기 때문에 데이터베이스에 오류가 발생하고 댓글이 저장되지 않게 막힌다. 이것이 바로 foreign key constraint(제약)이다.
  • 이처럼 관계형 DB 환경에서는 foreign key 생성시, 그 key가 참조된 DB에 반드지 존재해야 하는데 이를 참조 무결성(Referential Integrity)라고 한다. 즉, 한 객체가 다른 객체에 연결된 상태를 생성하려고 할 때, 그 연결을 받는 객체가 존재한다는 것을 보장하는 것이다.
  • 하지만 Planet Scale이 DB로 사용하는 Vitess는 scalability, 즉 DB를 잘게 쪼개서 여러 server에 분산시키는데 특화되어 있기 때문에 foreign key constraint(제약)을 지원하지 않기 때문에 위와 같은 상황이 발생하면 오류를 내서 멈추지 않고 그냥 실행된다.
  • 👉 이처럼 데이터베이스인 Vitess가 foreign key를 제약하지 못하기 때문에 Prisma에서 DB 설정을 바꿔서 참조 무결성 도움을 받을 수 있게 하면 된다.

📍 schema.prisma

//참조 무결성(referentialIntegrity) 추가
//참조 무결성이란,모델이 다른 모델을 참조하는 경우 해당 모델이 반드시 존재해야 함을 의미한다.
//예를 들어 Comment 모델이 user 필드를 정의하는 경우, User 모델도 반드시 존재해야한다.
generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["referentialIntegrity"] //참조 무결성 추가
}

//provider는 우리가 사용할 데이터베이스
datasource db {
  provider             = "mysql"
  url                  = env("DATABASE_URL")
  referentialIntegrity = "prisma" //참조 무결성 작업은 프리즈마가 한다
}

//...
  • MySQL의 안정성과 오픈소스인 Vitess의 확장성을 갖추고 관리되는 데이터베이스를 10초 안에 배포할 수 있다.
  • DB에 대한 배포, 브랜치, 쿼리는 UI에서 바로 할 수 있다. CLI 실행도 가능하다.

👉 MySQL을 좀 더 쉽게 스케일링할 수 있도록 하는 데이터베이스 클러스터링 시스템인 Vitess


  • PlanetScale은 Vitess를 기반으로 한다.
  • Vitess는 youtube를 스케일링하기 위해 구글에서 만든 도구로, 대기업들이 규모에 맞게 MySQL을 scale하기 위해 쓰는 방법이다.
    • 매초마다 수백만개의 쿼리를 처리해야 하거나 수심만개의 연결이 들어오고 수만개의 노드가 필요하면, 데이터베이스에 horizontal scaling도 해야 하고 sharding도 해야 하고 다운타임도 조정하고 등등의 작업이 필요한데, Vitess를 사용하면 이를 용이하게 해준다.

📝 Vitess의 장점


1. 수평 스케일(Horizontal Scale)

수평적 샤딩을 제공하여 앱에 복잡함 없이도 제한없는 수의 데이터베이스 샤드를 제공한다.

2. 고가용성(High Availability)

Vitess의 복제본(레플리카) 구성을 사용하면 예기치 않은 이벤트 발생 시 복제본으로 원활하게 페일오버(failover)할 수 있다.

3. MySQL 호환(MySQL Compatible)

MySQL과 호환되어 MySQL 과 마이그레이션하기 쉽다.

4. 온라인 스키마 마이그레이션(Online Schema Migrations)

온라인 스키마 마이그레이션을 제공한다.

5. 구체화된 뷰(Materialized Views)

6. 쿠버네티스 네이티브(Kubernetes Native)

7. 좋은 개발자 경험(Good Developer Experience)

git command를 쓰는 것처럼 CLI로 다양한 작업이 가능한데, 마치 깃을 쓰는 것처럼 데이터베이스를 다룰 수 있다.

8. Downtime이 없음

데이터베이스가 꺼지지 않기 때문에 사용자가 방해받지 않는다.


⚙️ Planet Scale 데이터 베이스 생성 후 연결하기


Planet Scale에 가입하면 관리자 패널이 보인다.

관리자 패널에서 플라넷 스케일 데이터베이스를 생성해도 되지만, 아래 처럼 palnet Scale CLI를 설치하여 CLI로 설치하는 방법도 있다.

플라넷 스케일은 공짜(plan type: Hobby)로 사용할 수 있는데 처음에는 크레딧 카드를 등록해야 한다.
그래서 처음에 플라넷 스케일 설치할 때는 관리자 패널에서 만드는게 좀 더 쉽다. ^^..

여튼 CLI를 설치하여 CLI로 데이터베이스를 생성해보자.

1. 먼저 mac에서 palnet Scale CLI 설치하기

  • brew install planetscale/tap/pscale로 pscale CLI 설치하기
  • brew install mysql-client로 mysql client 설치하기

2. CLI 사용하여 Planet Scale에 로그인하기

  • pscale auth login
    그러면 브라우저 열리는데 브라우저에 표시된 숫자랑 터미널에 숫자랑 맞으면 confirm 누르면 로그인 된다.

3. CLI 사용하여 데이터베이스를 생성하기

  • pscale database create <데이터베이스이름> --region gcp-asia-northeast3

    서울: gcp-asia-northeast3

4. CLI 사용하여 데이터베이스에 prisma 보안 연결하기

보통 데이터베이스 플랫폼(히로쿠, AWS 등)에서는 데이터베이스를 만들면 암호를 생성하는데, 그 암호를 환경 파일에 넣어서 관리해야 한다. 그래서 보통 찐 디비는 건드리지 않으면서 작업하기 위해 디비를 두 개 만들어서 컴퓨터에서 서버를 작동하는 가짜 디비를 사용하는 방식으로 커넥션 url을 보호한다. 그러고 나서 실제로 배포할 때 찐 디비를 사용한다.

하지만 Planet Scale을 사용하면 컴퓨터와 데이터베이스인 Planet Scale 사이에 일종의 secure tunnel(보안 터널)을 이용할 수 있다. 그래서 하나는 컴퓨터용, 하나는 Planet Scale용으로 디비를 따로 관리하는 방식을 사용하지 않아도 된다. 따라서 mysql 다운 받고 설치할 필요도 없고, .env에 암호를 따로 저장할 필요도 없다.

  • pscale connect <데이터베이스이름>
    위 CLI를 사용하면 암호 없이도 컴퓨터와 플라넷 스케일 사이에 보안 연결을 만들 수 있다.

ㅎㅠㅎ...넘나리 쉬운것

❗️ Planet Scale 데이터 베이스랑 연결 끊기면 안되기 때문에 콘솔 닫지 말 것!

5. 데이터베이스의 URL 내 어플리케이션에 연결하기

  • 그러고 나면 내 어플리케이션과 커넥트할 수 있는 Local address가 뜬다.
    그 주소를 복사하여 .env 파일에 넣어주면 된다.
    DATABASE_URL="mysql://127.0.0.1:3306/<데이터베이스이름>"

(참고)
IP 주소 127.0.0.1은 localhost 또는 루프백 주소 라고하는 특수 목적의 IPv4 주소이다.

❗️ .env 파일 .gitignore에 추가하는 거 잊지 말자!


프리즈마를 마저 설정해보자.

3-4. 프리즈마로 만든 나의 데이터베이스 스키마를 데이터베이스인 Planet Scale에 push하기 & SQL migration 자동으로 처리하기


pscale connect를 실행한 후, 즉 보안 tunnel에 연결한 후에 진행해야 된다.

npx prisma db push

referentialIntegrity는 더 이상 지원하지 않는다고 하니 relationMode로 바꿔주자.

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

//provider는 우리가 사용할 데이터베이스
datasource db {
  provider     = "mysql"
  url          = env("DATABASE_URL")
  relationMode = "prisma"
}

이제 플라넷 스케일 데이터베이스에서 대시보드를 보면, SQL 버전으로 적힌 User table을 확인할 수 있다.

이렇게 컴퓨터에 있는 Prisma와 Planet Scale 데이터베이스 사이에 직접적인 연결이 생성되었고, 스키마도 성공적으로 업데이트 했다.

우리는 데이터가 어떤 구조를 가질지만 작성해 주면 프리즈마가 알아서 다 해주니 참 편하다 ^^~~~

⚙️ 프리즈마가 제공하는 멋진 관리자 패널: 프리즈마 스튜디오

npx prisma studio 를 입력하면 데이터베이스 관리자 패널도 볼 수 있다.

  • 프리즈마 스튜디오가 schema.prisma를 읽어서 User model이 있다는 것을 알고, User를 관리할 수 있는 관리자 패널을 보여준다.
  • 여기서 새로운 유저를 추가할 수도 있고 User가 가지는 데이터 필드와 타입을 확인해 볼 수도 있다.
  • 그리고 여기서 데이터 추가도 할 수 있다.

클라이언트도 함께 생성되었는데, 이제 클라이언트를 보자.

3-5. 데이터베이스와 상호작용하기 위해 Prisma Client 생성하기 & Client에 자동완성으로 타입 추가하기


  1. 프리즈마 클라이언트를 설치한다.
    npm install @prisma/client
    백엔드에서, 즉 서비스에서 client를 직접 사용할 것이기 때문에 -D 옵션은 사용하지 않고 설치한다.

  2. libs 폴더에 client.ts 파일을 만들고 클라이언트를 생성한다.

// 📍 libs/client.ts
import { PrismaClient } from '@prisma/client';

const client = new PrismaClient();

Prisma Client가 생성되면 내 데이터베이스에 쿼리할 수 있게 된다.
자동생성기능을 사용해보자.

  • email에 숫자를 넣으니 string을 사용해야 한다고 뜬다. 굿굿

프리즈마 클라이언트는 프론트엔드 측인 브라우저에 실행해서는 안된다. 그럼 보안 다 털림 ㅠ

프리즈마 클라이언트 실행하기: API Routes


그러면 프리즈마 클라이언트는 어디에 실행해야 할까? 오직 서버에서만 실행해야 한다.

보통 프론트엔드는 React / 백엔드는 NodeJs로 만들 수 있는데, 이 둘은 다른 서버에 있다.
하지만 NextJS를 사용하면 백엔드 API를 만들기 위해 백엔드용 서버를 따로 구축할 필요가 없다.

API route

  • API route는 Next.js로 API를 빌드하기 위한 솔루션을 제공한다.
  • /pages/api 폴더 내의 모든 파일은 /api/*에 매핑되며 API endpoint로 처리된다.
  • server-side 전용 번들이며 client-side 번들 크기를 늘리지 않는다.
  • req: http.IncomingMessage의 인스턴스와 pre-built된 일부 미들웨어
  • res: http.ServerResponse의 인스턴스와 일부 helper함수
    • 예를 들어 다음 API 경로 pages/api/user.js는 상태 코드가 200인 json 응답을 반환한다.
export default function handler(req, res) {
  res.status(200).json({ name: 'John Doe' })
}

NextJS로 프론트엔드랑 백엔드 API를 다 만들어 보자.

1. /pages/api 폴더를 만들어 NextJS 서버에 API를 만든다.

2. api 폴더에 client-test.tsx 파일을 만든다.

api 라우트를 위한 규칙
:connection 핸들러인 함수를 export default 해주기만 하면 된다.

  • export default function handler ⭐️ 보통 이렇게 많이 씀
  • export default function view
  • export default function response

📍/pages/api/client-test.tsx

import { NextApiRequest, NextApiResponse } from "next";

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  res.json({
    ok: true,
    data: "test...",
  });
}

//이전에 Express를 사용해봤다면 이 모양이 controller function 처럼 생긴걸 알 수 있다.

타입스크립트를 사용하고 있기 때문에

  • req에는 NextApiRequest 타입을 넣어주면 된다.

    • NextApiRequest를 클릭해 보면 query, cookies 등이 있는 것을 확인할 수 있다.
  • res에는 NextApiResponse 타입을 넣어주면 된다.

    • NextApiResponse를 클릭해 보면 send, json, status, redirect 등이 있는 것을 확인할 수 있다.

이렇게 /api 폴더를 만드는 것 만으로도 NextJS가 api를 만들어 준다.

이렇게 api 라우트를 만들 수 있다,
이래서 NextJs가 풀스택을 위한 프레임워크란 거다 ㅇㅇ! 굿굿굿~
따로 서버를 만들 필요 없이도 api를 만들 수 있는 기능이 있다.

오케이! 그러면 이 api 라우트에 클라이언트로 정보를 보내주면 되겠다.

프리즈마 클라이언트를 export default하여 api로 보내보자.

📍/libs/client.ts

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

export default new PrismaClient();

📍/pages/api/client-test.tsx

import client from "@/libs/client";
import { NextApiRequest, NextApiResponse } from "next";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  //새로운 유저 생성하기
  await client.user.create({
    data: {
      name: "RM",
      email: "rm@bighit.com",
    },
  });

  //응답 성공으로
  res.json({
    ok: true,
  });
}
  • 이렇게 저장해 보면 프리즈마 스튜디오에 새로운 user 데이터가 생긴 것을 확인 할 수 있다.

굿굿

profile
Always have hope🍀 & constant passion🔥

0개의 댓글