@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'sqlite', 
      database: 'db.sqlite',
      synchronize: true
    }),
    UsersModule, 
    ReportsModule],
  controllers: [AppController],
  providers: [AppService],
})
@Entity()
export class User {
	// 자동으로 생성되는 칼럼
    // Primary Key 로 사용됨.
    @PrimaryGeneratedColumn()
    id: number;
    // 이하 DB Column
    @Column()
    email: string;
    @Column()
    password: string;
}
@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UsersController],
  providers: [UsersService]
})
@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'sqlite',
      database: 'db.sqlite',
      synchronize: true,
      entities: [User],
    }),
    UsersModule, 
    ReportsModule],
  controllers: [AppController],
  providers: [AppService],
})
    constructor(
        @InjectRepository(User) private repo: Repository<User>
    ) {}
    create(email: string, password: string) {
        const user = this.repo.create({email, password});
        return this.repo.save(user);
    }
@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;
    @Column()
    email: string;
    @Column()
    password: string;
    @AfterInsert()
    logInsert() {
        console.log(`Inserted User with ${this.id}`);
    }
}
    find(email: string) {
        return this.repo.find({ where: { email }})
    }
    findOne(id: number) {
        return this.repo.findOneBy({id})
    }
updateData(id: number, attrs: Partial<User>) {
}
    async update(id: number, attrs: Partial<User>) {
        const user = await this.findOne(id);
        if(!user) {
            throw new Error('user not found');
        }
        // attr의 값을 user에 대입한다.
        Object.assign(user, attrs)
        return this.repo.save(user);
    }
    @Column()
    @Exclude()
    password: string;
    @UseInterceptors(ClassSerializerInterceptor)
    @Get('/:id')
    async findUser(@Param('id') id: string) {
        const user = await this.usersService.findOne(parseInt(id));
        if (!user) throw new NotFoundException('user not found');
        return user;
    }
class CustomInterceptor {
intercept(context: ExecutionContext, next: CallHandler);
}
export class SerializeInterceptor implements NestInterceptor {
    constructor(private dto: any) {}
    intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
        return next.handle().pipe(
            map(
                (data: any) => {
                    return plainToInstance(UserDto, data, {
                        excludeExtraneousValues: true,
                    })
                }
            )
        )
    }
}
    @UseInterceptors(SerializeInterceptor)
    @Get()
    findAllUsers(@Query('email') email: string) {
        return this.usersService.find(email);
    }
import { Expose } from "class-transformer";
export class UserDto {
    @Expose()
    id: number;
    @Expose()
    email: string;
}
        return next.handle().pipe(
            map(
                (data: any) => {
                    return plainToInstance(UserDto, data, {
                        excludeExtraneousValues: true,
                    })
                }
            )
        )
    }
export function Serialize(dto: any) {
    return UseInterceptors(new SerializeInterceptor(UserDto));
}
를 interceptor 코드에 추가하고
    @Serialize(UserDto)
    @Get()
    findAllUsers(@Query('email') email: string) {
        return this.usersService.find(email);
    }
와 같이 사용시 여러 종류의 Dto에 대응하도록 구현할수 있다.
// report 에서 자신이 있는 부분을 알린다.
    @OneToMany(() => Report, (report) => report.user)
    reports: Report[];
// user에서 자신이 있는 부분을 알린다.
    @ManyToOne(() => User, (user) => user.reports)
    user: User;
this.repo
.createQueryBuilder()
.select('*')
.where('make = :make', {make: brand}) // :make에 make key 의 값이 들어간다. SQL Inject 방지용.
.andWhere('model = :model', {model})
.orderBy(':id', 'DESC')
.setParameters({ id })
.getRawMany()
import { ConfigService } from '@nestjs/config';
import { config } from 'dotenv';
import { DataSource } from 'typeorm';
config();
const configService = new ConfigService();
export default new DataSource({
  type: 'mysql',
  host: configService.get('DB_HOST'),
  port: configService.get<number>('DB_PORT'),
  username: configService.get('DB_USERNAME'),
  password: configService.get('DB_PASSWORD'),
  database: configService.get('DB_DATABASE'),
  synchronize: false,
  entities: ['src/**/*.entity.ts'],
  migrations: ['src/database/migrations/*.ts'],
  migrationsTableName: 'migrations',
});
    "typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js --dataSource ./data-source.ts",
    "migration:create": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:create ./src/database/migrations/Migration",
    "migration:generate": "npm run typeorm migration:generate ./src/database/migrations/Migration",
    "migration:run": "npm run typeorm  migration:run",
    "migration:revert": "npm run typeorm migration:revert",
import { MigrationInterface, QueryRunner } from 'typeorm';
export class Migration1675868794851 implements MigrationInterface {
  name = 'Migration1675868794851';
  public async up(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(
      `ALTER TABLE \`cat\` ADD \`kind\` varchar(255) NOT NULL`,
    );
  }
  public async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(`ALTER TABLE \`cat\` DROP COLUMN \`kind\``);
  }
}
npm run migration:run
npm run migration:revert