본문 바로가기
TIL

Nest.js, TypeScript 프로젝트 mindo - 2 Hot( Entity )

by 황민도 2024. 8. 22.

전 문서에서는 DB 연결을 하는 것까지 작성하였고

이어서 구성된 ERD 를 참고하여 Entities 작성 및 dto를 작성해 볼 예정이다.

 

일단 기본적으로 데코레이터를 작성하지 않고 기본적인 틀부터 작성을 해보겠다.

 

Entity 순서 : 1. user, 2. show, 3. schedule, 4. seat, 5. book 

 

1.

import { UserRole } from '../roles/user-role';
import { Book } from 'src/book/entities/book.entity';

export class User {
  id: number;
  email: string;
  password: string;
  nickname: string;
  points: number;
  role: UserRole;
  createdAt: Date;
  updatedAt: Date;
  books: Book[];
}
export enum UserRole {
  Customer = 'Customer',
  Admin = 'Admin',
}

 

2.

import { CategoryRole } from '../roles/category-role';
import { Schedule } from './schedule.entity';

export class Show {
  id: number;
  title: string;
  description: string;
  category: CategoryRole;
  place: string;
  price: number;
  thumbnail: string;
  createdAt: Date;
  updatedAt: Date;
  schedules: Schedule[];
}
export enum CategoryRole {
  Concert = 'Concert',
  Musical = 'Musical',
  Play = 'Play',
  Sports = 'Sports',
  Event = 'Event',
}

 

3.

import { Show } from './show.entity';
import { Seat } from './seat.entity';

export class Schedule {
  id: number;
  showId: number;
  date: Date;
  time: string;
  createdAt: Date;
  updatedat: Date;
  show: Show;
  seat: Seat;
}

자칫 여기서 ERD 구성에 따라 1(schedule):N(books) 연결로 인해 books: Book[]; 가 필요하다 생각 할 수 있지만

Schedule 은 예약정보를 가져올 필요가 없기 때문에 books: Book[]; 를 굳이 연결해줄 필요는 없다.

 

4.

import { Schedule } from './schedule.entity';

export class Seat {
  id: number;
  scheduleId: number;
  availableSeats: number;
  totalSeats: number;
  createdAt: Date;
  updatedAt: Date;
  schedule: Schedule;
}

 

5.

import { Schedule } from 'src/show/entities/schedule.entity';

export class Book {
  id: number;
  userId: number;
  scheduleId: number;
  createdAt: Date;
  updatedAt: Date;
  user: User;
  schedule: Schedule;
}

 


 

데코레이터를 곁들인 Entities

 

1. 

import {
  Column,
  CreateDateColumn,
  Entity,
  OneToMany,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
} from 'typeorm';
import { UserRole } from '../roles/user-role';
import { Book } from 'src/book/entities/book.entity';
import { getPoints } from '../points/points';

@Entity({
  name: 'users',
})
export class User {
  @PrimaryGeneratedColumn({ unsigned: true })
  id: number;

  @Column({ unique: true })
  email: string;

  @Column()
  password: string;

  @Column()
  nickname: string;

  @Column({ unsigned: true, default: getPoints.DEFAULT.POINTS })
  points: number;

  @Column({ type: 'enum', enum: UserRole, default: 'Customer' })
  role: UserRole;

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;

  @OneToMany((type) => Book, (book) => book.user)
  books: Book[];
}
export const getPoints = {
  DEFAULT: {
    POINTS: 1000000,
  },
  MAXIMUM: {
    POINTS: 50000,
  },
};

 

2.

import {
  Column,
  CreateDateColumn,
  Entity,
  OneToMany,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
} from 'typeorm';
import { CategoryRole } from '../roles/category-role';
import { Schedule } from './schedule.entity';

@Entity({
  name: 'shows',
})
export class Show {
  @PrimaryGeneratedColumn({ unsigned: true })
  id: number;

  @Column({ unique: true })
  title: string;

  @Column()
  description: string;

  @Column({ type: 'enum', enum: CategoryRole })
  category: CategoryRole;

  @Column()
  place: string;

  @Column()
  price: number;

  @Column({ nullable: true })
  thumbnail: string;

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;

