Skip to main content

ORM ์ด๋ž€?

๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ„์— ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™์œผ๋กœ ๋งคํ•‘ํ•ด์ฃผ๋Š” ๊ธฐ์ˆ 
ORM์€ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ฝ”๋“œ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• DB์˜ ํ…Œ์ด๋ธ” ์‚ฌ์ด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™์œผ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๊ธฐ์ˆ ์ด๋‹ค. ์ด๋ฅผ ํ†ตํ•ด SQL ์ง์ ‘ ์ž‘์„ฑ ์—†์ด ๊ฐ์ฒด ์ง€ํ–ฅ ๋ฐฉ์‹์œผ๋กœ DB์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

TypeORM

TypeORM์€ TypeScript์™€ JavaScript๋ฅผ ์œ„ํ•œ ORM ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. Node.js ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์™€ ํƒ€์ž… ์•ˆ์ •์„ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

TypeORM ์žฅ์ 

  • ํƒ€์ž… ์•ˆ์ •์„ฑ: TypeScript์˜ ํƒ€์ž… ์‹œ์Šคํ…œ์„ ํ™œ์šฉํ•˜์—ฌ ๋Ÿฐํƒ€์ž„ ์˜ค๋ฅ˜๋ฅผ ์ค„์ด๊ณ , ๊ฐœ๋ฐœ ์ค‘ ํƒ€์ž… ์˜ค๋ฅ˜๋ฅผ ์‚ฌ์ „์— ๋ฐฉ์ง€ํ•œ๋‹ค.
  • ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ํ†ตํ•œ ๋งคํ•‘: @Entity(), @Column(), @PrimaryGeneratedColumn() ๋“ฑ์˜ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ, ํด๋ž˜์Šค์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ” ๊ฐ„์˜ ๋งคํ•‘์„ ์„ค์ •ํ•œ๋‹ค.
  • ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํŒจํ„ด ์‚ฌ์šฉ: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž‘์—…์„ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ํ†ตํ•ด ์บก์Аํ™”ํ•˜์—ฌ ์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ด๊ณ , ๋ฐ์ดํ„ฐ ์ ‘๊ทผ์„ ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ ์œ ์ง€
    (์˜ˆ์‹œ)UserRepository๋ฅผ ์‚ฌ์šฉํ•˜์—ฌUserEntity์— ๋Œ€ํ•œ CRUD ์ž‘์—…์„ ์ฒ˜๋ฆฌ
  • ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ง€์›: ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ์˜ ๋ณ€๊ฒฝ ์ด๋ ฅ์„ ๊ด€๋ฆฌํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ตฌ์กฐ๋ฅผ ์‰ฝ๊ฒŒ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    (์˜ˆ์‹œ) ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ ์‹œ typeorm migration:generate์™€ typeorm migration:run ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ง€์›: MySQL, PostgreSQL, SQLite, MariaDB ๋“ฑ ๋‹ค์–‘ํ•œ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ง€์›
  • Active Record & Data Mapper ํŒจํ„ด ์ง€์›
    Active Record์™€ Data Mapper ํŒจํ„ด์ด ๋ญ์•ผ?
    ์˜์†์„ฑ์— ๋Œ€ํ•œ ๋กœ์ง(DB ์ ‘๊ทผ ๋“ฑ)์„ ๋ˆ„๊ฐ€/์–ด๋–ป๊ฒŒ ํ• ์ง€๋ฅผ ์ •ํ•˜๋Š” ํŒจํ„ด์ด๋‹ค.
    (์—”ํ‹ฐํ‹ฐ ์Šค์Šค๋กœ vs ๋ณ„๋„ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ/๋งคํผ)
    • Active Record ํŒจํ„ด: ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์ž์‹ ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
      ์˜ˆ) User, Order๊ณผ ๊ฐ™์€ ํด๋ž˜์Šค๋“ค์ด ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ชจ๋ธ
    • Data Mapper ํŒจํ„ด: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž‘์—…์„ ๋ณ„๋„์˜ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๊ฐ€ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
      ์˜ˆ) ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜์—ฌ, DB ์ž‘์—… ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.

๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด๋ž€?

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ ์ด๋ ฅ ๊ด€๋ฆฌ ๋ฐ ์ ์šฉ
๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ๋ฅผ ์ฒด๊ณ„์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ณ , ๋ฒ„์ €๋‹ํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋Šฅ์ด๋‹ค.
์‰ฝ๊ฒŒ ๋งํ•ด, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ(ํ…Œ์ด๋ธ”, ์ปฌ๋Ÿผ ๋“ฑ)์˜ ๊ตฌ์กฐ ๋ณ€ํ™”๋ฅผ ์ฝ”๋“œ๋กœ ๊ธฐ๋กํ•˜๊ณ , ์ด๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ์ ์šฉํ•˜๊ฑฐ๋‚˜ ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” ๋„๊ตฌ์ด๋‹ค. TypORM์—์„œ๋Š” ํ•„์š”ํ•œ ์‹œ์ ์— Up/Down ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์ ์šฉ(Up)ํ•˜๊ฑฐ๋‚˜ ๋˜๋Œ๋ฆด(Down) ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด์—์š”.

