[Nest.js] TypeORM Table ๊ด€๊ณ„๊ฐ€ ๋งบ์–ด์กŒ์„ ๋•Œ, Seeding (feat. Migration)

2023. 11. 30. 20:01ใ†Back-End ์ž‘์—…์‹ค/๋ฌธ์ œ ์ •๋ฆฌ

728x90
๋ฐ˜์‘ํ˜•

 

 

 

 

 

NestJS๋กœ ๋ฐฐ์šฐ๋Š” ๋ฐฑ์—”๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ:ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์˜ ์ฐจ์„ธ๋Œ€ ์„œ๋ฒ„ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋งŒ๋‚˜๋‹ค

COUPANG

www.coupang.com

"์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค."

 

 

 

GitHub - junyharang-coding-study/GraphQL-Study: GraphQL์„ ๊ณต๋ถ€ํ•˜๊ณ , ์‹ค์Šตํ•œ ์ฝ”๋“œ์—์š” ๐Ÿ˜€

GraphQL์„ ๊ณต๋ถ€ํ•˜๊ณ , ์‹ค์Šตํ•œ ์ฝ”๋“œ์—์š” ๐Ÿ˜€. Contribute to junyharang-coding-study/GraphQL-Study development by creating an account on GitHub.

github.com

 

 

 

 

๐Ÿ‘ท‍โ™‚๏ธ ์ž‘์—… ์ค‘์ธ ๋‚ด์šฉ

์ด ๊ธ€์€ Nest.JS๋ฅผ ์ด์šฉํ•ด GraphQL ์‹ค์Šต ํ™˜๊ฒฝ ๊ตฌ์„ฑ ์ค‘ Mock Data๋ฅผ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด TypeORM Extension์˜ Seeding์„ ํ•˜๋ฉด์„œ ๋ฐœ์ƒํ•œ ๋ฌธ์ œ์— ๋Œ€ํ•ด ์ •๋ฆฌํ•˜๋Š” ๊ธ€์ด์—์š”.

 


Seeding์„ ํ•ด์„œ ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜๋ฉด Enbedded SQLite์— ์œ„์™€ ๊ฐ™์ด Mock Data๊ฐ€ Table์— ๋งž์ถฐ ์ž…๋ ฅ๋œ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.


 

 

 

โš ๏ธ ๋ฌธ์ œ ๋ฐœ์ƒ!

team Table

 

People Table

 

Supply Table


ํ•˜์ง€๋งŒ ๋ฌธ์ œ๋Š” ์œ„์™€ ๊ฐ™์ด ๊ด€๊ณ„๊ฐ€ ๋งบ์–ด์ ธ ์žˆ๋Š” Table์—์„œ ํ•ด๋‹น Table์˜ FK ๊ฐ’์ด ๋“ค์–ด๊ฐ€์ง€ ์•Š๋Š”๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์–ด์š”.



 

๐Ÿค” ์›์ธ ๋ถ„์„

Team Entity

 

TeamSeeder

 

SupplyEntity

 

Supply Seeder


์ด๋Š” ์œ„์™€ ๊ฐ™์ด Supply๋Š” Team๊ณผ ๊ด€๊ณ„๋ฅผ ๋งบ๊ณ  ์žˆ๊ณ , FK๋กœ team_id๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์š”.

SupplyEntity


Supply Entity์— 10 ~ 11๋ฒˆ์งธ ์ค„์„ ๋ณด๋ฉด Team๊ณผ ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ๋งบ์—ˆ๊ณ , ํ•˜๋‚˜์˜ Supply๋Š” ์—ฌ๋Ÿฌ Team๊ณผ ๊ด€๊ณ„๋ฅผ ๋งบ๊ธฐ ๋•Œ๋ฌธ์— Many To One์œผ๋กœ ๊ด€๊ณ„๋ฅผ ๋งบ์–ด ์ฃผ์—ˆ์œผ๋ฉฐ, ์ด ๋•Œ, N + 1 ๋ฌธ์ œ๋ฅผ ๋ฐฉ์–ดํ•˜๊ณ ์ž Entity Type์„ Promise๋กœ ์„ค์ •ํ•ด ์ฃผ์—ˆ์–ด์š”.

์ด๊ฒƒ์€ ํ•ด๋‹น ๊ด€๊ณ„๋ฅผ Lazy Relations๋กœ ์„ค์ •ํ•˜๊ฒ ๋‹ค๋Š” ์˜๋ฏธ์—์š”.

Lay Relations(์ง€์—ฐ ๊ด€๊ณ„)๋ฅผ ๋งบ๊ฒŒ ๋˜๋ฉด ํ•ด๋‹น Entity์— ์ ‘๊ทผ ํ•  ๋•Œ๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋˜๊ฒŒ ๋ผ์š”.