  @OneToMany((type) => Schedule, (schedule) => schedule.show)
  schedules: Schedule[];
}

 

3.

import {
  Column,
  CreateDateColumn,
  Entity,
  ManyToOne,
  OneToMany,
  OneToOne,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
} from 'typeorm';
import { Show } from './show.entity';
import { Seat } from './seat.entity';
import { Book } from 'src/book/entities/book.entity';

@Entity({
  name: 'schedules',
})
export class Schedule {
  @PrimaryGeneratedColumn({ unsigned: true })
  id: number;

  @Column({ unsigned: true })
  showId: number;

  @Column()
  date: Date;

  @Column({ type: 'time' })
  time: string;

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedat: Date;

  @ManyToOne((type) => Show, (show) => show.schedules, { onDelete: 'CASCADE' })
  show: Show;

  @OneToOne((type) => Seat, (seat) => seat.schedule, { cascade: true })
  seat: Seat;
}

 

4.

import {
  Column,
  CreateDateColumn,
  Entity,
  JoinColumn,
  OneToOne,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
} from 'typeorm';
import { Schedule } from './schedule.entity';
import { seatCounts } from '../counts/seat-count';

@Entity({
  name: 'seats',
})
export class Seat {
  @PrimaryGeneratedColumn({ unsigned: true })
  id: number;

  @Column({ unsigned: true })
  scheduleId: number;

  @Column({ unsigned: true, default: seatCounts.DEFAULT.TOTAL_SEAT_COUNTS })
  availableSeats: number;

  @Column({ unsigned: true, default: seatCounts.DEFAULT.TOTAL_SEAT_COUNTS })
  totalSeats: number;

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;

  @OneToOne((type) => Schedule, { onDelete: 'CASCADE' })
  @JoinColumn()
  schedule: Schedule;
}
export const seatCounts = {
  DEFAULT: {
    TOTAL_SEAT_COUNTS: 100,
  },
};

 

5.

import { Schedule } from 'src/show/entities/schedule.entity';
import { User } from 'src/user/entities/user.entity';
import {
  Column,
  CreateDateColumn,
  Entity,
  ManyToOne,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
} from 'typeorm';

@Entity({
  name: 'books',
})
export class Book {
  @PrimaryGeneratedColumn({ unsigned: true })
  id: number;

  @Column({ unsigned: true })
  userId: number;

  @Column({ unsigned: true })
  scheduleId: number;

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;

  @ManyToOne((type) => User, (user) => user.books, { onDelete: 'CASCADE' })
  user: User;

  @ManyToOne((type) => Schedule, { onDelete: 'CASCADE' })
  schedule: Schedule;
}

 

 

[Nest] 70864  - 2024. 08. 23. 오후 4:37:17   ERROR [TypeOrmModule] Unable to connect to the database. Retrying (2)...
QueryFailedError: Referencing column 'showId' and referenced column 'id' in foreign key constraint 'FK_fd750e49050f4ee65a4bffb25f1' are incompatible.
    at Query.onResult (C:\Users\windo\nestjs-mindo\node_modules\typeorm\driver\src\driver\mysql\MysqlQueryRunner.ts:246:33)
    at Query.execute (C:\Users\windo\nestjs-mindo\node_modules\mysql2\lib\commands\command.js:36:14)
    at PoolConnection.handlePacket (C:\Users\windo\nestjs-mindo\node_modules\mysql2\lib\connection.js:481:34)
    at PacketParser.onPacket (C:\Users\windo\nestjs-mindo\node_modules\mysql2\lib\connection.js:97:12)
    at PacketParser.executeStart (C:\Users\windo\nestjs-mindo\node_modules\mysql2\lib\packet_parser.js:75:16)
    at Socket.<anonymous> (C:\Users\windo\nestjs-mindo\node_modules\mysql2\lib\connection.js:104:25)
    at Socket.emit (node:events:518:28)
    at addChunk (node:internal/streams/readable:559:12)
    at readableAddChunkPushByteMode (node:internal/streams/readable:510:3)
    at Readable.push (node:internal/streams/readable:390:5)

오류 발생

에러 메시지를 보면 showId와 id라는 컬럼이 외래 키 제약 조건에서 서로 호환되지 않는다는 내용인것 같다.

 

