Tan Kim

typeorm

TypeORM

TypeScript/Node.js용 ORM. 데코레이터로 엔티티를 정의하고, Repository 패턴으로 DB를 조작한다. MySQL, PostgreSQL, SQLite 등 지원.

엔티티 정의

import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, ManyToOne, OneToMany } from 'typeorm'
 
@Entity('users')
export class User {
  @PrimaryGeneratedColumn()
  id: number
 
  @Column({ unique: true })
  email: string
 
  @Column({ length: 100 })
  name: string
 
  @CreateDateColumn()
  createdAt: Date
 
  @OneToMany(() => Order, (order) => order.user)
  orders: Order[]
}
 
@Entity('orders')
export class Order {
  @PrimaryGeneratedColumn()
  id: number
 
  @Column()
  total: number
 
  @Column({ default: 'pending' })
  status: string
 
  @ManyToOne(() => User, (user) => user.orders)
  user: User
}

DataSource 설정

import { DataSource } from 'typeorm'
 
export const AppDataSource = new DataSource({
  type: 'mysql',
  host: process.env.DB_HOST,
  port: 3306,
  username: process.env.DB_USER,
  password: process.env.DB_PASS,
  database: process.env.DB_NAME,
  entities: [User, Order],
  migrations: ['src/migrations/*.ts'],
  synchronize: false,  // 프로덕션에서는 반드시 false
  logging: false,
})
 
await AppDataSource.initialize()

Repository 쿼리

const userRepo = AppDataSource.getRepository(User)
 
// 단건 조회
const user = await userRepo.findOneBy({ id: 1 })
const user = await userRepo.findOne({
  where: { email: 'test@example.com' },
  relations: ['orders'],
})
 
// 목록 조회
const users = await userRepo.find({
  where: { name: Like('%김%') },
  order: { createdAt: 'DESC' },
  take: 10,
  skip: 0,
})
 
// 생성
const user = userRepo.create({ email: 'test@example.com', name: '김탄' })
await userRepo.save(user)
 
// 수정
await userRepo.update({ id: 1 }, { name: '이름변경' })
 
// 삭제
await userRepo.delete({ id: 1 })
 
// 카운트
const count = await userRepo.count({ where: { status: 'active' } })

QueryBuilder

복잡한 쿼리에 사용.

const result = await AppDataSource
  .getRepository(User)
  .createQueryBuilder('user')
  .leftJoinAndSelect('user.orders', 'order')
  .where('user.id = :id', { id: 1 })
  .andWhere('order.status = :status', { status: 'paid' })
  .orderBy('order.createdAt', 'DESC')
  .getMany()
 
// 집계
const stats = await AppDataSource
  .getRepository(Order)
  .createQueryBuilder('order')
  .select('SUM(order.total)', 'total')
  .addSelect('COUNT(*)', 'count')
  .where('order.status = :status', { status: 'paid' })
  .getRawOne()

트랜잭션

// QueryRunner 방식 (권장 — 세밀한 제어 가능)
const queryRunner = AppDataSource.createQueryRunner()
await queryRunner.connect()
await queryRunner.startTransaction()
 
try {
  await queryRunner.manager.save(User, { name: '김탄' })
  await queryRunner.manager.save(Order, { total: 1000, userId: 1 })
  await queryRunner.commitTransaction()
} catch (err) {
  await queryRunner.rollbackTransaction()
} finally {
  await queryRunner.release()
}
 
// 간단한 트랜잭션
await AppDataSource.transaction(async (manager) => {
  await manager.save(User, { name: '김탄' })
  await manager.save(Order, { total: 1000, userId: 1 })
})

마이그레이션

# 마이그레이션 생성 (엔티티 변경 감지)
npx typeorm migration:generate src/migrations/AddPhoneToUser -d src/data-source.ts
 
# 마이그레이션 실행
npx typeorm migration:run -d src/data-source.ts
 
# 마이그레이션 되돌리기
npx typeorm migration:revert -d src/data-source.ts

주요 데코레이터

데코레이터 설명
@Entity 테이블 매핑
@PrimaryGeneratedColumn AUTO_INCREMENT PK
@Column 일반 컬럼
@CreateDateColumn 생성일 자동 기록
@UpdateDateColumn 수정일 자동 기록
@DeleteDateColumn 소프트 딜리트 날짜
@OneToMany / @ManyToOne 1:N 관계
@ManyToMany N:M 관계
@Index 인덱스

메모