NestJS์ TypeORM 2026: ๋ง์ด๊ทธ๋ ์ด์ , ๊ด๊ณ ์ค์ , ๊ธฐ์ ๋ฉด์ ํต์ฌ ์ ๋ฆฌ
NestJS์ TypeORM์ ํ์ฉํ ๋ฐฑ์๋ ๊ฐ๋ฐ ์ค์ ๊ฐ์ด๋. ๋ง์ด๊ทธ๋ ์ด์ ๊ด๋ฆฌ, ๊ด๊ณ ๋ชจ๋ธ๋ง, ํธ๋์ญ์ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ๊ณผ ๊ธฐ์ ๋ฉด์ ์์ ์์ฃผ ์ถ์ ๋๋ ์ง๋ฌธ์ ๋ค๋ฃน๋๋ค.

2026๋ ํ์ฌ, NestJS์ TypeORM์ Node.js ๋ฐฑ์๋ ๊ฐ๋ฐ์์ ๊ฐ์ฅ ์์ ์ ์ด๊ณ ๋๋ฆฌ ์ฌ์ฉ๋๋ ์กฐํฉ ์ค ํ๋์ ๋๋ค. NestJS๋ ๋ชจ๋ ๊ธฐ๋ฐ ์ํคํ ์ฒ์ ์์กด์ฑ ์ฃผ์ ์ผ๋ก ํ์ฅ ๊ฐ๋ฅํ ๊ตฌ์กฐ๋ฅผ ์ ๊ณตํ๊ณ , TypeORM์ TypeScript์ ํ์ ์์ ์ฑ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ณ์ธต๊น์ง ํ์ฅํฉ๋๋ค. ๊ทธ๋ฌ๋ ํ๋ก๋์ ํ๊ฒฝ์์์ ๋ง์ด๊ทธ๋ ์ด์ ์ด์, ๋ณต์กํ ๊ด๊ณ ์ค๊ณ, ํธ๋์ญ์ ๊ด๋ฆฌ ๋ฑ ์ค๋ฌด์์ ๋ง์ฃผํ๋ ๊ณผ์ ๋ ์ ์ง ์์ต๋๋ค.
์ด ๊ธ์์๋ TypeORM ๋ง์ด๊ทธ๋ ์ด์ ์ ๋ต, ์ฃผ์ ๊ด๊ณ ํจํด, ํธ๋์ญ์ ๊ตฌํ ๋ฐฉ๋ฒ์ ๊ตฌ์ฒด์ ์ธ ์ฝ๋์ ํจ๊ป ์ค๋ช ํฉ๋๋ค. ๋ํ ๊ธฐ์ ๋ฉด์ ์์ NestJS์ TypeORM์ ๋ํด ์์ฃผ ์ถ์ ๋๋ ์ง๋ฌธ๊ณผ ๋ชจ๋ฒ ๋ต๋ณ๋ ํจ๊ป ๋ค๋ฃน๋๋ค.
TypeORM์ ๊ธฐ์กด์ ormconfig.json์ด๋ createConnection ๋ฐฉ์์ ๋์ฒดํ๋ DataSource API๋ฅผ ํ์ค์ผ๋ก ์ฑํํ์ต๋๋ค. ์ด ๊ธ์ ๋ชจ๋ ์ฝ๋ ์์ ๋ ํ์ฌ DataSource API๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํฉ๋๋ค. ๋ ๊ฑฐ์ ์ค์ ๋ฐฉ์์ ์ฌ์ฉํ๋ ํ๋ก์ ํธ๋ ๋ง์ด๊ทธ๋ ์ด์
์ ๊ถ์ฅํฉ๋๋ค.
NestJS์์ TypeORM ์ค์ ํ๊ธฐ
์ฒซ ๋ฒ์งธ ๋จ๊ณ๋ TypeORM CLI๊ฐ ์ฌ์ฉํ ๋ ๋ฆฝ์ ์ธ DataSource ์ค์ ํ์ผ์ ์์ฑํ๋ ๊ฒ์ ๋๋ค. ์ด ํ์ผ์ NestJS ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ์ ๋ถ๋ฆฌํ๋ฉด CLI ๋๊ตฌ๊ฐ ๋ ๋ฆฝ์ ์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์ ์์ ์ ์ํํ ์ ์์ต๋๋ค.
import { DataSource } from 'typeorm';
import { config } from 'dotenv';
config(); // Load .env variables
export default new DataSource({
type: 'postgres',
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT || '5432'),
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
entities: ['src/**/*.entity.ts'],
migrations: ['src/migrations/*.ts'],
synchronize: false,
});synchronize: false ์ค์ ์ ๋งค์ฐ ์ค์ํฉ๋๋ค. ๊ฐ๋ฐ ํ๊ฒฝ์์๋ synchronize: true๋ก ์คํค๋ง๋ฅผ ์๋ ๋๊ธฐํํ๊ณ ์ถ์ ์ ์์ง๋ง, ํ๋ก๋์
ํ๊ฒฝ์์ ์ด๋ฅผ ํ์ฑํํ๋ฉด ์ปฌ๋ผ ์ญ์ , ๋ฐ์ดํฐ ํ์
๋ณ๊ฒฝ ๋ฑ์ด ์๊ณ ์์ด ์คํ๋์ด ๋ฐ์ดํฐ ์์ค๋ก ์ด์ด์ง ์ ์์ต๋๋ค. ํ๋ก๋์
์์๋ ๋ฐ๋์ ๋ง์ด๊ทธ๋ ์ด์
์ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
AppModule์์๋ ConfigService๋ฅผ ํตํด ํ๊ฒฝ ๋ณ์๋ฅผ ์ฃผ์
ํ๊ณ TypeORM์ ๋น๋๊ธฐ๋ก ์ค์ ํฉ๋๋ค.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
type: 'postgres',
host: config.get('DB_HOST'),
port: config.getOrThrow<number>('DB_PORT'),
username: config.get('DB_USER'),
password: config.get('DB_PASSWORD'),
database: config.get('DB_NAME'),
autoLoadEntities: true, // Auto-register entities from feature modules
synchronize: false, // Always false in production
}),
}),
],
})
export class AppModule {}config.get()๊ณผ config.getOrThrow()์ ์ฐจ์ด์ ์ฃผ๋ชฉํด์ผ ํฉ๋๋ค. getOrThrow()๋ ํ๊ฒฝ ๋ณ์๊ฐ ์ ์๋์ง ์์ ๊ฒฝ์ฐ ์์ธ๋ฅผ ๋ฐ์์ํต๋๋ค. ๋ฐ์ดํฐ๋ฒ ์ด์ค ํฌํธ์ ๊ฐ์ ํ์ ์ค์ ๊ฐ์ ์ด ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ์ค์ ์ค๋ฅ๋ฅผ ์ ํ๋ฆฌ์ผ์ด์
์์ ์์ ์ ์ฆ์ ๊ฐ์งํ ์ ์์ต๋๋ค.
๋ง์ด๊ทธ๋ ์ด์ : ์์ฑ, ์คํ, ๋กค๋ฐฑ
๋ง์ด๊ทธ๋ ์ด์
์ ์ ๋ฌธ์ ์ธ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ด๋ฆฌ์ ํต์ฌ์
๋๋ค. ์คํค๋ง ๋ณ๊ฒฝ ์ด๋ ฅ์ ๋ฒ์ ๊ด๋ฆฌํ๊ณ ์ฌํ ๊ฐ๋ฅํ ๋ฐฉ์์ผ๋ก ์ ์ฉํ๊ฑฐ๋ ๋๋๋ฆด ์ ์์ต๋๋ค. ๋ค์ npm ์คํฌ๋ฆฝํธ๋ฅผ package.json์ ์ ์ํ๋ฉด TypeORM CLI ์์
์ด ๊ฐ์ํ๋ฉ๋๋ค.
{
"migration:generate": "typeorm-ts-node-commonjs migration:generate src/migrations/$npm_config_name -d src/config/typeorm.config.ts",
"migration:run": "typeorm-ts-node-commonjs migration:run -d src/config/typeorm.config.ts",
"migration:revert": "typeorm-ts-node-commonjs migration:revert -d src/config/typeorm.config.ts"
}์ค์ ์ํฌํ๋ก์ฐ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค. ๋จผ์ Entity๋ฅผ ์์ ํ ํ npm run migration:generate --name=AddUserTable์ ์คํํ๋ฉด ํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง์ Entity ์ ์ ๊ฐ์ ์ฐจ์ด๋ฅผ ๊ฐ์งํ์ฌ ๋ง์ด๊ทธ๋ ์ด์
ํ์ผ์ด ์๋ ์์ฑ๋ฉ๋๋ค. npm run migration:run์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์
์ ์ ์ฉํ๊ณ , npm run migration:revert๋ก ๊ฐ์ฅ ์ต๊ทผ ๋ง์ด๊ทธ๋ ์ด์
์ ๋๋๋ฆด ์ ์์ต๋๋ค.
ํ๋ก์ ํธ์์ ํํ ๋ฐ์ํ๋ ์ค์๋ migration:generate๊ฐ ์๋์ผ๋ก ์ฐจ์ด๋ฅผ ๊ฐ์งํด์ฃผ๋๋ฐ๋ ๋ง์ด๊ทธ๋ ์ด์
์ ์๋์ผ๋ก ์์ฑํ๋ ๊ฒ์
๋๋ค. ์๋ ๋ง์ด๊ทธ๋ ์ด์
์ด ํ์ํ ๊ฒฝ์ฐ๋ ์ปฌ๋ผ๋ช
๋ณ๊ฒฝ์ ๋ฐ๋ฅธ ๋ฐ์ดํฐ ์ด์ ์ฒ๋ผ ๋ฐ์ดํฐ ๋ณํ ๋ก์ง์ด ํฌํจ๋ ๋์ ํํฉ๋๋ค.
์ปฌ๋ผ๋ช
๋ณ๊ฒฝ์ ALTER RENAME ๋์ DROP + ADD๋ก ์์ฑ๋ ์ ์์ต๋๋ค. ์ด ๊ฒฝ์ฐ ๊ธฐ์กด ๋ฐ์ดํฐ๊ฐ ์์ค๋ฉ๋๋ค. up() ๋ฉ์๋์ SQL ๋ฌธ์ ๋ฐ๋์ ํ์ธํ๊ณ , ํ์ํ๋ฉด ์๋์ผ๋ก ์์ ํด์ผ ํฉ๋๋ค.
Entity์ ๊ด๊ณ ๋ชจ๋ธ๋ง
TypeORM์ ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ์ฌ TypeScript ํด๋์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ตฌ์กฐ๋ฅผ ๋งคํํฉ๋๋ค. ์ฃผ์ ๊ด๊ณ ์ ํ์ OneToMany/ManyToOne, ManyToMany, OneToOne์ ์ธ ๊ฐ์ง์ ๋๋ค.
OneToMany์ ManyToOne
์ฌ์ฉ์์ ์ฃผ๋ฌธ์ ๊ด๊ณ๋ @OneToMany์ @ManyToOne์ ๋ํ์ ์ธ ์ฌ์ฉ ์ฌ๋ก์
๋๋ค.
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, CreateDateColumn } from 'typeorm';
import { Order } from './order.entity';
@Entity('users')
export class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ unique: true })
email: string;
@Column()
name: string;
@OneToMany(() => Order, (order) => order.user)
orders: Order[]; // No DB column created here; relation lives on the Order side
@CreateDateColumn()
createdAt: Date;
}import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
import { User } from './user.entity';
@Entity('orders')
export class Order {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column('decimal', { precision: 10, scale: 2 })
total: number;
@Column({ default: 'pending' })
status: string;
@ManyToOne(() => User, (user) => user.orders, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'user_id' }) // Explicit FK column name
user: User;
}์ฌ๊ธฐ์ ํต์ฌ์ @OneToMany ์ชฝ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ปฌ๋ผ์ ์์ฑํ์ง ์๋๋ค๋ ์ ์
๋๋ค. ์ธ๋ ํค๋ ํญ์ @ManyToOne ์ชฝ์ ์์นํฉ๋๋ค. @JoinColumn ๋ฐ์ฝ๋ ์ดํฐ๋ก ์ธ๋ ํค ์ปฌ๋ผ๋ช
์ ๋ช
์์ ์ผ๋ก ์ง์ ํ๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ฐ๋
์ฑ์ด ํฅ์๋๊ณ ๋ค์ด๋ฐ ์ถฉ๋์ ๋ฐฉ์งํ ์ ์์ต๋๋ค. onDelete: 'CASCADE' ์ต์
์ ์ฌ์ฉ์๊ฐ ์ญ์ ๋ ๋ ๊ด๋ จ ์ฃผ๋ฌธ๋ ์๋์ผ๋ก ์ญ์ ๋๋๋ก ํฉ๋๋ค.
ManyToMany
์ํ๊ณผ ์นดํ ๊ณ ๋ฆฌ์ ๊ด๊ณ์๋ ManyToMany ๊ด๊ณ๊ฐ ์ ํฉํฉ๋๋ค. TypeORM์ ์ค๊ฐ ํ ์ด๋ธ์ ์๋์ผ๋ก ์์ฑํฉ๋๋ค.
import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable } from 'typeorm';
import { Category } from './category.entity';
@Entity('products')
export class Product {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
name: string;
@Column('decimal', { precision: 10, scale: 2 })
price: number;
@ManyToMany(() => Category, (category) => category.products)
@JoinTable({ name: 'product_categories' }) // Owns the junction table
categories: Category[];
}@JoinTable ๋ฐ์ฝ๋ ์ดํฐ๋ ๊ด๊ณ์ ์์ ์ธก์ ๋ํ๋
๋๋ค. ํ์ชฝ์๋ง ์ด ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ๋ฐฐ์นํ ์ ์์ต๋๋ค. ์ค๊ฐ ํ
์ด๋ธ์ ์ ๋ ฌ ์์ ๋ฑ ์ถ๊ฐ ์ปฌ๋ผ์ด ํ์ํ ๊ฒฝ์ฐ, ๋ณ๋์ Entity๋ฅผ ์์ฑํ๊ณ ๋ ๊ฐ์ @ManyToOne ๊ด๊ณ๋ก ๊ตฌ์ฑํด์ผ ํฉ๋๋ค.
Node.js / NestJS ๋ฉด์ ์ค๋น๊ฐ ๋์ จ๋์?
์ธํฐ๋ํฐ๋ธ ์๋ฎฌ๋ ์ดํฐ, flashcards, ๊ธฐ์ ํ ์คํธ๋ก ์ฐ์ตํ์ธ์.
Repository ํจํด๊ณผ Feature Module
NestJS๋ Feature Module์ ํตํ ๋ชจ๋ํ๋ฅผ ๊ถ์ฅํฉ๋๋ค. ๊ฐ ๋ชจ๋์ TypeOrmModule.forFeature()๋ก Entity๋ฅผ ๋ฑ๋กํ๊ณ , ํด๋น Repository์ ์ ๊ทผํ ์ ์๊ฒ ๋ฉ๋๋ค.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Order } from '../entities/order.entity';
import { OrdersService } from './orders.service';
import { OrdersController } from './orders.controller';
@Module({
imports: [TypeOrmModule.forFeature([Order])],
providers: [OrdersService],
controllers: [OrdersController],
})
export class OrdersModule {}Service์์๋ @InjectRepository๋ฅผ ํตํด Repository๊ฐ ์ฃผ์
๋ฉ๋๋ค. ๋จ์ํ ์ฟผ๋ฆฌ์๋ Repository API๊ฐ ์ ํฉํ๊ณ , ๋ณต์กํ ์ฟผ๋ฆฌ์๋ QueryBuilder๋ฅผ ์ฌ์ฉํฉ๋๋ค.
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Order } from '../entities/order.entity';
@Injectable()
export class OrdersService {
constructor(
@InjectRepository(Order)
private readonly orderRepo: Repository<Order>,
) {}
async findByUser(userId: string): Promise<Order[]> {
return this.orderRepo.find({
where: { user: { id: userId } },
relations: ['user'], // Eager-load the user relation
order: { createdAt: 'DESC' }, // Most recent first
});
}
async findWithFilters(status: string, minTotal: number): Promise<Order[]> {
// QueryBuilder for complex queries
return this.orderRepo
.createQueryBuilder('order')
.leftJoinAndSelect('order.user', 'user')
.where('order.status = :status', { status })
.andWhere('order.total >= :minTotal', { minTotal })
.orderBy('order.total', 'DESC')
.getMany();
}
}findByUser ๋ฉ์๋๋ ์ค์ฒฉ๋ Where ์กฐ๊ฑด์ผ๋ก ๊ด๊ณ๋ฅผ ํํฐ๋งํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ค๋๋ค. relations ๋ฐฐ์ด์ ์ด๋ค ๊ด๊ณ๋ฅผ ๋ก๋ํ ์ง ์ ์ดํฉ๋๋ค. ์ด ์ค์ ์ด ์์ผ๋ฉด TypeORM์ ์ฃผ๋ฌธ ๋ฐ์ดํฐ๋ง ๋ฐํํ๊ณ ์ฌ์ฉ์ ์ ๋ณด๋ ํฌํจํ์ง ์์ต๋๋ค.
findWithFilters์ QueryBuilder๋ ๋ ์ธ๋ฐํ ์ ์ด๋ฅผ ์ ๊ณตํฉ๋๋ค. leftJoinAndSelect๋ ๊ด๊ณ๋ฅผ ๋ก๋ํ์ฌ ๊ฒฐ๊ณผ์ ํฌํจ์ํค๊ณ , ๋งค๊ฐ๋ณ์ํ๋ ์ฟผ๋ฆฌ๋ฅผ ํตํด SQL ์ธ์ ์
์ ํ์คํ๊ฒ ๋ฐฉ์งํฉ๋๋ค.
ํธ๋์ญ์ ์ ์ฌ๋ฐ๋ฅธ ๊ตฌํ
์ฌ๋ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์
์ ์์์ ์ผ๋ก ์คํํด์ผ ํ๋ ๊ฒฝ์ฐ, ํธ๋์ญ์
์ ํ์์ ์
๋๋ค. TypeORM์ DataSource.transaction() ๋ฉ์๋๋ฅผ ์ ๊ณตํฉ๋๋ค.
async createOrderWithItems(
userId: string,
items: { productId: string; quantity: number }[],
dataSource: DataSource,
): Promise<Order> {
return dataSource.transaction(async (manager) => {
// All operations use the transactional manager
const order = manager.create(Order, {
user: { id: userId },
total: 0,
status: 'pending',
});
const savedOrder = await manager.save(order);
let total = 0;
for (const item of items) {
const product = await manager.findOneByOrFail(Product, { id: item.productId });
total += product.price * item.quantity;
await manager.save(OrderItem, {
order: savedOrder,
product,
quantity: item.quantity,
unitPrice: product.price,
});
}
savedOrder.total = total;
return manager.save(savedOrder);
});
}ํธ๋์ญ์
๋ด์์๋ ๋ชจ๋ ์์
์ ๋ฐ๋์ manager๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค. ์ผ๋ฐ Repository๋ฅผ ์ฌ์ฉํ๋ฉด ํด๋น ์์
์ ํธ๋์ญ์
์ธ๋ถ์์ ์คํ๋๋ฏ๋ก, ์ดํ ๋จ๊ณ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํด๋ ๋กค๋ฐฑ๋์ง ์์ ๋ฐ์ดํฐ ๋ถ์ผ์น๊ฐ ๋ฐ์ํฉ๋๋ค. ์ฝ๋ฐฑ ๋ด์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ์ ์ฒด ํธ๋์ญ์
์ด ์๋์ผ๋ก ๋กค๋ฐฑ๋ฉ๋๋ค.
DataSource.transaction()์ ๋จ์ํ ๋ค๋จ๊ณ ์์
์ ์ ํฉํฉ๋๋ค. QueryRunner๋ ์ปค๋ฐ/๋กค๋ฐฑ ์์ ๊ณผ ์ธ์ด๋ธํฌ์ธํธ๋ฅผ ์๋์ผ๋ก ์ ์ดํ ์ ์์ด, ๋ฐฐ์น ์ฒ๋ฆฌ์์์ ๋ถ๋ถ ๋กค๋ฐฑ ๋ฑ ๋ ์ ๋ฐํ ์ ์ด๊ฐ ํ์ํ ๊ฒฝ์ฐ์ ์ฌ์ฉํฉ๋๋ค.
NestJS์ TypeORM ๊ธฐ์ ๋ฉด์ ํต์ฌ ์ง๋ฌธ
๊ธฐ์ ๋ฉด์ ์์ NestJS์ TypeORM์ ๊ดํ ์ง๋ฌธ์ ์ํคํ ์ฒ ์ดํด, ์ค๋ฌด ๋ฌธ์ ํด๊ฒฐ ๋ฅ๋ ฅ, ๊ทธ๋ฆฌ๊ณ ํํ ์ค์์ ๋ํ ์ธ์์ด๋ผ๋ ์ธ ๊ฐ์ง ์์ญ์ผ๋ก ๋๋ฉ๋๋ค.
ํ๋ก๋์
์์ synchronize๋ฅผ ๋นํ์ฑํํด์ผ ํ๋ ์ด์ ๋ ๋ฌด์์
๋๊น?
synchronize: true๋ก ์ค์ ํ๋ฉด ์ ํ๋ฆฌ์ผ์ด์
์์ ์ Entity ์ ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง๊ฐ ์๋์ผ๋ก ๋๊ธฐํ๋ฉ๋๋ค. ์ด ๊ณผ์ ์์ ์ปฌ๋ผ ์ญ์ , ๋ฐ์ดํฐ ํ์
๋ณ๊ฒฝ, ํ
์ด๋ธ ์ญ์ ๊ฐ ๊ธฐ๋ก ์์ด ์ํ๋ ์ ์์ผ๋ฉฐ, ํ๋ก๋์
ํ๊ฒฝ์์๋ ๋ฐ์ดํฐ ์์ค ์ํ์ด ์์ต๋๋ค. ๋ง์ด๊ทธ๋ ์ด์
์ ์ฌ์ฉํ๋ฉด ์คํค๋ง ๋ณ๊ฒฝ์ด ๋ฒ์ ๊ด๋ฆฌ๋๊ณ , ์ฌํ ๊ฐ๋ฅํ๋ฉฐ, ๋๋๋ฆด ์ ์์ต๋๋ค.
migration:generate์ migration:create์ ์ฐจ์ด์ ์ ๋ฌด์์
๋๊น?
migration:generate๋ ํ์ฌ Entity ์ ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง๋ฅผ ๋น๊ตํ์ฌ ์ฐจ์ด๋ฅผ ๊ฐ์งํ๊ณ ํ์ํ SQL ๋ฌธ์ ์๋ ์์ฑํฉ๋๋ค. ๋ฐ๋ฉด migration:create๋ ๋น ๋ง์ด๊ทธ๋ ์ด์
ํ์ผ์ ์์ฑํ๋ฉฐ, SQL ๋ฌธ์ ์๋์ผ๋ก ์์ฑํฉ๋๋ค. ํ์๋ ๋ฐ์ดํฐ ์ด์ ์ด๋ ์๋ ๋ฐ์ดํฐ ์ฝ์
๋ฑ ์คํค๋ง ๋ณ๊ฒฝ ์ด์ธ์ ์์
์ ์ฌ์ฉ๋ฉ๋๋ค.
OneToMany/ManyToOne ๊ด๊ณ์์ ์ธ๋ ํค๋ ์ด๋ ์ชฝ์ ์์นํฉ๋๊น?
์ธ๋ ํค๋ ํญ์ @ManyToOne ์ชฝ์ ์์นํฉ๋๋ค. @OneToMany ์ชฝ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ปฌ๋ผ์ ์์ฑํ์ง ์์ผ๋ฉฐ, TypeScript์์ ์ญ๋ฐฉํฅ ์ฐธ์กฐ๋ฅผ ํํํ๊ธฐ ์ํ ์ฉ๋๋ก๋ง ์ฌ์ฉ๋ฉ๋๋ค.
Repository API์ QueryBuilder๋ ์ด๋ป๊ฒ ๊ตฌ๋ถํ์ฌ ์ฌ์ฉํฉ๋๊น?
Repository API(find, findOne, save)๋ ๋จ์ํ CRUD ์์
๊ณผ ์ง์ ์ ์ธ ํํฐ ์กฐ๊ฑด์ ์ฟผ๋ฆฌ์ ์ ํฉํฉ๋๋ค. QueryBuilder๋ ๋ณต์กํ JOIN, ์๋ธ์ฟผ๋ฆฌ, ์ง๊ณ ํจ์, ๋์ ์ผ๋ก ๊ตฌ์ฑ๋๋ ์ฟผ๋ฆฌ๊ฐ ํ์ํ ๊ฒฝ์ฐ์ ์ฌ์ฉํฉ๋๋ค.
๋ชจ๋ ๊ฐ ์ํ ์์กด์ฑ์ ์ด๋ป๊ฒ ํด๊ฒฐํฉ๋๊น?
NestJS๋ ์ํ ์์กด์ฑ ํด๊ฒฐ์ ์ํด forwardRef()๋ฅผ ์ ๊ณตํฉ๋๋ค. ๊ทธ๋ฌ๋ ์ด ํจ์์ ์ฌ์ฉ์ ์ํคํ
์ฒ ๋ฌธ์ ๋ฅผ ์์ฌํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. ๊ณตํต ๋ก์ง์ ๋
๋ฆฝ์ ์ธ ๋ชจ๋๋ก ๋ถ๋ฆฌํ๋ ๊ฒ์ด ๋ ๋ฐ๋์งํ ์ ๊ทผ ๋ฐฉ์์
๋๋ค.
ํธ๋์ญ์ ๋ด์์ ์ผ๋ฐ Repository๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๋ป๊ฒ ๋ฉ๋๊น?
์ผ๋ฐ Repository๋ฅผ ํตํ ์์ ์ ํธ๋์ญ์ ์ธ๋ถ์์ ์คํ๋ฉ๋๋ค. ํด๋น ๋ณ๊ฒฝ ์ฌํญ์ ์ฆ์ ์ปค๋ฐ๋๋ฉฐ, ์ค๋ฅ ๋ฐ์ ์์๋ ๋กค๋ฐฑ๋์ง ์์ต๋๋ค. ์ด๋ ๋ฐ์ดํฐ ๋ถ์ผ์น์ ์์ธ์ด ๋ฉ๋๋ค.
Node.js / NestJS ๋ฉด์ ์ค๋น๊ฐ ๋์ จ๋์?
์ธํฐ๋ํฐ๋ธ ์๋ฎฌ๋ ์ดํฐ, flashcards, ๊ธฐ์ ํ ์คํธ๋ก ์ฐ์ตํ์ธ์.
๊ฒฐ๋ก
NestJS์ TypeORM์ ์กฐํฉ์ ํ์ ์์ ํ Node.js ๋ฐฑ์๋ ๊ฐ๋ฐ์ ์ํ ์ฑ์ํ ์ํ๊ณ๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด ๊ธ์ ํต์ฌ ๋ด์ฉ์ ์ ๋ฆฌํฉ๋๋ค.
- ๋ง์ด๊ทธ๋ ์ด์
์ ๋ก์ปฌ ๊ฐ๋ฐ์ ์ ์ธํ ๋ชจ๋ ํ๊ฒฝ์์
synchronize๋ฅผ ๋์ฒดํด์ผ ํฉ๋๋ค - ๊ด๊ณ ์ค๊ณ์์๋ ์ด๋ ์ชฝ์ด ์ธ๋ ํค๋ฅผ ๋ณด์ ํ๋์ง ์ ํํ ์ดํดํ๋ ๊ฒ์ด ํ์์ ์ ๋๋ค
- ํธ๋์ญ์ ์์๋ ์ผ๋ฐ Repository๊ฐ ์๋ ๋ฐ๋์ Manager๋ฅผ ํตํด ์์ ์ ์ํํด์ผ ํฉ๋๋ค
- QueryBuilder๋ Repository API๋ก ๋์ํ ์ ์๋ ๋ณต์กํ ์ฟผ๋ฆฌ๋ฅผ ์ํ ๋ณด์ ๋๊ตฌ์ ๋๋ค
- ๊ธฐ์ ๋ฉด์ ์์๋ ๋ฌธ๋ฒ ์ง์๋ฟ ์๋๋ผ, ์ํคํ ์ฒ ๊ฒฐ์ ์ ๊ทผ๊ฑฐ๋ฅผ ์ค๋ช ํ ์ ์๋ ๋ฅ๋ ฅ์ด ์ค์ํฉ๋๋ค
์ฐ์ต์ ์์ํ์ธ์!
๋ฉด์ ์๋ฎฌ๋ ์ดํฐ์ ๊ธฐ์ ํ ์คํธ๋ก ์ง์์ ํ ์คํธํ์ธ์.
๊ณต์
๊ด๋ จ ๊ธฐ์ฌ

