๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
BE/Node

Nest.js(4)

by Chars4785 2022. 6. 29.

MySQL ์„ค์ •

๐Ÿ’ก ORM์ด๋ž€?

ORM(Object-Relational Mapping)์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๊ด€๊ณ„๋ฅผ ๊ฐ์ฒด๋กœ ๋ฐ”๊พธ์–ด ๊ฐœ๋ฐœ์ž๊ฐ€ OOP๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‰ฝ๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋„๋ก ํ•ด ์ฃผ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. SQL๋ฌธ์„ ๊ทธ๋Œ€๋กœ ์ฝ”๋“œ์— ๊ธฐ์ˆ ํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ฟผ๋ฆฌ์…‹์œผ๋กœ ๋‹ค๋ฃจ๋Š” ๋ฐฉ์‹์—์„œ ์„ธ๋ถ€ ์ฟผ๋ฆฌ๋ฌธ์„ ์ถ”์ƒํ™”ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ฐœ์ „ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ์ž๋Š” ORM์—์„œ ์ œ๊ณตํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ์ผ๋ฐ˜์ ์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ˜ธ์ถœํ•˜๋“ฏ DB์— ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ •

DBeaver์™€ MySql๋ฅผ ์ด์šฉํ•ด์„œ ๊ฐœ๋ฐœ ํ•œ๋‹ค.

ORM ๊ฐ€๋Šฅํ•œ ๊ธฐ๋Šฅ์ค‘์— TypeORM ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

@nestjs/typeorm ํŒจํ‚ค์ง€์—์„œ ์ œ๊ณตํ•˜๋Š” TypeOrmModule์„ ์ด์šฉํ•˜์—ฌ DB์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

...
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
        ...
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'test',
      database: 'test',
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: true,
    }),
  ],
})
export class AppModule {}

 

- type: TypeOrmModule์ด ๋‹ค๋ฃจ๊ณ ์ž ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ํƒ€์ž…์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” MySQL์„ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค.

- host: ์—ฐ๊ฒฐํ•  ํ…Œ์ดํ„ฐ๋ฒ ์ด์Šค ํ˜ธ์ŠคํŠธ ์ฃผ์†Œ ์ž…๋ ฅ

- port: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์—ฐ๊ฒฐ์„ ์œ„ํ•ด ์—ด์–ด๋†“์€ ํฌํŠธ๋ฒˆํ˜ธ์ž…๋‹ˆ๋‹ค. MySQL์€ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ 3306๋ฒˆ ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

- username, password : ๋น„๋ฒˆ ๋ฐ ์•„์ด๋””

- database: ์—ฐ๊ฒฐํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค. ์•ž์„œ ๋งŒ๋“  test ์Šคํ‚ค๋งˆ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

- entities: ์†Œ์Šค ์ฝ”๋“œ ๋‚ด์—์„œ TypeORM์ด ๊ตฌ๋™๋  ๋•Œ ์ธ์‹ํ•˜๋„๋ก ํ•  ์—”ํ‹ฐํ‹ฐ ํด๋ž˜์Šค์˜ ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

- synchronize: ์„œ๋น„์Šค ๊ตฌ๋™์‹œ ์†Œ์Šค์ฝ”๋“œ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ๋ฅผ ๋™๊ธฐํ™” ํ• ์ง€ ์—ฌ๋ถ€์ž…๋‹ˆ๋‹ค. ๋กœ์ปฌํ™˜๊ฒฝ์—์„œ ๊ตฌ๋™ํ• ๋•Œ๋Š” ๊ฐœ๋ฐœ์˜ ํŽธ์˜๋ฅผ ์œ„ํ•ด true๋กœ ํ•ฉ๋‹ˆ๋‹ค. ( ์†Œ์Šค ์ฝ”๋“œ ๋ณ€๊ฒฝํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ๋ณ€๊ฒฝ๋œ๋‹ค. )

 

