1. Migration
Migration์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ณ๊ฒฝ์ฌํญ์ ์คํฌ๋ฆฝํธ๋ก ์์ฑํด์ ๋ฐ์ํ๋ค.- ํต์ ๋ ์ํฉ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง ๋ณ๊ฒฝ ๋ฐ ๋ณต๊ตฌ๋ฅผ ์งํํ ์ ์๋ค.
- Migration์ด ํ์ํ ์ด์ : ์ sync ์ต์
์ผ๋ก ๋ถ์กฑํ๊ฐ?
Controlled Changes: ์ํ๋ ์ํฉ์ ์ํ๋ ํํ๋ก ๋ง์ด๊ทธ๋ ์ด์ ์ ์์ ๋กญ๊ฒ ์คํ ํ ์ ์๋คReversible: ์งํํ ๋ง์ด๊ทธ๋ ์ด์ ์ ์ฝ๊ฒ ๋๋๋ฆด ์ ์๋ค.Versioning: ๋ง์ด๊ทธ๋ ์ด์ ์ ์คํค๋ง ๋ณ๊ฒฝ์ ๋ํ ํ์คํ ๋ฆฌ๋ฅผ ๋ด๊ณ ์๋ค. ๋๋ฒ๊น ์ ๋งค์ฐ ์ ์ฉํ๋ค.Consistency: ๋ค์ํ ํ๊ฒฝ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง๊ฐ ๊ฐ๊ฒ ์ ์ง๋๋๋ก ํ ์ ์๋ค.Complex Changes: ๋ณต์กํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ณํ๋ฅผ ์ง์ ์ปจํธ๋กค ํ ์ ์๋ค.
2. Migration ํ์ผ
1Migration12Migration2 # <------------- Database13Migration3 ## Migration ์ ์ฉ4Migration4 ## Migration ์ ์ฉ5Migration5 ## Migration ์ ์ฉ6Migration6 ## Migration ์ ์ฉ
3. Migration Configuration : ormconfig.json
1{2"type": "postgres",3"host": "localhost",4"port": 5432,5"username": "test",6"password": "test",7"database": "test",8"entities": ["src/entities/**/*.ts"],9"migrations": ["src/migrations/**/*.ts"], // Migration ํ์ผ ๊ฒฝ๋ก10"cli": {11"entitiesDir": "src/entity",12"migrationsDir": "src/migrations"13}14}
4. Migration ํ ์ด๋ธ ์์ฑ
4.1 RAW SQL
1import { MigrationInterface, QueryRunner } from 'typeorm'23export class CreateMovieAndDirectorTables1634567890123 implements MigrationInterface {4public async up(queryRunner: QueryRunner): Promise<void> {5await queryRunner.query(`6CREATE TABLE "director" (7"id" SERIAL NOT NULL,8"name" VARCHAR NOT NULL,9"dob" DATE,10"nationality" VARCHAR,11PRIMARY KEY ("id")12)13`)14await queryRunner.query(`15CREATE TABLE "movie" (16"id" SERIAL NOT NULL,17"title" VARCHAR NOT NULL,18"genre" VARCHAR,19"directorId" INTEGER,20PRIMARY KEY ("id"),21FOREIGN KEY ("directorId") REFERENCES "director" ("id") ON DELETE SET NULL22)23`)24}2526public async down(queryRunner: QueryRunner): Promise<void> {27await queryRunner.query(`DROP TABLE "movie"`)28await queryRunner.query(`DROP TABLE "director"`)29}30}
4.2 Migration API
1import { MigrationInterface, QueryRunner, Table, TableForeignKey } from 'typeorm'23export class CreateMovieAndDirectorTables1634567890123 implements MigrationInterface {4public async up(queryRunner: QueryRunner): Promise<void> {5/// ...์๋ต6await queryRunner.createTable(7new Table({8name: 'movie',9columns: [10{ name: 'id', type: 'int', isPrimary: true, isGenerated: true, generationStrategy: 'increment' },11{ name: 'title', type: 'varchar' },12{ name: 'genre', type: 'varchar', isNullable: true },13{ name: 'directorId', type: 'int', isNullable: true },14],15}),16true,17)18await queryRunner.createForeignKey(19'movie',20new TableForeignKey({21columnNames: ['directorId'],22referencedColumnNames: ['id'],23referencedTableName: 'director',24onDelete: 'SET NULL',25}),26)27}2829public async down(queryRunner: QueryRunner): Promise<void> {30await queryRunner.dropTable('movie')31await queryRunner.dropTable('director')32}33}
5. Migration ์นผ๋ผ ์ถ๊ฐ
5.1 RAW SQL
1import { MigrationInterface, QueryRunner } from 'typeorm'23export class AddDirectorBioColumn1634567890123 implements MigrationInterface {4public async up(queryRunner: QueryRunner): Promise<void> {5await queryRunner.query(`6ALTER TABLE "director"7ADD "dateOfBirth" DATE8`)9}1011public async down(queryRunner: QueryRunner): Promise<void> {12await queryRunner.query(`13ALTER TABLE "director"14DROP COLUMN "dateOfBirth"15`)16}17}
5.2 Migration API
1import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm'23export class AddDateOfBirthColumn1634567890124 implements MigrationInterface {4public async up(queryRunner: QueryRunner): Promise<void> {5await queryRunner.addColumn(6'director',7new TableColumn({8name: 'dateOfBirth',9type: 'date',10isNullable: true,11}),12)13}1415public async down(queryRunner: QueryRunner): Promise<void> {16await queryRunner.dropColumn('director', 'dateOfBirth')17}18}
6. Migration ์นผ๋ผ ์ด๋ฆ ๋ณ๊ฒฝ
6.1 RAW SQL
1import { MigrationInterface, QueryRunner } from 'typeorm'23export class RenameNameToFullName1634567890125 implements MigrationInterface {4public async up(queryRunner: QueryRunner): Promise<void> {5await queryRunner.query(`6ALTER TABLE "director"7RENAME COLUMN "name" TO "fullName"8`)9}1011public async down(queryRunner: QueryRunner): Promise<void> {12await queryRunner.query(`13ALTER TABLE "director"14RENAME COLUMN "fullName" TO "name"15`)16}17}
6.2 Migration API
1import { MigrationInterface, QueryRunner } from 'typeorm'23export class RenameNameToFullNameInDirector1634567890125 implements MigrationInterface {4public async up(queryRunner: QueryRunner): Promise<void> {5await queryRunner.renameColumn('director', 'name', 'fullName')6}78public async down(queryRunner: QueryRunner): Promise<void> {9await queryRunner.renameColumn('director', 'fullName', 'name')10}11}
7. Migration ์นผ๋ผ ํ์ ๋ณ๊ฒฝ
7.1 RAW SQL
1import { MigrationInterface, QueryRunner } from 'typeorm'23export class ChangeEmailTypeInDirector1634567890126 implements MigrationInterface {4public async up(queryRunner: QueryRunner): Promise<void> {5await queryRunner.query(`6ALTER TABLE "director"7ALTER COLUMN "email" TYPE TEXT8`)9}1011public async down(queryRunner: QueryRunner): Promise<void> {12await queryRunner.query(`13ALTER TABLE "director"14ALTER COLUMN "email" TYPE VARCHAR15`)16}17}
7.2 Migration API
1import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm'23export class ChangeEmailTypeInDirector1634567890126 implements MigrationInterface {4public async up(queryRunner: QueryRunner): Promise<void> {5await queryRunner.changeColumn(6'director',7'email',8new TableColumn({9name: 'email',10type: 'text',11}),12)13}1415public async down(queryRunner: QueryRunner): Promise<void> {16await queryRunner.changeColumn(17'director',18'email',19new TableColumn({20name: 'email',21type: 'varchar',22}),23)24}25}
8. Migration : Relationship ์์
8.1 RAW SQL
1import { MigrationInterface, QueryRunner } from 'typeorm'23export class CreateGenreAndMovieGenreTables1634567890127 implements MigrationInterface {4public async up(queryRunner: QueryRunner): Promise<void> {5await queryRunner.query(`6CREATE TABLE "genre" (7"id" SERIAL NOT NULL,8"name" VARCHAR NOT NULL,9PRIMARY KEY ("id")10)11`)12await queryRunner.query(`13CREATE TABLE "movie_genre" (14"movieId" INTEGER NOT NULL,15"genreId" INTEGER NOT NULL,16PRIMARY KEY ("movieId", "genreId"),17FOREIGN KEY ("movieId") REFERENCES "movie" ("id") ON DELETE CASCADE,18FOREIGN KEY ("genreId") REFERENCES "genre" ("id") ON DELETE CASCADE19)20`)21}2223public async down(queryRunner: QueryRunner): Promise<void> {24await queryRunner.query(`DROP TABLE "movie_genres_genre"`)25await queryRunner.query(`DROP TABLE "genre"`)26}27}
8.2 Migration API
1import { MigrationInterface, QueryRunner, Table, TableForeignKey } from 'typeorm'23export class CreateGenreAndMovieGenreTables1634567890127 implements MigrationInterface {4public async up(queryRunner: QueryRunner): Promise<void> {5await queryRunner.createTable(6new Table({7name: 'genre',8columns: [9{ name: 'id', type: 'int', isPrimary: true, isGenerated: true, generationStrategy: 'increment' },10{ name: 'name', type: 'varchar' },11],12}),13true,14)15//... ์๋ต16}1718public async down(queryRunner: QueryRunner): Promise<void> {19await queryRunner.dropTable('movie_genres_genre')20await queryRunner.dropTable('genre')21}22}
9. Migration CLI ์ปค๋งจ๋
1# Migration ํ์ผ ์์ฑ2npx typeorm migration:generate -n <MigrationName>34# Migration ์คํ5npx typeorm migration:run67# Migration ๋๋๋ฆฌ๊ธฐ8npx typeorm migration:revert