
NestJS에서 Passport 모듈은 인증 방식을 Strategy(전략)라는 플러그인 형태로 분리하여 사용합니다. 우리는 로그인 시점과 로그인 후 인증이 필요한 모든 요청에 대해 두 가지 핵심 전략을 구현해야 합니다.
1. 🛡️ Local Strategy 구현 (로그인)
Local Strategy는 사용자 이름(또는 이메일)과 비밀번호를 서버에서 검증하여 사용자를 인증하는 데 사용됩니다. 이는 주로 POST /auth/login 엔드포인트에서 사용됩니다.
1.1. 파일 구조 및 상속
@nestjs/passport에서 제공하는 PassportStrategy 헬퍼 클래스를 사용하여 기본 passport-local 라이브러리의 Strategy를 상속받습니다.
// src/auth/strategies/local.strategy.ts
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from '../auth.service';
@Injectable()
// 💡 PassportStrategy(Strategy)를 상속받아 LocalStrategy 정의
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
// 💡 super()를 통해 Local Strategy 기본 설정 전달
super({
// 💡 사용자 이름 필드를 'email'로 사용하도록 지정 (기본값은 'username')
usernameField: 'email',
});
}
// 💡 핵심 검증 메서드
// Passport가 호출하며, 클라이언트가 보낸 email과 password가 인자로 전달됨
async validate(email: string, password: string): Promise<any> {
// 1. AuthService를 통해 DB에서 사용자 검증 로직 수행
const user = await this.authService.validateUser(email, password);
// 2. 검증 실패 시:
if (!user) {
throw new UnauthorizedException('제공된 자격 증명이 유효하지 않습니다.');
}
// 3. 검증 성공 시:
// 💡 반환된 user 객체는 Local Guard가 실행된 후
// 요청 객체(req)의 'user' 속성에 할당됩니다 (req.user = user).
return user;
}
}
1.2. Local Guard를 통한 사용
컨트롤러에서 AuthGuard('local')을 사용하면 NestJS가 내부적으로 LocalStrategy의 validate() 메서드를 실행시킵니다.
// src/auth/auth.controller.ts (로그인 엔드포인트)
import { AuthGuard } from '@nestjs/passport';
// ...
@UseGuards(AuthGuard('local')) // 💡 LocalStrategy 실행 지시
@Post('login')
async login(@Request() req) {
// LocalStrategy가 성공적으로 반환한 user 객체가 req.user에 담겨 있습니다.
return this.authService.login(req.user); // 💡 JWT 토큰을 생성하고 반환
}
2. 🔑 JWT Strategy 구현 (인증)
JWT Strategy는 클라이언트가 로그인 후 요청 헤더에 담아 보낸 Access Token을 검증하는 데 사용됩니다. 이는 인증이 필요한 모든 API 엔드포인트에서 사용됩니다.
2.1. 파일 구조 및 설정
@nestjs/passport의 PassportStrategy를 사용하여 passport-jwt 라이브러리의 Strategy를 상속받습니다. JWT 전략은 super() 생성자에서 토큰 추출 방식과 비밀 키를 설정하는 것이 중요합니다.
// src/auth/strategies/jwt.strategy.ts
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
// 💡 PassportStrategy(Strategy)를 상속받아 JWTStrategy 정의
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private configService: ConfigService) {
super({
// 💡 1. 토큰 추출 방법: Authorization 헤더의 Bearer 스킴에서 추출
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
// 💡 2. 서명 무시 여부: false로 설정하여, 서버가 토큰 서명을 검증하도록 함
ignoreExpiration: false,
// 💡 3. 비밀 키: 환경 변수에서 JWT 비밀 키를 가져와 서명 검증에 사용
secretOrKey: configService.get<string>('JWT_SECRET'),
});
}
// 💡 핵심 검증 메서드
// 토큰이 유효하고 서명이 일치하면, 복호화된 Payload가 인자로 전달됨
async validate(payload: any) {
// 1. Payload를 통해 추가 DB 조회 (선택 사항)
// 예: 토큰 Payload의 userId를 이용해 DB에서 최신 사용자 정보를 다시 가져올 수 있음
// 2. 💡 최종적으로 반환된 객체는 JWT Guard가 실행된 후
// 요청 객체(req)의 'user' 속성에 할당됩니다 (req.user = { userId: 1, email: '...' }).
return { userId: payload.sub, email: payload.email };
}
}
2.2. JWT Guard를 통한 사용
인증된 사용자만 접근할 수 있는 라우트에 AuthGuard('jwt')를 적용합니다.
// src/users/users.controller.ts
import { AuthGuard } from '@nestjs/passport';
// ...
@UseGuards(AuthGuard('jwt')) // 💡 JwtStrategy 실행 지시
@Get('profile')
getProfile(@Request() req) {
// JwtStrategy에서 반환된 Payload 정보가 req.user에 담겨 있습니다.
return `사용자 ID: ${req.user.userId}, 이메일: ${req.user.email}`;
}
3. 🥇 Passport 전략의 이점
두 전략을 분리하여 사용함으로써 NestJS는 인증/인가 로직을 모듈화하고, 각 라우트의 성격에 따라 필요한 인증 레벨(로그인 또는 인증됨)을 명확하게 적용할 수 있습니다.
'Nest.js를 배워보자 > 7. NestJS 인증 시스템 구축 — JWT & Passport 실전' 카테고리의 다른 글
| Role 기반 권한 관리 (Role-Based Access Control, RBAC) (0) | 2025.12.02 |
|---|---|
| WT 인증 시스템 구축: Refresh Token 패턴 (0) | 2025.12.02 |
| NestJS 인증 시스템 구축: JWT & Passport 실전 (0) | 2025.12.02 |