โš ๏ธ synchronize ์˜ต์…˜์„ true๋กœ ํ•˜๋ฉด ์„œ๋น„์Šค๊ฐ€ ์‹คํ–‰๋˜๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์—ฐ๊ฒฐ๋  ๋•Œ ํ•ญ์ƒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ดˆ๊ธฐํ™” ๋˜๋ฏ€๋กœ ์ ˆ๋Œ€ ํ”„๋กœ๋•์…˜์—๋Š” true๋กœ ํ•˜์ง€ ๋งˆ์„ธ์š”!

 

TypeOrmModule.forRoot ํ•จ์ˆ˜์— ์ „๋‹ฌํ•˜๋Š” TypeOrmModuleOptions ๊ฐ์ฒด๋ฅผ ์ข€ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

export declare type TypeOrmModuleOptions = {
    retryAttempts?: number;
    retryDelay?: number;
    toRetry?: (err: any) => boolean;
    autoLoadEntities?: boolean;
    keepConnectionAlive?: boolean;
    verboseRetryLog?: boolean;
} & Partial<ConnectionOptions>;
  • retryAttempts: ์—ฐ๊ฒฐ์‹œ ์žฌ์‹œ๋„ ํšŒ์ˆ˜. ๊ธฐ๋ณธ๊ฐ’์€ 10์ž…๋‹ˆ๋‹ค.
  • retryDelay: ์žฌ์‹œ๋„ ๊ฐ„์˜ ์ง€์—ฐ ์‹œ๊ฐ„. ๋‹จ์œ„๋Š” ms์ด๊ณ  ๊ธฐ๋ณธ๊ฐ’์€ 3000์ž…๋‹ˆ๋‹ค.
  • toRetry: ์—๋Ÿฌ๊ฐ€๊ฐ€ ๋‚ฌ์„ ๋•Œ ์—ฐ๊ฒฐ์„ ์‹œ๋„ํ•  ์ง€ ํŒ๋‹จํ•˜๋Š” ํ•จ์ˆ˜. ์ฝœ๋ฐฑ์œผ๋กœ ๋ฐ›์€ ์ธ์ž err ๋ฅผ ์ด์šฉํ•˜์—ฌ ์—ฐ๊ฒฐ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  • autoLoadEntities: ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ž๋™ ๋กœ๋“œํ•  ์ง€ ์—ฌ๋ถ€.
  • keepConnectionAlive: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ข…๋ฃŒ ํ›„ ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•  ์ง€ ์—ฌ๋ถ€.
  • verboseRetryLog: ์—ฐ๊ฒฐ์„ ์žฌ์‹œ๋„ ํ•  ๋•Œ verbose ์—๋Ÿฌ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด์—ฌ์ค„ ์ง€ ์—ฌ๋ถ€. ๋กœ๊น…์—์„œ verbose ๋ฉ”์‹œ์ง€๋Š” ์ƒ์„ธ ๋ฉ”์‹œ์ง€๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

TypeOrmModuleOptions๋Š” ์œ„์—์„œ ์„ค๋ช…ํ•œ ํƒ€์ž…๊ณผ ConnectionOptions ํƒ€์ž…์˜ Partialํƒ€์ž…์„ ๊ต์ฐจ(&)ํ•œ ํƒ€์ž…์ž…๋‹ˆ๋‹ค. Partial ์ œ๋„ค๋ฆญ ํƒ€์ž…์€ ์„ ์–ธํ•œ ํƒ€์ž…์˜ ์ผ๋ถ€ ์†์„ฑ๋งŒ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ํƒ€์ž…์ž…๋‹ˆ๋‹ค. ๊ต์ฐจ ํƒ€์ž…์€ ๊ต์ฐจ์‹œํ‚จ ํƒ€์ž…์˜ ์†์„ฑ๋“ค์„ ๋ชจ๋‘ ๊ฐ€์ง€๋Š” ํƒ€์ž…์ž…๋‹ˆ๋‹ค. ConnectionOptions์˜ ์ •์˜๋ฅผ ๋ณด๋ฉด Nest๊ฐ€ ์ง€์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์–ด๋–ค ๊ฒƒ๋“ค์ธ์ง€ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. MySQL ์™ธ์—๋„ PostgreSQL, MsSQL, Oracle ๋“ฑ ๋ฟ ์•„๋‹ˆ๋ผ Native, Mongo, Amazon Aurora๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

