NestJs Chapter 2

yeopยท2022๋…„ 7์›” 13์ผ

Nest JS ์ •๋ฆฌ

๋ชฉ๋ก ๋ณด๊ธฐ
1/10

๐Ÿ”ท Modules

๋ชจ๋“ˆ์€ @Module() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ ์ฃผ์„์ด ๋‹ฌ๋ฆฐ ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค.
@Module() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” Nest๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์กฐ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
https://docs.nestjs.com/modules

Static Module

์–ด๋– ํ•œ ์„ค์ •๋„ ์ ์šฉ๋˜์–ด ์žˆ์ง€ ์•Š์€ ๋ชจ๋“ˆ

Dynamic Module

์„ค์ •์ด ์ ์šฉ๋˜์–ด ์žˆ๊ฑฐ๋‚˜ ์„ค์ •์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“ˆ

Global modules

์ฆ‰์‹œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ์ œ๊ณต์ž ์„ธํŠธ(์˜ˆ: ๋„์šฐ๋ฏธ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋“ฑ)๋ฅผ ์ œ๊ณตํ•˜๋ ค๋ฉด @Global() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋“ˆ์„ ์ „์—ญ์ ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

๋˜๋Š” forRoot์•ˆ์—์„œ global: true๋ฅผ ํ†ตํ•ด์„œ๋„ ์ „์—ญ ๋ชจ๋“ˆ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

return {
	global:true,
	module: JwtModule,
	providers: [JwtService],
	exports: [JwtService],
};

๐Ÿ”ท Standard providers

์•„๋ž˜ ์ฝ”๋“œ๋Š” providers: [CatsService]์˜ ์ถ•์•ฝํ˜•์ž…๋‹ˆ๋‹ค.

providers: [
{ provide: CatsService, useClass: CatsService },
];

useClass (Class providers)

provider์˜ ํƒ€์ž… (์ฃผ์ž…๋˜์•ผ ํ•  ์ธ์Šคํ„ด์Šค ํด๋ž˜์Šค ์ด๋ฆ„)
ํ”„๋กœ๋ฐ”์ด๋”๋กœ ์‚ฌ์šฉํ•  ํด๋ž˜์Šค

useValue (Value providers)

์ฃผ์ž…ํ•œ provider์˜ ์ธ์Šคํ„ด์Šค

๐Ÿ”ท MiddleWare

MiddleWare ์„ ์–ธ

Nestjs์—์„œ์˜ middleware๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ express์˜ middleware์™€ ๋™์ผํ•˜๋‹ค.
NestMiddleware๋ฅผ implementsํ•œ ํ›„ use ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด class๋กœ ๊ตฌํ˜„ํ•˜๊ฑฐ๋‚˜ ํ•จ์ˆ˜๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ express์—์„œ์™€ ๊ฐ™์ด, ๋์—๋Š” ํ•ญ์ƒ next๋ฅผ ํ˜ธ์ถœ ํ•ด์•ผํ•œ๋‹ค.

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

export function jwtMiddleware(req: Request, res: Response, next: NextFunction) {
  console.log(req.headers);
  next();
}

MiddleWare ์ ์šฉ

1. ๋ผ์šฐํ„ฐ ํŠน์ • ์ ์šฉ

forRoutes ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ๋ผ์šฐํ„ฐ์™€ RequestMethod๋ฅผ ํŠน์ •ํ•œ๋‹ค.
exclude ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ํŠน์ • ๋ผ์šฐํ„ฐ์™€ RequestMethod๋ฅผ ์ œ์™ธ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

export class AppModule implements NestModule{
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(jwtMiddleware).forRoutes({
      path: '/graphql',
      method: RequestMethod.POST,
    });
  }
}

2. ์ „์ฒด ์ ์šฉ

main.ts ํŒŒ์ผ์—์„œ app.use(jwtMiddleware)๋ฅผ ์„ ์–ธํ•˜๋ฉด ์•ฑ ์ „์ฒด์— ์ ์šฉ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ฒƒ์€ middleware๋ฅผ function์œผ๋กœ ์„ ์–ธํ–ˆ์„ ๊ฒฝ์šฐ์—๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค. class๋กœ ์„ ์–ธํ–ˆ์„ ๊ฒฝ์šฐ 1๋ฒˆ ๋ฐฉ๋ฒ•๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

๐Ÿ”ท Context

