현재 진행 중인 프로젝트에서 데이터베이스의 stores라는 테이블에 새로운 컬럼을 추가해야 하는 일이 생겼다.
pgadmin4에서 쿼리문으로 'ALTER TABLE stores ADD COLUMN coordinates geometry(Point, 4326) default NULL' 이런 식으로 컬럼을 생성했다.
그런 다음에 stores 테이블에 기존에 있던 Ma, La 값을 가지고 coordinates의 값을 업데이트 해주었다.
처음에는 잘 되다가 중간에 갑자기 서버가 끊기면서 해당 컬럼이 존재하지 않는다는 오류가 떴다. 그리고 실제로 pgadmin4에 봤더니 컬럼이 삭제되어있었다.
아직까지도 정확한 이유는 모르지만 어쨌든 migration으로 스키마의 변경 사항을 버전 관리도 하고 데이터베이스의 변경을 추적하고, 자동으로 적용할 수 있다는 이점 덕에 더 안전한 관리가 가능하다고 생각이 들어서 migration을 만들어보기로 했다.
위의 reference들을 따라하면서 진행했다.
npx typeorm migration:create ./src/migrations/addColumn
// {TIMESTAMP}-addColumn.ts
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddColumn1686766534159 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {}
public async down(queryRunner: QueryRunner): Promise<void> {}
}
// 1686766534159-addColumn.ts
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddColumn1686766534159 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "stores" ADD COLUMN "coordinates" geometry(Point, 4326) DEFAULT NULL`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "stores" DROP COLUMN "coordinates"`);
}
}
// stores.entity.ts
import { Users } from '../auth/users.entity';
import { Reviews } from '../reviews/reviews.entity';
import { Tables } from '../tables/tables.entity';
import { Waitings } from '../waitings/waitings.entity';
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
OneToMany,
OneToOne,
Point,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity()
export class Stores extends BaseEntity {
@PrimaryGeneratedColumn()
storeId: number;
@Column()
storeName: string;
@Column()
description: string;
@Column()
category: string;
@Column()
maxWaitingCnt: number;
@Column()
currentWaitingCnt: number;
@Column('numeric')
La: number;
@Column('numeric')
Ma: number;
@Column()
address: string;
@Column({ nullable: true })
distance: number;
@Column({ nullable: true })
oldAddress: string;
@Column({ default: 60 })
cycleTime: number;
@Column()
tableForTwo: number;
@Column()
tableForFour: number;
@Column({ type: 'float', default: 0 })
rating: number;
// 얘다
@Column('geometry', {
spatialFeatureType: 'Point',
srid: 4326,
nullable: true,
})
coordinates: Point;
@CreateDateColumn({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date;
@UpdateDateColumn({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
updatedAt: Date;
@OneToMany(() => Waitings, (waiting) => waiting.store)
waitings: Waitings[];
@OneToMany(() => Reviews, (review) => review.store)
reviews: Reviews[];
@OneToOne(() => Tables, (table) => table.store)
tables: Tables;
@OneToOne(() => Users, (user) => user.store)
user: Users;
}
// typeOrm.config.ts
import { Stores } from './src/stores/stores.entity';
import { ConfigService } from '@nestjs/config';
import { config } from 'dotenv';
import { DataSource } from 'typeorm';
import { AddColumn1686766534159 } from './src/migrations/1686766534159-addColumn';
import { Waitings } from './src/waitings/waitings.entity';
import { Reviews } from './src/reviews/reviews.entity';
import { Tables } from './src/tables/tables.entity';
import { Users } from './src/auth/users.entity';
config();
const configService = new ConfigService();
export const dataSource = new DataSource({
type: 'postgres',
host: configService.get('POSTGRES_HOST'),
port: 5432,
username: configService.get('POSTGRES_USERNAME'),
password: configService.get('POSTGRES_PASSWORD'),
database: 'temp',
entities: [Stores, Waitings, Reviews, Tables, Users],
migrations: [AddColumn1686766534159],
// migrationsTableName: 'custom_migration_table',
});
dataSource
.initialize()
.then(() => {
console.log('Data Source has been initialized!');
})
.catch((error) => {
console.error('Error during Data Source initialization', error);
});
import 할 때 경로의 앞에 꼭 “./”을 붙여줘야 한다. 그러지 않으면 module을 못 찾는다.
package.json의 scripts에는 아래 내용을 추가해야 한다.
"typeorm": "ts-node ./node_modules/typeorm/cli",
"typeorm:run-migrations": "npm run typeorm migration:run -- -d ./typeOrm.config.ts",
"typeorm:generate-migration": "npm run typeorm -- -d ./typeOrm.config.ts migration:generate ./migrations/$npm_config_name",
"typeorm:create-migration": "npm run typeorm -- migration:create ./migrations/$npm_config_name",
"typeorm:revert-migration": "npm run typeorm -- -d ./typeOrm.config.ts migration:revert"
npm run typeorm:run-migrations위 과정들을 거치고 나서 원하던 작업(stores 테이블에 새로 추가된 coordinates 값 업데이트)을 수행하니까 중간에 끊기지 않고 잘 수행이 된다.
원하는 51만여개의 row에 coordinates 컬럼의 값이 잘 업데이트 되었다.
귀찮더라도... pgadmin4에서 쿼리로 컬럼 생성하는거보다 어렵더라도.. 잘 모르겠더라도.. 안전하고 더 체계적이고 유용한 마이그레이션을 통해 데이터베이스를 관리하는 것이 좋다..