๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
โš’ Backend/Node.js

[Node.js] TypeORM์ด๋ž€? (feat. ORM Library)

by Fomagran ๐Ÿ’ป 2022. 8. 2.
728x90
๋ฐ˜์‘ํ˜•

์•ˆ๋…•ํ•˜์„ธ์š” Foma ์ž…๋‹ˆ๋‹ค!

 

์˜ค๋Š˜์€ ์ €๋ฒˆ ๊ธ€ Sequelize์— ์ด์–ด์„œ Node.js ORM ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ TypeORM์— ๋Œ€ํ•ด์„œ ๋‹ค๋ค„๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

 

ํ‰์†Œ์— Typescript๋กœ ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒƒ์„ ์„ ํ˜ธํ•˜๊ธฐ ๋•Œ๋ฌธ์— "Sequelize๋ฅผ Typescript๋กœ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‚˜?" ํ•˜๊ณ  ์ฐพ์•„๋ดค๋”๋‹ˆ

 

Typescript ๊ธฐ๋ฐ˜์œผ๋ก  TypeORM ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ๊ฐ™๋”๋ผ๊ตฌ์š”.

 

(๋ฌผ๋ก  Sequlize๋„ Typescript๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ ์—์„œ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)

 

๊ทธ๋ž˜์„œ ์˜ค๋Š˜์€ TypeORM์ด ๋ฌด์—‡์ธ์ง€์— ๋Œ€ํ•ด ์ •๋ฆฌํ•ด ๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

 

๋ฐ”๋กœ ์‹œ์ž‘ํ• ๊ฒŒ์š”~


ORM์ด๋ž€?

 

๋จผ์ € Sequelize๋ฅผ ์•Œ๊ธฐ ์œ„ํ•ด์„œ ORM์˜ ๊ฐœ๋…์„ ์•Œ์•„์•ผ ํ•˜๋Š”๋ฐ์š”.

 

๊ทธ ์ด์œ ๋Š” ๋ฐ”๋กœ Sequelize๊ฐ€ Node.js์˜ ORM ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๊ธฐ ๋•Œ๋ฌธ์ด์ฃ .

 

ORM์€ Object Relational Mapping์˜ ์ค„์ž„๋ง๋กœ ๊ฐ์ฒด์™€ ๊ด€๊ณ„๋ฅผ ๋งคํ•‘ํ•ด์ฃผ๋Š” ๊ฒƒ ์ž…๋‹ˆ๋‹ค.

 

 

์ข€ ๋” ๊ตฌ์ฒด์ ์œผ๋กœ ๊ฐ์ฒด์™€ ๊ด€๊ณ„๋ฅผ ๋งคํ•‘ํ•ด์ค€๋‹ค๋Š” ๊ฒŒ ์–ด๋–ค ๊ฒƒ์ผ๊นŒ์š”?

 

์˜ˆ๋ฅผ ๋“ค๋ฉด ๊ฐ์ฒด ์ง€ํ–ฅ ์–ธ์–ด์—์„œ๋Š” ๋ชจ๋ธ์„ ์ •์˜ํ•  ๋•Œ Class๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์—์„  Table์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

๋ฐ”๋กœ ์—ฌ๊ธฐ์„œ ๊ฐ์ฒด ์ง€ํ–ฅ ์–ธ์–ด๋กœ ๋œ Class๋ฅผ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์˜ Table์™€ ์—ฐ๊ฒฐ์‹œ์ผœ ์ค€๋‹ค๋Š” ๊ฒƒ์ด์ฃ .

 

์ด๊ฒƒ์€ ์ง์ ‘ SQL๋ฌธ์„ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ณ , ๊ฐ์ฒด ์ง€ํ–ฅ์ ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๊ฒƒ์ด์ฃ .

 

(ORM์— ๋” ๊นŠ์ด ์•Œ๊ณ  ์‹ถ์œผ์‹  ๋ถ„๋“ค์€ ์—ฌ๊ธฐ ์—์„œ ํ™•์ธํ•ด ์ฃผ์„ธ์š”!)


