
영속성이란 데이터를 생성한 프로그램이 종료되어도, "데이터는" 사라지지 않는 데이터의 특성을 말한다.

ORM은 DataBase와 Web Aplication의 연결을 객체지향적으로 도와주는 역할을 한다.!
ORM(Object Relational Mapping)은 객체로 연결해준다라는 의미로, 어플리케이션과 데이터베이스를 연결할 때, SQL 언어가 아닌, 어플리케이션 개발언어로 데이터베이스를 접근할 수 있게 해주는 툴이다.
ORM은 SQL 문법 대신 어플리케이션의 개발언어를 그대로 사용할 수 있게 하므로, 개발 언어의 일관성과 가독성을 높여준다는 장점이 있다.
👉 쉽게 말하자면,
DB (SQL 언어) ------ ORM Tools -------> 개발 언어 (Java, JS, JS, C 등)

이게 무슨 말인가 하면, ORM은 도구(Tools)로 언어에 따른 여러 종류가 있는데, 아래서 설명할 Prisma를 사용한 이유는 내가 개발한 어플리케이션의 환경이 TS이기 때문이다.

Prisma는 데이터베이스 작업을 간단하게 만들어주는 오픈 소스 ORM(Object-Relational Mapper)이다.
다른 ORM과는 다른 Prisma의 특징은 무엇일까?
migrate)을 제공한다. 👉 이를 통해 항상 웹 애플리케이션과 데이터데이스를 호환할 수 있다. generate라는 강력한 쿼리 빌더가 포함되어 있어 👉 오류 방지에 도움된다.studio 명령어를 통해 웹 브라우저에서 가시적으로 데이터를 테이블 형태로 보고 편집할 수 있는 UI를 제공한다. 👉 덕분에 코드를 작성하지 않고도 명시적으로 데이터를 확인할 수 있다.npm install prisma @prisma/client
npx prisma init
npm install prisma typescript ts-node @types/node --save-dev
# 만약 prisma 디렉터리를 src 하위로 옮긴다면, package.json에 아래 내용을 추가
"prisma": {
"schema": "src/prisma/schema.prisma"
}
DATABASE_URL="postgresql://username:password@hostname:5432/dbName?schema=schemaName"
import { PrismaClient } from "@prisma/client";
const prismaClient = new PrismaClient();
prisma.schema 파일 안에 작성model User {
id Int @id @default(autoincrement())
name String
createdAt DateTime @default(now()) }
npx prisma migrate dev
prisma.schema에 작성한 model의 타입을 만들어주는 역할(제너레이터) 👉 코드 안전성 부여 npx prisma generate
npx prisma studio
// & prisma studio와 현재 개발 서버를 동시에 띄워야 한다.
npm run dev
일대일(1-1) 관계에서는 참조할 테이블의 키를 외래키(FK)로 가져온다.


One-To-many의 관계일 경우, many에 one의 정보를 column으로 추가한다.

다수-다수관계일 때에는 중간에 중계 테이블을 만든다. (이때 통상적으로 양측 테이블의 이름을 합쳐 camelCase로 짓는다.)
그리고 각 테이블은 새로 만든 중계 테이블과 join해 정보를 가져온다.


