Skip to content

Latest commit

ย 

History

History
516 lines (388 loc) ยท 15.8 KB

README.md

File metadata and controls

516 lines (388 loc) ยท 15.8 KB

NestJS 101

1. nestjs cli

nestjs cli ๋ฅผ ์ด์šฉํ•˜๋ฉด ์‰ฝ๊ฒŒ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑ ํ• ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

# nestjs cli ์„ค์น˜
$ npm i -g @nestjs/cli
# ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
$ nest new project-name

2. OpenAPI ์„ค์ •

๋‹ค์Œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

$ npm install --save @nestjs/swagger swagger-ui-express

์ดํ›„ main.ts์— SwaggerModule ์„ค์ •์„ ํ•ฉ๋‹ˆ๋‹ค

# main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestExpressApplication } from "@nestjs/platform-express";
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';

async function bootstrap() {

  const app = await NestFactory.create<NestExpressApplication>(AppModule);

  const options = new DocumentBuilder()
    .setTitle('ํ”„๋กœ์ ํŠธ ๋ช…')
    .setDescription('ํ”„๋กœ์ ํŠธ ์„ค๋ช…')
    .setVersion('1.0')
    .build();
  const document = SwaggerModule.createDocument(app, options);
  SwaggerModule.setup('docs', app, document);

  await app.listen(process.env.PORT);
}
bootstrap();

request ์š”์ฒญ์— ๋Œ€ํ•œ Validation(๊ฒ€์ฆ) ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ๋‹ค์Œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์„ค์น˜๊ฐ€ ํ•„์š” ํ•ฉ๋‹ˆ๋‹ค.

$ npm install class-validator

Global ์„ค์ •์„ main.ts์— ์ถ”๊ฐ€ ํ•ฉ๋‹ˆ๋‹ค. Transform payload objects ์„ค์ •์„ ํ•˜์—ฌ ์ž๋™ ๋ณ€ํ™˜ ์ฒ˜๋ฆฌ๋ฅผ ํ•ฉ๋‹ˆ๋‹ค.

# main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestExpressApplication } from "@nestjs/platform-express";
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {

  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  app.useGlobalPipes(new ValidationPipe({transform: true})); // Validate with ์ž๋™ ๋ณ€ํ™˜ ์ฒ˜๋ฆฌ
  const options = new DocumentBuilder()
    .setTitle('ํ”„๋กœ์ ํŠธ ๋ช…')
    .setDescription('ํ”„๋กœ์ ํŠธ ์„ค๋ช…')
    .setVersion('1.0')
    .build();
  const document = SwaggerModule.createDocument(app, options);
  SwaggerModule.setup('docs', app, document);

  await app.listen(process.env.PORT);
}
bootstrap();

์ดํ›„ ์˜ˆ์ œ๋Š” auto-validation์—์„œ ํ™•์ธํ•˜์„ธ์š”.

express์—์„œ๋Š” dotenv๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋Š”๋ฐ, nestjs์—์„œ๋Š” Configuration์ด ์ œ๊ณต ๋ฉ๋‹ˆ๋‹ค. ์„ค์น˜๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

$ npm i --save @nestjs/config

์‚ฌ์šฉ๋ฒ•์€ app.module.ts์— ์„ ์–ธ์„ ํ•˜์—ฌ์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. isGlobal ์„ค์ •์„ ํ•ด๋‘๋ฉด ๋‹ค๋ฅธ ๋ชจ๋“ˆ์—์„œ imports ํ•˜์ง€ ์•Š๊ณ  ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ .env ํŒŒ์ผ์„ ์ฝ์–ด์„œ ๋ณ€์ˆ˜ํ™” ํ•˜์—ฌ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';

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

๋” ์ž์„ธํ•œ ์˜ˆ์ œ๋Š” Using the ConfigService๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

nestjs์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ Logger class๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. main.js์—์„œ ApplicationModule์—์„œ logger ์˜ต์…˜์„ ์ด์šฉํ•ด์„œ ๋ ˆ๋ฒจ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ ˆ๋ฒจ์€ 'log', 'error', 'warn', 'debug', 'verbose' ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

const app = await NestFactory.create(ApplicationModule, {
  logger: ['error', 'warn'],
});
await app.listen(3000);

์‚ฌ์šฉ๋ฒ•์€ Using the logger for application logging ๊ฐ™์ด ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

import { Logger, Injectable } from '@nestjs/common';