๊ฐ request์— ๋Œ€ํ•ด request context๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. context๊ฐ€ ํ•จ์ˆ˜๋กœ ์ •์˜๋˜๋ฉด ๊ฐ request๋งˆ๋‹ค ํ˜ธ์ถœ๋˜๊ณ  req ์†์„ฑ์— request ๊ฐ์ฒด๋ฅผ ๋ฐ›๋Š”๋‹ค.

context: ({ req }) => ({ user: req['user'] }),
  //app.module์— graphQlModule์˜ ์ธ์ž๋กœ ํ• ๋‹น

- Resolver์—์„œ์˜ ์‚ฌ์šฉ๋ฐฉ๋ฒ•

@Query((returns) => User)
  me(@Context() context) {
    if (!context.user) {
      return;
    } else {
      return context.user;
    }
  }

๐Ÿ”ท Guard

Authentication์„ ์œ„ํ•ด ํ•„์š”ํ•œ ํด๋ž˜์Šค์ด๋‹ค.
(๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์€ ์‚ฌ๋žŒ์€ post๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์—†๋„๋ก!!)
CanActivate๋ฅผ implementsํ•˜๋ฉฐ Boolean ํ˜•์‹์„ ๋ฆฌํ„ดํ•œ๋‹ค.

โ€ป ์ด ๊ณผ์ •์—์„œ rest context์™€ gql context์˜ ํ˜•์‹์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— GqlExecutionContext๋ฅผ ์‚ฌ์šฉํ•ด gql context๋กœ ๋ณ€ํ™˜์‹œ์ผœ์ค€๋‹ค.

โ“ resolver์—์„œ ๋ฐ”๋กœ @Context๋กœ context ๋ฐ›์•„์„œ gql context๋กœ ๋ฐ”๊พธ๊ณ  return true, false ์„ ์–ธํ•˜๋Š” ๊ฒƒ๊ณผ์˜ ์ฐจ์ด์ ?

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext) {
    const gqlContext = GqlExecutionContext.create(context).getContext();
    const user = gqlContext['user'];
    if (!user) {
      return false;
    }
    return true;
  }
}

์ ์šฉ๋ฐฉ๋ฒ•

@Query((returns) => User)
  @UseGuards(AuthGuard)
  me() {}

๐Ÿ”ท Custom decorators

์„ ์–ธ๋ฐฉ๋ฒ•

createParamDecorator๋ฅผ ์‚ฌ์šฉํ•ด ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•œ๋‹ค.

// auth.decorator.ts ํŒŒ์ผ
export const AuthUser = createParamDecorator(
  (data: unknown, context: ExecutionContext) => {
    const gqlContext = GqlExecutionContext.create(context).getContext();
    const user = gqlContext['user'];
    return user;
  },
);

์ ์šฉ๋ฐฉ๋ฒ•

  • @Context๋ฅผ ์‚ฌ์šฉํ•œ context.user์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ•ด์ค€๋‹ค.
  • @Context๋ฅผ ๋ถˆ๋Ÿฌ ์˜ฌ ๋•Œ๋งˆ๋‹ค gqlContext๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ์–ด์•ผํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์„ ๋œ์–ด์ค€๋‹ค.
@Query((returns) => User)
  @UseGuards(AuthGuard)
  me(@AuthUser() authUser: User) {
    return authUser;
  }

๐Ÿ”ถ QnA

Dynamic module์„ ์ •์˜ํ•  ๋•Œ providers์˜ provide๊ฐ€ ์˜๋ฏธํ•˜๋Š” ๊ฒƒ์€?

@Module({})
@Global()
export class JwtModule {
  static forRoot(options: JwtModuleOptions): DynamicModule {
    return {
      module: JwtModule,
      exports: [JwtService],
      providers: [
        {
          provide: CONFIG_OPTIONS,
          useValue: options,
        },
        JwtService,
      ],
    };
  }
}

โ›” Error

- JsonWebTokenError: jwt malformed

verify ํ•  ์ˆ˜ ์—†๋Š” ์ž˜๋ชป๋œ token์„ ์คฌ์„ ๊ฒฝ์šฐ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ

์‹ค์ œ ํ”„๋กœ๊ทธ๋žจ ์ƒ์—์„œ๋Š” ์ด๋ฏธ ํ”„๋กœ๊ทธ๋žจ์—์„œ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” token์„ ์‚ฌ์šฉํ•ด์„œ verifyํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•  ์ˆ˜ ์—†๋Š” ์—๋Ÿฌ์ด๋‹ค.
/playground์—์„œ testํ•  ๋•Œ๋งŒ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ

0๊ฐœ์˜ ๋Œ“๊ธ€