TypeORM Config ์„ค์ •

npm i @nestjs/config
Nest.js์— ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹ค์–‘ํ•œ config ์„ค์ •์„ ๋•๋Š” @nestjs/config ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜์—ฌ ์‚ฌ์šฉํ•œ๋‹ค.
config๊ฐ€ ์ถฉ๋Œ๋˜์ง€ ์•Š๊ณ , ์‚ฌ์šฉํ•˜๊ธฐ ํŽธํ•˜๊ฒŒ ํ•œ๋‹ค.
import { registerAs } from '@nestjs/config';
import { DataSource, DataSourceOptions } from 'typeorm';

const config = {
  type: 'postgres', // DB ํƒ€์ž…
  host: `${process.env.DB_HOST || 'localhost'}`,
  port: parseInt(`${process.env.DB_PORT || '5432'}`, 10),
  username: `${process.env.DB_USERNAME || 'test'}`,
  password: `${process.env.DB_PASSWORD || 'test'}`,
  database: `${process.env.DB_DATABASE || 'db_test'}`,
  // ๋ฒˆ๋“ค๋ง๋œ ํŒŒ์ผ์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋„๋ก ์„ค์ •
  entities: ['dist/**/**/*.entity{.ts,.js}'], 
  // ๋ฒˆ๋“ค๋ง๋œ ํŒŒ์ผ์˜ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํŒŒ์ผ์„ ๊ฐ€๋ฆฌํ‚ค๋„๋ก ์„ค์ •
  migrations: ['dist/migrations/*{.ts,.js}'],
  autoLoadEntities: true, // ์—”ํ‹ฐํ‹ฐ ํŒŒ์ผ์„ ์ž๋™์œผ๋กœ ๊ฐ์ง€ํ•˜์—ฌ ์ž„ํฌํŠธ
  synchronize: false,
};

export default registerAs('typeorm', () => config);
export const connectionSource = new DataSource(config as DataSourceOptions);
๊ฐ•์กฐ๋œ synchronize ์˜ต์…˜์€ nest.js ์„œ๋ฒ„๊ฐ€ ์‹œ์ž‘๋˜๋ฉด, ์‹ค์ œ DB์™€ ์—”ํ‹ฐํ‹ฐ ํŒŒ์ผ ๊ฐ„์— ๋ญ”๊ฐ€ ์•ˆ๋งž๋Š” ๋ถ€๋ถ„์ด ์žˆ์„ ๋•Œ(์ปฌ๋Ÿผ์ด ์ถ”๊ฐ€๋˜๊ฑฐ๋‚˜, ํ…Œ์ด๋ธ”์ด ์ถ”๊ฐ€๋˜๊ฑฐ๋‚˜ ํ•˜๋Š” ์ž‘์—…๋“ค์ด ๋ฐ˜์˜ ์•ˆ๋˜์–ด ์žˆ์„ ๋•Œ) ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์ž๋™์„ ๋งž์ถฐ์ค€๋‹ค.
ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” TypeORM ์„ค์ •์˜ synchronize ์˜ต์…˜์„ ์ง€์–‘ํ•ด์•ผํ•œ๋‹ค.
์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋ฐ์ดํ„ฐ ์†์‹ค์ด๋‚˜ ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ ์œ„ํ—˜์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ
synchronize ์˜ต์…˜์€ ์—”ํ‹ฐํ‹ฐ ์ฝ”๋“œ์™€ DB ์Šคํ‚ค๋งˆ๋ฅผ ์„œ๋ฒ„ ์‹œ์ž‘ ์‹œ ์ž๋™์œผ๋กœ ๋งž์ถฐ์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ํ•œ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ ๊ฐœ๋ฐœ ์ค‘์—” ํŽธ๋ฆฌํ•˜์ง€๋งŒ, ํ”„๋กœ๋•์…˜์—์„œ๋Š” ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ์œผ๋กœ ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ญ์ œ๋  ์œ„ํ—˜์ด ์žˆ์–ด ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ช…๋ น์–ด