@Injectable()
class MyService {
  private readonly logger = new Logger(MyService.name);

  doSomething() {
    this.logger.log('Doing something...');
  }
}

์„ธ์…˜์€ express๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ express-session ๋ชจ๋“ˆ์„ ์„ค์น˜ํ•ด์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

$ npm i express-session

์‚ฌ์šฉ๋ฒ•์€ main.ts์— ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.(์ด๋Š” express-session ์‚ฌ์šฉ๋ฒ•์„ ์ž์„ธํžˆ ๋ณด์„ธ์š”.)

import * as session from 'express-session';
// somewhere in your initialization file
app.use(
  session({
    secret: 'my-secret',
    resave: false,
    saveUninitialized: false,
  }),
);

6. helmet & cors ์„ค์ •

helmet ๊ณผ cors ์„ค์ •์€ ๋‹ค๋ฅธ ์„ค์ • ํ•จ์ˆ˜ ๋ณด๋‹ค ๋จผ์ € ์„ค์ •๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๊ฒฝ๋กœ๋ฅผ ์ •์˜ํ•œ ํ›„ helmet๊ณผ cors๋ฅผ ์„ค์ •ํ•  ๊ฒฝ์šฐ ์ด๋ฏธ ์„ค์ •๋œ ๊ฒฝ๋กœ์˜ ๋ฏธ๋“ค์›จ์–ด๋Š” ์ ์šฉ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

helmet์€ http ํ•ด๋”๋ฅผ ์ ์ •ํ•˜๊ฒŒ ์„ค์ •ํ•˜์—ฌ ์›น ์ทจ์•ฝ์ ์œผ๋กœ ๋ถ€ํ„ฐ ์•ฑ์„ ๋ณดํ˜ธ ํ•ฉ๋‹ˆ๋‹ค. ์„ค์น˜

$ npm i --save helmet

์‚ฌ์šฉ๋ฒ•

import * as helmet from 'helmet';
// somewhere in your initialization file
app.use(helmet());

cors๋Š” ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์—์„œ ๋ฆฌ์†Œ์Šค๋ฅผ ์š”์ฒญ ํ•  ์ˆ˜ ์žˆ๋„๋กํ•˜๋Š” ์„ค์ •์ž…๋‹ˆ๋‹ค. ์„ค์น˜๋Š” ๋”ฐ๋กœ ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

const app = await NestFactory.create(AppModule);
app.enableCors();
await app.listen(3000);

main.ts ์˜ˆ์ œ

๋‹ค์Œ์€ ์ œ๊ฐ€ ์‚ฌ์šฉํ•œ ์˜ˆ์ œ ์ž…๋‹ˆ๋‹ค.

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestExpressApplication } from "@nestjs/platform-express";
import * as helmet from 'helmet';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { ValidationPipe } from '@nestjs/common';
import * as session from 'express-session';
import * as passport from 'passport';
import flash = require('connect-flash');

async function bootstrap() {

  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  app.setGlobalPrefix('api'); // prefix ์„ค์ •
  app.useGlobalPipes(new ValidationPipe({ transform: true })); // validate ์‚ฌ์šฉ ์„ค์ •
  app.use(helmet({
    contentSecurityPolicy: false,
  })); // helmet ์„ค์ •๊ณผ CSP ์ œ์™ธ (google analytics ์‚ฌ์šฉ์‹œ ์ œ์™ธ ํ•ด์•ผํ•จ)
  app.enableCors({
    origin: [
      /^(.*)/,
    ],
    methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
    preflightContinue: false,
    credentials: true,
    optionsSuccessStatus: 204,
    allowedHeaders:
      'Origin,X-Requested-With,Content-Type,Accept,Authorization,authorization,X-Forwarded-for',
  }); // cors ์„ค์ • credentials ์„ค์ •์„ ํ•ด์•ผ credentials ์ •๋ณด๋„ ํ•จ๊ป˜ ์ „๋‹ฌํ•จ

  const options = new DocumentBuilder()
    .setTitle('ํ”„๋กœ์ ํŠธ ๋ช…')
    .setDescription('ํ”„๋กœ์ ํŠธ ์„ค๋ช…')
    .setVersion('1.0')
    .addBearerAuth() // openapi ๋ฌธ์„œ์—์„œ ๊ถŒํ•œ ์ฒ˜๋ฆฌ ์ถ”๊ฐ€
    .build();
  const document = SwaggerModule.createDocument(app, options);
  SwaggerModule.setup('docs', app, document); // openapi ์‚ฌ์šฉ

  app.use(session({
    secret: process.env.SECCRET || 'keyboard cat',
    resave: false,
    saveUninitialized: false,
  })); // session ์‚ฌ์šฉ
  // passport ์„ค์ •
  app.use(passport.initialize()); 
  app.use(passport.session());
  // request์— ๊ฐ’์„ ์ถ”๊ฐ€ ํ•˜๋Š” flash ์ถ”๊ฐ€ 
  app.use(flash());

  await app.listen(process.env.PORT);
}
bootstrap();