ormconfig.json ํ™œ์šฉ

Nest๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์—ฐ๊ฒฐ๋Š” ๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์— ormconfig.jsonํŒŒ์ผ์ด ์žˆ๋‹ค๋ฉด TypeOrmModule.forRoot()์— ์˜ต์…˜ ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ฃผ์˜ํ•  ์ ์ด JSONํŒŒ์ผ์—๋Š” ์—”ํ‹ฐํ‹ฐ์˜ ๊ฒฝ๋กœ๋ฅผ __dirname์œผ๋กœ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋นŒ๋“œ ํ›„ ์ƒ์„ฑ๋˜๋Š” ๋””๋ ‰ํ† ๋ฆฌ์ด๋ฆ„์ธ dist๋ฅผ ๋ถ™์—ฌ์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ormconfig.json
{
  "type": "mysql",
  "host": "localhost",
  "port": 3306,
  "username": "root",
  "password": "test",
  "database": "test",
  "entities": ["dist/**/*.entity{.ts,.js}"],
  "synchronize": true
}
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    TypeOrmModule.forRoot(),
  ],
})
export class AppModule {}

ํšŒ์› ๊ฐ€์ž…์„ ์š”์ฒญํ•œ ์œ ์ €์˜ ์ •๋ณด ์ €์žฅํ•˜๊ธฐ

Nest๋Š” ๋ฆฌํผ์ง€ํ† ๋ฆฌ ํŒจํ„ด์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋Š” ๋ฐ์ดํ„ฐ ์›๋ณธ์— ์•ก์„ธ์Šคํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๋…ผ๋ฆฌ๋ฅผ ์บก์Šํ™”ํ•˜๋Š” ํด๋ž˜์Šค ๋˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ์ž…๋‹ˆ๋‹ค. ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋Š” ๊ณตํ†ต ๋ฐ์ดํ„ฐ ์•ก์„ธ์Šค ๊ธฐ๋Šฅ์— ์ง‘์ค‘ํ•ด ๋” ๋‚˜์€ ์œ ์ง€๊ด€๋ฆฌ๋ฅผ ์ œ๊ณตํ•˜๊ณ  ๋„๋ฉ”์ธ ๋ชจ๋ธ ๊ณ„์ธต์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์•ก์„ธ์Šคํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ์ˆ ์ด๋‚˜ ์ธํ”„๋ผ๋ฅผ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

  • user.entity.ts
import { Column, Entity, PrimaryColumn } from 'typeorm';

@Entity('User')
export class UserEntity {
  @PrimaryColumn()
  id: string;

  @Column({ length: 30 })
  name: string;

  @Column({ length: 60 })
  email: string;

  @Column({ length: 30 })
  password: string;

  @Column({ length: 60 })
  signupVerifyToken: string;
}

์„œ๋ฒ„๋ฅผ ๊ตฌ๋™ํ•˜๋ฉด DB ์— ํ…Œ์ด๋ธ”์ด ์ƒ์„ฑ๋œ๋‹ค. ๋™๊ธฐํ™” ์˜ต์…˜์„ true ๋กœ ํ•ด๋‘์—ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

ํŠธ๋žœ์žญ์…˜ ์ ์šฉ

TypeORM์—์„œ ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ 3๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

  • QueryRunner๋ฅผ ์ด์šฉํ•ด์„œ ๋‹จ์ผ DB ์ปค๋„ฅ์…˜ ์ƒํƒœ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๊ธฐ
  • transaction ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์„œ ์ด์šฉํ•˜๊ธฐ
  • @Transaction, @TransactionManager, @TransactionRepository ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ

QueryRunner ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