TypeORM์ด๋ž€?

 

๊ณต์‹ ํ™ˆํŽ˜์ด์ง€์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์†Œ๊ฐœํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

TypeORM is an ORM that can run in NodeJS, Browser, Cordova, PhoneGap, Ionic, React Native, NativeScript, Expo, and Electron platforms and can be used with TypeScript and JavaScript (ES5, ES6, ES7, ES8). Its goal is to always support the latest JavaScript features and provide additional features that help you to develop any kind of application that uses databases - from small applications with a few tables to large scale enterprise applications with multiple databases.
TypeORM supports both Active Record and Data Mapper patterns, unlike all other JavaScript ORMs currently in existence, which means you can write high quality, loosely coupled, scalable, maintainable applications the most productive way.
TypeORM is highly influenced by other ORMs, such as Hibernate, Doctrine and Entity Framework.

 

๊ฐ„๋‹จํ•˜๊ฒŒ ํ•ด์„ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

" TypeORM์€ Node.js, Browser, React Native ํ”Œ๋žซํผ ๋“ฑ์—์„œ JS,TS์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ORM์ž…๋‹ˆ๋‹ค. ์†Œ๊ทœ๋ชจ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ถ€ํ„ฐ ๋Œ€๊ทœ๋ชจ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•˜๋Š”๋ฐ ๋„์›€์ด ๋˜๋Š” ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ORM๊ณผ ๋‹ฌ๋ฆฌ ์•กํ‹ฐ๋ธŒ ๋ ˆ์ฝ”๋“œ ํŒจํ„ด๊ณผ ๋ฐ์ดํ„ฐ ๋งคํผ ํŒจํ„ด์„ ๋ชจ๋‘ ์ง€์›ํ•˜์—ฌ ํ™•์žฅ ๊ฐ€๋Šฅํ•˜๋ฉฐ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ๊ฐ€๋Šฅํ•œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐ€์žฅ ์ƒ์‚ฐ์ ์ธ ๋ฐฉ๋ฒ•์œผ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. "


Active Record Pattern์ด๋ž€?

 

์•กํ‹ฐ๋ธŒ ๋ ˆ์ฝ”๋“œ ํŒจํ„ด์€ ๋ชจ๋ธ ์ž์ฒด ๋‚ด์—์„œ ๋ชจ๋“  ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ•˜๊ณ  ๋ชจ๋ธ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ƒ์„ฑ,์‚ญ์ œ,์กฐํšŒ,์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ๋ฐฉ์‹์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

 

์•„๋ž˜์™€ ๊ฐ™์ด User ๋ชจ๋ธ์˜ ํ”„๋กœํผํ‹ฐ์™€ ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from "typeorm"

@Entity()
export class User extends BaseEntity {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    firstName: string

    @Column()
    lastName: string

    @Column()
    isActive: boolean

    static findByName(firstName: string, lastName: string) {
        return this.createQueryBuilder("user")
            .where("user.firstName = :firstName", { firstName })
            .andWhere("user.lastName = :lastName", { lastName })
            .getMany()
    }
}

 

์œ„ User ๋ชจ๋ธ์„ ์ด์šฉํ•˜์—ฌ user ๊ฐ์ฒด๋ฅผ ์ด๋ฏธ ๊ตฌํ˜„๋œ ๋ฉ”์„œ๋“œ๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ƒ์„ฑ, ์‚ญ์ œ, ์กฐํšŒ, ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋˜ํ•œ ์œ„์—์„œ User ๋ชจ๋ธ์— ์ง์ ‘ ์ปค์Šคํ…€ํ•˜์—ฌ ๊ตฌํ˜„ํ•œ ๋ฉ”์„œ๋“œ ๋˜ํ•œ ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

const user = new User()
user.firstName = "Timber"
user.lastName = "Saw"
user.isActive = true
await user.save()