Update ๋‚ด์—ญ

๋ชจ๋“ˆ ์ตœ์‹ ํ™” ์—…๋ฐ์ดํŠธ (2024.07.8)

  • nestjs 10.3.0
  • ์ „๋ฐ˜์ ์ธ ๋ชจ๋“ˆ ์—…๋Žƒ
  • npm => pnpm ๋ชจ๋“ˆ์„ ์ด์šฉํ•œ ์„ค์น˜๋กœ ๋ณ€๊ฒฝ

firebase ์—ฐ๊ณ„ (2023.07.19)

src/common/firebase ์ดํ•˜ firebase ์—ฐ๊ณ„ ์ฒ˜๋ฆฌ ์ถ”๊ฐ€

  1. firebase ์—์„œ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ ํ›„ ์ง„ํ–‰
  2. firebase.config.json ํŒŒ์ผ์ด src/common/firebase ๋””๋ ‰ํ† ๋ฆฌ์— ์กด์žฌ ํ•ด์•ผ ํ•จ
  3. http://localhost:3000/firebaseInfo API ํ˜ธ์ถœ์‹œ header์— authorization ์ถ”๊ฐ€ํ•ด์„œ ํ˜ธ์ถœ์‹œ firebase ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ํ™•์ธ

module update ์ ์šฉ (2023.03.16)

์ฃผ์š” ๋ณ€๊ฒฝ ๋‚ด์—ญ์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • nestjs 9.3.x ์ ์šฉ
  • typeorm 0.3.x ์ ์šฉ

nestjs-pino ์ ์šฉ (2023.01.17)

๋กœ๊น…์‹œ ์š”์ฒญ ์ •๋ณด๋ฅผ ํ•จ๊ป˜ ํ‘œ์‹œํ•˜์—ฌ, ๊ฐ ๋กœ๊ทธ๋ณ„ ์—ฐ๊ฒฐ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ƒ์„ธ๋‚ด์šฉ

์ถ”๊ฐ€ ์„ค์น˜ ๋ชจ๋“ˆ ์ •๋ณด

throttler-ratelimiting ์ฒ˜๋ฆฌ (2022.09.16)

sequenceDiagram;
    autonumber;
    actor User;
    loop 60์ดˆ ๋™์•ˆ 4๊ฐœ ์ œํ•œ
        User->>+ valid: ๊ณ„์ • ํ™•์ธ;
        valid -->>- User: ์„ฑ๊ณต;
    end;
Loading
  • @nestjs/throttler

    throttler ๋ชจ๋“ˆ์„ ๋ณ€๊ฒฝํ•ด์„œ ๋กœ๊ทธ์ธ ๊ณ„์ • ๊ธฐ์ค€์œผ๋กœ ํŠน์ • ์‹œ๊ฐ„๋™์•ˆ ์š”์ฒญ ์ œํ•œ ์ฒ˜๋ฆฌ

  • ์ถ”๊ฐ€ ๋ฐ ๋ณ€๊ฒฝ ์†Œ์Šค ๋ชฉ๋ก
    • src/app.module.ts : throttler ๋ชจ๋“ˆ ์„ค์ •
    • src/app.controller.ts : ์‚ฌ์šฉ ์˜ˆ์ œ(valid method)
    • src/common/core/throttler.guard.ts : guard ์„ค์ •
    • src/common/core/throttler-storage-redis.service.ts_b : redis๋ฅผ storage ์‚ฌ์šฉ์‹œ ์˜ˆ์ œ

chaching ์ฒ˜๋ฆฌ (2022.09.11)