์œ„์˜ SupplyEntity์˜ 11๋ฒˆ์งธ ์ค„ team ๊ฐ์ฒดํ˜• ๋ณ€์ˆ˜๋Š” Pramise<TeamEntity>๋กœ ๋ฐ›์•„์•ผ ํ•˜๋Š”๋ฐ,

Supply Seeder

Seeder๋ฅผ ๋ณด๋ฉด Team Table์— ๊ฐ’์ด ๋“ค์–ด๊ฐ€๊ณ  ๋‚˜์„œ ํ•ด๋‹น Table์˜ teamId๋กœ ํ•œ ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์™€
๊ฐ๊ฐ์˜ ์ƒ์ˆ˜ํ˜• ๋ณ€์ˆ˜์— ๋„ฃ์–ด์ฃผ๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ์–ด์š”.

Supply Seeder


๊ทธ๋ฆฌ๊ณ  ๋‚˜์„œ Supply Table์— insert๋ฅผ ํ•  ๋•Œ, ์œ„์™€ ๊ฐ™์ด team Field์— ํ•ด๋‹น ๊ฐ์ฒด๋ฅผ ๋„ฃ์–ด์ฃผ๋ ค๊ณ  ํ•˜๋‹ˆ
๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฑธ ์•Œ ์ˆ˜ ์žˆ์–ด์š”.


 

 

๐Ÿป ๋ฌธ์ œ ํ•ด๊ฒฐ!

์ตœ์ดˆ ์ด๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์•„๋ž˜ ์˜์กด์„ฑ์„ ์„ค์น˜ํ•ด ์ฃผ์–ด์•ผ ํ•ด์š”.

npm install -D ts-node tsconfig-paths


์œ„ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด TypeScript ํŒŒ์ผ ์‹คํ–‰ ์‹œ ๋„์›€์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ,
-D Options(์˜ต์…˜)์€ ์ด ํŒจํ‚ค์ง€๋“ค์ด devDependencies์— ์„ค์น˜ ๋˜๋„๋ก ํ•˜๋Š” ์˜ต์…˜์ด์—์š”. 
์ด๊ฒƒ์€ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋งŒํ•„์š”ํ•˜๊ณ , ์šด์˜ํ™˜๊ฒฝ์—์„œ๋Š” ์“ฐ์ง€ ์•Š๊ฒ ๋‹ค๋Š” ์˜๋ฏธ์—์š”.


์ด๋ ‡๊ฒŒ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜๋ฉด TypeScript ์ฝ”๋“œ๋ฅผ ์ง์ ‘ Node.js ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ ts-node๊ฐ€ ์„ค์น˜๋˜๊ณ , ์ด๊ฒƒ์€ Node.js๋Š” JavaScript ์ฝ”๋“œ๋งŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ,
์ด๋ฅผ ํ†ตํ•ด TypeScript ํŒŒ์ผ์„ ์ง์ ‘ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ํŒจํ‚ค์ง€์—์š”.


๊ทธ๋ฆฌ๊ณ , TypeScript ํ”„๋กœ์ ํŠธ์—์„œ ์„ค์ •๋œ tsconfig.json ํŒŒ์ผ์˜ paths ์„ค์ •์„ ํ™œ์šฉํ•˜์—ฌ Module ๊ฒฝ๋กœ๋ฅผ ํ•ด์„ํ•  ์ˆ˜
์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ํŒจํ‚ค์ง€๊ฐ€ ๋ฐ”๋กœ tsconfig-paths์—์š”. ์ด๊ฒƒ์„ ํ†ตํ•ด TypeScript๋กœ ์ž‘์„ฑ๋œ ์ฝ”๋“œ์—์„œ Import ๊ตฌ๋ฌธ์„
์‚ฌ์šฉํ•  ๋•Œ, ๊ฒฝ๋กœ๋ฅผ ์„ค์ •ํ•˜๋ฉด ํ•ด๋‹น ๊ฒฝ๋กœ๋ฅผ ํ•ด์„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ค€๋‹ต๋‹ˆ๋‹ค.

type-script-nest-graph-ql-์‹ค์Šต/data-source.ts


์œ„์™€ ๊ฐ™์ด Project(ํ”„๋กœ์ ํŠธ) Root(๋ฃจํŠธ) Directory(๋””๋ ‰ํ„ฐ๋ฆฌ)์— ์œ„ ๋‚ด์šฉ์„ ์ž…๋ ฅํ•ด ์ฃผ์–ด์•ผ ํ•ด์š”.
์ด์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ๊ณณ์— ์ž‘์„ฑํ•ด ์ฃผ์—ˆ์–ด์š”.

package.json


๊ทธ๋ฆฌ๊ณ  ์œ„์™€ ๊ฐ™์ด package.json script{}์— TypeORM migraion ๊ด€๋ จ Script๋ฅผ ๋„ฃ์–ด์ฃผ์—ˆ์–ด์š”.