QueryRunner๋ฅผ ์ด์šฉํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์„ ์™„์ „ํžˆ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Connection  ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค.

...
import { Connection, ... } from 'typeorm';

@Injectable()
export class UsersService {
  constructor(
        ...
        private connection: Connection,
  ) { }
    ...
}

์ด์ œ Connection ๊ฐ์ฒด์—์„œ ํŠธ๋žœ์žญ์…˜์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ ์ €๋ฅผ ์ €์žฅํ•˜๋Š” ๋กœ์ง์— ํŠธ๋žœ์žญ์…˜์„ ๊ฑธ์–ด๋ด…์‹œ๋‹ค.

private async saveUserUsingQueryRunner(name: string, email: string, password: string, signupVerifyToken: string) {
  const queryRunner = this.connection.createQueryRunner();

  await queryRunner.connect();
  await queryRunner.startTransaction();

  try {
    const user = new UserEntity();
    user.id = ulid();
    user.name = name;
    user.email = email;
    user.password = password;
    user.signupVerifyToken = signupVerifyToken;

    await queryRunner.manager.save(user);

    // throw new InternalServerErrorException(); // ์ผ๋ถ€๋Ÿฌ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ ๋ณธ๋‹ค

    await queryRunner.commitTransaction();
  } catch (e) {
    // ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋กค๋ฐฑ
    await queryRunner.rollbackTransaction();
  } finally {
    // ์ง์ ‘ ์ƒ์„ฑํ•œ QueryRunner๋Š” ํ•ด์ œ์‹œ์ผœ ์ฃผ์–ด์•ผ ํ•จ
    await queryRunner.release();
  }
}

- ์ฃผ์ž…๋ฐ›์€ Connection ๊ฐ์ฒด์—์„œ QueryRunner๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

- QueryRunner์—์„œ DB์— ์—ฐ๊ฒฐ ํ›„ ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

- ์ •์ƒ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ–ˆ๋‹ค๋ฉด ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•˜์—ฌ ์˜์†ํ™”์‹œํ‚ต๋‹ˆ๋‹ค.

- DB ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ ํ›„ ์ปค๋ฐ‹์„ ํ•ด์„œ ์˜์†ํ™”๋ฅผ ์™„๋ฃŒํ•ฉ๋‹ˆ๋‹ค.

- ์ด ๊ณผ์ •์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ง์ ‘ ๋กค๋ฐฑ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

- finally๊ตฌ๋ฌธ์„ ํ†ตํ•ด ์ƒ์„ฑํ•œ QueryRunner ๊ฐ์ฒด๋ฅผ ํ•ด์ œํ•ฉ๋‹ˆ๋‹ค. ์ƒ์„ฑํ•œ QueryRunner๋Š” ํ•ด์ œ์‹œ์ผœ ์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

transaction ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์„œ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

transaction ๋ฉ”์†Œ๋“œ๋Š” EntityManager๋ฅผ ์ฝœ๋ฐฑ์œผ๋กœ ๋ฐ›์•„ ์‚ฌ์šฉ์ž๊ฐ€ ์–ด๋–ค ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด ์ค๋‹ˆ๋‹ค.

private async saveUserUsingTransaction(name: string, email: string, password: string, signupVerifyToken: string) {
  await this.connection.transaction(async manager => {
    const user = new UserEntity();
    user.id = ulid();
    user.name = name;
    user.email = email;
    user.password = password;
    user.signupVerifyToken = signupVerifyToken;

    await manager.save(user);

    // throw new InternalServerErrorException();
  })
}

@Transaction, @TransactionManager, @TransactionRepository ์ด์šฉ

โ‚ฉโ‚ฉ

๋งˆ์ด๊ทธ๋ ˆ์ด์…˜

