본문 바로가기
NestJs

NestJs 환경에서 faker (더미데이터 w/ seeder) 사용하기

by 새싹개발자1호 2024. 7. 19.

오늘은 더미데이터를 생성하기 위해 사용되는 Faker 에 대해 알아보려고한다

시제품을 개발하거나, 단위 테스트를 작성할 때 가짜 데이터가 필요할 때가 자주 있습니다.
이럴 때, 직접 가짜 데이터를 하드코딩 할 수도 있겠지만, 좀더 편하고 빠르게 가짜 데이터를 
얻을 수 있는 방법이 바로 Faker.js라는 라이브러리입니다.

NestJS에서 더미데이터 생성(with seeder)

Nest에서 searching(검색), sortring(정렬) 및 pagination을 구현하는데 있어 직접 생성해 본 유저 
데이터로는 원하는 테스트를 해보는 것이 부족하다고 판단하였다.
그렇다고 직접 어떤 데이터를 생성하는 것은 비효율 적이라고 판단이 들고,
더욱이나 협업간 개발속도에 따라 테스팅을 하기 힘든 상황이 올수도 있다
그래서 원할한 테스팅을 위한 더미데이터를 생성하는 법을 알아보기로 하자.

공식 라이브러리문서

https://www.npmjs.com/package/nestjs-seeder

https://github.com/faker-js/faker

패키지 설치

npm i faker

패키지 임포트

ES 모듈을 사용하는 프로젝트에서는 import 키워드를 사용해서 패키지를  임포트합니다.

import faker from "faker";

 

단계별 진행

실제로 진행했던 프로젝트를 예를들어서 진행할려고한다
먼저  Trello clone coding 프로젝트때 사용하였던 Board 엔티티

import { IsHexColor, IsNotEmpty, IsNumber, IsString } from 'class-validator';
import {
  Column,
  CreateDateColumn,
  DeleteDateColumn,
  Entity,
  OneToMany,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
} from 'typeorm';
import { BoardMembers } from './board-member.entity';
import { MESSAGES_CONSTANT } from 'src/constants/messages.constants';
import { List } from 'src/lists/entities/list.entity';

@Entity('boards')
export class Board {
  @PrimaryGeneratedColumn({ unsigned: true })
  id: number;

  @IsNumber()
  @Column({ unsigned: true })
  ownerId: number;

  /**
   * 보드 이름
   * @example "보드1번"
   */
  @IsString()
  @IsNotEmpty({ message: MESSAGES_CONSTANT.BOARD.COMMON.NAME.REQUIRED })
  @Column()
  name: string;

  /**
   * 색상
   * @example "#FF0000"
   */
  @IsHexColor({ message: MESSAGES_CONSTANT.BOARD.COMMON.BACKGROUND_COLOR.INVALID_TYPE })
  @IsNotEmpty({ message: MESSAGES_CONSTANT.BOARD.COMMON.BACKGROUND_COLOR.REQUIRED })
  @Column()
  background_color: string;

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;

  @DeleteDateColumn({select:false})
  deletedAt: Date;

  @OneToMany(() => BoardMembers, (boardMembers) => boardMembers.board)
  members: BoardMembers[];

  @OneToMany(() => List, (list) => list.board, { cascade: true })
  lists: List[];
}

사용을 위한 디렉토리 생성

app 모듈에서 Board 엔티티를 등록해주고, Faker 사용을 위한 Seeding 디렉토리를 생성해준후
하위폴더로 factories와 seeds 디렉토리를 생성해주며, 실제 DB 와 연결하기 위해 seeds.ts폴더도 생성해주도록한다. // 이렇게 데릭토리를 분리를 한 이유는, 여러종류의 더미데이터를 생성함과 동시에 추후를 위해
구분짓기 위해서 구별하였다.

 

실제 Board 더미데이터를 생성하기 위해  factories 에서 Board.factory.ts 를 생성

import { setSeederFactory } from 'typeorm-extension';

import { Faker } from '@faker-js/faker';
import { Board } from 'src/board/entities/board.entity';

export default setSeederFactory(Board, (faker: Faker) => {
  const board = new Board();
  board.name = faker.lorem.words(3);
  board.background_color = '#FF0000';
  return board;
});

위 코드를 보면 실제 DB에 필요한 요소인 board.name 과 board.background_color 에 대해 값들을 정의해줬는대
Name 부분을 자세히 보면 faker.lorem.words(3)이 보이는데

  • lorem: Lorem은 주로 더미 텍스트를 생성하는 모듈입니다. 흔히 사용되는 "Lorem ipsum" 텍스트처럼, 임의의 단어 또는 문장을 생성합니다.
  • words(3): words 함수는 지정된 수의 단어를 생성합니다. 여기서 3은 세 단어를 생성하라는 의미입니다.