์ด๋Š” ๊ณต์‹ ๋ฌธ์„œ์™€ ์ข€ ๋‹ค๋ฅด๊ฒŒ ์„ค์ •ํ•ด ์ฃผ์—ˆ๋Š”๋ฐ,
์ด์œ ๋Š” TypeORM CLI๊ฐ€ ์ ˆ๋Œ€ ๊ฒฝ๋กœ๋ฅผ ์ดํ•ดํ•˜์ง€ ๋ชปํ•ด ๊ณต์‹ ๋ฌธ์„œ๋Œ€๋กœ ํ•˜๊ฒŒ ๋˜๋ฉด
can not found module Error๋ฅผ ๋‚ด๋ฑ‰๊ธฐ ๋•Œ๋ฌธ์ด์—์š”.

๊ทธ๋ž˜์„œ ์ž„์˜๋กœ ์ ˆ๋Œ€ ๊ฒฝ๋กœ๋ฅผ ์ดํ•ด์‹œํ‚ค๊ธฐ ์œ„ํ•ด ์œ„์—์„œ ts-node์™€ tsconfig-paths ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•œ ๊ฑฐ๊ณ ,
Script ๋ฌธ์— ts-node -r tsconfig-paths/register๋ฅผ ๋„ฃ์–ด์ค€ ๊ฑฐ์—์š”.

npm ๋ช…๋ น์–ด(์˜ต์…˜) ์ƒ ์„ธ ๋‚ด ์šฉ
npm run migration:run ๊ฐ๊ฐ์˜ Migration File์˜ up()์—์„œ ์ž‘์„ฑํ•œ ๋‚ด์šฉ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— Query ์ „๋‹ฌ ๋ฐ ๋ฐ˜์˜.
npm run migration:revert ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์— ์ ์šฉ๋œ ๊ฐ๊ฐ์˜ Migration File์˜ down()์—์„œ ์ž‘์„ฑํ•œ ๋‚ด์šฉ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— Query ์ „๋‹ฌ ๋ฐ ๋ฐ˜์˜. 

๋‹จ, ๊ฐ€์žฅ ์ตœ๊ทผ์˜ Migration File์˜ up()์„ ํ†ตํ•ด ์ „๋‹ฌํ•œ ๋‚ด์šฉ๋งŒ ๋‹จ, ํ•œ๋ฒˆ ๋˜๋Œ๋ฆผ.
npm run migration:create ์ƒˆ๋กœ์šด Migration File ์ƒ์„ฑ.
npm run migration:grnerate Migration ์ž๋™ ์ƒ์„ฑ.
npm run schema:drop ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ชจ๋“  ์Šคํ‚ค๋งˆ ์‚ญ์ œ๋ฅผ ์œ„ํ•œ Query ์ „๋‹ฌ ๋ฐ ๋ฐ˜์˜.
npm run schema: sync ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ๋ฅผ Update(์ตœ์‹ ํ™”)ํ•˜์—ฌ ํ˜„์žฌ ์†Œ์Šค ์ฝ”๋“œ์˜ Entity์™€ ์ผ์น˜ํ•˜๋„๋ก ์ž‘์—….



์—ฌ๊ธฐ๊นŒ์ง€ ํ•œ ๋’ค Entity๋ฅผ ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค. Entity ์—ญ์‹œ ์ด ๊ณณ์„ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.


npm run migration:generate


์œ„ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜๋ฉด

src/database/migrations/1701333148773-Migration.ts

 

import { MigrationInterface, QueryRunner } from "typeorm";