๋งŒ์•ฝ ์ด์ „์— ์ €์žฅํ•ด ๋‘” ๋ฐ์ดํ„ฐ๊ฐ€ ํ˜„์žฌ์˜ ๋„๋ฉ”์ธ ๊ตฌ์กฐ์™€ ๋‹ค๋ฅด๋‹ค๋ฉด ๋ชจ๋“  ๋ฐ์ดํ„ฐ์˜ ๊ฐ’์„ ์ˆ˜์ •ํ•  ์ผ๋„ ์ƒ๊น๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ณผ์ • ์—ญ์‹œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.  ์‰ฝ๊ฒŒ ์ด์•ผ๊ธฐ ํ•˜๋ฉด ํ˜„์žฌ ์ž‘์„ฑ๋œ DB ์Šคํ‚ค๋งˆ์™€ ์‹ค์ œ DB ์Šคํ‚ค๋งˆ๋ฅผ syncํ•ด์ฃผ๋Š” ๊ฒƒ์ด๋‹ค

- ์ฒซ์งธ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์œ„ํ•œ SQL๋ฌธ์„ ์ง์ ‘ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค. 

- ๋งŒ์•ฝ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด ์ž˜๋ชป ์ ์šฉ๋˜์—ˆ๋‹ค๋ฉด ๋งˆ์ง€๋ง‰ ์ ์šฉํ•œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‚ฌํ•ญ์„ ๋˜๋Œ๋ฆฌ๋Š” ์ž‘์—…๋„ ๊ฐ„๋‹จํžˆ ๋ช…๋ น์–ด๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

- ๋ฌผ๋ก  ๋ฐ์ดํ„ฐ์˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋Š” ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด๋ผ๋ฉด ์›๋ณตํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•ด์•ผ ํ•˜๊ธฐ๋Š” ํ•ฉ๋‹ˆ๋‹ค. 

- ๋กค๋ง ์—…๋ฐ์ดํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด ์•„๋‹ˆ๋ผ๋ฉด ์ ์šฉํ•˜๊ธฐ ์ „์— DB๋ฅผ ๋ฐฑ์—…ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋ง์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค

- TypeORM ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์ด์šฉํ•˜๋ฉด ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ฝ”๋“œ๋ฅผ ์ผ์ •ํ•œ ํ˜•์‹์œผ๋กœ ์†Œ์Šค ์ €์žฅ์†Œ์—์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ณ€๊ฒฝ์ ์„ ์†Œ์Šค ์ฝ”๋“œ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค

- TypeORM ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์ ์šฉํ•˜์—ฌ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ด๋ ฅ์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์–ธ์ œ ์–ด๋–ค ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด ์ผ์–ด๋‚ฌ๋Š”์ง€๋ฅผ ํŠน์ • ํ…Œ์ด๋ธ”์— ๊ธฐ๋กํ•˜๊ณ , ํ•„์š”ํ•  ๊ฒฝ์šฐ ์ฒ˜์Œ๋ถ€ํ„ฐ ์ˆœ์„œ๋Œ€๋กœ ๋‹ค์‹œ ์ˆ˜ํ–‰ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฏธ๋“ค์›จ์–ด

Nest์˜ ๋ฏธ๋“ค์›จ์–ด๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ Express์˜ ๋ฏธ๋“ค์›จ์–ด์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. Express ๋ฌธ์„œ์—๋Š” ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๊ธฐ์ˆ ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์–ด๋–ค ํ˜•ํƒœ์˜ ์ฝ”๋“œ๋ผ๋„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์š”์ฒญ๊ณผ ์‘๋‹ต์— ๋ณ€ํ˜•์„ ๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์š”์ฒญ-์‘๋‹ต ์ฃผ๊ธฐ๋ฅผ ๋๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด next()๋กœ ํ˜ธ์ถœ ์Šคํƒ์ƒ ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด์—๊ฒŒ ์ œ์–ด๊ถŒ์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

