Prisma : 테이블 정의가 편리함, 직관적인 CRUD 문법 사용, 데이터 관리 GUI 툴 제공npx prisma command로 초기화하기 npx prisma init --datasource-provider postgresql
.env 파일의 URL 수정 및 포트 번호 추가하기johndoe:randompassword > postgres:password로 설정mydb > comazon_dev로 설정PORT=3000Shift + Alt + F 단축키로 코드를 자동 정렬하기model User {
id String @id @default(uuid())
email String
firstName String
lastName String
address String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
@id는 해당 필드가 데이터를 식별하는 고유 id 역할을 한다는 사실을 명시함@default(uuid())는 디폴트 값으로 uuid(36자로 이루어진 id 형식)을 사용하라는 점을 명시함@unique는 해당 필드이름의 데이터가 중복 없이 고유해야한다는 점을 명시함@default(now())는 데이터가 업데이트된 시점을 디폴트로 사용함model User {
// ...
membership Membership @default(BASIC)
}
enum Membership {
BASIC
PREMIUM
}
model User {
id String @id @default(uuid())
email String @unique
firstName String
lastName String
address String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([firstName, lastName])
}
위와 같이 정의하면 firstName과 lastName의 조합이 unique해야 함 (firstName끼리, lastName끼리는 겹칠 수 있지만 firstName과 lastName이 동시에 겹칠 수는 없음. 즉, 성끼리, 이름끼리는 겹칠 수 있지만 동명이인이 있을 수는 없음)
npx prisma migrate dev 입력
1-1. Error: P1001: Can't reach database server at localhost:5432 오류 발생 (Window 11 ver.)
(1) SQL Shell (psql) 실행
(2) 아래의 이미지와 같이 Server, Database, Port, Username 란에서는 전부 엔터만 치고 넘어가기
(3) postgres 사용자의 암호: 본인이 PostgreSQL 설치 과정에서 설정한 암호 입력
(4) postgres=# 뒤에 CREATE DATABASE comazon_dev; CREATE DATABASE 명령어 입력
(5) npx prisma migrate dev 재시도

migration name에서는 일반적으로 통용되는 init을 입력
npx prisma studio 입력 > usermode 클릭 > add record 클릭해서 데이터 추가해보기 > save change 눌러서 저장

migration.sql 파일에는 마지막 migration 이후의 변경사항을 schema 형태로 저장함
테이블에 이미 데이터가 있는 경우 필드 추가 후 migration할 경우의 오류
age Int 등의 필드를 생성한 후 migration하려고 한다면 기존 데이터에는 age 값이 없는데 age를 필수 필드로 생성하려고 하는 중이므로 오류가 발생함
필드 삭제 시에는 문제되지 않음
해결책
age Int?로 optional하게 생성한 후 이후에 기존 데이터에서 age값을 추가해주고 나서 필수 필드로(?를 지우고 나서) 다시한번 migrate해줘야 함?를 붙이면 필드가 optional해짐default attribute를 사용해서 값이 없는 row에 값을 넣는 방식도 가능App.js 파일import * as dotenv from 'dotenv';
dotenv.config();
import express from 'express';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const app = express();
app.use(express.json());
app.get('/users', async (req, res) => {
// user 목록 조회
const users = await prisma.user.findMany();
res.send(users);
});
app.get('/users/:id', async (req, res) => {
const { id } = req.params;
// id에 해당하는 user 조회
const user = await prisma.user.findUnique({
where: { id },
});
res.send(user);
});
app.post('/users', async (req, res) => {
// 리퀘스트 바디 내용으로 유저 생성
const user = await prisma.user.create({
data: req.body,
});
res.status(201).send(user);
});
app.patch('/users/:id', async (req, res) => {
const { id } = req.params;
// 리퀘스트 바디 내용으로 id에 해당하는 유저 수정
const user = await prisma.user.update({
where: { id },
data: req.body,
})
res.send(user);
});
app.delete('/users/:id', async (req, res) => {
const { id } = req.params;
// id에 해당하는 유저 삭제
await prisma.user.delete({
where: { id },
})
res.sendStatus(204);
});
app.listen(process.env.PORT || 3000, () => console.log('Server Started'));
users.http 파일GET http://localhost:3000/users
###
GET http://localhost:3000/users/b8f11e76-0a9e-4b3f-bccf-8d9b4fbf331e
###
POST http://localhost:3000/users
Content-Type: application/json
{
"email": "yjkim@example.com",
"firstName": "유진",
"lastName": "김",
"address": "충청북도 청주시 북문로 210번길 5"
}
###
PATCH http://localhost:3000/users/b8f11e76-0a9e-4b3f-bccf-8d9b4fbf331e
Content-Type: application/json
{
"address": "서울특별시 강남구 무실로 234번길 45-6"
}
###
DELETE http://localhost:3000/users/b8f11e76-0a9e-4b3f-bccf-8d9b4fbf331e
mock.js, 입력을 처리할 코드를 담은seed.js 등을 활용해서 넣음// mock.js 파일
export const USERS = [
{
id: 'b8f11e76-0a9e-4b3f-bccf-8d9b4fbf331e',
email: 'honggd@example.com',
firstName: '길동',
lastName: '홍',
address: '서울특별시 강남구 무실로 123번길 45-6',
createdAt: '2023-07-16T09:00:00Z',
updatedAt: '2023-07-16T09:00:00Z',
},
{
id: '6c3a18b0-11c5-4d97-9019-9ebe3c4d1317',
email: 'kimyh@example.com',
firstName: '영희',
lastName: '김',
address: '경기도 고양시 봉명로 789번길 21',
createdAt: '2023-07-16T09:30:00Z',
updatedAt: '2023-07-16T09:30:00Z',
},
{
id: 'fd3ae0a5-8dd5-40b6-b8fd-48870f731db1',
email: 'lee.cs@example.com',
firstName: '철수',
lastName: '이',
address: '인천광역시 남구 향교로 567번길 8-2',
createdAt: '2023-07-16T10:00:00Z',
updatedAt: '2023-07-16T10:00:00Z',
},
{
id: '70e1e61d-f2ae-4d7d-bf8f-d65eafdb6a45',
email: 'parkjy@example.com',
firstName: '지영',
lastName: '박',
address: '대전광역시 중구 성남로 432번길 76',
createdAt: '2023-07-16T10:30:00Z',
updatedAt: '2023-07-16T10:30:00Z',
},
{
id: '73cb9639-d8b7-4f11-9a62-53f4187f3f11',
email: 'jungminsoo@example.com',
firstName: '민수',
lastName: '정',
address: '부산광역시 동래구 수림로 987번길 33-7',
createdAt: '2023-07-16T11:00:00Z',
updatedAt: '2023-07-16T11:00:00Z',
},
];
1.seed.js 파일 작성
import { PrismaClient } from '@prisma/client';
import { USERS } from './mock';
const prisma = new PrismaClient();
async function main() {
// 기존 데이터 삭제
await prisma.user.deleteMany();
// mock.js 데이터 삽입
await prisma.user.createMany({
data: USERS,
skipDuplicates: true,
});
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
package.json 파일에 아래 코드 추가 "prisma":{
"seed":"node prisma/seed.js"
}
npx prisma db seed command로 실행npx prisma studio로 확인
// App.js
import * as dotenv from 'dotenv';
dotenv.config();
import express from 'express';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const app = express();
app.use(express.json());
app.get('/users', async (req, res) => {
const { offset = 0, limit = 10, order = 'newest' } = req.query;
let orderBy;
switch (order) {
case 'oldest':
orderBy = { createdAt: "asc"};
break;
case 'newest':
case 'default':
orderBy = { createdAt: "desc"};
}
const users = await prisma.user.findMany({
orderBy,
skip: parseInt(offset),
take: parseInt(limit),
});
res.send(users);
});
// ...
https://www.codeit.kr/topics/js-server-with-relational-db/lessons/6798
superstruct 라이브러리 이용: 터미널에 npm list 입력 > superstruct, is-email, is-uuid가 설치되어있는지 확인하고 만약 설치되지 않은 것들이 있다면 설치해주기structs.js 파일 생성 > import * as s from 'superstruct';
import isEmail from 'is-email';
export const CreateUser = s.object({
email: s.define('Email', isEmail), // 이메일주소 형식인지 검사
firstName: s.size(s.string(), 1, 30), // 1~30글자 사이인지 검사
lastName: s.size(s.string(), 1, 30),
address: s.string(),
})
export const PatchUser = s.partial(CreateUser);
npm run dev로 서버 시작users.http로 테스트try-catch문으로 감싸주기Alt + Click을 누르고 커서를 선택하면 커서를 한번에 여러군데에 배치해서 여러곳을 동시에 수정할 수 있음 (같은 내용을 여러번 다른 위치에 입력해야 할 때 유용)https://www.codeit.kr/topics/js-server-with-relational-db/lessons/6802
Primary key: 사용자가 정한 row를 식별할 수 있는 column으로, 사용자가 지정하게 됨 (ORM을 사용하는 경우 자동으로 정해지기도 함)
예: id값

Foreign key: 다른 테이블의 Primary key를 참조하는 column으로, 예를들어 Order table에서 User table의 id column을 참조하는 userid라는 column을 만든다면 이게 바로 Foreign key이다.



카디널리티(Cardinality): A개체와 B개체 사이의 관계가 있다고 가정했을 때, A개체 하나가 B개체 몇개와 연결되어있을 수 있는지, B개체 하나가 A개체 몇개와 연결되어있을 수 있는지를 뜻함
예: 하나의 User는 여러개의 Order를 할 수 있지만, 하나의 Order는 하나의 User에 의해서 이루어짐 ➡️ 일대다 관계
Product는 여러 User에 의해 찜해질 수 있고, User는 여러 Product를 찜할 수 있음 ➡️ 다대다 관계

일대일 관계도 존재함
Crow's Foot Notation: 일대다에서 일은 |로, 다는 삼지창모양으로 나타내기

다쪽 개체의 테이블에 Foreign key를 추가해야 함


Prisma schema 파일 작성npx prisma migrate dev 로 migrate하기model Order {
user User
라고 입력한 후 ctrl + Alt + F를 누르면 자동으로 @relation(fields: [userId], references: [id])가 생성되고, User 모델에도 Order Order[]가 생성됨
user User와 Order Order[])는 데이터베이스에는 저장되지 않으며 prisma에서 관계를 형성할 때만 참조됨model User에 자동 생성된 배열 userPreference UserPreference[]을 userPreference UserPreference?로 수정하고, model UserPreference의 userId String에 @unique 추가하기User 모델에 savedProducts Product[] 추가, Product 모델에 savedUsers User[] 추가) ➡️ migration 진행 onDelete: Cascade: Foreign 키가 가리키는 데이터가 삭제되면 기존 데이터도 삭제onDelete: Restrict: 만약 특정 데이터를 참조하는 데이터들이 있으면 데이터를 삭제할 수 없음onDelete: setNull: Foreign key가 가리키는 데이터가 삭제되면 Foreign key를 NULL로 변경 (onDelete 옵션의 기본값!!)onDelete: SetDefault: Foreign key가 가리키는 데이터가 삭제되면 Foreign key를 디폴트 값으로 설정model Order {
// ...
user User @relation(fields: [userId], reference: [id], onDelete: SetDefault)
userId String @default("Anonymous") // prisma에서는 반드시 큰따옴표를 사용해야 함!
}
https://www.codeit.kr/topics/js-server-with-relational-db/lessons/6964
https://www.codeit.kr/topics/js-server-with-relational-db/lessons/6965