Passport
Passport는 Node.js 생태계에서 가장 널리 사용되는 인증(Authentication) 라이브러리 중 하나로, 이미 수많은 실무 애플리케이션에서 검증된 안정성과 확장성을 가지고 있다. NestJS에서는 @nestjs/passport 모듈을 통해 Passport를 프레임워크에 자연스럽게 통합할 수 있으며, 이를 통해 인증 로직을 Nest의 Guard, Strategy, Provider 구조에 맞게 표준화할 수 있다. Passport의 가장 큰 장점은 다양한 인증 방식을 전략(Strategy)이라는 개념으로 분리해 제공한다는 점이다. JWT, Local, OAuth 등 여러 인증 메커니즘을 동일한 패턴으로 처리할 수 있으며, @nestjs/passport는 이러한 Passport의 동작 방식을 NestJS 스타일로 래핑하여 일관되고 유지보수하기 쉬운 인증 구조를 만들 수 있게 해준다.
npm install 패키지
npm i @nestjs/passport passport passport-jwt
@nestjs/passport
- AuthGuard('jwt'), PassportStrategy 제공
- 실제 인증 로직은 없음
passport
- Passport 코어 엔진
- 전략을 실행하고 흐름을 관리함
passport-jwt
- JWT 인증 전략
- 토큰 추출, 서명 검증, 만료 체크 수행
- Strategy, ExtractJwt 제공
JwtStrategy 와 JwtAuthGuard
깜짝 놀랄정도로 아주 편하다. 왜 passport를 쓰라는지 느낌이온다. 본 포스트도 공식문서를 따라가면서 정리중이다.
https://docs.nestjs.com/recipes/passport#implementing-passport-jwt
간단히 단계를 설명하자면
- JwtStrategy 생성 - 공식문서 셋팅 그대로
- JwtAuthGuard 생성
- UseGuards() 데코레이터 이용해서 사용 - 끝
src/auth/에 작업을 이어 나가보자 기존에 있던 Guard는 삭제했다.
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { jwtConstants } from './constants';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: jwtConstants.secret,
});
}
// 성공하면 reqeust객체에 user로 넣어줌
async validate(payload: any) {
return { id: payload.uuid, role: payload.role };
}
}
- jwtFromReqeust: Authorization에서 추출해서 Bearer 와 토큰 분리해서 작업을 알아서 함
- ignoreExpriation: 기본적으로 false 해두라고 함
- secretOrKey: 우리가 jwt 발급하면서 만들어둔 시크릿키 넣으면 됨
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtGuard extends AuthGuard('jwt') {}
그리고 JwtAuthGuard를 하나 만들고 위와 같이 셋팅한다. 공식문서와 똑같다.
이 작업만 해주면 Authorization에서 뽑아서 토큰검사를 하고, 정상이면 다음 스텝 비정상이면 401 에러가 나온다.
역할 기반 검증 추가하기
커스텀 데코레이터를 하나 만든다. 이 데코레이터는 해당 라우터에 들어가는 역할을 검증하는 데코레이터이다.
import { Reflector } from '@nestjs/core';
export const Roles = Reflector.createDecorator<string[]>();
역할기반 가드를 하나 만들어야한다.
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';
import { Roles } from './roles.decorator';
@Injectable()
export class RoleGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const roles = this.reflector.getAllAndOverride(Roles, [
context.getHandler(),
context.getClass(),
]);
if (!roles) {
return true;
}
// 권한 확인하기
const { user } = context.switchToHttp().getRequest();
return roles.includes(user.role);
}
}
이제 다음 부분은 특히 신중하게 설계해야 한다. 이번 구조에서는 가드를 전역으로 등록하고, JwtAuthGuard가 먼저 실행된 뒤 RoleGuard가 이어서 실행되는 흐름을 사용한다. JwtAuthGuard는 요청에 포함된 토큰을 검증하고, 검증에 성공하면 사용자 정보를 request.user에 담는다. 그 다음 RoleGuard에서는 요청 경로와 request.user에 담긴 역할 정보를 기준으로 접근 가능 여부를 판단하여 true 또는 false를 반환한다.
여기까지 구현하면 인증과 인가에 대한 기본적인 셋업은 완료된 셈이다. 하지만 여기서 한 가지 더 고민해야 할 부분이 있다. JwtAuthGuard를 전역으로 활성화한 상태에서는 로그인과 회원가입 요청도 모두 가드를 타게 된다는 점이다.로그인은 토큰을 발급받기 전 단계이기 때문에, 이 요청까지 JWT 검증을 요구하면 로그인 자체가 불가능해진다. 따라서 실제 서비스에서 JwtAuthGuard -> RoleGuard 흐름은 유지하되, 로그인과 회원가입 같은 인증 이전 단계의 엔드포인트는 예외로 통과시켜주는 처리가 반드시 필요하다. 이를 위해 @Public() 같은 메타데이터를 활용해 특정 경로에서는 JwtAuthGuard와 RoleGuard를 건너뛰도록 설계하는 것이 일반적인 접근 방식이다.
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { jwtConstants } from './constants';
import { JwtStrategy } from './jwt.strategy';
import { APP_GUARD } from '@nestjs/core';
import { JwtAuthGuard } from './jwt.auth.guard';
import { RoleGuard } from './role.guard';
@Module({
imports: [
JwtModule.register({
global: true,
secret: jwtConstants.secret,
signOptions: { expiresIn: '30m' }, //30분
}),
],
providers: [
// 순서대로 가드 켜짐
{ provide: APP_GUARD, useClass: JwtAuthGuard },
{ provide: APP_GUARD, useClass: RoleGuard },
AuthService,
JwtStrategy,
],
exports: [AuthService],
})
export class AuthModule {}
Public() 데코레이터 생성하기
import { SetMetadata } from '@nestjs/common';
export const IS_PUBLIC = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC, true);
생성 한 후 @Public()이면 토큰 검사를 패스해줘야 하기 때문에 JwtAuthGuard로 간다.
import { ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { Observable } from 'rxjs';
import { IS_PUBLIC } from './public.decorator';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
constructor(private readonly reflector: Reflector) {
super();
}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) {
return true;
}
return super.canActivate(context);
}
}
참고
https://docs.nestjs.com/recipes/passport
Documentation | NestJS - A progressive Node.js framework
Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Rea
docs.nestjs.com
'NestJS > 개발' 카테고리의 다른 글
| 실시간 채팅 서버 개발 - 01 (Redis 및 NestJS 셋팅) (1) | 2026.01.14 |
|---|---|
| Redis/BullMQ 이용하여 연산 작업 따로하기 (0) | 2025.12.23 |
| JWT - 역할 기반 관리하기 (1) | 2025.12.21 |
| JWT - 토큰 활용하기 (0) | 2025.12.21 |
| JWT - 로그인 후 발급하기 (1) | 2025.12.21 |