export class Migration1701333148773 implements MigrationInterface {
    name = 'Migration1701333148773'

    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`CREATE TABLE "software" ("software_id" varchar(255) PRIMARY KEY NOT NULL, "used_by" varchar(255) NOT NULL, "developed_by" varchar NOT NULL, "description" varchar NOT NULL, CONSTRAINT "UQ_4ef15228013fdc93b76c9339283" UNIQUE ("software_id"))`);
        await queryRunner.query(`CREATE TABLE "team" ("team_id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "manager" varchar(255) NOT NULL, "office" varchar(5) NOT NULL, "extension_number" varchar(5) NOT NULL, "mascot" varchar(10), "cleaning_duty" varchar(10) NOT NULL, "project" varchar(10) NOT NULL, CONSTRAINT "UQ_a35a345d4436b82adf6bb76f3ce" UNIQUE ("team_id"))`);
        await queryRunner.query(`CREATE TABLE "people" ("people_id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "last_name" varchar(255) NOT NULL, "first_name" varchar(255) NOT NULL, "sex" varchar(6) NOT NULL, "blood_type" varchar(2) NOT NULL, "serveYears" integer NOT NULL, "role" varchar(100) NOT NULL, "hometown" varchar NOT NULL, "team_id" integer, CONSTRAINT "UQ_017abdefead91b361b5e7ac108f" UNIQUE ("people_id"))`);
        await queryRunner.query(`CREATE TABLE "role" ("role_id" varchar(255) PRIMARY KEY NOT NULL, "job" varchar(255) NOT NULL, "requirement" varchar NOT NULL, CONSTRAINT "UQ_df46160e6aa79943b83c81e496e" UNIQUE ("role_id"))`);
        await queryRunner.query(`CREATE TABLE "equipment" ("equipment_id" varchar(255) PRIMARY KEY NOT NULL, "used_by" varchar(255) NOT NULL, "count" integer NOT NULL, "new_or_used" varchar(10) NOT NULL, CONSTRAINT "UQ_88b371a2f0b8ae33b1060f00189" UNIQUE ("equipment_id"))`);
        await queryRunner.query(`CREATE TABLE "supply" ("supply_id" varchar(255) PRIMARY KEY NOT NULL, "teamTeamId" integer, CONSTRAINT "UQ_ea803c518d575c82cd33f5668df" UNIQUE ("supply_id"))`);
        await queryRunner.query(`CREATE TABLE "temporary_people" ("people_id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "last_name" varchar(255) NOT NULL, "first_name" varchar(255) NOT NULL, "sex" varchar(6) NOT NULL, "blood_type" varchar(2) NOT NULL, "serveYears" integer NOT NULL, "role" varchar(100) NOT NULL, "hometown" varchar NOT NULL, "team_id" integer, CONSTRAINT "UQ_017abdefead91b361b5e7ac108f" UNIQUE ("people_id"), CONSTRAINT "FK_6b7b841de9de9db18199f718ee9" FOREIGN KEY ("team_id") REFERENCES "team" ("team_id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
        await queryRunner.query(`INSERT INTO "temporary_people"("people_id", "last_name", "first_name", "sex", "blood_type", "serveYears", "role", "hometown", "team_id") SELECT "people_id", "last_name", "first_name", "sex", "blood_type", "serveYears", "role", "hometown", "team_id" FROM "people"`);
        await queryRunner.query(`DROP TABLE "people"`);
        await queryRunner.query(`ALTER TABLE "temporary_people" RENAME TO "people"`);
        await queryRunner.query(`CREATE TABLE "temporary_supply" ("supply_id" varchar(255) PRIMARY KEY NOT NULL, "teamTeamId" integer, CONSTRAINT "UQ_ea803c518d575c82cd33f5668df" UNIQUE ("supply_id"), CONSTRAINT "FK_cd14bd0c792703a7fce50828bb0" FOREIGN KEY ("teamTeamId") REFERENCES "team" ("team_id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
        await queryRunner.query(`INSERT INTO "temporary_supply"("supply_id", "teamTeamId") SELECT "supply_id", "teamTeamId" FROM "supply"`);
        await queryRunner.query(`DROP TABLE "supply"`);
        await queryRunner.query(`ALTER TABLE "temporary_supply" RENAME TO "supply"`);
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`ALTER TABLE "supply" RENAME TO "temporary_supply"`);
        await queryRunner.query(`CREATE TABLE "supply" ("supply_id" varchar(255) PRIMARY KEY NOT NULL, "teamTeamId" integer, CONSTRAINT "UQ_ea803c518d575c82cd33f5668df" UNIQUE ("supply_id"))`);
        await queryRunner.query(`INSERT INTO "supply"("supply_id", "teamTeamId") SELECT "supply_id", "teamTeamId" FROM "temporary_supply"`);
        await queryRunner.query(`DROP TABLE "temporary_supply"`);
        await queryRunner.query(`ALTER TABLE "people" RENAME TO "temporary_people"`);
        await queryRunner.query(`CREATE TABLE "people" ("people_id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "last_name" varchar(255) NOT NULL, "first_name" varchar(255) NOT NULL, "sex" varchar(6) NOT NULL, "blood_type" varchar(2) NOT NULL, "serveYears" integer NOT NULL, "role" varchar(100) NOT NULL, "hometown" varchar NOT NULL, "team_id" integer, CONSTRAINT "UQ_017abdefead91b361b5e7ac108f" UNIQUE ("people_id"))`);
        await queryRunner.query(`INSERT INTO "people"("people_id", "last_name", "first_name", "sex", "blood_type", "serveYears", "role", "hometown", "team_id") SELECT "people_id", "last_name", "first_name", "sex", "blood_type", "serveYears", "role", "hometown", "team_id" FROM "temporary_people"`);
        await queryRunner.query(`DROP TABLE "temporary_people"`);
        await queryRunner.query(`DROP TABLE "supply"`);
        await queryRunner.query(`DROP TABLE "equipment"`);
        await queryRunner.query(`DROP TABLE "role"`);
        await queryRunner.query(`DROP TABLE "people"`);
        await queryRunner.query(`DROP TABLE "team"`);
        await queryRunner.query(`DROP TABLE "software"`);
    }

}


