Prisma를 이용해 게시판을 개발해보자
[게시판 프로젝트] 시작하기
[게시판 프로젝트] 설계
위 그림을 바탕으로 각 테이블간의 관계 및 요구사항을 정리해보자.
게시판 프로젝트 테이블 관계 및 요구사항 정리
사용자( Users )는 1개의 사용자 정보 ( UserInfo )를 가지고 있다.
- Users 테이블과 UserInfo 테이블은 1:1 관계를 가지고 있다.
사용자( Users )는 여러개의 게시글 ( Posts )을 등록할 수 있다.
- Users 테이블과 Posts 테이블을 1:N 관계를 가지고 있다.
사용자( Users )는 여러개의 댓글 ( Commnets )을 작성할 수 있다.
- Users 테이블과 Comments 테이블은 1:N 관계를 가지고 있다.
하나의 게시글 ( Posts )은 여러개의 댓글 ( Comments )이 작성될 수 있다.
- Posts 테이블과 Comments 테이블은 1:N 관계를 가지고 있다.
[게시판 프로젝트] 라이브러리 설치
# 프로젝트를 초기화합니다.
yarn init -y
# 라이브러리를 설치합니다.
yarn add express prisma @prisma/client cookie-parser jsonwebtoken
# nodemon 라이브러리를 DevDependency로 설치합니다.
yarn add -D nodemon
# 설치한 Prisma를 초기화 하여, Prisma를 사용할 수 있는 구조를 생성합니다.
npx prisma init
- package.json에 "type":"module"도 추가 ( ES6 문법 사용하기 위함 )
프로젝트 구조 ( 게시판 프로젝트 )
내 프로젝트 폴더 이름
├── .env
├── .gitignore
├── package.json
├── yarn.lock
├── prisma
│ └── schema.prisma
└── src
└── app.js
SQL로 미리 확인해보기
CREATE TABLE Users
(
userId INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(191) UNIQUE NOT NULL,
password VARCHAR(191) NOT NULL,
createdAt DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updatedAt DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)
);
CREATE TABLE UserInfos
(
userInfoId INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
userId INTEGER UNIQUE NOT NULL, -- 1:1 관계 이므로 UNIQUE 조건을 삽입합니다.
name VARCHAR(191) NOT NULL,
age INTEGER NOT NULL,
gender VARCHAR(191) NOT NULL,
profileImage VARCHAR(191) NULL,
createdAt DATETIME(3)
NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updatedAt DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)
);
ALTER TABLE UserInfos
ADD CONSTRAINT FK_UserInfos_Users
FOREIGN KEY (userId) REFERENCES Users (userId) ON DELETE CASCADE;
CREATE TABLE Posts
(
postId INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
userId INTEGER NOT NULL,
title VARCHAR(191) NOT NULL,
content VARCHAR(191) NOT NULL,
createdAt DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updatedAt DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)
);
ALTER TABLE Posts
ADD CONSTRAINT FK_Posts_Users
FOREIGN KEY (userId) REFERENCES Users (userId) ON DELETE CASCADE;
CREATE TABLE Comments
(
commentId INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
userId INTEGER NOT NULL,
postId INTEGER NOT NULL,
content VARCHAR(191) NOT NULL,
createdAt DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updatedAt DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)
);
ALTER TABLE Comments
ADD CONSTRAINT FK_Comments_Posts
FOREIGN KEY (postId) REFERENCES Posts (postId) ON DELETE CASCADE;
ALTER TABLE Comments
ADD CONSTRAINT FK_Comments_Users
FOREIGN KEY (userId) REFERENCES Users (userId) ON DELETE CASCADE;
[게시판 프로젝트] Prisma 설계하기
Prisma model 구현하기
먼저, 구현될 요구사항을 바탕으로 Prisma의 모델을 작성해보자.
각 테이블의 요구사항을 바탕으로 schema.prisma 파일의 모델을 작성해보자.
사용자 ( Users ) 테이블
Name | 타입 ( Type ) | NULL | default |
userId ( PK ) | INTEGER | NOT NULL | AUTO_INCREMENT |
STRING | NOT NULL | ||
password | STRING | NOT NULL | |
createdAt | DATETIME | NOT NULL | 현재 시간 |
updatedAt | DATETIME | NOT NULL | 현재 시간 |
게시글 ( Posts ) 테이블
Name | 타입 ( Type ) | NULL | default |
postId ( PK ) | INTEGER | NOT NULL | AUTO_INCREMENT |
title | STRING | NOT NULL | |
content | TEXT | NOT NULL | |
ceratedAt | DATETIME | NOT NULL | 현재 시간 |
updatedAt | DATETIME | NOT NULL | 현재 시간 |
사용자 정보 ( UsersInfos ) 테이블
Name | 타입 ( Type ) | NULL | default |
userInfoId ( PK ) | INTEGER | NOT NULL | AUTO_INCREMENT |
name | STRING | NOT NULL | |
age | INTEGER | NULL | |
gender | STRING | NOT NULL | |
profileImage | STRING | NULL | |
createdAt | DATETIME | NOT NULL | 현재 시간 |
updatedAt | DATETIME | NOT NULL | 현재 시간 |
댓글 ( Comments ) 테이블
Name | 타입 ( Type ) | NULL | default |
commentId ( PK ) | INTEGER | NOT NULL | AUTO_INCREMENT |
content | STRING | NOT NULL | |
createdAt | DATETIME | NOT NULL | 현재 시간 |
updatedAt | DATETIME | NOT NULL | 현재 시간 |
위 작성한 테이블을 기준으로 Prisma model을 구현해보자.
schema.prisma에 Prisma mode을 입력한다.
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model Users{
userId Int @id @default(autoincrement()) @map("userId")
email String @unique @map("email")
password String @map("password")
createdAt DateTime @default(now()) @map("createdAt")
updatedAt DateTime @updatedAt @map("updatedAt")
@@map("Users")
}
model Posts{
postId Int @id @default(autoincrement()) @map("postId")
title String @map("title")
content String @map("content") @db.Text
createdAt DateTime @default(now()) @map("createdAt")
updatedAt DateTime @updatedAt @map("updatedAt")
@@map("Posts")
}
model UserInfos{
userInfoId Int @id @default(autoincrement()) @map("userInfoId")
name String @map("name")
age Int? @map("age")
gender String @map("gender")
profileImage String? @map("profileImage")
createdAt DateTime @default(now()) @map("createdAt")
updatedAt DateTime @updatedAt @map("updatedAt")
@@map("UserInfos")
}
model Comments{
commentId Int @id @default(autoincrement()) @map("commentId")
content String @map("content")
createdAt DateTime @default(now()) @map("createdAt")
updatedAt DateTime @updatedAt @map("updatedAt")
@@map("Comments")
}
Prisma 1:1 관계
요구사항 중 "사용자 ( Users )는 1개의 사용자 정보 ( UserInfo )를 가지고 있다." 에서 사용자와 사용자 정보 모델의 경우 1:1 관계를 가지고 있는 것을 확인할 수 있다. 해당 모델을 비교해 Prisma model은 어떤 방법으로 관계를 설정하는지 확인해보자.
// schema.prisma
model Users {
userId Int @id @default(autoincrement()) @map("userId")
email String @unique @map("email")
password String @map("password")
createdAt DateTime @default(now()) @map("createdAt")
updatedAt DateTime @updatedAt @map("updatedAt")
userInfos UserInfos? // 사용자(Users) 테이블과 사용자 정보(UserInfos) 테이블이 1:1 관계를 맺습니다.
@@map("Users")
}
model UserInfos {
userInfoId Int @id @default(autoincrement()) @map("userInfoId")
userId Int @unique @map("userId") // 사용자(Users) 테이블을 참조하는 외래키
name String @map("name")
age Int? @map("age")
gender String @map("gender")
profileImage String? @map("profileImage")
createdAt DateTime @default(now()) @map("createdAt")
updatedAt DateTime @updatedAt @map("updatedAt")
// Users 테이블과 관계를 설정합니다.
user Users @relation(fields: [userId], references: [userId], onDelete: Cascade)
@@map("UserInfos")
}
사용자 ( Users ) 모델은 사용자 정보 ( UserInfos ) 모델과 1:1관계를 가지고 있다.
여기서, 1:1 관계란 한 사용자가 하나의 사용자 정보만 가질 수 있고, 한 사용자 정보는 한 사용자에게만 속할 수 있다는 것을 의미한다.
이런 1:1 관계를 설정할 때는 다음과 같은 내용을 포함해야 한다.
- 관계를 설정하려는 모델 ( UserInfos )에서 어떤 모델과 관계를 맺을지 ( Users ) 설정해야 한다.
- 관계를 맺게되는 모델 ( Users )에서 어떤 모델이 관계를 맺는지 ( UserInfos ) 설정해야한다.
- 관계를 맺게되는 모델 ( Users )에서 타입을 지정할 때, Optional Parameter( ? )를 지정해 줘야한다. ( 사용자는 사용자 정보가 존재하지 않을 수 있기 때문 )
사용자 정보 ( UserInfos ) 모델에서 설정한 부분을 자세히 살펴보자.
● Users
- 일반적인 Int, String과 같은 타입이 아닌, 참조할 다른 모델을 지정한다.
- 사용자 ( Users ) 모델을 참조하므로 Users로 작성되어있다
● fields
● 사용자 정보 ( UserInfos ) 모델에서 사용할 외래키 ( Forien Key ) 컬럼을 지정한다.
● 여기서는 userId 컬럼으로 외래키를 지정했다.
● references
● key : 참조하는 다른 모델의 Column를 지정한다.
● 여기서는 사용자 ( Users ) 모델의 userId 컬럼을 참조한다.
● onDelete | onUpdate
● 참조하는 모델이 삭제 or 수정될 경우 어떤 행위를 할 지 설정한다.
● Cascade 옵션을 선택해 사용자가 삭제될 경우 그에 연결된 사용자 정보도 함께 삭제되도록 설정했다.
Prisma 1:N 연관 관계
요구사항 중 "사용자 ( Users )는 여러개의 게시글 ( Posts )을 등록할 수 있다." 에서 사용자와 게시글 모델의 경우 1:N 관계를 가지고 있는 것을 확인할 수 있다. 이번에도 2가지의 모델의 Prisma model에서 어떻게 관계를 설정하는지 확인해보자.
먼저 게시글 ( Posts ) model에서 관계를 설정하는 부분을 살펴보자.
// schema.prisma
model Users {
userId Int @id @default(autoincrement()) @map("userId")
email String @unique @map("email")
password String @map("password")
createdAt DateTime @default(now()) @map("createdAt")
updatedAt DateTime @updatedAt @map("updatedAt")
userInfos UserInfos? // 사용자(Users) 테이블과 사용자 정보(UserInfos) 테이블이 1:1 관계를 맺습니다.
posts Posts[] // 사용자(Users) 테이블과 게시글(Posts) 테이블이 1:N 관계를 맺습니다.
@@map("Users")
}
model Posts {
postId Int @id @default(autoincrement()) @map("postId")
userId Int @map("userId") // 사용자(Users) 테이블을 참조하는 외래키
title String @map("title")
content String @map("content") @db.Text
createdAt DateTime @default(now()) @map("createdAt")
updatedAt DateTime @updatedAt @map("updatedAt")
// Users 테이블과 관계를 설정합니다.
user Users @relation(fields: [userId], references: [userId], onDelete: Cascade)
@@map("Posts")
}
사용자 ( Users ) 모델과 게시글 ( Posts ) 모델은 1:N 관계를 가지고 있다.
여기서 1:N 관계랑 한 사용자는 여러개의 게시글을 작성할 수 있다는 것을 의미한다.
이런 1:N 관계를 설정할 때는 다음과 같은 내용을 포함해야 한다.
- 관계를 설정하려는 모델 ( Posts )에서 어떤 모델과 관계를 맺을지 ( Users ) 설정해야한다.
- 관계를 맺게되는 모델 ( Users )에서 어떤 모델이 관계를 맺는지 ( Posts ) 설정해야한다.
- 관계를 맺게되는 모델 ( Users )에서 타입을 지정할 때, 배열 연산자 ( [] )를 작성해줘야한다. ( 사용자는 여러개의 게시글을 가질 수 있기 때문 )
현재 게시글 모델의 경우 작성한 사용자가 회원 탈퇴 ( onDelete )하게 될 경우 작성한 모든 게시글이 삭제되도록 구현되어 있다. 이런 설정은 @relation 어노테이션을 사용해 지정한다.
// Users 테이블과 관계를 설정합니다.
user Users @relation(fields: [userId], references: [userId], onDelete: Cascade)
여기서 User는 게시글 ( Posts )이 참조하는 다른 모델을 지정하고, fields는 게시글 ( Posts ) 모델에서 사용할 외래키 컬럼을 지정한다. references는 참조하는 다른 모델의 컬럼을 지정하고, onDelete는 참조하는 모델이 삭제될 경우 어떤 행위를 할 지 설정한다.
onDelete의 경우, Cascade 옵션으로 사용자가 삭제될 경우 연관된 게시글 또한 삭제되도록 설정했다.
댓글(Comments) 또한, 게시글 ( Posts )과 마찬가지로 사용자 ( Users ) 모델과 1:N 관계를 가지고 있다.
댓글 ( Comments ) model에서 관계를 설정하는 부분을 살펴보자.
model Users {
userId Int @id @default(autoincrement()) @map("userId")
email String @unique @map("email")
password String @map("password")
createdAt DateTime @default(now()) @map("createdAt")
updatedAt DateTime @updatedAt @map("updatedAt")
userInfos UserInfos? // 사용자(Users) 테이블과 사용자 정보(UserInfos) 테이블이 1:1 관계를 맺습니다.
posts Posts[] // 사용자(Users) 테이블과 게시글(Posts) 테이블이 1:N 관계를 맺습니다.
comments Comments[] // 사용자(Users) 테이블과 댓글(Comments) 테이블이 1:N 관계를 맺습니다.
@@map("Users")
}
model Posts {
postId Int @id @default(autoincrement()) @map("postId")
userId Int @map("userId") // 사용자(Users) 테이블을 참조하는 외래키
title String @map("title")
content String @map("content") @db.Text
createdAt DateTime @default(now()) @map("createdAt")
updatedAt DateTime @updatedAt @map("updatedAt")
// Users 테이블과 관계를 설정합니다.
user Users @relation(fields: [userId], references: [userId], onDelete: Cascade)
comments Comments[] // 게시글(Posts) 테이블과 댓글(Comments) 테이블이 1:N 관계를 맺습니다.
@@map("Posts")
}
model Comments {
commentId Int @id @default(autoincrement()) @map("commentId")
postId Int @map("postId") // 게시글(Posts) 테이블을 참조하는 외래키
userId Int @map("userId") // 사용자(Users) 테이블을 참조하는 외래키
content String @map("content")
createdAt DateTime @default(now()) @map("createdAt")
updatedAt DateTime @updatedAt @map("updatedAt")
// Posts 테이블과 관계를 설정합니다.
post Posts @relation(fields: [postId], references: [postId], onDelete: Cascade)
// Users 테이블과 관계를 설정합니다.
user Users @relation(fields: [userId], references: [userId], onDelete: Cascade)
@@map("Comments")
}
게시판 프로젝트 최종 Prisma model
// schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model Users {
userId Int @id @default(autoincrement()) @map("userId")
email String @unique @map("email")
password String @map("password")
createdAt DateTime @default(now()) @map("createdAt")
updatedAt DateTime @updatedAt @map("updatedAt")
userInfos UserInfos? // 사용자(Users) 테이블과 사용자 정보(UserInfos) 테이블이 1:1 관계를 맺습니다.
posts Posts[] // 사용자(Users) 테이블과 게시글(Posts) 테이블이 1:N 관계를 맺습니다.
comments Comments[] // 사용자(Users) 테이블과 댓글(Comments) 테이블이 1:N 관계를 맺습니다.
@@map("Users")
}
model Posts {
postId Int @id @default(autoincrement()) @map("postId")
userId Int @map("userId") // 사용자(Users) 테이블을 참조하는 외래키
title String @map("title")
content String @map("content") @db.Text
createdAt DateTime @default(now()) @map("createdAt")
updatedAt DateTime @updatedAt @map("updatedAt")
// Users 테이블과 관계를 설정합니다.
user Users @relation(fields: [userId], references: [userId], onDelete: Cascade)
comments Comments[] // 게시글(Posts) 테이블과 댓글(Comments) 테이블이 1:N 관계를 맺습니다.
@@map("Posts")
}
model UserInfos {
userInfoId Int @id @default(autoincrement()) @map("userInfoId")
userId Int @unique @map("userId") // 사용자(Users) 테이블을 참조하는 외래키
name String @map("name")
age Int? @map("age")
gender String @map("gender")
profileImage String? @map("profileImage")
createdAt DateTime @default(now()) @map("createdAt")
updatedAt DateTime @updatedAt @map("updatedAt")
// Users 테이블과 관계를 설정합니다.
user Users @relation(fields: [userId], references: [userId], onDelete: Cascade)
@@map("UserInfos")
}
model Comments {
commentId Int @id @default(autoincrement()) @map("commentId")
postId Int @map("postId") // 게시글(Posts) 테이블을 참조하는 외래키
userId Int @map("userId") // 사용자(Users) 테이블을 참조하는 외래키
content String @map("content")
createdAt DateTime @default(now()) @map("createdAt")
updatedAt DateTime @updatedAt @map("updatedAt")
// Posts 테이블과 관계를 설정합니다.
post Posts @relation(fields: [postId], references: [postId], onDelete: Cascade)
// Users 테이블과 관계를 설정합니다.
user Users @relation(fields: [userId], references: [userId], onDelete: Cascade)
@@map("Comments")
}
Prisma DB, Table 생성
.env 설정
# .env
DATABASE_URL="mysql://root:aaaa4321@express-database.qeradf.ap-northeast-2.rds.amazonaws.com:3306/community_hub"
자신이 대여중인 아마존 RDS EndPoint를 참조해서 경로를 설정한다.
아래 명령어를 입력해 db와 앞서 설계한 테이블을 생성한다.
# 해당 프로젝트에 schema.prisma에 정의된 테이블을 MySQL에 생성합니다.
npx prisma db push
'Javascript' 카테고리의 다른 글
[Javascript] .env ( 환경 변수 읽어 오기 ) (0) | 2024.09.09 |
---|---|
[Javascript] bcrypt ( 단방향 암호화, 복호화 ) (0) | 2024.09.09 |
[Javascript] Prettier ( 코드 서식 관리 도구 ) (0) | 2024.09.04 |
[Javascript][Node.js] 에러 처리 미들웨어 (0) | 2024.09.04 |
[Javascript][Node.js] joi ( 유효성 검증 ) (0) | 2024.09.04 |