{
  "scripts": {
    "typeorm": "ts-node ./node_modules/typeorm/cli",
    "migration:run": "npm run typeorm migration:run -- -d ./src/config/typeorm.ts",
    "migration:generate": "npm run typeorm -- -d ./src/config/typeorm.ts migration:generate ./src/migrations/$npm_config_name",
    "migration:create": "npm run typeorm -- migration:create ./src/migrations/$npm_config_name",
    "migration:revert": "npm run typeorm -- -d ./src/config/typeorm.ts migration:revert",
    "migration:show": "npm run typeorm -- -d ./src/config/typeorm.ts migration:show"
  }
}
  • migration:run: ๋Œ€๊ธฐ ์ค‘์ธ ๋ชจ๋“  ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    โ†’ ์‹ค์ œ๋กœ ์ƒ์„ฑ๋œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํŒŒ์ผ์„ DB์— ๋ฐ˜์˜ํ•ด์ค€๋‹ค.
    โ†’ DB์˜ ์ปฌ๋Ÿผ์ด ๋ฐ”๋€Œ์—ˆ๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์ด ์ƒˆ๋กœ ์ถ”๊ฐ€๋˜์—ˆ๊ฑฐ๋‚˜ ํ•˜๋Š” ๋ชจ๋“  ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์‹ค์ œ DB์— ๋ฐ˜์˜ํ•ด์ค€๋‹ค.
    ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์‹คํ–‰ํ•ด์„œ ์ƒ์„ฑ๋œ ํŒŒ์ผ์˜ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด,up ๊ณผ down ๋ฉ”์„œ๋“œ๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค. up๋ฉ”์„œ๋“œ๋Š” run์„ ํ–ˆ์„ ๋•Œ ์‹คํ–‰๋˜๊ณ , down ๋ฉ”์„œ๋“œ๋Š” revert๋ฅผ ํ–ˆ์„ ๋•Œ ์‹คํ–‰์ด ๋œ๋‹ค.
    Image Pn
  • migration:generate: ์—”ํ‹ฐํ‹ฐ์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ์˜ ์ฐจ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒˆ๋กœ์šด ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํŒŒ์ผ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    --name=Add_CreatedAt_To_Post ์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์˜ ์ด๋ฆ„์„ ๋ถ™์ผ ์ˆ˜ ์žˆ๋‹ค.
  • migration:create: ๋นˆ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํŒŒ์ผ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    โ†’ ์“ฐ๋Š” ์ด์œ : ๋‚ด๊ฐ€ ์›ํ•˜๋Š” SQL ์ปค๋งจ๋“œ๋ฅผ ์ง์ ‘ ์ž…๋ ฅํ•˜๊ณ , ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์ž๋™์œผ๋กœ ์ธ์‹ํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์„ ํ•˜๊ณ  ์‹ถ์ง€ ์•Š์„ ๋•Œ
  • migration:revert: ๊ฐ€์žฅ ์ตœ๊ทผ์— ์‹คํ–‰๋œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ๋˜๋Œ๋ฆฝ๋‹ˆ๋‹ค. โ†’ run์„ ํ†ตํ•ด์„œ ๋ฐ˜์˜๋œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ๋‹ค์‹œ ๋Œ๋ฆด ๋•Œ, ์ฆ‰ ๋ณต๊ตฌํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์ปค๋งจ๋“œ
  • migration:show: ์ ์šฉ๋œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜๊ณผ ๋Œ€๊ธฐ ์ค‘์ธ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ชฉ๋ก์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
    โ†’ ํ˜„์žฌ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด ์–ด๋””๊นŒ์ง€ ์ง„ํ–‰์ด ๋๊ณ , ์–ด๋””๋ถ€ํ„ฐ ์ง„ํ–‰์ด ๋˜์ง€ ์•Š์•˜๋Š”์ง€๋ฅผ ํ‘œ์‹œ

์ฃผ์˜์‚ฌํ•ญ

  • ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— ํ•ญ์ƒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋ฐฑ์—…ํ•˜์„ธ์š”.
  • ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์‹คํ–‰ํ•  ๋•Œ๋Š” ํŠนํžˆ ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํŒŒ์ผ์€ ๋ฒ„์ „ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์— ํฌํ•จ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • synchronize ์˜ต์…˜์„ false๋กœ ์„ค์ •ํ•œ ํ›„์—๋Š” ๋ชจ๋“  ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ํ†ตํ•ด ๊ด€๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ํ”„๋ก ํŠธ ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ๋ฐฑ์—”๋“œ 101 (NestJS, TypeORM) ๊ฐ•์˜
    • [์‹ค์Šต] TypeORM ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ - DB ์ปฌ๋Ÿผ ์ถ”๊ฐ€ ํ•ด๋ณด๊ธฐ