TypeORM 정리글
https://velog.io/@pi1199/TypeORM
쿼리 최적화 진행시 최적화 결과가 얼마나 개선 되었는지 측정하기 위해서는 더미 데이터가 필요합니다.
개선하기 이전에 더미데이터를 통해 쿼리 성능을 측정하고, 개선 후에도 같은 데이터로 측정하고자 합니다.
유의미한 결과를 얻기 위해서는 많은 양의 데이터가 필요합니다.
데이터가 적다면 그만큼 성능 변화량도 적기에 이것이 개선 되었는지, 단순히 네트워크나 순간 서버의 성능 문제인지 측정하기가 어렵습니다. 따라서 더미 데이터가 필요합니다.
이렇게 db에 더미 데이터를 미리 삽입하는 것을 seeding이라 합니다.
DB에 직접 추가하는 방법은 테이블 간의 관계를 명확하게 할 수 없습니다. 그리고 서비스 로직을 직접 이용하기에는 관계된 테이블을 순서를 지켜 만들어야 하고, 각 데이터를 만드는데 DB 접근이 필요하므로 매우 느린 작업입니다.
NestJS 환경에서 TypeORM을 활용한다면, Seed 작업에서도 동일한 방식으로 Repository를 주입받아 사용할 수 있습니다.
그러나 Seed 작업은 일반적으로 서비스 로직과는 분리된 독립적인 스크립트로 작성됩니다. 이 때문에 @InjectRepository와 같은 의존성 주입 기능을 사용할 수 없으며, 대신 DataSource를 직접 초기화해서 Repository를 가져오는 방식으로 동작합니다.
Seed 작업은 두가지 방법으로 진행할 수 있습니다.
1. Seed 작업을 NestJS 모듈로 관리하는 방법
2. 독립적인 스크립트를 작성하는 방법
저는 쿼리 최적화를 진행하기 위해서 로컬 환경에서 Seed 작업을 진행하고자 하였습니다. 그렇기에
또한, 이 Seed작업은 더미 데이터이기 때문에 실제 서비스 로직에는 포함되지 않아야 합니다.
따라서 별도의 스크립트를 작성하여 더미 데이터를 추가하기로 하였습니다.
위에서 선택한 대로 독립적인 스크립트를 작성하기로 하였습니다.
가장 먼저 애플리케이션과 독립적으로 동작하기 때문에 DB설정이 별도로 필요합니다.
//src/seeds/datasource.ts
import 'reflect-metadata';
import { DataSource } from 'typeorm';
import { Applicant } from '../entity/applicant.entity';
import { Summary } from '../entity/summary.entity';
import { Tag } from '../entity/tag.entity';
import { Ticle } from '../entity/ticle.entity';
import { User } from '../entity/user.entity';
// 데이터 소스 초기화
export const AppDataSource = new DataSource({
type: 'mysql', // 사용할 데이터베이스 종류
host: 'localhost', // 데이터베이스 호스트
port: 3306, // 데이터베이스 포트
username: 'root', // 사용자 이름
password: 'password', // 비밀번호
database: 'ticle_test', // 데이터베이스 이름
synchronize: true, // 개발 중에는 true로 설정 (생성된 엔티티에 따라 테이블을 동기화)
logging: true, // 쿼리 로깅
entities: [User, Ticle, Tag, Applicant, Summary], // 사용할 엔티티 목록
});
두번째로는 각각의 데이터를 삽입하는 로직을 실행하기 위한 스크립트가 필요합니다. (main 함수 역할)
또한, 이 스크립트를 실행하기 위한 명령어도 작성해 주었습니다.
// pacakage.json
"scripts": {
...
"seed": "ts-node src/seeds/seed.ts"
},
//src/seeds/seed.ts
import { seedApplicants } from './applicant.seeder';
import { AppDataSource } from './datasource';
import { seedTags } from './tag.seeder';
import { seedTicles } from './ticle.seeder';
import { seedUsers } from './user.seeder';
const seedDatabase = async () => {
try {
// 데이터 소스 초기화
await AppDataSource.initialize();
console.log('Database connected successfully!');
// 데이터를 Seed
await seedUsers(AppDataSource, 1000);
await seedTicles(AppDataSource, 100_000);
await seedTags(AppDataSource, 100);
await seedApplicants(AppDataSource, 1000);
console.log('Seeding completed!');
process.exit(0);
} catch (error) {
console.error('Error seeding database:', error);
process.exit(1);
}
};
seedDatabase();
세번째로는 각 테이블을 삽입하기 위한 함수가 필요합니다.
예시로 하나만 보겠습니다.
faker 라이브러리를 이용하여 더미 데이터를 생성하도록 하였습니다.
https://www.npmjs.com/package/@faker-js/faker
// src/seeds/user.seeder.ts
import { faker } from '@faker-js/faker';
import { DataSource } from 'typeorm';
import { User } from '../entity/user.entity';
export const seedUsers = async (dataSource: DataSource, count: number) => {
const userRepository = dataSource.getRepository(User);
for (let i = 0; i < count; i++) {
const user = userRepository.create({
username: faker.internet.userName(),
password: faker.internet.password(),
nickname: faker.name.firstName(),
email: faker.internet.email(),
introduce: faker.lorem.sentence(),
profileImageUrl: faker.image.avatar(),
provider: 'local',
});
await userRepository.save(user);
}
console.log('Users seeded!');
};
위와 같이 더미 데이터를 생성할 수 있습니다.
이때, 주의할 점은 관계를 포함할 수 있기 때문에 순서에 유의해야 합니다.