windo@DESKTOP-6SMB85M MINGW64 ~/nestjs-mindo (main)
$ npm i --save typeorm-naming-strategies

added 1 package, and audited 749 packages in 2s

114 packages are looking for funding
  run `npm fund` for details

37 moderate severity vulnerabilities

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

기억을 되살려서 Entity 참조테이블 데코레이터에 JoinColumn 을 사용하지 않기 위해 기억을 더듬어

패키지를 설치 하였다.

 

import { ConfigModule, ConfigService } from '@nestjs/config';
import { Book } from 'src/book/entities/book.entity';
import { Schedule } from 'src/show/entities/schedule.entity';
import { Seat } from 'src/show/entities/seat.entity';
import { Show } from 'src/show/entities/show.entity';
import { User } from 'src/user/entities/user.entity';
import { SnakeNamingStrategy } from 'typeorm-naming-strategies';

export const typeOrmConfig = {
  imports: [ConfigModule],
  inject: [ConfigService],
  useFactory: (configService: ConfigService) => ({
    type: configService.get('DB_TYPE'),
    host: configService.get('DB_HOST'),
    port: configService.get('DB_PORT'),
    username: configService.get('DB_USERNAME'),
    password: configService.get('DB_PASSWORD'),
    database: configService.get('DB_NAME'),
    // entities: [User, Show, Schedule, Seat, Book],
    autoLoadEntities: true,
    synchronize: configService.get('DB_SYNC'),
    namingStrategy: new SnakeNamingStrategy(),
  }),
};

그리고 namingStrategy 를 사용하여 위와같이 작성을 해주었다.

 

그러나 오류는 계속 같은 문구로 발생 하였다.

 

그냥 직접적으로 연결해주었다.

 

  @ManyToOne((type) => Show, (show) => show.schedules, { onDelete: 'CASCADE' })
  @JoinColumn({ name: 'showId' })
  show: Show;

 

[Nest] 86984  - 2024. 08. 23. 오후 5:16:13     LOG [NestFactory] Starting Nest application...
[Nest] 86984  - 2024. 08. 23. 오후 5:16:13     LOG [InstanceLoader] TypeOrmModule dependencies initialized +9ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:13     LOG [InstanceLoader] ConfigHostModule dependencies initialized +1ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:13     LOG [InstanceLoader] AppModule dependencies initialized +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:13     LOG [InstanceLoader] UserModule dependencies initialized +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:13     LOG [InstanceLoader] ShowModule dependencies initialized +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:13     LOG [InstanceLoader] BookModule dependencies initialized +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:13     LOG [InstanceLoader] AuthModule dependencies initialized +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:13     LOG [InstanceLoader] ConfigModule dependencies initialized +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:13     LOG [InstanceLoader] ConfigModule dependencies initialized +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [InstanceLoader] TypeOrmCoreModule dependencies initialized +565ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [InstanceLoader] TypeOrmModule dependencies initialized +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [InstanceLoader] TypeOrmModule dependencies initialized +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [InstanceLoader] TypeOrmModule dependencies initialized +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RoutesResolver] AppController {/}: +4ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/, GET} route +1ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RoutesResolver] UserController {/user}: +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/user, POST} route +1ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/user, GET} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/user/:id, GET} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/user/:id, PATCH} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/user/:id, DELETE} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RoutesResolver] ShowController {/show}: +1ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/show, POST} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/show, GET} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/show/:id, GET} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/show/:id, PATCH} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/show/:id, DELETE} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RoutesResolver] BookController {/book}: +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/book, POST} route +1ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/book, GET} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/book/:id, GET} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/book/:id, PATCH} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/book/:id, DELETE} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RoutesResolver] AuthController {/auth}: +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/auth, POST} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/auth, GET} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/auth/:id, GET} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/auth/:id, PATCH} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [RouterExplorer] Mapped {/auth/:id, DELETE} route +0ms
[Nest] 86984  - 2024. 08. 23. 오후 5:16:14     LOG [NestApplication] Nest application successfully started +2ms

 

정상작동... 어디서부터 틀어진 것인지 모르겠다. 다시한번 살펴보아야겠다.