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 컬럼 μΆ”κ°€ 해보기