[Nest.js] Guard와 Custom Decorator 및 Bearer Token Middleware 개선하기

궁금하면 500원·2024년 8월 16일
0

Nest.js의 Guard는 요청을 보호하고 특정 조건을 충족하는지 검사하기 위한 기능입니다.

주로 인증, 권한 부여 등의 보안 관련 로직을 처리하는 데 사용됩니다.

Guard는 요청이 컨트롤러나 라우트 핸들러에 도달하기 전에 실행되며, 요청을 통과시킬지 여부를 결정합니다.

1. AuthGuard 생성하는 방법

Nest.js에서 AuthGuard는 주로 사용자 인증을 처리하는 데 사용됩니다.

AuthGuard는 CanActivate 인터페이스를 구현해야 하며, true를 반환하면 요청을 허용하고, false를 반환하면 요청을 차단합니다.

예시: AuthGuard 구현

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

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

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

    // 로그인 여부 검사 (가령 JWT 또는 세션 인증)
    return !!user;
  }
}

이용 방법

컨트롤러에 @UseGuards 데코레이터를 적용하여 AuthGuard를 사용할 수 있습니다.

import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';

@Controller('profile')
export class ProfileController {
  @Get()
  @UseGuards(AuthGuard)
  getProfile() {
    return "This is a protected route.";
  }
}

2. Custom Decorator 생성하는 방법

Nest.js에서 데코레이터는 메타데이터를 사용해 특정 로직을 추가하거나 사용자 정의 데이터를 처리할 수 있습니다.

예를 들어, 요청의 특정 값을 추출하는 데 유용합니다.

예시: Custom Decorator 생성

여기서는 @User 데코레이터를 만들어 요청 객체에서 user 정보를 가져오도록 하겠습니다.

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const User = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user;
  },
);

이용 방법

컨트롤러에서 이 데코레이터를 사용하면, 자동으로 요청에서 user 정보를 추출할 수 있습니다.

import { Controller, Get } from '@nestjs/common';
import { User } from './user.decorator';

@Controller('profile')
export class ProfileController {
  @Get()
  getProfile(@User() user: any) {
    return user;
  }
}

3. Bearer Token Middleware에서 토큰 검증 개선 방법

Bearer 토큰을 검증할 때, 토큰 유효성을 체크하고, 유효한지 여부에 따라 사용자의 인증 상태를 결정할 수 있습니다.

이 과정에서 보통 jsonwebtoken 라이브러리를 활용해 JWT 토큰을 검증합니다.

예시: Bearer Token Middleware 생성 및 검증

1. Middleware 생성

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

@Injectable()
export class AuthMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const authHeader = req.headers['authorization'];
    if (authHeader) {
      const token = authHeader.split(' ')[1]; // Bearer Token 분리
      if (token) {
        try {
          // JWT 검증
          const decoded = jwt.verify(token, 'your-secret-key');
          req.user = decoded;
        } catch (err) {
          // 검증 실패 시 처리
          return res.status(401).json({ message: 'Invalid token' });
        }
      }
    }
    next();
  }
}

2.Middleware 등록

Middleware는 app.use를 통해 전역으로 등록하거나, 특정 경로에만 적용할 수 있습니다.

import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { AuthMiddleware } from './auth.middleware';
import { ProfileController } from './profile.controller';

@Module({
  controllers: [ProfileController],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(AuthMiddleware)
      .forRoutes({ path: 'profile', method: RequestMethod.GET });
  }
}

3.검증 로직 개선:

Bearer 토큰 검증을 개선하려면, 토큰의 만료 시간을 체크하거나, 리프레시 토큰 전략을 추가할 수 있습니다.

예를 들어

  • 만료 시간 체크: jwt.verify 함수는 토큰이 만료되면 TokenExpiredError를 던집니다.

  • 리프레시 토큰 처리: 토큰이 만료되면 클라이언트에게 리프레시 토큰을 발급하는 전략을 사용합니다.

토큰 만료 체크 예시

try {
  const decoded = jwt.verify(token, 'your-secret-key');
  req.user = decoded;
} catch (err) {
  if (err.name === 'TokenExpiredError') {
    return res.status(401).json({ message: 'Token expired' });
  }
  return res.status(401).json({ message: 'Invalid token' });
}

이러한 방식을 통해 AuthGuard, 커스텀 데코레이터, 그리고 Bearer 토큰 검증을 안전하고 효율적으로 구현하여 사이드 프로젝트에 적용하는 기회가 되었습니다.

profile
꾸준히, 의미있는 사이드 프로젝트 경험과 문제해결 과정을 기록하기 위한 공간입니다.

0개의 댓글