await user.remove()
const users = await User.find({ skip: 2, take: 5 })
const newUsers = await User.findBy({ isActive: true })
const timber = await User.findOneBy({ firstName: "Timber", lastName: "Saw" })
//๊ตฌํ˜„ํ•œ ๋ฉ”์„œ๋“œ
const timber = await User.findByName("Timber", "Saw")

Data Mapper Pattern์ด๋ž€?

 

๋ฐ์ดํ„ฐ ๋งคํผ ํŒจํ„ด์€ "๋ ˆํฌ์ง€ํ† ๋ฆฌ" ๋ผ๋Š” ๋ณ„๋„์˜ ํด๋ž˜์Šค์—์„œ ๋ชจ๋“  ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ•˜๊ณ  ์ด "๋ ˆํฌ์ง€ํ† ๋ฆฌ"๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ƒ์„ฑ, ์‚ญ์ œ, ์กฐํšŒ, ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์•กํ‹ฐ๋ธŒ ๋ ˆ์ฝ”๋“œ ํŒจํ„ด๊ณผ ๊ฐ™์ด User ๋ชจ๋ธ์„ ์ •์˜ํ•ด ์ค๋‹ˆ๋‹ค.

 

์ฐจ์ด์ ์€ User ๋ชจ๋ธ์„ BaseEntity๋กœ ํ™•์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    firstName: string

    @Column()
    lastName: string

    @Column()
    isActive: boolean
}

 

์•„๋ž˜์™€ ๊ฐ™์ด User์˜ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์™€ User ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— ์ƒ์„ฑ, ์‚ญ์ œ, ์กฐํšŒ, ์ˆ˜์ •์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

const userRepository = dataSource.getRepository(User)

// example how to save DM entity
const user = new User()
user.firstName = "Timber"
user.lastName = "Saw"
user.isActive = true
await userRepository.save(user)

// example how to remove DM entity
await userRepository.remove(user)

// example how to load DM entities
const users = await userRepository.find({ skip: 2, take: 5 })
const newUsers = await userRepository.findBy({ isActive: true })
const timber = await userRepository.findOneBy({
    firstName: "Timber",
    lastName: "Saw",
})

๋Š๋‚€ ์ 

 

Sequelize๋ฅผ ๋‹ค๋ค„๋ณด์•˜์„ ๋•Œ ๋ชจ๋ธ์„ ๋งŒ๋“ค๊ณ  ์ง์ ‘ ์‹œํ€„์„ ์ž‘์„ฑํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์—์„œ ์ง„์งœ ํŽธ๋ฆฌํ•˜๊ณ  ํ˜๋ช…์ ์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

 

๊ทผ๋ฐ TypeORM์„ ๋ฐœ๊ฒฌํ•˜๊ณ  ์‚ฌ์šฉํ•ด ๋ณด๋‹ˆ๊น ๋ชจ๋ธ์„ ๋งŒ๋“œ๋Š” ๋ถ€๋ถ„์ด๋‚˜ , ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ ์—์„œ ๋” ์ง๊ด€์ ์ด๊ณ , ๋ญ”๊ฐ€ ๋ฐฑ์—”๋“œ๋ฅผ ํ”„๋ก ํŠธ ์—”๋“œ์ฒ˜๋Ÿผ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฑธ ๋Š๋ผ๊ฒŒ ๋˜์—ˆ๋‹ค.

 

์žฅ๋‹จ์ ์ด ์žˆ๊ฒ ์ง€๋งŒ ๋‘˜ ์ค‘์— ํ•˜๋‚˜๋ฅผ ๊ณ ๋ฅธ๋‹ค๋ฉด TypeORM์„ ์‚ฌ์šฉํ•  ๊ฒƒ ๊ฐ™๋‹ค.


Reference

 

 

Active Record vs Data Mapper - typeorm

Using the Data Mapper approach, you define all your query methods in separate classes called "repositories", and you save, remove, and load objects using repositories. In data mapper your entities are very dumb - they just define their properties and may h

orkhan.gitbook.io

 

728x90
๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€