이것 외에도 공식문서를 참조하면 많은 메서드를 알수있으니 항시 Faker를 사용할땐 문서를 꼭 읽어보면서 해야한다

Seeds 파일을 통해 위 Factory에서 정의한 보드들을 생성해보자

import { BoardMembers } from 'src/board/entities/board-member.entity';
import { Board } from 'src/board/entities/board.entity';
import { User } from 'src/user/entities/user.entity';
import { DataSource } from 'typeorm';
import { Seeder, SeederFactoryManager } from 'typeorm-extension';
export default class BoardSeeder implements Seeder {
  public async run(dataSource: DataSource, factoryManager: SeederFactoryManager): Promise<void> {
    const userRepository = dataSource.getRepository(User);
    const users = await userRepository.find();

    const boardFactory = factoryManager.get(Board);
    const boards = await boardFactory.saveMany(5); // 여러 보드를 생성

    const boardMemberRepository = dataSource.getRepository(BoardMembers);
    for (const board of boards) {
      board.ownerId = users[0].id; // 첫 번째 유저를 소유자로 설정
      await dataSource.getRepository(Board).save(board);

      // 모든 유저를 해당 보드의 멤버로 추가
      for (const user of users) {
        const boardMember = new BoardMembers();
        boardMember.user = user;
        boardMember.board = board;
        await boardMemberRepository.save(boardMember);
      }
    }
  }
}

 

이렇게 Factory 에서 정의한 데이터를 seeding 을 통해 실제로 생성하는 모습을 볼수있다

생성을하였으면? 당연히 DB 에도 저장해보도록 해보자

import 'reflect-metadata';
import { DataSource, DataSourceOptions } from 'typeorm';
import { runSeeders, SeederOptions } from 'typeorm-extension';
import UserSeeder from './seeds/user.seeder';
import UserFactory from './factories/user.factory';
import { User } from 'src/user/entities/user.entity';
import { BoardMembers } from 'src/board/entities/board-member.entity';
import { Board } from 'src/board/entities/board.entity';
import { List } from 'src/lists/entities/list.entity';
import { Card } from 'src/cards/entities/card.entity';
import { Activity } from 'src/activity/entities/activity.entity';
import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
import BoardSeeder from './seeds/board.seeder';
import boardFactory from './factories/board.factory';
import { CardAssigness } from 'src/cards/entities/card-assigness.entity';
import ListSeeder from './seeds/list.seeder';
import listFactory from './factories/list.factory';
import CardSeeder from './seeds/card.seeder';
import cardFactory from './factories/card.factory';
import cardAssaignessFactory from './factories/card-assaigness.factory';
import * as dotenv from 'dotenv';

dotenv.config();

(async () => {
  const options: DataSourceOptions & SeederOptions = {
    namingStrategy: new SnakeNamingStrategy(),
    type: 'mysql',
    host: process.env.DB_HOST,
    port: parseInt(process.env.DB_PORT, 10),
    username: process.env.DB_USERNAME,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME,
    synchronize: JSON.parse(process.env.DB_SYNC),
    entities: [User, Board, BoardMembers, List, Card, CardAssigness, Activity],
    seeds: [UserSeeder, BoardSeeder, ListSeeder, CardSeeder],
    factories: [UserFactory, boardFactory, listFactory, cardFactory, cardAssaignessFactory],
  };

  const dataSource = new DataSource(options);
  await dataSource.initialize();

  await runSeeders(dataSource);
  console.log('Seeding completed.');
  process.exit();
})();

이게 실제 내가 작성한 seeds.ts파일이다
.env 에서 정의한 민감한 데이터들을 토대로 실제 DB와 연결해주는 모습이다

 

사용후기

처음엔, 사용해보지 않은 라이브러리 이다보니 덜컥 겁이났지만, 실제로 사용하고보니 ,그렇게까지
겁을 먹을 필요가없다라고 느껴졌고,
내가 구현한 API를 테스트 하기위해서 팀원들의 코드가 완성될때까지 기다리지 않고
바로 테스트를 진행할수 있어서 정말 편했다

앞으로 생성형API를 만들땐, 항시 더미데이터를 먼저 생성한 후 작업을 진행할려고한다

'NestJs' 카테고리의 다른 글

CDN 이란?, S3 와 CDN 을 연결하기  (0) 2024.08.07
HTTP 통신  (0) 2024.07.29
SSE  (0) 2024.07.19
service.spec.ts 단위테스트 작성해보기(트랜잭션) in jest, nest  (0) 2024.07.16
Controller.spec.ts 에 테스트코드 작성해보기  (0) 2024.07.15