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 |
인덱스 |