๋ฏธ๋“ค์›จ์–ด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ž‘์—…๋“ค์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

  • ์ฟ ํ‚ค ํŒŒ์‹ฑ: ์ฟ ํ‚ค๋ฅผ ํŒŒ์‹ฑํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์šด ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•˜๋ฉด ๋ผ์šฐํ„ฐ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋งค๋ฒˆ ์ฟ ํ‚ค๋ฅผ ํŒŒ์‹ฑํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
  • ์„ธ์…˜ ๊ด€๋ฆฌ: ์„ธ์…˜ ์ฟ ํ‚ค๋ฅผ ์ฐพ๊ณ , ํ•ด๋‹น ์ฟ ํ‚ค์— ๋Œ€ํ•œ ์„ธ์…˜์˜ ์ƒํƒœ๋ฅผ ์กฐํšŒํ•ด์„œ ์š”์ฒญ์— ์„ธ์…˜ ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋‹ค๋ฅธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์„ธ์…˜ ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด ์ค๋‹ˆ๋‹ค.
  • ์ธ์ฆ/์ธ๊ฐ€: ์‚ฌ์šฉ์ž๊ฐ€ ์„œ๋น„์Šค์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋‹จ, Nest๋Š” ์ธ๊ฐ€๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ ๊ฐ€๋“œ(Guard)๋ฅผ ์ด์šฉํ•˜๋„๋ก ๊ถŒ์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ณธ๋ฌธ(body) ํŒŒ์‹ฑ: ๋ณธ๋ฌธ์€ POST/PUT ์š”์ฒญ์œผ๋กœ ๋“ค์–ด์˜ค๋Š” json ํƒ€์ž…๋ฟ ์•„๋‹ˆ๋ผ ํŒŒ์ผ ์ŠคํŠธ๋ฆผ๊ณผ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฐ์ดํ„ฐ๋ฅผ ์œ ํ˜•์— ๋”ฐ๋ผ ์ฝ๊ณ  ํ•ด์„ํ•œ ๋‹ค์Œ ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋„ฃ๋Š” ์ž‘์—…์„ ํ•ฉ๋‹ˆ๋‹ค. ์•ž์„œ ์ปจํŠธ๋กค๋Ÿฌ ์žฅ์—์„œ ๋ณด์•˜๋˜ ๋ณธ๋ฌธ์€ ์ด๋ ‡๊ฒŒ ๋ถ„์„๋œ ๊ฒฐ๊ณผ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฏธ๋“ค์›จ์–ด๋Š” ํ•จ์ˆ˜๋กœ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ NestMiddleware ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋“ค์–ด์˜จ ์š”์ฒญ์— ํฌํ•จ๋œ ์ •๋ณด๋ฅผ ๋กœ๊น…ํ•˜๊ธฐ ์œ„ํ•œ Logger๋ฅผ ๋ฏธ๋“ค์›จ์–ด๋กœ ๊ตฌํ˜„ํ•ด ๋ด…์‹œ๋‹ค.

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log('Request...');
    next();
  }
}

๋ฏธ๋“ค์›จ์–ด๋ฅผ ๋ชจ๋“ˆ์— ํฌํ•จ์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ•ด๋‹น ๋ชจ๋“ˆ์€ NestModule ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. NestModule์— ์„ ์–ธ๋œ configureํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

  • logger.middleware.ts
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { LoggerMiddleware } from './logger/logger.middleware';
import { UsersModule } from './users/users.module';

@Module({
  imports: [UsersModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer): any {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('/users');
  }
}

/users ๊ฒฝ๋กœ๋กœ ๋“ค์–ด์˜ค๋Š” ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•ด ๋ณด๋ฉด ์ฝ˜์†”์— Request... ์ด ์ฐํžˆ๋Š” ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

MiddlewareConsumer

์ด์ „ ์ฝ”๋“œ์—์„œ confiure ๋ฉ”์„œ๋“œ์— ์ธ์ž๋กœ ์ „๋‹ฌ๋œ MiddlewareConsumer ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด์„œ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์–ด๋””์— ์ ์šฉํ•  ์ง€ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. apply ๋ฉ”์†Œ๋“œ์˜ ์›ํ˜•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

excludeํ•จ์ˆ˜๋Š” ์˜ˆ์ƒํ•˜๋“ฏ์ด ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š์„ ๋ผ์šฐํŒ… ๊ฒฝ๋กœ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

...
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer): any {
    consumer
      .apply(LoggerMiddleware, Logger2Middleware)
      .exclude({ path: 'users', method: RequestMethod.GET },)
      .forRoutes(UsersController)
  }
}