๊ทธ๋Ÿผ ์œ„์™€ ๊ฐ™์ด ํŒŒ์ผ์ด ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋  ๊ฑฐ์—์š”.
์ด ํŒŒ์ผ์„ ๋ณด๋ฉด DML Query๊ฐ€ ์ž‘์„ฑ ๋˜์–ด ์žˆ๋Š”๋ฐ, ์ด๋Š” Entity๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ TypeORM์ด ๋งŒ๋“ค์–ด ์ค€๊ฑฐ์—์š”.

 

์ด๋ฒˆ์—๋Š” Team๊ณผ Supply์— ๋Œ€ํ•œ Migraion์„ ๋งŒ๋“ค์–ด ๋ณผ๊ฒŒ์š”.

typeorm migraion:create <Seed File Name>

 


์ด ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ์œ„์™€ ๊ฐ™์ด Project Root Directory์— ์œ„์™€ ๊ฐ™์ด
TypeScript ํ™•์žฅ์ž๋ฅผ ๊ฐ–์€ ํŒŒ์ผ์ด ์ƒ์„ฑ๋˜๋Š”๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.


์ฃผ๋‹ˆ๋Š” ์œ„์™€ ๊ฐ™์ด Directory๋ฅผ ๋ณ€๊ฒฝํ•ด์„œ ์‚ฌ์šฉํ•ด ๋ณด๋ ค๊ณ  ํ•ด์š”.


SeedTeam.ts (๋ณ€๊ฒฝ ์ „)

 

SeedTeam.ts (๋ณ€๊ฒฝ ๋’ค)

up()์— ๋Œ€ํ•ด ๋ถ„์„์„ ํ•ด๋ณผ๊ฒŒ์š”. ์ด Method(๋ฉ”์„œ๋“œ)๋Š” Database(๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค)
Scheam(์Šคํ‚ค๋งˆ)๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ , ํŠน์ • Table(ํ…Œ์ด๋ธ”)์„ ์ƒ์„ฑํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋Š” ์นœ๊ตฌ์—์š”.

up()์€ queryRunner.createTable()์„ ํ™œ์šฉํ•˜์—ฌ team์ด๋ผ๋Š” ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ค๊ฒŒ ํ•ด์ฃผ๋Š”๋ฐ,

์ด ๋•Œ, team_id๋Š” bigint Type์˜ ์ปฌ๋Ÿผ์„ ๋งŒ๋“ค๊ณ , ๊ธฐ๋ณธ ํ‚ค์ด๋ฉฐ,
์ž๋™์œผ๋กœ ์ƒ์„ฑ๋˜๋Š” increment ์ „๋žต์„ ์‚ฌ์šฉํ•˜๊ฒ ๋‹ค๊ณ  ๋ช…์‹œํ•ด ์ค€ ๊ฒƒ์ด์—์š”.

name์€ varchart Type์œผ๋กœ Null์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š๊ฒ ๋‹ค๊ณ  ๋ช…์‹œํ•ด ์ค€ ๋ถ€๋ถ„์ด์—์š”.

isPrimary๋Š” ํ•ด๋‹น ์ปฌ๋Ÿผ์ด ๊ธฐ๋ณธ ํ‚ค์ธ์ง€ ์—ฌ๋ถ€๋ฅผ ๋ช…์‹œํ•ด์ฃผ๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ ๊ฒƒ์ด๊ณ ,
isGenerated๋Š” ํ•ด๋‹น ์ปฌ๋Ÿผ์ด ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๋ช…์‹œํ•ด ์ฃผ๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ ๊ฒƒ์ด๊ณ ,
generationStrategy๋Š” ์ž๋™ ์ƒ์„ฑ๋˜๋Š” ์ปฌ๋Ÿผ ์ „๋žต์„ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•ด ๋ช…์‹œํ•œ ๊ฒƒ์ธ๋ฐ,
์œ„ ์ฝ”๋“œ๋Š” Increament ์ „๋žต์„ ์‚ฌ์šฉํ•˜๊ฒ ๋‹ค๊ณ  ๋ช…์‹œํ•œ ๋ถ€๋ถ„์ด์—์š”.

์ด๋ ‡๊ฒŒ ํ•ด์„œ team Table์„ ์ƒ์„ฑํ•˜๋Š” Migration์„ ๋งŒ๋“ค์—ˆ๊ณ , up()์„ ํ†ตํ•ด Table์„ ์ƒ์„ฑํ•˜๊ณ ,
down()์„ ํ†ตํ•ด ํ•ด๋‹น Table์ด ์‚ญ์ œ๋  ์ˆ˜ ์žˆ๋„๋ก Logic์„ ๊ตฌํ˜„ํ•˜์˜€์–ด์š”.

src/database/seeds/team.seed.ts