📦 src
┣ 📂 contexts
┃ ┣ 📂 users
┃ ┃ ┣ 📜 users.controller.ts
┃ ┃ ┗ 📜 users.service.ts
┃ ┗ 📜 index.ts
┣ 📂 prisma
┃ ┣ 📂 migrations
┃ ┣ 📜 client.prisma.ts
┃ ┗ 📜 schema.prisma
┗ 📜 app.ts
📜 .env
import bodyParser from "body-parser";
import express from "express";
import controllers from "./contexts";
const app = express();
const port = 포트번호;
const jsonParser = bodyParser.json();
app.use(jsonParser);
app.use(controllers);
app.listen(port, () => {
console.log("**----------------------------------**");
console.log(" ========== Server is On!!! ========= ");
console.log("**----------------------------------**");
});
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
encryptedPassword String
profile UserProfile?
createdAt DateTime @default(now())
}
model UserProfile {
id Int @id @default(autoincrement())
nickname String?
name String?
gender String?
age Int?
phoneNumber String?
address String?
user User @relation(fields: [userId], references: [id])
userId Int @unique
}
import { PrismaClient } from "@prisma/client";
const prismaClient = new PrismaClient();
export default prismaClient;
❗️TIP 1
서버에서 에러가 뜨면(400, 404 등) 서버가 꺼지게 된다. 그럼 매번 재시동을 해줘야 하는데, 이문제를 해결하는 방법이 있다.
👉 세번째 인자인next()를 사용하면 된다.
❗️TIP 2
res: Response<Omit<User, "encryptedPassword">>안에 omit을 여러개 사용하고 싶다면 👉|를 사용하면 된다.
import { User } from "@prisma/client";
import { hash } from "bcrypt";
import { NextFunction, Request, Response } from "express";
import prismaClient from "../../prisma/client.prisma";
interface userInfoType = {
email: string;
password: string;
nickname: string;
name: string;
gender: string;
age: number;
}
const createUser = async(
req:Request<never, unknown, userInfoType>,
res: Response<Omit <User, "encryptedPassword>>,
next : NextFunction
) => {
try {
const { email, password, nickname, name, gender, age } = req.body;
if (!email.trim()) throw new Error("No email");
if (!password.trim()) throw new Error("No password");
// 요청헤더로 받은 password 해싱
const encryptedPassword = await hash(password, 12);
const user = await prismaClient.user.create({
data : {
email,
encryptedPassword,
profile: {
create: {
nickname, name, gender, age
}
}
}
});
res.json(user);
} catch (e) {
next(e)
}
};
const getUsers = async (_: Request, res: Response) => {
const users = await prismaClient.user.findMany();
res.json(users);
}
const getUser = async (
req: Request<{ userId : string }>,
res: Response
) => {
const parsedUserId = Number(req.params.userId);
if (isNaN(parsedUserId)) throw new Error("error");
const user = await prismaClient.user.findUnique({
where : { id : parsedUserId }.
select : {
id : true,
email : true,
created : true,
profile: true,
},
include : {
//! SQL의 JOIN 역할 👉 만약 특정 cols만 가져오고 싶다면 안에 객체를 넣으면 됨
profile: true
}
});
res.json(user);
}
const UpdateUser = async(
req: Reqeust<{ userId : string },
Omit<User, "encryptedPassword">,
{ email : string }>,
res: Response
) => {
const parsedUserId = Number(req.params.userId);
if (isNaN(parsedUserId)) throw new Error("error");
const { email } = req.body;
const user = await prismaClient.user.update({
where : { id : parsedUserId },
data : { email },
select : {
id : true, email : true, createdAt : true
} // 특정 cols만 가져올 것
});
if (!user) throw new Error("유저 없음");
res.json(user);
}
const deleteUser = async(
req: Request<{ userId : string }>,
res : Response
) => {
const parsedUserId = Number(req.params.userId);
if (isNaN(parsedUserId)) throw new Error("error");
const user = await prismaClient.user.delete({
where : { id : parsedUserId }
})
res.json(user);
}
const usersService = {
createUser,
getUsers,
getUser,
updateUser,
deleteUser
}
export default usersService;
import { Router } from "express";
import usersService from "./users.service";
const usersController = Router();
usersController.post("/", usersService.createUser);
usersController.get("/", usersService.getUsers);
usersController.get("/:userId", usersService.getUser);
usersController.put("/:userId", usersService.updateUser);
usersController.delete("/:userId", usersService.deleteUser);
export default usersController;
import { Router } from "express";
import usersController from "./users/users.controller";
const controllers = Router();
controllers.use("/users", usersController);
export default controllers;
📂 출처
✨ NextJS + TypScript + Prisma