graph LR;
    request[request] -->|URI| get{GET};
    get --> |Y| nocache{NoCache};
    get --> |N| callP(next);
    nocache --> |Y| callng(next);
    nocache --> |N| hascache{HasCache};
    hascache --> |N| callg(next);
    callg --> savecache(SaveCache);
    hascache --> |Y| getcache(GetCache);
    callP --> hasevict{hasEvict};
    hasevict --> |Y| keys(getKeys);
    hasevict --> |N| uri(uri);
    keys --> removecache(removeCache);
    uri --> removecache;
    removecache --> return[RETURN];
    callng --> return;
    getcache --> return;
    savecache --> return;
Loading
  • httpcache.interceptor.ts

    URL ๊ธฐ์ค€์œผ๋กœ http method๊ฐ€ get ์ผ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ ๊ฐ’์„ ์ €์žฅ ์ฒ˜๋ฆฌํ•˜๋ฉฐ post, delete, put ๋“ฑ์ด ํ˜ธ์ถœ ๋  ๊ฒฝ์šฐ URL ๊ธฐ์ค€์œผ๋กœ ๊ธฐ์กด ์บ์‹œ๋ฅผ ์‚ญ์ œ

  • cache.decorator.ts

@NoCache ์บ์‹œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ @CacheEvict ์บ์‹œ ์‚ญ์ œ์‹œ ๋‹ค๋ฅธ url ํ•จ๊ป˜ ์ฒ˜๋ฆฌ

@nestjs/config๋ฅผ ํ™œ์šฉํ•œ ์„ค์ •ํŒŒ์ผ ๊ด€๋ฆฌ (2022.09.15)

src/common/config ์— ๊ฐ ํ•ญ๋ชฉ์˜ ์„ค์ • ํŒŒ์ผ์„ ์ ์žฌํ›„ ์‚ฌ์šฉ

  • logging
  • database
    • SnakeNamingStrategy ์ถ”๊ฐ€
    • QueryLogger ์ถ”๊ฐ€

@nestjs/config๋ฅผ ์ด์šฉํ•œ ์ƒ์ˆ˜์ฒ˜๋ฆฌ (2022.04.15)

.env ์—์„œ ํ•„์š”ํ•œ ์ƒ์ˆ˜๋ฅผ ๋กœ๋“œํ•˜๋Š” ์ฒ˜๋ฆฌ

  • logger์— ๋Œ€ํ•œ ์ƒ์ˆ˜ ์ฒ˜๋ฆฌ
  • typeorm์— ๋Œ€ํ•œ ์ƒ์ˆ˜ ์ฒ˜๋ฆฌ

์‚ฌ์šฉ ์˜ˆ์ œ

src/app.module.ts ํŒŒ์ผ์—์„œ forRootAsync๊ณผ useFactory์„ ์ด์šฉํ•˜์—ฌ .env ํŒŒ์ผ์˜ ์ƒ์ˆ˜๋ฅผ ํ™œ์šฉ

...
 TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (configService: ConfigService) => ({
        type: 'sqlite',
        database: configService.get('DB_HOST'),
        dropSchema: configService.get('DB_DROP') === 'true',
        entities: ['dist/**/*.entity{.ts,.js}'],
        synchronize: configService.get('DB_SYNC') === 'true',
        logging: configService.get('DB_LOGGING') === 'true',
        logger: 'file',
      }),
    }),
....

winston + winston-daily-rotate-file ์ถ”๊ฐ€ (2022.04.14)

์ถ”๊ฐ€ ๋ชจ๋“ˆ ์„ค์น˜

  • winston : winston
  • nest-winston : nestjs์—์„œ winstion ์‚ฌ์šฉ ์ฒ˜๋ฆฌ
  • winston-daily-rotate-file : ๋‚ ์งœ๋ณ„ ํŒŒ์ผ ์ƒ์„ฑ ๋ฐ ๊ด€๋ฆฌ ์ง€์›
# nestjs์—์„œ winston ๊ณผ ๊ด€๋ จ๋œ ๋ชจ๋“ˆ ์ถ”๊ฐ€ 
$ npm i nest-winston winston winston-daily-rotate-file

์‚ฌ์šฉ ์˜ˆ์ œ

main.js์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ •ํ•˜์—ฌ ์‚ฌ์šฉ

import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';

...