๊ทธ๋ฆฌ๊ณ  ์œ„์™€ ๊ฐ™์ด team Table์— ๋“ค์–ด๊ฐˆ Mock Data๋ฅผ ๋ช…์‹œํ•ด ์ฃผ์—ˆ์–ด์š”.

SeedTeam.ts (๋ณ€๊ฒฝ ๋’ค)


ํ•˜์ง€๋งŒ, TypeORM์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ณ , Entity๋ฅผ ๊ตฌ์„ฑํ•ด์ค€ ๋’ค 

data-source.ts


์œ„์™€ ๊ฐ™์ด synchronize๋ฅผ true๋กœ ํ•ด์ฃผ๋ฉด TypeORM์ด ์•Œ์•„์„œ Entity๋ฅผ ๋ถ„์„ํ•ด์„œ Table๊ณผ Column์„ ๋งŒ๋“ค์–ด ์ฃผ์–ด์š”.

ํ•˜์ง€๋งŒ, ์›ํ•˜๋Š” ์‹œ์ ์˜ ์ •์˜๋œ ์Šคํ‚ค๋งˆ(Entity)๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์œ„์™€ ๊ฐ™์ด ํ•˜๊ฒŒ ๋˜๋ฉด ์„œ๋ฒ„๊ฐ€ ๊ธฐ๋™๋  ๋•Œ๋งˆ๋‹ค ์Šคํ‚ค๋งˆ์— ๋”ฐ๋ฅธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ตฌ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งค์šฐ ์‹ ์ค‘ํ•ด์•ผ ํ•ด์š”.

๊ทธ๋ž˜์„œ ๋งŽ์ด ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด Migration์ด์—์š”.

data-source.ts


๊ทธ๋ž˜์„œ ์œ„์™€ ๊ฐ™์ด ๋ณ€๊ฒฝํ•ด ์ฃผ์—ˆ์–ด์š”.

npm run migration:generate

728x90


์œ„ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜๋ฉด

src/database/migrations/1701333148773-Migration.ts


์ด๋ ‡๊ฒŒ ๋งŒ๋“ค์–ด์ง„๋‹ค๊ณ  ํ–ˆ๋Š”๋ฐ,
๋ณด์‹œ๋‹ค์‹œํ”ผ DDL ๋ฌธ์ด ๋“ค์–ด๊ฐ€ ์žˆ๊ณ , TABLE์„ ๋งŒ๋“œ๋Š” SQL์ด ๋“ค์–ด๊ฐ€ ์žˆ๋Š”๊ฑธ ๋ณผ ์ˆ˜ ์žˆ์–ด์š”.

์ด ๊ตฌ๋ฌธ๋“ค๋กœ ์ธํ•ด synchronize๋ฅผ False๋กœ ํ•ด๋„ Table์ด ์•Œ์•„์„œ ๋งŒ๋“ค์–ด์ ธ์š”.






SeedSupply.ts (๋ณ€๊ฒฝ ์ „)

 

SeedSupply.ts (๋ณ€๊ฒฝ ํ›„)


์ตœ์ดˆ ์œ„์™€ ๊ฐ™์ด team Table์„ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์–ด์š”.
๊ทธ๋ฆฌ๊ณ  team Table๊ณผ ๊ด€๊ณ„๋ฅผ ๋งบ๊ธฐ ์œ„ํ•ด team_id๋ฅผ FK๋กœ ์„ค์ •ํ•ด ์ฃผ์—ˆ์–ด์š”.


SeedSupply.ts (๋ณ€๊ฒฝ ํ›„)


๊ทธ๋ฆฌ๊ณ  DML Native Query๋ฅผ ์ด์šฉํ•ด์„œ Mock Data๋ฅผ ๋„ฃ์„ ์ˆ˜ ์žˆ๋„๋ก ์ฒ˜๋ฆฌํ•ด ์ฃผ์—ˆ์–ด์š”.



import { MigrationInterface, QueryRunner, Table } from "typeorm";

