현재 백앤드의 entity따라서 db와 동기화되는 typeorm의 sync 옵션을 사용했는데
해당 기능의 문제는 db 컬럼명 수정을 하더라도 변경이 아닌 삭제후 생성으로 기존의 데이터가 유실됨
데이터가 존재하는 경우 db 마이그레이션시 db error 발생 ⇒ 관련 데이터 다 지우고 스키마 마이그레이션 진행
💡 운영(라이브/프로덕션)환경 전까지는 데이터 유실보다 속도가 중요해 잘 사용했지만
운영환경으로 올리게 됨으로써 전략 수정이 필요
개발추가 공수 (migration 파일 관리를 철저히 해야 한다)
여전히 데이터가 존재하는 경우 db 마이그레이션시 뻗음
단점의 1번은 안전성과 트레이드 오프 관계라 어쩔 수 없지만 2번은 필수적으로 해결해야 할 부분
[단점2의 보완사항] - 생성된 마이그레이션 코드를 직접 수정
# CASE) db 구조 변경으로 fk 관련한 수정 사항 발생
grand, parent, child 테이블 존재
grand - parent = 1:N
parent - child = 1:1 과 같은 관계에서
grand - parent = 1:N
grand - child = 1:1 과 같은 관계로 db 구조 변경
즉 child의 부모 테이블이 parent -> grand 로 변경되는 경우
[수정전] db 스키마 마이그레이션 코드만 존재
import { MigrationInterface, QueryRunner } from "typeorm";
export class fkTest1667979162145 implements MigrationInterface {
name = 'fkTest1667979162145'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`child\` DROP FOREIGN KEY \`FK_1e32750d2d74bb369f3894c379b\``);
await queryRunner.query(`DROP INDEX \`REL_1e32750d2d74bb369f3894c379\` ON \`child\``);
await queryRunner.query(`ALTER TABLE \`child\` CHANGE \`parent_id\` \`grand_id\` int NOT NULL`);
await queryRunner.query(`ALTER TABLE \`child\` ADD UNIQUE INDEX \`IDX_ca31559c5ebea54d722bdbdc92\` (\`grand_id\`)`);
await queryRunner.query(`CREATE UNIQUE INDEX \`REL_ca31559c5ebea54d722bdbdc92\` ON \`child\` (\`grand_id\`)`);
await queryRunner.query(`ALTER TABLE \`child\` ADD CONSTRAINT \`FK_ca31559c5ebea54d722bdbdc927\` FOREIGN KEY (\`grand_id\`) REFERENCES \`grand\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`child\` DROP FOREIGN KEY \`FK_ca31559c5ebea54d722bdbdc927\``);
await queryRunner.query(`DROP INDEX \`REL_ca31559c5ebea54d722bdbdc92\` ON \`child\``);
await queryRunner.query(`ALTER TABLE \`child\` DROP INDEX \`IDX_ca31559c5ebea54d722bdbdc92\``);
await queryRunner.query(`ALTER TABLE \`child\` CHANGE \`grand_id\` \`parent_id\` int NOT NULL`);
await queryRunner.query(`CREATE UNIQUE INDEX \`REL_1e32750d2d74bb369f3894c379\` ON \`child\` (\`parent_id\`)`);
await queryRunner.query(`ALTER TABLE \`child\` ADD CONSTRAINT \`FK_1e32750d2d74bb369f3894c379b\` FOREIGN KEY (\`parent_id\`) REFERENCES \`parent\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION`);
}
}
[수정 후] FK, Index 와 같은 제약사항이 삭제된 이후 데이터 마이그레이션 코드를 삽입
import { MigrationInterface, QueryRunner } from "typeorm";
export class fkTest1667979162145 implements MigrationInterface {
name = 'fkTest1667979162145'
private async holdNewData(queryRunner: QueryRunner){
const result = await queryRunner.query(
`SELECT
child.id,
child.parent_id,
parent.grand_id
FROM
child
JOIN
parent
ON
parent.id = child.parent_id`
);
console.log(result)
return result;
}
private async saveNewData(queryRunner: QueryRunner, newData){
await Promise.all(newData.map(async nd => await queryRunner.query(
`UPDATE child SET grand_id = ${nd.grand_id} WHERE id = ${nd.id}`
)));
}
public async up(queryRunner: QueryRunner): Promise<void> {
const newData = await this.holdNewData(queryRunner);
await queryRunner.query(`ALTER TABLE \`child\` DROP FOREIGN KEY \`FK_1e32750d2d74bb369f3894c379b\``);
await queryRunner.query(`DROP INDEX \`REL_1e32750d2d74bb369f3894c379\` ON \`child\``);
await this.saveNewData(queryRunner, newData);
await queryRunner.query(`ALTER TABLE \`child\` CHANGE \`parent_id\` \`grand_id\` int NOT NULL`);
await queryRunner.query(`ALTER TABLE \`child\` ADD UNIQUE INDEX \`IDX_ca31559c5ebea54d722bdbdc92\` (\`grand_id\`)`);
await queryRunner.query(`CREATE UNIQUE INDEX \`REL_ca31559c5ebea54d722bdbdc92\` ON \`child\` (\`grand_id\`)`);
await queryRunner.query(`ALTER TABLE \`child\` ADD CONSTRAINT \`FK_ca31559c5ebea54d722bdbdc927\` FOREIGN KEY (\`grand_id\`) REFERENCES \`grand\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION`);
}
public async holdPrevData(queryRunner: QueryRunner){
//up에서 동작하는 함수와 반대 기능
}
public async savePrevData(queryRunner: QueryRunner, newData){
//up에서 동작하는 함수와 반대 기능
}
public async down(queryRunner: QueryRunner): Promise<void> {
const newData = await this.holdPrevData(queryRunner);
await queryRunner.query(`ALTER TABLE \`child\` DROP FOREIGN KEY \`FK_ca31559c5ebea54d722bdbdc927\``);
await queryRunner.query(`DROP INDEX \`REL_ca31559c5ebea54d722bdbdc92\` ON \`child\``);
await this.savePrevData(queryRunner, newData);
await queryRunner.query(`ALTER TABLE \`child\` DROP INDEX \`IDX_ca31559c5ebea54d722bdbdc92\``);
await queryRunner.query(`ALTER TABLE \`child\` CHANGE \`grand_id\` \`parent_id\` int NOT NULL`);
await queryRunner.query(`CREATE UNIQUE INDEX \`REL_1e32750d2d74bb369f3894c379\` ON \`child\` (\`parent_id\`)`);
await queryRunner.query(`ALTER TABLE \`child\` ADD CONSTRAINT \`FK_1e32750d2d74bb369f3894c379b\` FOREIGN KEY (\`parent_id\`) REFERENCES \`parent\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION`);
}
}