2026๋ NestJS์ GraphQL ์๋ฒฝ ๊ฐ์ด๋: ์คํค๋ง ์ค๊ณ, ๋ฆฌ์กธ๋ฒ ๊ตฌํ, ๋ฉด์ ๋๋น
NestJS 11์์ GraphQL ํตํฉ์ ๋ค๋ฃน๋๋ค. ์ฝ๋ ํผ์คํธ์ ์คํค๋ง ํผ์คํธ ์ค๊ณ ๋ฐฉ์, ๋ฆฌ์กธ๋ฒ ํจํด, DataLoader๋ฅผ ํ์ฉํ N+1 ๋ฌธ์ ํด๊ฒฐ, ์ธ์ฆ ๋ฐ ์ธ๊ฐ ๊ตฌํ์ ํ์ตํฉ๋๋ค.

Node.js 24 ํต์ฌ ๊ธฐ๋ฅ ์์ ๋ถ์: URLPattern, ํผ๋ฏธ์ ๋ชจ๋ธ, ๋ฉด์ ๋๋น ๊ฐ์ด๋ (2026๋ ํ)
Node.js 24 LTS(Krypton)์ ์ฃผ์ ์ ๊ธฐ๋ฅ์ธ URLPattern, ํผ๋ฏธ์ ๋ชจ๋ธ, ๋ช ์์ ๋ฆฌ์์ค ๊ด๋ฆฌ๋ฅผ ์ค์ ์ฝ๋ ์์ ์ ๋ฉด์ ๋๋น ์ง๋ฌธ์ผ๋ก ์์ธํ๊ฒ ๋ถ์ํ๋ค.

2026๋ NestJS ๋ง์ดํฌ๋ก์๋น์ค: ์ํคํ ์ฒ, gRPC, ๋ฉด์ ์ง๋ฌธ ์๋ฒฝ ๊ฐ์ด๋
NestJS ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ์ ํต์ฌ ๊ฐ๋ , gRPC ํธ๋์คํฌํธ ๊ตฌ์ฑ, ์คํธ๋ฆฌ๋ฐ ํจํด, ์์ ์ฑ ํจํด, ๋ฉด์ ๋น์ถ ์ง๋ฌธ์ ์ค๋ฌด ์ค์ฌ์ผ๋ก ๋ค๋ฃน๋๋ค.