์ „์—ญ์œผ๋กœ ์ ์šฉํ•˜๊ธฐ

ํ•จ์ˆ˜๋กœ ์ •์˜๋œ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ๋‹ค์‹œ ๋งŒ๋“ค์–ด ๋ด…์‹œ๋‹ค.

import { Request, Response, NextFunction } from 'express';

export function logger3(req: Request, res: Response, next: NextFunction) {
  console.log(`Request3...`);
  next();
};

๊ทธ๋ฆฌ๊ณ  main.ts์—์„œ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

import { logger3 } from './logger3/logger3.middleware';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(logger3);
  await app.listen(3000);
}
bootstrap();

exclude ์˜ต์…˜์„ ๋‹ค์‹œ ํ’€๊ณ  ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด logger3 ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ๋จผ์ € ์ ์šฉ๋˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ’ก ํ•จ์ˆ˜๋กœ ๋งŒ๋“  ๋ฏธ๋“ค์›จ์–ด์˜ ๋‹จ์ ์€ DI ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ์ฃผ์ž…๋ฐ›์•„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

JWT ( Guard )

- ์ธ์ฆ์€ ์š”์ฒญ์ž๊ฐ€ ์ž์‹ ์ด ๋ˆ„๊ตฌ์ธ์ง€ ์ฆ๋ช…ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

- ์ธ๊ฐ€(Authorization)๋Š” ์ธ์ฆ์„ ํ†ต๊ณผํ•œ ์œ ์ €๊ฐ€ ์š”์ฒญํ•œ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€๋ฅผ ํŒ๋ณ„ํ•˜๋Š” ๊ฒƒ์„ ๋งํ•ฉ๋‹ˆ๋‹ค

 

๋ฏธ๋“ค์›จ์–ด๋Š” ์‹คํ–‰ ์ปจํ…์ŠคํŠธ(ExecutionContext)์— ์ ‘๊ทผํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€๋“œ๋Š” ์‹คํ–‰ ์ปจํ…์ŠคํŠธ ์ธ์Šคํ„ด์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด ๋‹ค์Œ ์‹คํ–‰๋  ์ž‘์—…์„ ์ •ํ™•ํžˆ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ€๋“œ๋ฅผ ์ด์šฉํ•œ ์ธ๊ฐ€

๊ฐ€๋“œ๋Š” CanActivate ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    return this.validateRequest(request);
  }

  private validateRequest(request: any) {
    return true;
  }
}

์‹คํ–‰ ์ปจํ…์ŠคํŠธ

canActivate ํ•จ์ˆ˜๋Š” ExecutionContext ์ธ์Šคํ„ด์Šค๋ฅผ ์ธ์ž๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค. ExecutionContext๋Š” ArgumentsHost๋ฅผ ์ƒ์†๋ฐ›๋Š”๋ฐ, ์š”์ฒญ๊ณผ ์‘๋‹ต์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” http๋กœ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ์ œ๊ณตํ•˜๋Š” ํ•จ์ˆ˜ ์ค‘ switchToHttp() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ€๋“œ ์ ์šฉ

๊ฐ€๋“œ๋ฅผ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์•ž์„œ ์˜ˆ์™ธ ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ปจํŠธ๋กค๋Ÿฌ ๋ฒ”์œ„ ๋˜๋Š” ๋ฉ”์„œ๋“œ ๋ฒ”์œ„๋กœ ์ ์šฉํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด @UseGuards(AuthGuard) ์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. AuthGuard ์ธ์Šคํ„ด์Šค์˜ ์ƒ์„ฑ์€ Nest๊ฐ€ ๋งก์•„์„œ ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์—ฌ๋Ÿฌ ์ข…๋ฅ˜์˜ ๊ฐ€๋“œ๋ฅผ ์ ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์‰ผํ‘œ๋กœ ์ด์–ด ์„ ์–ธํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