const app = await NestFactory.create(AppModule, {
    logger: WinstonModule.createLogger({
      format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.json(),
      ),
      transports: [
        new winston.transports.Console(),
        new (require('winston-daily-rotate-file'))({
          dirname: path.join(__dirname, './../logs/debug/'), // ํŒŒ์ผ ์ €์žฅ ์œ„์น˜
          filename: 'debug-%DATE%.log', // ํŒŒ์ผ ๋ช…
          datePattern: 'YYYY-MM-DD-HH', // ํŒŒ์ผ๋ช…์˜ ๋‚ ์งœ(DATE) ํŒจํ„ด
          level: 'debug', // ๋กœ๊ทธ ๋ ˆ๋ฒจ
          zippedArchive: true, //์••์ถ• ์—ฌ๋ถ€
          maxSize: '20m', // ํ•œ๊ฐœ์˜ ํŒŒ์ผ ์ตœ๋Œ€ ํฌ๊ธฐ
          maxFiles: '14d' // ํŒŒ์ผ์˜ ์ตœ๋Œ€ ์œ ์ง€ ๋‚ ์งœ
        }),
        new (require('winston-daily-rotate-file'))({
          dirname: path.join(__dirname, './../logs/info/'),
          filename: 'info-%DATE%.log',
          datePattern: 'YYYY-MM-DD-HH',
          level: 'info',
          zippedArchive: true,
          maxSize: '20m',
          maxFiles: '14d'
        }),
      ],
    })
  });

...  

ACCESS ๋กœ๊น… middleware ์ถ”๊ฐ€(2022.04.11)

src/common/middleware/AppLoggerMiddleware.ts ํŒŒ์ผ ์ถ”๊ฐ€ ๋ฐ src/app.module.ts์— ๋‹ค์Œ ๋‚ด์šฉ ์ถ”๊ฐ€

// src/common/middleware/AppLoggerMiddleware.ts
import { Request, Response, NextFunction } from "express";
import { Injectable, NestMiddleware, Logger } from "@nestjs/common";

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  private logger = new Logger("HTTP");

  use(request: Request, response: Response, next: NextFunction): void {
    const { ip, method, originalUrl } = request;
    const userAgent = request.get("user-agent") || "";

    response.on("finish", () => {
      const { statusCode } = response;
      const contentLength = response.get("content-length");

      this.logger.log(
        `${method} ${originalUrl} ${statusCode} ${contentLength} - ${userAgent} ${ip}`,
      );
      if (method !== 'GET') {
        this.logger.debug(request.body);
      }
    });

    next();
  }
}

// src/app.module.ts
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer): void {
    consumer.apply(AppLoggerMiddleware).forRoutes('*');
  }
}

ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ถ”๊ฐ€(2021.06.23)

ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

user.controller.spec.ts ํŒŒ์ผ์„ ๊ธฐ์ค€์œผ๋กœ End to End ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์ „์ฒด ํ…Œ์ŠคํŠธ ์‹คํ–‰

npm run test

ํŠน์ • ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์‹คํ–‰

node node_modules/jest/bin/jest.js src/user/user.controller.spec.ts

๋กœ๊ทธ์ธ & DB ์—ฐ๊ฒฐ ์˜ˆ์ œ ์ถ”๊ฐ€(2021.06.17)

  • typeorm + sqlite ์กฐํ•ฉ์˜ ์‚ฌ์šฉ์ž CRUD ๊ตฌํ˜„
  • passport + local strategy๋ฅผ ์ด์šฉํ•œ ๋กœ๊ทธ์ธ/๋กœ๊ทธ์•„์›ƒ ์ฒ˜๋ฆฌ
    • test-auth-chapter-sample๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ๊ตฌํ˜„ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

auth ๋ชจ๋“ˆ ์•„๋ž˜์— ๋กœ๊ทธ์ธ ๊ด€๋ จ ์„ค์ •์ด ์žˆ์Šต๋‹ˆ๋‹ค.

common ๋””๋ ‰ํ† ๋ฆฌ ์•„๋ž˜์— ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ filters / guards ๊ฐ€ ์ถ”๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

user ๋ชจ๋“ˆ ์•„๋ž˜์— ์‚ฌ์šฉ์ž ์ •๋ณด CRUD ๊ตฌํ˜„ ์˜ˆ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ƒ์„ธํ•œ ์˜ˆ์ œ๋Š” ๋‹ค์Œ 2๊ฐœ์˜ ํ”„๋กœ์ ํŠธ๋ฅผ ์ฐธ๊ณ  ํ•˜์„ธ์š”.

  1. nestjs-realworld-example-app
  2. test-auth-chapter-sample

์ฐธ๊ณ ์ž๋ฃŒ