export class SeedTeam1701325303468 implements MigrationInterface {
  public async up(queryRunner: QueryRunner): Promise<void> {
    const teamTableExist = await queryRunner.hasTable("team");

    if (!teamTableExist) {
      await queryRunner.createTable(
        new Table({
          name: "team",
          columns: [
            { name: "team_id", type: "integer", isPrimary: true, isGenerated: true, generationStrategy: "increment" },
            { name: "manager", type: "varchar", isNullable: false },
            { name: "office", type: "varchar", isNullable: false },
            { name: "extension_number", type: "varchar", isNullable: false },
            { name: "mascot", type: "varchar", isNullable: false },
            { name: "cleaning_duty", type: "varchar", isNullable: false },
            { name: "project", type: "varchar", isNullable: false },
          ],
        }),
        true,
      );
    }

    //Seeding Data ์‚ฝ์ž…
    await queryRunner.query(
      `INSERT INTO \`team\` (\`manager\`, \`office\`, \`extension_number\`, \`mascot\`, \`cleaning_duty\`, \`project\`) VALUES ('Mandy Warren', '101A', '#5709', 'Panda','Monday','Hyperion')`,
    );
    await queryRunner.query(
      `INSERT INTO \`team\` (\`manager\`, \`office\`, \`extension_number\`, \`mascot\`, \`cleaning_duty\`, \`project\`) VALUES ('Stewart Grant', '101B', '#4012', 'Tadpole','Tuesday','Zen')`,
    );
    await queryRunner.query(
      `INSERT INTO \`team\` (\`manager\`, \`office\`, \`extension_number\`, \`mascot\`, \`cleaning_duty\`, \`project\`) VALUES ('Smantha Wheatly', '102A', '#3852', 'Falcon','Wednesday','Duranno')`,
    );
    await queryRunner.query(
      `INSERT INTO \`team\` (\`manager\`, \`office\`, \`extension_number\`, \`mascot\`, \`cleaning_duty\`, \`project\`) VALUES ('Francis Buckley', '103B', '#1039', 'Beaver','Thursday','Genghis')`,
    );
    await queryRunner.query(
      `INSERT INTO \`team\` (\`manager\`, \`office\`, \`extension_number\`, \`mascot\`, \`cleaning_duty\`, \`project\`) VALUES ('Blake Smith', '104A', '#7750', 'Wildcat','Friday','Acheron')`,
    );
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.dropTable("team");
  }
}
๋ฐ˜์‘ํ˜•


TeamSeed๋Š” ์œ„์™€ ๊ฐ™์ด ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์–ด์š”.

npm run migration:run

 

โœ˜ JunySS ๐Ÿ‡ฐ  ๎‚ฐ ~/Programming/study/graphql-study/type-script-nest-graph-ql-์‹ค์Šต ๎‚ฐ ๎‚  master ±โœš ๎‚ฐ npm run migration:run

> type-script-nest-graph-ql-์‹ค์Šต@0.0.1 migration:run
> npm run typeorm  migration:run


> type-script-nest-graph-ql-์‹ค์Šต@0.0.1 typeorm
> ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js --dataSource ./data-source.ts migration:run