@UseGuards(AuthGuard)
@Controller()
export class AppController {
  constructor(private readonly appService: AppService) { }

  @UseGuards(AuthGuard)
  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

๊ฐ€๋“œ๋ฅผ ์ด์šฉํ•œ ์ธ๊ฐ€ ์ฒ˜๋ฆฌ

์ปจํŠธ๋กค๋Ÿฌ์˜ “์œ ์ € ์ •๋ณด ์กฐํšŒ” ๊ตฌํ˜„์„ ๋‹ค์‹œ ์‚ดํŽด๋ด…์‹œ๋‹ค.

@Get(':id')
async getUserInfo(@Headers() headers: any, @Param('id') userId: string): Promise<UserInfo> {
  const jwtString = headers.authorization.split('Bearer ')[1];

  this.authService.verify(jwtString);

  return this.usersService.getUserInfo(userId);
}

ํ˜„์žฌ ๊ตฌํ˜„๋ฐฉ์‹์€ ํ—ค๋”์— ํฌํ•จ๋œ JWT ํ† ํฐ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๋Š” ๋กœ์ง์„ ๋ชจ๋“  ์—”๋“œํฌ์ธํŠธ์— ์ค‘๋ณต ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋งค์šฐ ๋น„ํšจ์œจ์ ์ด๊ณ  DRY ์›์น™์—๋„ ์œ„๋ฐฐ๋ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” Nest์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฐ€๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ์ด๋ฅผ ํ•ธ๋“ค๋Ÿฌ ์ฝ”๋“œ์—์„œ ๋ถ„๋ฆฌํ•ด ๋ด…์‹œ๋‹ค.

์ผ๋‹จ ์•ž์„œ ์˜ˆ์‹œ๋กœ ๋งŒ๋“  ๊ฒƒ๊ณผ ๊ฐ™์ด AuthGuard๋ฅผ ์ ์šฉํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

import { Request } from 'express';
import { Observable } from 'rxjs';
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { AuthService } from './auth/auth.service';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService) { }

  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    return this.validateRequest(request);
  }

  private validateRequest(request: Request) {
    const jwtString = request.headers.authorization.split('Bearer ')[1];

    this.authService.verify(jwtString);

    return true;
  }
}

validateRequest ๋ฉ”์„œ๋“œ์— ์•ž์„œ ์ปจํŠธ๋กค๋Ÿฌ์— ๊ตฌํ˜„๋œ ๋กœ์ง์„ ์˜ฎ๊ฒจ์™”์Šต๋‹ˆ๋‹ค. ๊ฐ€๋“œ๋Š” ์ค€๋น„๋˜์—ˆ์œผ๋‹ˆ AuthGuard๋ฅผ ์ ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ „์—ญ์œผ๋กœ ์ ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ํšŒ์›๊ฐ€์ž…, ๋กœ๊ทธ์ธ ๋“ฑ ์•ก์„ธ์Šค ํ† ํฐ ์—†์ด ์š”์ฒญํ•˜๋Š” ๊ธฐ๋Šฅ์€ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ํšŒ์› ์กฐํšŒ ์—”๋“œํฌ์ธํŠธ์—๋งŒ ์ ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋ถ„๋ฆฌ์‹œํ‚ค๊ณ , ๋ถ„๋ฆฌ๋œ ์ปจํŠธ๋กค๋Ÿฌ์— ์ ์šฉํ•ด๋„ ๋ฉ๋‹ˆ๋‹ค.

'BE > Node' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

Nest ๋งต  (0) 2022.07.28
Nest.js(5)  (0) 2022.07.02
๊ด€์  ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ(AOP)  (0) 2022.06.24
Nest.js(2)  (0) 2022.06.23
Nest.js(1)  (0) 2021.11.12

๋Œ“๊ธ€