typeORM 마이그레이션 진행하기

김동현·2023년 12월 26일
0

Nestjs

목록 보기
1/6
post-thumbnail

💡 typeOrm 마이그레이션 준비

  • package.json에 아래 script 추가
"migration:create": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:create ./{migration경로}/migrations/Migration",

// 절대 경로 사용 시 tsconfig-paths/register를 넣어줘야 절대 경로를 해석할 수 있다.
"typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js --dataSource {dataSource 경로}",
"migration:gen": "npm run typeorm migration:generate ./{migration경로}/migrations/Migration",
"migration:run": "npm run typeorm migration:run",
"migration:revert": "npm run typeorm migration:revert"
"migration:create": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:create ./{migration경로}/migrations/Migration",

// 절대 경로 사용 시 tsconfig-paths/register를 넣어줘야 절대 경로를 해석할 수 있다.
"typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js --dataSource {dataSource 경로}",
"migration:gen": "yarn typeorm migration:generate ./{migration경로}/migrations/Migration",
"migration:run": "yarn typeorm migration:run",
"migration:revert": "yarn typeorm migration:revert"
  • 파일이 있는 해당 경로에 migrations 폴더를 생성한다.

    • 이 후 migrationsRun: true 옵션을 준다면 migrations 폴더 하위에 있는 마이그레이션 파일들이 서버 시작 시에 마이그레이션 된다!
  • mysqlConfigService TypeOrmOption추가한다.

createTypeOrmOptions(): TypeOrmModuleOptions {
        return {
            ...
            migrations: [__dirname + `/migrations/*.{js,ts}`],
            migrationsTableName: 'migrations',
            migrationsRun: true, // 마이그레이션을 실행하겠다! <- migration run 커멘드로도 가능합니다!
        };
    }

💡 초기 Table 세팅

  1. 개발을 진행하고 synchronize: true로 먼저 테이블을 생성한다.
  2. Database 툴의 DDL로 Table 생성 SQL을 추출한다.

  1. IF NOT EXISTS를 추가하여 없을 때만 테이블을 생성하도록 만들어 준다. << 혹시 생성되어 있을 수도 있으니!
CREATE TABLE IF NOT EXISTS book ( -- IF NOT EXISTS 추가
  id int NOT NULL AUTO_INCREMENT COMMENT 'books의 UniqueId',
  title varchar(100) COLLATE utf8mb3_unicode_ci NOT NULL COMMENT '책 제목',
  price int NOT NULL COMMENT '책 가격',
  book_report varchar(1000) COLLATE utf8mb3_unicode_ci DEFAULT NULL COMMENT '독후감',
  start_date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '책을 읽기 시작한 날짜',
  end_date timestamp NULL DEFAULT NULL COMMENT '책을 다 읽은 날짜',
  created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성된 시간',
  updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '마지막으로 업데이트 된 시간',
  deleted_at timestamp NULL DEFAULT NULL COMMENT '삭제된 시간',
  PRIMARY KEY (id),
  UNIQUE KEY title (title)
) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci COMMENT='책 정보를 저장하기 위한 TABLE';
  • 이제부터는 마이그레이션으로 진행하자 🙂

💡 마이그레이션 파일 생성 방법

  1. migration:create
    • create는 빈 마이그레이션 파일을 생성합니다.
    • 스스로 테이블을 수정하는 Query문을 작성하여야 합니다.
import { MigrationInterface, QueryRunner } from "typeorm";

export class Migration1703569575648 implements MigrationInterface {
    name = 'Migration1703569575648'
    
    public async up(queryRunner: QueryRunner): Promise<void> {}
    public async down(queryRunner: QueryRunner): Promise<void> {}
}
  • migration:create는 빈파일만 생성해 주면 되므로 --dataSource의 위치를 알필요가 없다.
  • 아래의 사진과 같이 동일하게 typeorm을 기준으로 create를 해주면 create 옵션에 -d(--dataSource)가 없다는 것을 볼 수 있다.
  • 그렇기에 create는 :gen, :run, :rever와 다르게 typeormcli.js를 최상위로하여 마이그레이션 파일을 만들면 된다 :)

  1. migration:gen
  • migration:gen은 현재 데이터베이스와 entity 파일을 비교하여 변경된 부분에 대한 query문을 작성한 채 migration 파일을 생성하여 줍니다.
    • ⚠️ migration:generate는 컬럼의 길이나 타입이 변경될 때 해당 컬럼을 DROP한 뒤 새로 생성되기 때문에 데이터가 날아버리는 이슈가 있다.
    • 그렇기에 컬럼의 제약 조건을 수정할 때는 create로 생성한 후 SQL을 작성하는 것이 안전하다 🙂

아래와 같이 --dataSource를 적용해야 해당 dataSource를 구성하는 환경의 DB와 entity를 비교하여 작성해 준다. :)

  1. "typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js --dataSource {dataSource 경로}",

  2. "migration:gen": "npm run typeorm migration:generate ./{migration경로}/migrations/Migration",

import { MigrationInterface, QueryRunner } from "typeorm";

export class Migration1703569575648 implements MigrationInterface {
    name = 'Migration1703569575648'

    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`ALTER TABLE \`user\` ADD \`uuid\` varchar(20) NOT NULL COMMENT '유저 기본키' DEFAULT '80E4671019'`);
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`ALTER TABLE \`user\` DROP COLUMN \`uuid\``);
    }
}
  • 50자에서 10자로 제약을 줄인다면, 데이터가 소실될 수 있기에 의도한 바라고 한다.
    • 설득이 되기도 한다 🙂

  • 이건 좀 슬프다 😟

p.s

😎 Mygration 파일에는 inject가 되지 않는다.
해당 부분에 query가 아닌 로직으로 마이그레이션을 진행하고 싶다면 아래와 같이 queryRunner 안의 manager를 통해 레파지토리를 가져와 실행하면 된다

export class Migration1703575944818 implements MigrationInterface {
    public async up(queryRunner: QueryRunner): Promise<void> {
        const userRepository: Repository<UserEntity> = queryRunner.manager.getRepository(UserEntity);
    }

    public async down(queryRunner: QueryRunner): Promise<void> {}
}

(참고) 스택 오버 플로우

참고 자료

[NestJS] TypeORM을 사용하여 마이그레이션 | path alias 절대경로
TypeORM 0.3에서 migration 해보기
Using TypeORM Migration in NestJS with Postgres Database
[typeORM] Migration 이슈
https://github.com/typeorm/typeorm/issues/3357

profile
달려보자

0개의 댓글