query: SELECT * FROM "sqlite_master" WHERE "type" = 'table' AND "name" = 'migrations'
query: SELECT * FROM "migrations" "migrations" ORDER BY "id" DESC
0 migrations are already loaded in the database.
2 migrations were found in the source code.
2 migrations are new migrations must be executed.
query: PRAGMA foreign_keys = OFF
query: BEGIN TRANSACTION
query: SELECT * FROM "sqlite_master" WHERE "type" = 'table' AND "name" = 'supply'
query: CREATE TABLE "supply" ("supply_id" varchar NOT NULL, "team_id" integer NOT NULL, CONSTRAINT "FK_88d10c4e569685c8345705b213f" FOREIGN KEY ("team_id") REFERENCES "team" ("team_id") ON DELETE CASCADE)
query: INSERT INTO `supply` (`supply_id`, `team_id`) VALUES ('ergonomic mouse', 1)
query: INSERT INTO `supply` (`supply_id`, `team_id`) VALUES ('mug', 1)
query: INSERT INTO `supply` (`supply_id`, `team_id`) VALUES ('webcam', 2)
query: INSERT INTO `supply` (`supply_id`, `team_id`) VALUES ('hoodie', 2)
query: INSERT INTO `supply` (`supply_id`, `team_id`) VALUES ('chair', 3)
query: INSERT INTO `supply` (`supply_id`, `team_id`) VALUES ('usb hub', 3)
query: INSERT INTO `supply` (`supply_id`, `team_id`) VALUES ('headphone', 4)
query: INSERT INTO `supply` (`supply_id`, `team_id`) VALUES ('stempler', 4)
query: INSERT INTO `supply` (`supply_id`, `team_id`) VALUES ('calculator', 5)
query: INSERT INTO `supply` (`supply_id`, `team_id`) VALUES ('t shirt', 5)
query: INSERT INTO "migrations"("timestamp", "name") VALUES (1701325299045, ?) -- PARAMETERS: ["SeedSupply1701325299045"]
Migration SeedSupply1701325299045 has been  executed successfully.
query: SELECT * FROM "sqlite_master" WHERE "type" = 'table' AND "name" = 'team'
query: SELECT * FROM "sqlite_master" WHERE "type" = 'table' AND "name" = 'team'
query: CREATE TABLE "team" ("team_id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "manager" varchar NOT NULL, "office" varchar NOT NULL, "extension_number" varchar NOT NULL, "mascot" varchar NOT NULL, "cleaning_duty" varchar NOT NULL, "project" varchar NOT NULL)
query: INSERT INTO `team` (`manager`, `office`, `extension_number`, `mascot`, `cleaning_duty`, `project`) VALUES ('Mandy Warren', '101A', '#5709', 'Panda','Monday','Hyperion')
query: INSERT INTO `team` (`manager`, `office`, `extension_number`, `mascot`, `cleaning_duty`, `project`) VALUES ('Stewart Grant', '101B', '#4012', 'Tadpole','Tuesday','Zen')
query: INSERT INTO `team` (`manager`, `office`, `extension_number`, `mascot`, `cleaning_duty`, `project`) VALUES ('Smantha Wheatly', '102A', '#3852', 'Falcon','Wednesday','Duranno')
query: INSERT INTO `team` (`manager`, `office`, `extension_number`, `mascot`, `cleaning_duty`, `project`) VALUES ('Francis Buckley', '103B', '#1039', 'Beaver','Thursday','Genghis')
query: INSERT INTO `team` (`manager`, `office`, `extension_number`, `mascot`, `cleaning_duty`, `project`) VALUES ('Blake Smith', '104A', '#7750', 'Wildcat','Friday','Acheron')
query: INSERT INTO "migrations"("timestamp", "name") VALUES (1701325303468, ?) -- PARAMETERS: ["SeedTeam1701325303468"]
Migration SeedTeam1701325303468 has been  executed successfully.
query: COMMIT
query: PRAGMA foreign_keys = ON


์œ„์™€ ๊ฐ™์ด Query๊ฐ€ ๋‚ ์•„๊ฐ€๋ฉด์„œ Mock Data๊ฐ€ ๋„ฃ์–ด์ง„๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

๋งŒ์•ฝ ์œ„ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ–ˆ๋Š”๋ฐ, ์ด๋ฏธ ์–ด๋–ค Table์ด ๋งŒ๋“ค์–ด์ ธ ์žˆ๋‹ค๋Š”
Error๊ฐ€ ๋‚˜์˜ค๊ฒŒ ๋˜๋ฉด

npm run migration:generate


์ด ๋ช…๋ น์–ด๋Š” ์ž…๋ ฅํ•˜์ง€ ๋ง๊ณ , ์ง„ํ–‰ํ•˜๋ฉด ๋˜๊ณ ,
์ด๋ฏธ ์ž…๋ ฅํ–ˆ๋‹ค๋ฉด ์ด ๋ช…๋ น์–ด๋กœ ๋งŒ๋“ค์–ด์ง„ TypeScript ํŒŒ์ผ์„ ์‚ญ์ œํ•ด ์ฃผ๋ฉด ๋ผ์š”.

ํ•ด๋‹น Error๋Š” generate๋กœ Table์„ ์ด๋ฏธ ๋งŒ๋“ค์—ˆ๋Š”๋ฐ, Seed์—์„œ ๋˜ Table์„ ๋งŒ๋“œ๋ ค๊ณ  ํ•˜๋‹ˆ ๋ฐœ์ƒ๋˜๋Š” ๋ฌธ์ œ์ด๊ธฐ ๋•Œ๋ฌธ์ด์—์š”.


select * from supply;

 

select * from team;


๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๊ฐ’๋„ ์ž˜ ๋“ค์–ด๊ฐ„ ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

 

 

 

 

NestJS๋กœ ๋ฐฐ์šฐ๋Š” ๋ฐฑ์—”๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ:ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์˜ ์ฐจ์„ธ๋Œ€ ์„œ๋ฒ„ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋งŒ๋‚˜๋‹ค

COUPANG

www.coupang.com

"์ด ํฌ์ŠคํŒ…์€ ์ฟ ํŒก ํŒŒํŠธ๋„ˆ์Šค ํ™œ๋™์˜ ์ผํ™˜์œผ๋กœ, ์ด์— ๋”ฐ๋ฅธ ์ผ์ •์•ก์˜ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค."

 

 

 

 

GitHub - junyharang-coding-study/GraphQL-Study: GraphQL์„ ๊ณต๋ถ€ํ•˜๊ณ , ์‹ค์Šตํ•œ ์ฝ”๋“œ์—์š” ๐Ÿ˜€

GraphQL์„ ๊ณต๋ถ€ํ•˜๊ณ , ์‹ค์Šตํ•œ ์ฝ”๋“œ์—์š” ๐Ÿ˜€. Contribute to junyharang-coding-study/GraphQL-Study development by creating an account on GitHub.

github.com

 

 

 

 

 

 

๐Ÿง ์ฐธ๊ณ  ์ž๋ฃŒ

 

How to seed TypeORM

There is no need for any packages. It is very straightforward. I am using it to seed my production server with some initial data like…

sushilkbansal.medium.com

 

 

 

[NestJS] TypeORM ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜

TypeORM ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ธฐ

velog.io

 

 

 

 

 

 

 

728x90
๋ฐ˜์‘ํ˜•