๐ ORM ๊ธฐ์ ์คํ ๋น๊ต - boostcampwm-2024/web17-juchumjuchum GitHub Wiki
ORM ๊ธฐ์ ์คํ ๋น๊ต
๋ถ์ผ | ์์ฑ์ | ์์ฑ์ผ |
---|---|---|
BE | ๊น๋ฏผ์ | 24๋ 10์ 28์ผ |
Sequelize
- ํ ๋ ๋ง์ด ์ฌ์ฉํ๋ node ์ง์์ ORM ๊ธฐ์
- ์ค๋๋ ๋งํผ ๊ฑฐ๋ํ ์ปค๋ฎค๋ํฐ ํ์ฑ๊ณผ ๊ธฐ์ ์ ์์ ์ฑ์ด ๋๋ค.
์ง์ ๊ธฐ๋ฅ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ์ ๊ธฐ๋ฐ์ผ๋ก ํ Model ๊ฐ์ฒด
- ๋ง์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ง์
- ์ฟผ๋ฆฌ์ ๋ฐ๋ฅธ ๊ธฐ๋ณธ ๋ฉ์๋ ์ง์
- validator์ ์ ์ฝ์กฐ๊ฑด ์ค์ ๊ฐ๋ฅ
- raw ์ฟผ๋ฆฌ ์ง์
- ๋ ผ๋ฆฌ์ ์ญ์ ์ง์
- Lazy Loading/Eager Loading ์ง์
๋ด๊ฐ ์๊ฐํ๋ ์ฅ์
- ๊ณต์๋ฌธ์ - ์ค๋๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ธฐ ๋๋ฌธ์ ๊ณต์๋ฌธ์๊ฐ ์ ์ ๋ฆฌ๋์ด์๋ค.
๋ด๊ฐ ์๊ฐํ๋ ๋จ์
- ์ฝ๋ ๊ฐ๋ ์ฑ: ์ฌ์ฉํ๊ธฐ์ ๊ฐ์ฅ ํฐ ๋จ์ ์ด๋ผ๊ณ ์๊ฐ, ๋ชจ๋ธ ์ค์ ์ ์นผ๋ผ์ ํ์ ์ด๋ ํน์ง์ ์์ฑํ๋ ์ฝ๋๋, ์ฐ์ฐ ๊ด๋ จ ๋ฉ์๋์ ์ฝ๋๊ฐ ๋๋ฌ์ธ ์ ์๋ค.
- ์ฟผ๋ฆฌ๊ฐ ๋๋ฆฌ๋ค: ๋ฉ์๋์์ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ ๋ฐฉ์์ด๊ธฐ ๋๋ฌธ์ ๊ทธ์ ๋ค๋ฅธ ์ค๋ฒํค๋๊ฐ ์์ ์ ์๋ค.
- ๋์ ๋ฌ๋์ปค๋ธ: ORM ๋ฉ์๋๊ฐ sql๊ณผ ๊ฑฐ๋ฆฌ๊ฐ ๋ฉ ์ ์๊ธฐ ๋๋ฌธ์ sql์ ์๋ค๊ณ ํด๋ ์ด๋ ค์ธ ์ ์๋ค.
- ๋ณต์กํ ์ฝ๋์ ์ทจ์ฝํ ์ ์๋ค: ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฌ๋ ๋ฉ์๋ ํ์์ด ๋งค์ฐ ๋ณต์กํ๊ณ ์ง๊ด์ ์ด์ง ์์, ์๋์น ์๊ฒ ๋๋ฆฐ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๊ฑฐ๋ ์๋ชป๋ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ๊ฐ๋ฅ์ฑ์ด ํฌ๋ค.
TypeORM
- ์๋ ๊น์ง ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ ORM
- JPA์ ๊ฐ์ฅ ๊ทผ์ ํ ํํ์ด๋ค.
์ง์ ๊ธฐ๋ฅ
- DataMapper์ ActiveRecord ํํ์ ์ฝ๋ ์ง์ (๊ฐ์ธ์ ์ผ๋ก DataMapper๊ฐ ์ข๋ค๊ณ ์๊ฐ)
- Entity๊ฐ๋
์กด์ฌ
- ๋ค์ํ ๋ฐ์ฝ๋ ์ดํฐ ์ง์
- Embedded Entities ์กด์ฌ(์ํฐํฐ ๋ด๋ถ ์ ๋๋ค๋ฅธ ์ํฐํฐ)
- ๋ทฐ ์ํฐํฐ, tree ์ํฐํฐ ์กด์ฌ
- Datasource ์ฌ์ฉ (DB ์ปค๋ฅ์ ์ถ์ํ)
- ์ฐ๊ด ๊ด๊ณ ๊ด๋ จ ๋ฐ์ฝ๋ ์ดํฐ ์ ๊ณต
- Cascade ์ต์ ์ ๊ณต
- ์ฆ์/์ง์ฐ ๋ก๋ฉ ์ ๊ณต
- ์ํฐํฐ ๋งค๋์ ์ ๊ณต
- repository ์์ฑ ๋ฐ ์ํฐํฐ ๊ด๋ จ ์ฒ๋ฆฌ
- ์ฟผ๋ฆฌ ๋น๋ ์ ๊ณต
- ๋ค์ํ ๋ฐ์ฝ๋ ์ดํฐ ์ง์
- Transactional ๋ฐ์ฝ๋ ์ดํฐ ์ ๊ณต(0.3์ดํ ๋ฒ์ ์๋ ์ง์ X)
- ์ธ๋ฑ์ค ๋ฐ์ฝ๋ ์ดํฐ
- ์ํฐํฐ ์ด๋ฒคํธ ์ ๊ณต
๋ด๊ฐ ์๊ฐํ๋ ์ฅ์
- ์ฌ๋ฌ๊ฐ์ง ๋ฐ์ฝ๋ ์ดํฐ ์ง์ - ๋ค์ํ ๋ฐ์ฝ๋ ์ดํฐ๋ก ์ฌ์ฉ๋๋ ์ฝ๋ ์๋ ์ค์ด๊ณ ๊ฐ๋ ์ฑ๋ ๋์ผ ์ ์๋ค.
- JPA ์ฌ์ฉ์์๊ฒ ๋ฎ์ ๋ฌ๋์ปค๋ธ - ์ํฐํฐ ๊ฐ๋ ๊ณผ ๋๋ถ์ด ๊ด๋ จ ๊ธฐ๋ฅ๋ค์ด JPA์ ๊ฐ์ฅ ์ ์ฌํ์ฌ, ์ด๋ฏธ JPA๋ฅผ ๋ฐฐ์ด ์ฌ๋๋ค์ ์น์ํ๊ฒ ์ฌ์ฉ๊ฐ๋ฅ (๋จ, ์ผ๋ถ ๊ธฐ๋ฅ์ด ์์ด์ ๋ถํธํจ์ด ์์ ์ ์์)
- ๊ธํ๋ฉด ์ฟผ๋ฆฌ ๋น๋ ์ฌ์ฉ๊ฐ๋ฅ - ๊ฐ์ฅ ํฐ ์ฅ์ ์ด๋ผ๊ณ ์๊ฐ, ๋ณต์กํ ์ฟผ๋ฆฌ๋ ์ต์ ํ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ๋ ์ฟผ๋ฆฌ ๋น๋๋ฅผ ์ฌ์ฉํ๋ฉด ์ฝ๊ฒ ์์ฑ ๊ฐ๋ฅ
๋ด๊ฐ ์๊ฐํ๋ ๋จ์
- ๋ฒ์ ๋ฌธ์
- ์์ง ์ ์ ๋ฒ์ ์ด ์ถ์๋์ง ์์์ ๊ธฐ์ ์ ์์ ์ฑ์ด ๋ฎ์ ์ ์๋ค.
- 0.3 ์ดํ ๋ฒ์ ๋ฌธ์ (ํธ๋์ญ์ ๋ฐ์ฝ๋ ์ดํฐ ์ง์ X, ์ด์ ๋ฒ์ ํธํ๋ฌธ์ )
- 0.4๋ก ๋ฐ๋๊ฒ ๋ ๋ ๊ธฐ์กด์ ์ฝ๋๊ฐ ํธํ์ด ์๋ ์๋ ์์
- ํ์ ์์ ์ฑ์ ๋ฌธ์ - AnyORM์ด๋ผ๋ ๋ณ๋ช ๋ต๊ฒ ํ์ ์ ๋ํด ์ ๋๋ก ์ฒดํฌํ ์ ์๋ค.
- ํด๋จผ ์๋ฌ๋ฅผ ์ก์ง ๋ชปํจ - ์ฟผ๋ฆฌ์ ์ ๋ฌ๋๋ ํ์ ์ ์ปดํ์ผ ๋จ๊ณ์ ํ์ธํ์ง ์์ผ๋ฏ๋ก ์ด์ ์ฐ๊ด๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
- ๊ณต์๋ฌธ์๊ฐ ๋ค๋ฅธ ORM์ ๋นํด ์ค๋ช ์ด ๋ถ์คํ๋ค๊ณ ๋๋
Prisma
- ์ต๊ทผ์ ํ์ํ ORM
- 2024๊ธฐ์ค์ผ๋ก ๊ฐ์ฅ ๋ง์ด ๋ค์ด๋ก๋ ํ ORM์ด๋ค.
- ํ์ ์์ ์ฑ๊ณผ schema๋ฅผ ํตํ ์ํฐํฐ ์์ฑ์ด ์ฃผ์ ๊ธฐ๋ฅ์ด๋ค.
์ง์๊ธฐ๋ฅ
- ๋ฐ์ดํฐ ๋ชจ๋ธ์ ์คํค๋ง ํํ๋ก ์ ์ธ
- ํ ์ด๋ธ ๋ฟ๋ง ์๋๋ผ ์ฐ๊ด๊ด๊ณ๋ ์ธ๋ฑ์ค, ๋ทฐ๋ ์ ์ธํ ์ ์๋ค.
- ์์ฑํ ์คํค๋ง๋ฅผ DB์ ๋ฐ์ํ ์ ์๋ค.
- ํน์ DB์ ์ง์๋์ง ์์ ์ ์๋ ๊ธฐ๋ฅ์ด ์์ผ๋ฏ๋ก ์ฃผ์ ํ์
- ์ฟผ๋ฆฌ ์คํ์ ์ํ Prisma Client
- ์คํค๋ง ๊ธฐ๋ฐ์ผ๋ก ์๋์ผ๋ก TypeScript ํ์ ์ ์์ฑํด์ค โ ํ์ ์์ ์ฑ์ด ๋ณด์ฅ
- ์ผ๋ฐ์ ์ธ CRUD์ ์ฐ๊ด ๊ด๊ณ ์ฟผ๋ฆฌ, ํํฐ๋ง ์ ๋ ฌ, ํ์ด์ง๋ค์ด์ ๋ฑ ๋ค์ํ ์ข ๋ฅ์ ์ฟผ๋ฆฌ์ ๋ํ ๋ฉ์๋๋ฅผ ์ ๊ณต
- Custom validation ์ ๊ณต
- logging๊ณผ metrix ๊ธฐ๋ฅ ์ ๊ณต
๋ด๊ฐ ์๊ฐํ๋ ์ฅ์
- ํ์ ์ ๋งค์ฐ ๊ฐ๋ ฅํจ - ๋ค๋ฅธ ORM๊ณผ ๋ฌ๋ฆฌ ์ปดํ์ผ ๋จ๊ณ์์ ํ์ ์ ๋ํ ์๋ฌ๋ฅผ ์ ๊ณตํด์ค๋ค. ์ฟผ๋ฆฌ ๊ด๋ จ ๋ฉ์๋๋ฅผ ์คํํ ๋ ํ์ ์ ์ฒดํฌํด์ฃผ๊ธฐ ๋๋ฌธ์ ํธํ๋ค.
- ๋ชจ๋ธ ์ ์ธ์ด ๊ฐ์ฅ ๋ณด๊ธฐ ํธํ๋ค. - ๋ชจ๋ธ ์ ์ธ์ sql์ create ์ฟผ๋ฆฌ ๋ฌธ๊ณผ ์ ์ฌํด์ ์ง๊ด์ ์ด๋ค.
๋ด๊ฐ ์๊ฐํ๋ ๋จ์
- ๋๋ฆฌ๋ค: ๋ค๋ฅธ ORM์ ๋นํด ๋๋ฆด ์๋ ์๋ค. Typingํ๋ ๊ฒ๊ณผ ๋๋ถ์ด์ ํด๋จผ ์๋ฌ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด ๋๋ค๋ฅธ ์ฟผ๋ฆฌ๊ฐ ์คํํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ex)drop์ select ์ฟผ๋ฆฌ๊ฐ ์คํ
- ๋ณต์กํ ์ฟผ๋ฆฌ์ ์ฝํ ์ ์๋ค: nested ์ฟผ๋ฆฌ๊ฐ ํฌํจ๋๋ ์ฟผ๋ฆฌ์ ๊ฐ์ด ๋ณต์กํ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ๋ ์ด๋ ค์ธ ์ ์๋ค.
์ฝ๋ ๋น๊ต
๋ชจ๋ธ ์ ์ธ
-
Sequelize
import { Model, DataTypes } from 'sequelize'; import sequelize from './database'; class User extends Model { public id!: number; public name!: string; public email!: string; public password!: string; public readonly createdAt!: Date; public readonly updatedAt!: Date; } User.init( { id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true, }, name: { type: DataTypes.STRING(100), allowNull: false, }, email: { type: DataTypes.STRING, allowNull: false, unique: true, }, password: { type: DataTypes.STRING(100), allowNull: false, }, }, { sequelize, tableName: 'users', timestamps: true, } ); export default User;
-
TypeORM
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column({ type: 'varchar', length: 100 }) name: string; @Column({ type: 'varchar', unique: true }) email: string; @Column({ type: 'varchar', length: 100 }) password: string; @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; }
-
Prisma
Prisma๋
schema.prisma
๋ผ๋ ํ์ผ์์ ์ ์ธํ๋ค.model User { id Int @id @default(autoincrement()) name String @db.VarChar(100) email String @unique password String @db.VarChar(100) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }
๋จ์ ์กฐํ
โtesterโ๋ผ๋ ์ด๋ฆ์ ๊ฐ์ง ์ ์ ์กฐํ
-
Sequelize
import User from './models/User'; const user = await User.findOne({ where: { name: "tester', }, });
-
TypeORM
import { getRepository } from 'typeorm'; import { User } from './entities/User'; const userRepository = getRepository(User); const user = await userRepository.findOne({ where: { name: 'tester' }, });
-
Prisma
import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); const user = await prisma.user.findFirst({ where: { name: 'tester', }, });
๋ณต์กํ ์กฐ๊ฑด ์กฐํ
โtesterโ๋ผ๋ ์ด๋ฆ์ ๊ฐ์ก๊ณ , ๋์ด๋ 25์ธ ์ด์, ๊ฐ์
์ผ์ด 2024-10-30
์ด์์ธ ์ ์ ๋ฅผ ์กฐํํ๊ณ ๋์ด์์ผ๋ก ์ ๋ ฌํ ํ 5๋ช
์ ์ถ์ถํ๋ค.
-
Sequelize
import { Op } from 'sequelize'; import User from './models/User'; const users = await User.findAll({ where: { name: "tester", age: { [Op.gte]: 25, }, createdAt: { [Op.gte]: new Date("2024-10-30"), }, }, order: ["age", "DESC"](/boostcampwm-2024/web17-juchumjuchum/wiki/"age",-"DESC"), limit: 5, });
-
TypeORM
import { getRepository, MoreThanOrEqual } from 'typeorm'; import { User } from './entities/User'; const userRepository = getRepository(User); const users = await userRepository.find({ where: { name: "tester", age: MoreThanOrEqual(25), createdAt: MoreThanOrEqual(new Date("2024-10-30")), }, order: { age: "DESC", }, take: 5, });
-
Prisma
import { PrismaClient } from '@prisma/client'; const users = await prisma.user.findMany({ where: { name: "๊น๋ฏผ์", age: { gte: 25, }, createdAt: { gte: new Date("2023-01-01"), }, }, orderBy: { age: "desc", }, take: 5, });
์ ๋ฆฌ
๋ฌ๋ ์ปค๋ธ(์ฌ๋๋ง๋ค ๊ธฐ์ค์ด ๋ค๋ฆ)
Prisma < TypeORM < Sequelize
JPA๋ฅผ ์๋ ๊ฒฝ์ฐ
TypeORM < Prisma < Sequelize
์ฑ๋ฅ
Prisma < Sequelize ? TypeORM
์ฑ๋ฅ์ TypeORM์ด ๊ฐ์ฅ ์ข๊ธด ํ์ง๋ง ๊ทน๋จ์ ์ธ ์ฐจ์ด๋ ๋ํ๋์ง ์๋๋ค.
ํนํ Sequlize์ TypeORM์ ์ํฉ์ ๋ฐ๋ผ์ ์๋ ์ฐจ์ด๊ฐ ๋ฌ๋ผ์ง๊ฒ ๋๋ฏ๋ก ๋๊ฐ ์ฐ์ธ์ธ์ง ํ์คํ๊ฒ ์ ์ ์๋ค.
์ฌ์ฉ์ฑ
Sequelize < TypeORM โค Prisma
์ด๊ฒ๋ ๋ฌ๋ ์ปค๋ธ์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ฌ๋๋ง๋ค ๋ค๋ฅผ ์๋ ์๋ค๊ณ ์๊ฐํ๋ค.
Typescript๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ์๋ง๋ Prisma๊ฐ ๊ฐ์ฅ ์ฌ์ฉ์ฑ์ด ์ข์ ์ ์๋ค. ์๋ํ๋ฉด ์ปดํ์ผ ๋จ๊ณ์ ํ์ ์ ๊ด๋ จ๋ ์๋ฌ๋ฅผ ์ฒดํฌํ ์ ์๊ธฐ ๋๋ฌธ์ ๊ทธ์ ๋ฐ๋ผ ํ์ ๊ด๋ จ๋ ์์ ์ ์ํํ ๋ ์ํ ํ ์ ์๋ค.
TypeORM์ ๋ค์ํ ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ง์ํ๊ณ , ๋ณต์กํ ์ฟผ๋ฆฌ๊ฐ ํ์ํ ๊ฒฝ์ฐ query builder ๊ธฐ๋ฅ์ ํ์ฉํ ์ ์๋ค.